让 SQL 查询更快更好

Posted

技术标签:

【中文标题】让 SQL 查询更快更好【英文标题】:Make SQL query faster and better 【发布时间】:2017-03-26 21:44:50 【问题描述】:

我有一个案例,我需要从包含大量聚合数据的表中收集销售统计数据。该表有几百万个条目,数据库本身约为 22Gb。大量数据可供使用!

我有这个有效的查询,但我觉得我应该做得更好 - 而且它也很慢。

我在 CentOS 7 上使用带有 sqlsrv 扩展的 php 连接到 MSSQL 数据库(ERP 系统)。

我在这个查询中的 php 变量是这样设置的:

$customer = '12345';
$year = '2016';
$compared_year = '2015';

所以,正如您可能已经猜到的那样,我正在收集 2016 年和 2015 年每个月的给定客户的销售统计数据。

SELECT
ProdTr.CustNo AS customernummer,

(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $year . "01' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS sum_period_1,
(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $year . "02' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS sum_period_2,
(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $year . "03' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS sum_period_3,
(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $year . "04' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS sum_period_4,
(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $year . "05' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS sum_period_5,
(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $year . "06' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS sum_period_6,
(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $year . "07' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS sum_period_7,
(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $year . "08' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS sum_period_8,
(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $year . "09' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS sum_period_9,
(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $year . "10' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS sum_period_10,
(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $year . "11' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS sum_period_11,
(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $year . "12' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS sum_period_12,

(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $year . "01' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS dg_period_1,
(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $year . "02' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS dg_period_2,
(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $year . "03' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS dg_period_3,
(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $year . "04' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS dg_period_4,
(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $year . "05' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS dg_period_5,
(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $year . "06' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS dg_period_6,
(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $year . "07' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS dg_period_7,
(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $year . "08' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS dg_period_8,
(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $year . "09' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS dg_period_9,
(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $year . "10' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS dg_period_10,
(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $year . "10' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS dg_period_11,
(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $year . "11' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS dg_period_12,

(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "01' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_sum_period_1,
(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "02' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_sum_period_2,
(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "03' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_sum_period_3,
(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "04' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_sum_period_4,
(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "05' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_sum_period_5,
(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "06' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_sum_period_6,
(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "07' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_sum_period_7,
(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "08' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_sum_period_8,
(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "09' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_sum_period_9,
(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "10' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_sum_period_10,
(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "11' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_sum_period_11,
(SELECT SUM(DAm) AS 'Sum' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "12' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_sum_period_12,

(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "01' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_dg_period_1,
(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "02' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_dg_period_2,
(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "03' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_dg_period_3,
(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "04' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_dg_period_4,
(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "05' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_dg_period_5,
(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "06' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_dg_period_6,
(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "07' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_dg_period_7,
(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "08' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_dg_period_8,
(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "09' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_dg_period_9,
(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "10' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_dg_period_10,
(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "11' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_dg_period_11,
(SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM(DAm),0)) AS 'DG' FROM ProdTr WHERE AcYrPr = '" . $compared_year . "12' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') AS compared_dg_period_12

FROM ProdTr

WHERE AcYrPr LIKE 201612
AND ProdTr.TrTp = 1
AND ProdTr.CustNo = '" . $customer . "'

GROUP BY ProdTr.CustNo, ProdTr.TrTp, ProdTr.AcYrPr

请注意,查询运行良好,给出了我期望的结果 - 但对于有很多订单/交易的客户来说,它太慢了。

关于如何加快速度的任何指示?

非常感谢您的宝贵时间!

【问题讨论】:

你看过 GROUP BY 吗?即使你不能在一个查询中运行整个事情,你也可以在一个查询中完成,比如说,四个并将结果放入一个关联数组中。 【参考方案1】:

您的查询应该使用条件聚合:

SELECT ProdTr.CustNo AS customernummer,
       SUM(CASE WHEN AcYrPr = '" . $year . "01' THEN DAm ELSE 0 END) as sum_period_1,
       SUM(CASE WHEN AcYrPr = '" . $year . "02' THEN DAm ELSE 0 END) as sum_period_2,
       . . .
WHERE AcYrPr LIKE 201612 AND ProdTr.TrTp = 1 AND
      ProdTr.CustNo = '" . $customer . "'
GROUP BY ProdTr.CustNo, ProdTr.TrTp;

子查询应该不是必需的。

【讨论】:

谢谢!这在从 GROUP BY 子句中删除 ProdTr.AcYrPr 时效果很好 请问您如何放置 (SELECT (((SUM(DAm)-(SUM(StcCst)*-1))*100)/NULLIF(SUM( DAm),0)) 作为 ProdTr 的“DG”,其中 AcYrPr = '" . $year . "01' AND ProdTr.TrTp = 1 AND ProdTr.CustNo = '" . $customer . "') 作为 dg_period_1,在这种情况下句法?非常感谢! @MagnusAlexander 。 . .我错过了group by 别担心 :) 非常感谢!

以上是关于让 SQL 查询更快更好的主要内容,如果未能解决你的问题,请参考以下文章

哪个sql查询更快?

SQL查询性能调优--如何使查询更快

数据集与Sql查询(选择,过程,函数)哪个更好用?

让我的查询更短、更快、更好

连接多个大表时如何进行更快的查询?

MongoDB:运行更快的查询,这更好 Pymongo 或 MongoEngine