SQL 针对一个 table 的多个 WHERE 子句,带有 CASE 语句
SQL Multiple WHERE Clauses against one table, with CASE statements
我写了这个查询,它似乎正在收集正确的结果,但是,它也需要很长时间。我只是想知道是否有办法让它更有效率?
(我知道它效率低下,因为它正在创建数据 tables 并将它们重新组合在一起 - 我只是不知道如何解决它,特别是包含 CASE 问题)。
我在 Excel 工作,使用 ODBC 连接到 AS400。问号允许用户在 Excel 个单元格中输入参数。
with W as
(
select yr as YEAR, pd as PERIOD, sum(amt1 + amt2 + amt3 + amt4) as SALES
from TABLE
group by yr, pd
order by yr desc, pd desc
), X as
(
select yr as YEAR, pd as PERIOD, sum(amt1 + amt2 + amt3 + amt4) as BSALES
from TABLE
where type = 'B'
group by yr, pd
order by yr desc, pd desc
), Y as
(
select
CASE WHEN pd = 1 THEN yr - 1 ELSE yr END as YEAR,
CASE WHEN pd = 1 THEN 12 ELSE pd - 1 END as PERIOD,
SUM(OM) as MODOM
from TABLE
group by yr, pd
order by yr desc, pd desc
), Z as
(
select
CASE WHEN pd = 1 THEN yr - 1 ELSE yr END as YEAR,
CASE WHEN pd = 1 THEN 12 ELSE pd - 1 END as PERIOD,
SUM(BOM) as BMODOM
from TABLE
where type = 'B'
group by yr, pd
order by yr desc, pd desc
)
select w.YEAR, w.PERIOD, w.SALES, x.BSales, y.MODOM, z.BMODOM
from w inner join x
on w.YEAR = x.YEAR and w.PERIOD = x.PERIOD
inner join y
on w.YEAR = y.YEAR and w.PERIOD = y.PERIOD
inner join z
on w.YEAR = z.YEAR and w.PERIOD = z.PERIOD
where w.YEAR between ? and ? and w.PERIOD between ? and ?
order by YEAR desc, PERIOD desc
出于隐私目的,我不得不稍微更改代码,但我相信这一切都是正确的。
示例数据:
Yr Pd Type Amt OM
18 2 A 45 181
18 2 B 33 163
18 2 A 40 153
18 1 B 39 136
18 1 B 24 142
18 1 B 53 143
18 1 A 41 186
18 1 A 78 197
17 12 A 98 139
17 12 A 54 159
17 12 B 78 181
17 12 B 45 101
17 11 A 28 134
17 11 A 77 192
17 11 A 75 110
17 11 B 60 135
17 11 B 83 170
17 10 B 72 114
17 10 A 26 118
17 10 A 95 111
17 9 A 12 112
17 9 B 14 171
示例结果
Yr Pd Sales Bsales MODOM BMODOM
18 2 118 33 804 421
18 1 235 116 580 282
17 12 275 123 741 305
17 11 323 143 343 114
17 10 193 72 283 171
请注意,我需要 TOTAL 级别的 SALES,然后是 TYPE B 级别的 SALES。我还需要总级别的 OM,然后还需要 B 类级别 - 但是 - 我需要抵消一个周期。因此,17-10 的 MODOM 反映了 table 期间 17-9 的 OM 总数。 (我希望这是有道理的)。
EDIT 我有这个落后。 17-10 的 MODOM 实际上会反映 17-11 的 OM 值,而不是相反。更正了预期结果。
Yr Pd Sales Bsales MODOM BMODOM
18 2 118 33 0 0
18 1 235 116 497 163
17 12 275 123 804 421
17 11 323 143 580 282
17 10 193 72 741 305
因此,使用条件聚合至少可以摆脱 2 个常见的 table 表达式,这会有所帮助。在您的最终演示查询之前不排序而不是在每个常见的 table 表达式中排序也会有所帮助。使用实际日期,例如First of First Month of a Period 可以帮助消除在定义上一期间时可能成本高昂的 case 表达式。
有几种写法,但这会给你一个条件聚合的例子。
并注意 LEFT JOIN 而不是 INNER,因为如果您使用 INNER
,您的第一个周期将始终放弃您的查询
WITH PeriodSales AS(
SELECT
yr as YEAR
,pd AS PERIOD
,SUM(amt) as SALES
,SUM(CASE WHEN type = 'B' THEN amt END) as BSALES
FROM
Table
GROUP BY
yr
,pd
)
, PreviousPeriod AS
(
SELECT
CASE WHEN pd = 12 THEN yr + 1 ELSE yr END as YEAR
,CASE WHEN pd = 12 THEN 1 ELSE pd + 1 END as PERIOD
,SUM(OM) as MODOM
,SUM(CASE WHEN type = 'B' THEN OM END) as BMODOM
FROM
Table
GROUP BY
CASE WHEN pd = 12 THEN yr + 1 ELSE yr END
,CASE WHEN pd = 12 THEN 1 ELSE pd + 1 END
)
SELECT
ps.YEAR
,ps.PERIOD
,ps.SALES
,ps.BSALES
,pp.MODOM
,pp.BMODOM
FROM
PeriodSales ps
LEFT JOIN PreviousPeriod pp
ON ps.YEAR = pp.YEAR
AND ps.PERIOD = pp.PERIOD
ORDER BY
ps.YEAR DESC
,ps.PERIOD DESC
根据您的编辑,要与 "Previous Period" 对齐以获得您想要的 OM 量,您实际上需要添加句点而不是在我使用的示例中减去句点。我已经测试过了,它确实有效。在不了解 table 和执行计划等的情况下,我们无法发现许多其他影响性能的因素。
我写了这个查询,它似乎正在收集正确的结果,但是,它也需要很长时间。我只是想知道是否有办法让它更有效率? (我知道它效率低下,因为它正在创建数据 tables 并将它们重新组合在一起 - 我只是不知道如何解决它,特别是包含 CASE 问题)。
我在 Excel 工作,使用 ODBC 连接到 AS400。问号允许用户在 Excel 个单元格中输入参数。
with W as
(
select yr as YEAR, pd as PERIOD, sum(amt1 + amt2 + amt3 + amt4) as SALES
from TABLE
group by yr, pd
order by yr desc, pd desc
), X as
(
select yr as YEAR, pd as PERIOD, sum(amt1 + amt2 + amt3 + amt4) as BSALES
from TABLE
where type = 'B'
group by yr, pd
order by yr desc, pd desc
), Y as
(
select
CASE WHEN pd = 1 THEN yr - 1 ELSE yr END as YEAR,
CASE WHEN pd = 1 THEN 12 ELSE pd - 1 END as PERIOD,
SUM(OM) as MODOM
from TABLE
group by yr, pd
order by yr desc, pd desc
), Z as
(
select
CASE WHEN pd = 1 THEN yr - 1 ELSE yr END as YEAR,
CASE WHEN pd = 1 THEN 12 ELSE pd - 1 END as PERIOD,
SUM(BOM) as BMODOM
from TABLE
where type = 'B'
group by yr, pd
order by yr desc, pd desc
)
select w.YEAR, w.PERIOD, w.SALES, x.BSales, y.MODOM, z.BMODOM
from w inner join x
on w.YEAR = x.YEAR and w.PERIOD = x.PERIOD
inner join y
on w.YEAR = y.YEAR and w.PERIOD = y.PERIOD
inner join z
on w.YEAR = z.YEAR and w.PERIOD = z.PERIOD
where w.YEAR between ? and ? and w.PERIOD between ? and ?
order by YEAR desc, PERIOD desc
出于隐私目的,我不得不稍微更改代码,但我相信这一切都是正确的。
示例数据:
Yr Pd Type Amt OM
18 2 A 45 181
18 2 B 33 163
18 2 A 40 153
18 1 B 39 136
18 1 B 24 142
18 1 B 53 143
18 1 A 41 186
18 1 A 78 197
17 12 A 98 139
17 12 A 54 159
17 12 B 78 181
17 12 B 45 101
17 11 A 28 134
17 11 A 77 192
17 11 A 75 110
17 11 B 60 135
17 11 B 83 170
17 10 B 72 114
17 10 A 26 118
17 10 A 95 111
17 9 A 12 112
17 9 B 14 171
示例结果
Yr Pd Sales Bsales MODOM BMODOM
18 2 118 33 804 421
18 1 235 116 580 282
17 12 275 123 741 305
17 11 323 143 343 114
17 10 193 72 283 171
请注意,我需要 TOTAL 级别的 SALES,然后是 TYPE B 级别的 SALES。我还需要总级别的 OM,然后还需要 B 类级别 - 但是 - 我需要抵消一个周期。因此,17-10 的 MODOM 反映了 table 期间 17-9 的 OM 总数。 (我希望这是有道理的)。
EDIT 我有这个落后。 17-10 的 MODOM 实际上会反映 17-11 的 OM 值,而不是相反。更正了预期结果。
Yr Pd Sales Bsales MODOM BMODOM
18 2 118 33 0 0
18 1 235 116 497 163
17 12 275 123 804 421
17 11 323 143 580 282
17 10 193 72 741 305
因此,使用条件聚合至少可以摆脱 2 个常见的 table 表达式,这会有所帮助。在您的最终演示查询之前不排序而不是在每个常见的 table 表达式中排序也会有所帮助。使用实际日期,例如First of First Month of a Period 可以帮助消除在定义上一期间时可能成本高昂的 case 表达式。
有几种写法,但这会给你一个条件聚合的例子。
并注意 LEFT JOIN 而不是 INNER,因为如果您使用 INNER
,您的第一个周期将始终放弃您的查询WITH PeriodSales AS(
SELECT
yr as YEAR
,pd AS PERIOD
,SUM(amt) as SALES
,SUM(CASE WHEN type = 'B' THEN amt END) as BSALES
FROM
Table
GROUP BY
yr
,pd
)
, PreviousPeriod AS
(
SELECT
CASE WHEN pd = 12 THEN yr + 1 ELSE yr END as YEAR
,CASE WHEN pd = 12 THEN 1 ELSE pd + 1 END as PERIOD
,SUM(OM) as MODOM
,SUM(CASE WHEN type = 'B' THEN OM END) as BMODOM
FROM
Table
GROUP BY
CASE WHEN pd = 12 THEN yr + 1 ELSE yr END
,CASE WHEN pd = 12 THEN 1 ELSE pd + 1 END
)
SELECT
ps.YEAR
,ps.PERIOD
,ps.SALES
,ps.BSALES
,pp.MODOM
,pp.BMODOM
FROM
PeriodSales ps
LEFT JOIN PreviousPeriod pp
ON ps.YEAR = pp.YEAR
AND ps.PERIOD = pp.PERIOD
ORDER BY
ps.YEAR DESC
,ps.PERIOD DESC
根据您的编辑,要与 "Previous Period" 对齐以获得您想要的 OM 量,您实际上需要添加句点而不是在我使用的示例中减去句点。我已经测试过了,它确实有效。在不了解 table 和执行计划等的情况下,我们无法发现许多其他影响性能的因素。