使用 Case 语句的 Group By 不计算零
Group By with Case Statement Doesn't Count Zeros
我有以下 SQL:
WITH MYFILTER AS (
SELECT
CASE WHEN STATE IN ('A', 'B') then
case
when (NUM/DENOM - 1.0) < -0.3 then 'Less than -30%'
when (NUM/DENOM - 1.0) >= -0.3
and (NUM/DENOM - 1.0) < -0.2 then '-30% to -20%'
when (NUM/DENOM - 1.0) >= -0.2
and (NUM/DENOM - 1.0) < -0.1 then '-20% to -10%'
when (NUM/DENOM - 1.0) >= -0.1
and (NUM/DENOM - 1.0) < 0.0 then '-10% to 0%'
when (NUM/DENOM - 1.0) >= 0.0
and (NUM/DENOM - 1.0) < 0.1 then '0% to 10%'
when (NUM/DENOM - 1.0) >= 0.1
and (NUM/DENOM - 1.0) < 0.2 then '10% to 20%'
when (NUM/DENOM - 1.0) >= 0.2
and (NUM/DENOM - 1.0) < 0.3 then '20% to 30%'
when (NUM/DENOM - 1.0) >= 0.3 THEN 'At least 30%'
end
ELSE case
when (NUM/DENOM < -0.3 then 'Less than -30%'
when (NUM/DENOM >= -0.3
and (NUM/DENOM < -0.2 then '-30% to -20%'
when (NUM/DENOM >= -0.2
and (NUM/DENOM < -0.1 then '-20% to -10%'
when (NUM/DENOM >= -0.1
and (NUM/DENOM < 0.0 then '-10% to 0%'
when (NUM/DENOM >= 0.0
and (NUM/DENOM < 0.1 then '0% to 10%'
when (NUM/DENOM >= 0.1
and (NUM/DENOM < 0.2 then '10% to 20%'
when (NUM/DENOM >= 0.2
and (NUM/DENOM < 0.3 then '20% to 30%'
when (NUM/DENOM >= 0.3 THEN 'At least 30%'
end
END AS indrange
FROM MYTABLE
WHERE DENOM <> 0 AND
YEAR = 2020 AND
MONTH = 11
)
SELECT
indrange,
count (*) AS total
FROM FILTER
GROUP BY indrange
只要比率不在其中一个范围内(例如,我的 table 没有 NUM/DENOM - 1 > 0.3 的行),那么我最终得到的结果不包括“至少 30%”作为具有 0 值的行。相反,该行根本不存在。我将如何更改代码以使其仍包含具有相应 0 值的“至少 30%”行?换句话说,我明白了:
INDRANGE TOTAL
Less than -30% 285
-30% to -20% 1,608
-20% to -10% 7,409
-10% to 0% 164,212
0% to 10% 169,665
10% to 20% 1
但我想要这个:
INDRANGE TOTAL
Less than -30% 285
-30% to -20% 1,608
-20% to -10% 7,409
-10% to 0% 164,212
0% to 10% 169,665
10% to 20% 1
20% to 30% 0
At Least 30% 0
我该怎么做?这是 DB2。
未经测试:但根据我之前的评论得出类似的结果。
- 添加了一个 MyRanges cte,列出每个范围 1 次,从而确保每个范围都包含在最终查询中。
- 在最终查询中使用 Union all 将其与 MyFilter 的结果合并
- 从 count(*) 结果中减去 1,因为我们将 myRanges 联合的结果膨胀了 1
.
WITH MYRANGES AS(
SELECT 'Less than -30%' as INDRANGE FROM DUAL UNION ALL
SELECT '-30% to -20%' as INDRANGE FROM DUAL UNION ALL
SELECT '-20% to -10%' as INDRANGE FROM DUAL UNION ALL
SELECT '-10% to 0%' as INDRANGE FROM DUAL UNION ALL
SELECT '0% to 10%' as INDRANGE FROM DUAL UNION ALL
SELECT '10% to 20%' as INDRANGE FROM DUAL UNION ALL
SELECT '20% to 30%' as INDRANGE FROM DUAL UNION ALL
SELECT 'At Least 30%' as INDRANGE FROM DUAL),
MYFILTER AS (SELECT
CASE WHEN STATE IN ('A', 'B') then
case
when (NUM/DENOM - 1.0) < -0.3 then 'Less than -30%'
when (NUM/DENOM - 1.0) >= -0.3
and (NUM/DENOM - 1.0) < -0.2 then '-30% to -20%'
when (NUM/DENOM - 1.0) >= -0.2
and (NUM/DENOM - 1.0) < -0.1 then '-20% to -10%'
when (NUM/DENOM - 1.0) >= -0.1
and (NUM/DENOM - 1.0) < 0.0 then '-10% to 0%'
when (NUM/DENOM - 1.0) >= 0.0
and (NUM/DENOM - 1.0) < 0.1 then '0% to 10%'
when (NUM/DENOM - 1.0) >= 0.1
and (NUM/DENOM - 1.0) < 0.2 then '10% to 20%'
when (NUM/DENOM - 1.0) >= 0.2
and (NUM/DENOM - 1.0) < 0.3 then '20% to 30%'
when (NUM/DENOM - 1.0) >= 0.3 THEN 'At least 30%'
end
ELSE case
when (NUM/DENOM < -0.3 then 'Less than -30%'
when (NUM/DENOM >= -0.3
and (NUM/DENOM < -0.2 then '-30% to -20%'
when (NUM/DENOM >= -0.2
and (NUM/DENOM < -0.1 then '-20% to -10%'
when (NUM/DENOM >= -0.1
and (NUM/DENOM < 0.0 then '-10% to 0%'
when (NUM/DENOM >= 0.0
and (NUM/DENOM < 0.1 then '0% to 10%'
when (NUM/DENOM >= 0.1
and (NUM/DENOM < 0.2 then '10% to 20%'
when (NUM/DENOM >= 0.2
and (NUM/DENOM < 0.3 then '20% to 30%'
when (NUM/DENOM >= 0.3 THEN 'At least 30%'
end
END AS indrange
FROM MYTABLE
WHERE DENOM <> 0 AND
YEAR = 2020 AND
MONTH = 11
),
SELECT indrange, count (*)-1 AS total
FROM (SELECT * FROM MyFilter UNION ALL
SELECT * FROM MyRanges) as MyFilterandAllRanges
GROUP BY indrange
好的,既然我已经做了一些事情,我已经考虑过了,我可以做得更好...
所以将最终查询修改为...,我们不必搞乱数字和数学。我们基本上是在获取范围的“主列表”并将其外部连接到我们的总数,以便始终包括所有范围,并且只有那些具有匹配项的范围才会显示总数。我们使用 coalesce 来处理一些可能不存在的事实并替换所需的 0.
SELECT MyRanges.indrange, coalesce(total,0) as total
FROM MyRanges
LEFT JOIN (SELECT count(*) total, Indrange
FROM MyFilter
GROUP BY indrange) as sub
on MyRanges.indrange = sub.indrange
您可以在范围和您现有的 CTE 之间使用 LEFT JOIN
。例如:
with myranges (indrange) as (
select 'Less than -30%' from sysibm.sysdummy1
union all select '-30% to -20%' from sysibm.sysdummy1
union all select '-20% to -10%' from sysibm.sysdummy1
union all select '-10% to 0%' from sysibm.sysdummy1
union all select '0% to 10%' from sysibm.sysdummy1
union all select '10% to 20%' from sysibm.sysdummy1
union all select '20% to 30%' from sysibm.sysdummy1
union all select 'At Least 30%' from sysibm.sysdummy1
),
myfilter as (
-- here add you existing CTE
)
select r.indrange, count(f.indrange) AS total
from myranges r
left join myfilter f on f.indrange = r.indrange
group by r.indrange
我有以下 SQL:
WITH MYFILTER AS (
SELECT
CASE WHEN STATE IN ('A', 'B') then
case
when (NUM/DENOM - 1.0) < -0.3 then 'Less than -30%'
when (NUM/DENOM - 1.0) >= -0.3
and (NUM/DENOM - 1.0) < -0.2 then '-30% to -20%'
when (NUM/DENOM - 1.0) >= -0.2
and (NUM/DENOM - 1.0) < -0.1 then '-20% to -10%'
when (NUM/DENOM - 1.0) >= -0.1
and (NUM/DENOM - 1.0) < 0.0 then '-10% to 0%'
when (NUM/DENOM - 1.0) >= 0.0
and (NUM/DENOM - 1.0) < 0.1 then '0% to 10%'
when (NUM/DENOM - 1.0) >= 0.1
and (NUM/DENOM - 1.0) < 0.2 then '10% to 20%'
when (NUM/DENOM - 1.0) >= 0.2
and (NUM/DENOM - 1.0) < 0.3 then '20% to 30%'
when (NUM/DENOM - 1.0) >= 0.3 THEN 'At least 30%'
end
ELSE case
when (NUM/DENOM < -0.3 then 'Less than -30%'
when (NUM/DENOM >= -0.3
and (NUM/DENOM < -0.2 then '-30% to -20%'
when (NUM/DENOM >= -0.2
and (NUM/DENOM < -0.1 then '-20% to -10%'
when (NUM/DENOM >= -0.1
and (NUM/DENOM < 0.0 then '-10% to 0%'
when (NUM/DENOM >= 0.0
and (NUM/DENOM < 0.1 then '0% to 10%'
when (NUM/DENOM >= 0.1
and (NUM/DENOM < 0.2 then '10% to 20%'
when (NUM/DENOM >= 0.2
and (NUM/DENOM < 0.3 then '20% to 30%'
when (NUM/DENOM >= 0.3 THEN 'At least 30%'
end
END AS indrange
FROM MYTABLE
WHERE DENOM <> 0 AND
YEAR = 2020 AND
MONTH = 11
)
SELECT
indrange,
count (*) AS total
FROM FILTER
GROUP BY indrange
只要比率不在其中一个范围内(例如,我的 table 没有 NUM/DENOM - 1 > 0.3 的行),那么我最终得到的结果不包括“至少 30%”作为具有 0 值的行。相反,该行根本不存在。我将如何更改代码以使其仍包含具有相应 0 值的“至少 30%”行?换句话说,我明白了:
INDRANGE TOTAL
Less than -30% 285
-30% to -20% 1,608
-20% to -10% 7,409
-10% to 0% 164,212
0% to 10% 169,665
10% to 20% 1
但我想要这个:
INDRANGE TOTAL
Less than -30% 285
-30% to -20% 1,608
-20% to -10% 7,409
-10% to 0% 164,212
0% to 10% 169,665
10% to 20% 1
20% to 30% 0
At Least 30% 0
我该怎么做?这是 DB2。
未经测试:但根据我之前的评论得出类似的结果。
- 添加了一个 MyRanges cte,列出每个范围 1 次,从而确保每个范围都包含在最终查询中。
- 在最终查询中使用 Union all 将其与 MyFilter 的结果合并
- 从 count(*) 结果中减去 1,因为我们将 myRanges 联合的结果膨胀了 1
.
WITH MYRANGES AS(
SELECT 'Less than -30%' as INDRANGE FROM DUAL UNION ALL
SELECT '-30% to -20%' as INDRANGE FROM DUAL UNION ALL
SELECT '-20% to -10%' as INDRANGE FROM DUAL UNION ALL
SELECT '-10% to 0%' as INDRANGE FROM DUAL UNION ALL
SELECT '0% to 10%' as INDRANGE FROM DUAL UNION ALL
SELECT '10% to 20%' as INDRANGE FROM DUAL UNION ALL
SELECT '20% to 30%' as INDRANGE FROM DUAL UNION ALL
SELECT 'At Least 30%' as INDRANGE FROM DUAL),
MYFILTER AS (SELECT
CASE WHEN STATE IN ('A', 'B') then
case
when (NUM/DENOM - 1.0) < -0.3 then 'Less than -30%'
when (NUM/DENOM - 1.0) >= -0.3
and (NUM/DENOM - 1.0) < -0.2 then '-30% to -20%'
when (NUM/DENOM - 1.0) >= -0.2
and (NUM/DENOM - 1.0) < -0.1 then '-20% to -10%'
when (NUM/DENOM - 1.0) >= -0.1
and (NUM/DENOM - 1.0) < 0.0 then '-10% to 0%'
when (NUM/DENOM - 1.0) >= 0.0
and (NUM/DENOM - 1.0) < 0.1 then '0% to 10%'
when (NUM/DENOM - 1.0) >= 0.1
and (NUM/DENOM - 1.0) < 0.2 then '10% to 20%'
when (NUM/DENOM - 1.0) >= 0.2
and (NUM/DENOM - 1.0) < 0.3 then '20% to 30%'
when (NUM/DENOM - 1.0) >= 0.3 THEN 'At least 30%'
end
ELSE case
when (NUM/DENOM < -0.3 then 'Less than -30%'
when (NUM/DENOM >= -0.3
and (NUM/DENOM < -0.2 then '-30% to -20%'
when (NUM/DENOM >= -0.2
and (NUM/DENOM < -0.1 then '-20% to -10%'
when (NUM/DENOM >= -0.1
and (NUM/DENOM < 0.0 then '-10% to 0%'
when (NUM/DENOM >= 0.0
and (NUM/DENOM < 0.1 then '0% to 10%'
when (NUM/DENOM >= 0.1
and (NUM/DENOM < 0.2 then '10% to 20%'
when (NUM/DENOM >= 0.2
and (NUM/DENOM < 0.3 then '20% to 30%'
when (NUM/DENOM >= 0.3 THEN 'At least 30%'
end
END AS indrange
FROM MYTABLE
WHERE DENOM <> 0 AND
YEAR = 2020 AND
MONTH = 11
),
SELECT indrange, count (*)-1 AS total
FROM (SELECT * FROM MyFilter UNION ALL
SELECT * FROM MyRanges) as MyFilterandAllRanges
GROUP BY indrange
好的,既然我已经做了一些事情,我已经考虑过了,我可以做得更好...
所以将最终查询修改为...,我们不必搞乱数字和数学。我们基本上是在获取范围的“主列表”并将其外部连接到我们的总数,以便始终包括所有范围,并且只有那些具有匹配项的范围才会显示总数。我们使用 coalesce 来处理一些可能不存在的事实并替换所需的 0.
SELECT MyRanges.indrange, coalesce(total,0) as total
FROM MyRanges
LEFT JOIN (SELECT count(*) total, Indrange
FROM MyFilter
GROUP BY indrange) as sub
on MyRanges.indrange = sub.indrange
您可以在范围和您现有的 CTE 之间使用 LEFT JOIN
。例如:
with myranges (indrange) as (
select 'Less than -30%' from sysibm.sysdummy1
union all select '-30% to -20%' from sysibm.sysdummy1
union all select '-20% to -10%' from sysibm.sysdummy1
union all select '-10% to 0%' from sysibm.sysdummy1
union all select '0% to 10%' from sysibm.sysdummy1
union all select '10% to 20%' from sysibm.sysdummy1
union all select '20% to 30%' from sysibm.sysdummy1
union all select 'At Least 30%' from sysibm.sysdummy1
),
myfilter as (
-- here add you existing CTE
)
select r.indrange, count(f.indrange) AS total
from myranges r
left join myfilter f on f.indrange = r.indrange
group by r.indrange