在 CTE 中使用 Order By 和 Left or right join
Posted
技术标签:
【中文标题】在 CTE 中使用 Order By 和 Left or right join【英文标题】:using Order By and Left or right join in CTE 【发布时间】:2020-11-14 07:37:01 【问题描述】:请考虑这张表:
Min Max Desc
------------------------------------------------
10 24 Total
10 14 Between 10 and 14
15 19 Between 15 and 19
20 24 Between 20 and 24
还有这个CTE
:
with p as
(
SELECT top 100 percent [min],
[max],
[Desc]
FROM [MyBoundaries]
ORDER BY rownumber
)
select
p.[Desc],
COUNT(CASE when (tbl.col_2 >= 0 AND tbl.col_2 < 5) THEN 1 END),
COUNT(CASE when (tbl.col_2 >= 5 AND tbl.col_2 < 10) THEN 1 END),
COUNT(CASE when (tbl.col_2 >= 10 AND tbl.col_2 < 15) THEN 1 END),
COUNT(CASE when (tbl.col_2 >= 15 AND tbl.col_2 < 20) THEN 1 END)
from [dbo].[MyTable] AS tbl
inner join p on tbl.col_1 >= p.min and tbl.col_1 <= p.max
where tbl.[YEAR] = 2020
group by p.[Desc]
我对此CTE
有多个问题:
[MyBoundaries]
中记录的顺序未维护,我希望结果与表 [MyBoundaries]
的顺序完全一致。
如果我没有在边界(20, 24)
中记录,那么这个边界不包含在输出中,但我想要:
Between 20 and 24 0 0 0 0
我将 inner join
更改为 left
或 'right` 加入,但输出未更改。
我该如何解决这些问题?
谢谢
【问题讨论】:
表格中的行没有顺序。表操作符没有任何顺序感;所以不清楚你认为如果你有订单你会输入什么。结果集具有(部分)顺序。 Order by without limit/top 不在最外层是无操作的。解释您的期望和期望的原因 - 使用参考权威文档的理由。否则,我们无法解决您的误解,只能重写文档——不知道您在误解什么。另外:错误的代码和没有期望并不能告诉我们您想要什么。 minimal reproducible example 请注意所有建议的解决方案如何在外部查询中包含 order by 子句。这与 phil 的评论直接相关并对其进行了扩展。结果集中的行也没有特定的顺序,除非生成它的查询也有 order by 子句。由于查询计划,GROUP by 子句往往会导致出现顺序 - 但如果没有 ORDER BY 子句,则无法保证。 【参考方案1】:你可以使用:
select
p.[Desc],
COUNT(CASE when (tbl.col_2 >= 0 AND tbl.col_2 < 5) THEN 1 END),
COUNT(CASE when (tbl.col_2 >= 5 AND tbl.col_2 < 10) THEN 1 END),
COUNT(CASE when (tbl.col_2 >= 10 AND tbl.col_2 < 15) THEN 1 END),
COUNT(CASE when (tbl.col_2 >= 15 AND tbl.col_2 < 20) THEN 1 END),
from (SELECT * FROM [dbo].[MyTable] WHERE [YEAR] = 2020) AS tbl -- filter on source
right join [MyBoundaries] p on tbl.col_1 >= p.min and tbl.col_1 <= p.max -- right join
group by p.[Desc]
order by MIN(p.[rownumber]);
【讨论】:
【参考方案2】:这看起来是横向连接的好地方:
select b.[Desc], t.*
from [MyBoundaries] b
outer apply (
select
sum(case when t.col_2 >= 0 and t.col_2 < 5 then 1 else 0 end),
sum(case when t.col_2 >= 5 and t.col_2 < 10 then 1 else 0 end),
sum(case when t.col_2 >= 10 and t.col_2 < 15 then 1 else 0 end),
sum(case when t.col_2 >= 15 and t.col_2 < 20 then 1 else 0 end)
from [dbo].[MyTable] t
where t.col_1 >= b.min and t.col_1 <= b.max and t.year = 2020
) t
order by b.rownumber
【讨论】:
【参考方案3】:有多种改变可以做你想做的事:
您可以使用left join
,但您要保留所有行的表需要放在第一位。
CTE 需要返回 rownumber
,因此您可以将其包含在 order by
中。
对另一个表的过滤需要在on
子句中。
所以:
with b as (
SELECT b.*
FROM [MyBoundaries] b
ORDER BY rownumber
)
select b.[Desc],
COUNT(CASE when tbl.col_2 >= 0 AND tbl.col_2 < 5 THEN 1 END),
COUNT(CASE when tbl.col_2 >= 5 AND tbl.col_2 < 10 THEN 1 END),
COUNT(CASE when tbl.col_2 >= 10 AND tbl.col_2 < 15 THEN 1 END),
COUNT(CASE when tbl.col_2 >= 15 AND tbl.col_2 < 20 THEN 1 END)
from b left join
[dbo].[MyTable] tbl
on tbl.col_1 >= b.min and tbl.col_1 <= b.max and
tbl.[YEAR] = 2020
group by b.[Desc]
order by b.rownumber;
我将 CTE 名称更改为 b
,因为这对“边界”更有意义。此外,我建议您避免使用像 desc
、min
和 max
这样的 SQL 保留字作为列名 - 以及 year
这是一个关键字,将来可能会被保留.
【讨论】:
【参考方案4】:也许查询看起来像这样
with p_cte as (
select top 100 percent [min], [max], [Desc]
from MyBoundaries)
select pc.[Desc]
sum(case when (t.col_2 >= 0 and t.col_2 < 5) then 1 end) ZeroToFive,
sum(case when (t.col_2 >= 5 and t.col_2 < 10) then 1 end) FiveToTen,
sum(case when (t.col_2 >= 10 and t.col_2 < 15) then 1 end) TenToFifteen,
sum(case when (t.col_2 >= 15 and t.col_2 < 20) then 1 end) FiftennToTwenty
from p_cte pc
left join [dbo].[MyTable] t on pc.[min] <= t.col_1
and pc.[max] >= t.col_1
and t.[YEAR] = 2020
group by pc.[Desc]
order by pc.[min], pc.[Desc] desc;
【讨论】:
以上是关于在 CTE 中使用 Order By 和 Left or right join的主要内容,如果未能解决你的问题,请参考以下文章
使用 LEFT JOIN 和 ORDER BY...LIMIT 查询慢,使用 Filesort
使用 LEFT JOIN + ORDER BY 时如何避免 FileSort?
ORDER BY date SQL with LEFT JOIN 用于消息传递