我经常遇到需要比较来自同一来源的不同时期的汇总数据的情况。
我通常以这种方式处理它:
SELECT
COALESCE(SalesThisYear.StoreId, SalesLastYear.StoreId) StoreId
, SalesThisYear.Sum_Revenue RevenueThisYear
, SalesLastYear.Sum_Revenue RevenueLastYear
FROM
(
SELECT StoreId, SUM(Revenue) Sum_Revenue
FROM Sales
WHERE Date BETWEEN '2017-09-01' AND '2017-09-30'
GROUP BY StoreId
) SalesThisYear
FULL JOIN (
SELECT StoreId, SUM(Revenue) Sum_Revenue
FROM Sales
WHERE Date BETWEEN '2016-09-01' AND '2016-09-30'
GROUP BY StoreId
) SalesLastYear
ON (SalesLastYear.StoreId = SalesThisYear.StoreId)
-- execution time 337 ms
在我看来,它不是很优雅,因为它访问了两次表,但它确实有效。
另一种实现相同目的的方法是:
SELECT
Sales.StoreId
, SUM(CASE YEAR(Date) WHEN 2017 THEN Revenue ELSE 0 END) RevenueThisYear
, SUM(CASE YEAR(Date) WHEN 2016 THEN Revenue ELSE 0 END) RevenueLastYear
FROM
Sales
WHERE
Date BETWEEN '2017-09-01' AND '2017-09-30'
or Date BETWEEN '2016-09-01' AND '2016-09-30'
GROUP BY
StoreId
-- execution time 548 ms
两个解决方案在我的数据集上执行的几乎相同(所选时段中的1,929,419行,其所有位置的索引),第一个在时间上稍好一些。如果我包含更多句点并不重要,第一个句点在我的数据集上总是更好。
这只是一个简单的例子,但有时它涉及两个以上的间隔甚至一些逻辑(例如比较isoweek / weekday而不是月/日,比较不同的商店等)。
虽然我已经找到了几种方法来实现同样的目标,但我想知道是否有一种聪明的方法来实现同样的目标。也许更清洁的解决方案,或更适合大数据集(超过TB)。
例如,我认为第二个对大数据集的资源密集程度较低,因为它对表进行单个索引扫描。另一方面,第一个需要两个Index Scans和一个Merge。如果桌子太大而不适合记忆,会发生什么?或者第一个总是更好?
答案 0 :(得分:0)
很少有这种做事方式总是更好,特别是当他们做非常相似的事情时。
然而,我建议您尝试尽可能利用最佳实践,例如尽量减少查询中标量函数的使用,因为这会抑制索引的使用。
例如,通过将您的第二个查询更改为以下内容,我认为您至少会看到一些改进性能:
SELECT
Sales.StoreId
, SUM(CASE WHEN Date BETWEEN '2017-09-01' AND '2017-09-30' THEN Revenue ELSE 0 END) RevenueThisYear
, SUM(CASE WHEN Date BETWEEN '2016-09-01' AND '2016-09-30' THEN Revenue ELSE 0 END) RevenueLastYear
FROM
Sales
WHERE
Date BETWEEN '2017-09-01' AND '2017-09-30'
or Date BETWEEN '2016-09-01' AND '2016-09-30'
GROUP BY
StoreId
答案 1 :(得分:0)
第二个看起来更好。但我猜年份部分正在减慢查询速度。让我们把这一年拿出去。 2017-01-01将在今年范围内更大(' 2017-09-01' AND' 2017-09-30')并且去年范围更少(' 2016-09-01' AND' 2016-09-30')。
SELECT
Sales.StoreId
, SUM(CASE WHEN date > 2017-01-01 THEN Revenue ELSE 0 END) RevenueThisYear
, SUM(CASE WHEN date < 2017-01-01 THEN Revenue ELSE 0 END) RevenueLastYear
FROM
Sales
WHERE
Date BETWEEN '2017-09-01' AND '2017-09-30'
or Date BETWEEN '2016-09-01' AND '2016-09-30'
GROUP BY
StoreId
如果完全加入效果很好,那就试试吧。
SELECT
COALESCE(SalesThisYear.StoreId, SalesLastYear.StoreId) StoreId
, sum(SalesThisYear.Revenue) RevenueThisYear
, sum(SalesLastYear.Revenue) RevenueLastYear
FROM Sales SalesThisYear full join
Sales SalesLastYear
ON SalesLastYear.StoreId = SalesThisYear.StoreId
WHERE SalesThisYear.Date BETWEEN '2017-09-01' AND '2017-09-30'
AND SalesLastYear.Date BETWEEN '2016-09-01' AND '2016-09-30'
GROUP BY COALESCE(SalesThisYear.StoreId, SalesLastYear.StoreId)
修改*
SELECT Sales.StoreId
, SUM(CASE WHEN date > '2017-01-01' THEN Revenue ELSE 0 END) RevenueThisYear
, SUM(CASE WHEN date < '2017-01-01' THEN Revenue ELSE 0 END) RevenueLastYear
FROM
(Select store_id, date, revenue
from Sales
WHERE Date BETWEEN '2017-09-01' AND '2017-09-30'
or Date BETWEEN '2016-09-01' AND '2016-09-30') q
GROUP BY StoreId