我如何共享一些 WHERE 子句,但仍然为多个总和添加唯一的子句?

How can I share some WHERE clauses but still add unique ones for multiple sums?

我有一个 table 和一些简单的数字,我想在不同的日期范围内求和。示例:

date     | cost | storeId | stateId |
-------------------------------------
1/1/2021 | 100  | 1       |  1      |
1/1/2020 | 30   | 2       |  2      |

我想对不同时间跨度的成本求和,即现在 1 个月内、现在 2 个月内等记录成本的总和。我还有几个需要 JOIN 的条件。这导致我创建了一个大型查询,该查询重用相同的连接和 where 子句(日期范围除外)和 UNIONs:

SELECT SUM(cost)
FROM table1 t1
LEFT JOIN table2 t2 ON t2.id = t1.storeId
LEFT JOIN table3 t3 ON t3.id = t1.stateId
WHERE t1.date > '1monthAgo' t2.otherId = 1 AND t2.anotherId = 2 AND t3.someId = 1
UNION
SELECT SUM(cost)
FROM table1 t1
LEFT JOIN table2 t2 ON t2.id = t1.storeId
LEFT JOIN table3 t3 ON t3.id = t1.stateId
WHERE t1.date > '2monthsAgo' t2.otherId = 1 AND t2.anotherId = 2 AND t3.someId = 1
UNION
SELECT SUM(cost)
FROM table1 t1
LEFT JOIN table2 t2 ON t2.id = t1.storeId
LEFT JOIN table3 t3 ON t3.id = t1.stateId
WHERE t1.date > '3monthsAgo' t2.otherId = 1 AND t2.anotherId = 2 AND t3.someId = 1

在我的实际查询中,这些连接和 where 子句要复杂得多,使这个查询不仅一团糟,而且非常慢(约 4 秒)。我尝试用 CASE 语句重写它,现在有:

SELECT 
SUM(CASE t1.date > '1monthAgo' THEN t1.cost ELSE 0 END),
SUM(CASE t1.date > '2monthsAgo' THEN t1.cost ELSE 0 END),
SUM(CASE t1.date > '3monthsAgo' THEN t1.cost ELSE 0 END),
FROM table1 t1
LEFT JOIN table2 t2 ON t2.id = t1.storeId
LEFT JOIN table3 t3 ON t3.id = t1.stateId
WHERE t2.otherId = 1 AND t2.anotherId = 2 AND t3.someId = 1

这显示的数据略有不同(全部在一行中)但运行速度更快(1 秒)并且不会重复 JOIN 和 WHERE 子句。然而,我的真实查询在 6 个不同的日期范围内有 4 个不同的总和,因此有 24 个 CASE 语句,我认为这不是正确的方法。

是否可以有一个共享 JOIN 和 WHERE 子句但允许我查询不同日期范围以收集总和的查询?

一个选项是按 EOMONTH 分组,这样每个月都有一个日期,然后每个月都在单独的行中:

SELECT 
  MonthEnding = EOMONTH(t1.date),
  TotalCost   = SUM(t1.cost)
FROM table1 t1
LEFT JOIN table2 t2 ON t2.id = t1.storeId
LEFT JOIN table3 t3 ON t3.id = t1.stateId
WHERE t2.otherId = 1 AND t2.anotherId = 2 AND t3.someId = 1
GROUP BY EOMONTH(t1.date);

如果这些“月份”实际上与日历月份不一致,那么您可以加入 VALUES table 月份编号

SELECT 
  v.MonthsAgo,
  TotalCost   = SUM(t1.cost)
FROM table1 t1
LEFT JOIN table2 t2 ON t2.id = t1.storeId
LEFT JOIN table3 t3 ON t3.id = t1.stateId
JOIN (VALUES
  (CAST('20210521' AS date), CAST('20210621' AS date), 1),
  (CAST('20210421' AS date), CAST('20210521' AS date), 2),
  (CAST('20210321' AS date), CAST('20210421' AS date), 3)
) v(StartDate, EndDate, MonthsAgo)
  ON t1.date >= v.StartDate AND t1.date < v.EndDate
WHERE t2.otherId = 1 AND t2.anotherId = 2 AND t3.someId = 1
GROUP BY v.MonthsAgo;