Sql server 在自引用表中选择透视数据
我有一张桌子,比如说满是地方。例如:Sql server 在自引用表中选择透视数据,sql-server,Sql Server,我有一张桌子,比如说满是地方。例如: 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
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
是否可以切换查询轮以使空值以某种方式向右对齐?假设您的层次结构具有固定数量的级别,您可以使用尽可能多的自联接来检索每个车间的一行:
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
,您需要将最后一个连接更改为左连接,并检查国家的空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是否为null
,则每个车间生产线都会再次尝试加入该表,未找到匹配项,并返回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
进行索引,这种“旋转”将执行得非常快 假设您的层次结构具有固定数量的级别,您可以使用尽可能多的自联接来检索每个车间的一行:
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
,您需要将最后一个连接更改为左连接,并检查国家的空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是否为null
,则每个车间生产线都会再次尝试加入该表,未找到匹配项,并返回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
进行索引,这种“旋转”将执行得非常快 另一个选项是我的标准递归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
结果将是
另一个选项是将我的标准递归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
结果将是
这不是一个层次结构。这更像是旋转,而不是其他任何东西。如果知道级别的数量是固定的,并且使用hierarchyid而不是自引用,则可以使用级别编号来透视,或者编写等效的异常。需要HierarchyID来进行级别计算哦,也许这就是我需要的。我所做的对层次结构正确吗?那不是层次结构。这更像是旋转,而不是其他任何东西。如果知道级别的数量是固定的,并且使用hierarchyid而不是自引用,则可以使用级别编号来透视,或者编写等效的异常。需要HierarchyID来进行级别计算哦,也许这就是我需要的。我所做的对于层次结构是否正确?成本是多少?此外,如果您需要高级层次结构处理,最好花点精力添加一个层次结构ID。即使如此,在这种情况下(0.01658成本)可能也无法击败自连接@PanagiotisKanavos,正如我提到的“另一个选项”。毫无疑问,自联接是非常有效的。但是,这里的键是范围键(R1/R2)。它们促进了非递归聚合和导航。成本是多少?此外,如果您需要高级层次结构处理,最好花点精力添加一个层次结构ID。即使如此,在这种情况下(0.01658成本)可能也无法击败自连接@PanagiotisKanavos,正如我提到的“另一个选项”。毫无疑问,自联接是非常有效的。但是,这里的键是范围键(R1/R2)。它们促进了非递归聚合和导航。