我有一个脚本可以遍历所有订单历史记录。打印结果需要几分钟,但我注意到我执行了几个足够相似的 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/>";
}
这样的事情可能吗?它更快吗?我现有的方法又慢又丑,我想要一种更快的方法来做到这一点。
我们可以对表进行一次遍历以获取所有用户的所有三个聚合:
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
列将允许完全从索引中满足查询。(使用status
列上的相等谓词,我们还可以尝试使用status
作为前导列,然后是user
列,然后是total
.
如果我们只需要一小部分用户的结果,例如我们只从第一个查询中获取前 10 行,那么运行单独的查询可能会更快。我们只是将条件WHERE s.user = :user
合并到查询中,就像在原始代码中一样。但是只运行一个查询而不是三个单独的查询。
我们可以将它与第一个查询结合起来,将其放入内联视图中,将其包装在括号中并将其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
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句