对现有sql结果执行查询?从sql结果的子集查找结果

raogr8fs  于 2021-06-21  发布在  Mysql
关注(0)|答案(2)|浏览(293)

我有一个贯穿所有订单历史的脚本。打印结果需要几分钟,但是我注意到我执行了几个非常相似的sql语句,我想知道您是否可以对现有的sql结果执行另一个查询。
例如:

-- first SQL request
SELECT * FROM orders
WHERE status = 'shipped'

然后,在foreach循环中,我想从这个结果中找到信息。我天真的方法是执行这三个查询。请注意与上述查询的相似性。

-- grabs customer's LTD sales
SELECT SUM(total) FROM orders
WHERE user = :user
AND status = 'shipped'    

-- grabs number of orders customer has made
SELECT COUNT(*) FROM orders
WHERE user = :user
AND status = 'shipped'
AND total != 0

-- grabs number of giveaways user has won
SELECT COUNT(*) FROM orders
WHERE user = :user
AND status = 'shipped'
AND total = 0

当我搜索的结果是第一个查询的子集时,我会多次查询同一个表。我希望在不执行更多sql调用的情况下从第一个查询中获取信息。一些伪代码:

$stmt1 = $db->prepare("
    SELECT * FROM orders
    WHERE status = 'shipped'
");
$stmt1->execute();

foreach($stmt1 as $var) {
    $username = $var['username'];

    $stmt2 = $stmt1->workOn("
        SELECT SUM(total) FROM this
        WHERE user = :user
    ");
    $stmt2->execute(array(
        ':user' => $username
    ));
    $lifesales = $stmt2->fetchColumn();

    $stmt3 = $stmt1->workOn("
        SELECT COUNT(*) FROM this
        WHERE user = :user
        AND total != 0
    ");
    $stmt3->execute(array(
        ':user' => $username
    ));
    $totalorders = $stmt3->fetchColumn();

    $stmt4 = $stmt1->workOn("
        SELECT COUNT(*) FROM this
        WHERE user = :user
        AND total = 0
    ");
    $stmt4->execute(array(
        ':user' => $username
    ));
    $totalgaws = $stmt4->fetchColumn();

    echo "Username: ".$username;
    echo "<br/>Lifetime Sales: ".$lifesales;
    echo "<br/>Total Orders: ".$totalorders;
    echo "<br/>Total Giveaways: ".$totalgaws;
    echo "<br/><br/>";
}

这样的事情可能吗?速度快吗?我现有的方法是缓慢和丑陋的,我想一个更快的方法来做这件事。

rt4zxlrg

rt4zxlrg1#

我们可以对表进行一次遍历,以获得所有用户的所有三个聚合:

SELECT s.user
     , SUM(s.total)        AS `ltd_sales`
     , SUM(s.total <> 0)   AS `cnt_prior_sales`
     , SUM(s.total  = 0)   AS `cnt_giveaways`
  FROM orders s
 WHERE s.status = 'shipped'
 GROUP
    BY s.user

大套的话会很贵的。但是,如果我们对所有订单、所有用户都需要这样做,那么这可能比单独进行相关子查询要快。
前导列为的索引 user 将允许mysql使用 GROUP BY 操作。包括 status 以及 total 索引中的列将允许完全通过索引满足查询(等式 predicate 在 status 列,我们还可以尝试使用 status 作为第一列,后面是 user 列,然后后跟 total .
如果我们只对一小部分用户需要这个结果,例如,我们只从第一个查询中提取前10行,那么运行一个单独的查询可能会更快。我们只需要合并条件 WHERE s.user = :user 进入查询,如在原始代码中。但是只运行一个查询,而不是三个单独的查询。
我们可以将其与第一个查询结合起来,方法是将其放入一个内联视图,将其 Package 在paren中并放入 FROM 子句作为行源

SELECT o.*

     , t.ltd_sales
     , t.cnt_prior_sale
     , t.cnt_giveaways

  FROM orders o 

  JOIN ( 
         SELECT s.user
              , SUM(s.total)        AS `ltd_sales`
              , SUM(s.total <> 0)   AS `cnt_prior_sales`
              , SUM(s.total  = 0)   AS `cnt_giveaways`
           FROM orders s
          WHERE s.status = 'shipped'
          GROUP
             BY s.user
      ) t
   ON t.user = o.user 

WHERE o.status = 'shipped'

我不太清楚那个名为“先前”销售的专栏。。。这是返回所有已发货订单,而不考虑比较任何日期(订单日期、履行日期、装运日期),我们通常会将其与“之前”的含义联系起来。
后续行动
注意到问题已被修改,删除条件“ status = 'shipped' “从用户所有订单的计数。。。
我会注意到我们可以从 WHERE 条件聚合中的子句。
并不是所有这些结果都是op需要的,但作为一个示范。。。

SELECT s.user
     , SUM(IF(s.status='shipped',s.total,0))       AS `ltd_sales_shipped`
     , SUM(IF(s.status<>'shipped',s.total,0))       AS `ltd_sales_not_shipped`

     , SUM(s.status='shipped' AND s.total <> 0)   AS `cnt_shipped_orders`
     , SUM(s.status='canceled')                   AS `cnt_canceled`

     , SUM(s.status='shipped' AND s.total  = 0)   AS `cnt_shipped_giveaways`
  FROM orders s
 GROUP
    BY s.user
9gm1akwq

9gm1akwq2#

一旦结果从数据库返回,就不能在其上运行sql。但是,您可以将它们存储在临时表中,以便重用它们。
https://dev.mysql.com/doc/refman/8.0/en/create-temporary-table.htmlhttpshttp://dev.mysql.com/doc/refman/8.0/en/create-table-select。htmlhttps://dev.mysql.com/doc/refman/8.0/en/insert-select.html
您需要创建一个临时表,并插入select语句中的所有数据,然后可以对该表运行查询。不知道这对你是否有帮助。
对于您的特殊情况,您可以执行以下操作:

select user, (total = 0) as is_total_zero, count(*), sum(total) 
from orders
where status = 'shipped'
group by user, total = 0

但是,您必须进行一些额外的求和,以获得第二个查询的结果,该查询将为您提供每个用户的求和,因为它们将被分为两个不同的组,具有不同的is\u total\u零值。

相关问题