我有一张桌子,比如说有很多地方。例如:
ID Name ParentId
--------------------------
1 UK NULL
2 England 1
3 Bedfordshire 2
4 Bedford 3
5 ShopA 4
6 Hertfordshire 2
7 Stevenage 6
8 ShopB 7
9 ShopsX 6
我想运行一个查询,将数据作为层次结构
返回UK | England | Bedfordshire | Bedford | ShopA
UK | England | Herfordshire | Stevenage | ShopB
UK | England | Herfordshire | ShopsX | NULL
注意最后一行,我不想按如下方式调用它:
NULL | UK | England | Herfordshire | ShopsX
使用这样的查询:
SELECT c.name as cname, b.name as bname, a.name as aname
FROM table a
left JOIN table b
ON b.Id = a.Parentid
left join table c
ON c.Id = b.Parentid
我得到的结果是第一个值为NULL。
喜欢
NULL | UK | England | Herfordshire | ShopsX
是否可以将查询切换为圆形,以便NULL以某种方式向右对齐?
答案 0 :(得分:1)
假设您的层次结构具有固定数量的级别,您可以使用与每个商店检索一行的级别一样多的自连接:
select country.name,region.name,county.name,town.name,shop.name
from shops country
inner join shops region on region.ParentID=country.id
inner join shops county on county.ParentID=region.id
inner join shops town on town.ParentID=county.id
inner join shops shop on shop.parentid=town.id
这将返回:
UK|England|Bedfordshire |Bedford |ShopA
UK|England|Hertfordshire|Stevenage |ShopB
要获取ShopsX
这个没有商店的类别,您需要将最后一个连接更改为左连接和检查country
的空ParentID:
select country.name,region.name,county.name,town.name,shop.name
from shops country
inner join shops region on region.ParentID=country.id
inner join shops county on county.ParentID=region.id
inner join shops town on town.ParentID=county.id
left join shops shop on shop.parentid=town.id
where country.parentid is null
返回
UK|England|Bedfordshire |Bedford |ShopA
UK|England|Hertfordshire|Stevenage |ShopB
UK|England|Hertfordshire|ShopsX |NULL
如果您没有检查Country.ParentID is null
,则每个Shop行都会尝试再次加入表格,找不到匹配项,仍然返回NULL:
UK | England | Bedfordshire | Bedford | ShopA
England | Bedfordshire | Bedford | ShopA | NULL
UK | England | Hertfordshire| Stevenage| ShopB
England | Hertfordshire| Stevenage | ShopB | NULL
UK | England | Hertfordshire| ShopsX | NULL
如果ID
和ParentID
被编入索引,则此“旋转”将执行得非常快。
答案 1 :(得分:1)
另一种选择是建立层次结构的标准递归cte的混搭。 (添加了COC或指挥链)
然后我们应用一点xml来解析COC。目前有9个职位,但易于扩展或收缩。
Declare @YourTable table (id int,Name varchar(50),ParentId int)
Insert into @YourTable values
(1 ,'UK', NULL)
,(2 ,'England', 1)
,(3 ,'Bedfordshire', 2)
,(4 ,'Bedford', 3)
,(5 ,'ShopA', 4)
,(6 ,'Hertfordshire', 2)
,(7 ,'Stevenage', 6)
,(8 ,'ShopB', 7)
,(9 ,'ShopsX', 6)
Declare @Nest varchar(25) = '|-----' --<< Optional: Added for readability
;with cteP as (
Select Seq = cast(10000+Row_Number() over (Order by Name) as varchar(500))
,ID
,ParentId
,Lvl=1
,Name
,COC = cast(Name as varchar(max))
From @YourTable
Where ParentId is null
Union All
Select Seq = cast(concat(p.Seq,'.',10000+Row_Number() over (Order by r.Name)) as varchar(500))
,r.ID
,r.ParentId
,p.Lvl+1
,r.Name
,COC = p.COC+'||'+cast(r.Name as varchar(max))
From @YourTable r
Join cteP p on r.ParentId = p.ID)
,cteR1 as (Select *,R1=Row_Number() over (Order By Seq) From cteP)
,cteR2 as (Select A.ID,R2=Max(B.R1) From cteR1 A Join cteR1 B on (B.Seq like A.Seq+'%') Group By A.Seq,A.ID )
Select C.*
From cteR1 A
Join cteR2 B on A.ID=B.ID
Cross Apply (
Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(max)')))
,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(max)')))
,Pos5 = ltrim(rtrim(xDim.value('/x[5]','varchar(max)')))
,Pos6 = ltrim(rtrim(xDim.value('/x[6]','varchar(max)')))
,Pos7 = ltrim(rtrim(xDim.value('/x[7]','varchar(max)')))
,Pos8 = ltrim(rtrim(xDim.value('/x[8]','varchar(max)')))
,Pos9 = ltrim(rtrim(xDim.value('/x[9]','varchar(max)')))
From (Select Cast('<x>' + replace((Select A.COC as [*] For XML Path('')),'||','</x><x>')+'</x>' as xml) as xDim) as A
) C
Where R1=R2
Order By A.R1
<强>返回强>
如果它有助于可视化。
最终选择是:
Select A.R1
,B.R2
,A.ID
,A.ParentId
,A.Lvl
,Name = Replicate(@Nest,A.Lvl-1) + A.Name
From cteR1 A
Join cteR2 B on A.ID=B.ID
Order by R1
结果将是