Sql 基于层次结构的透视数据
我有一个层次结构的数据,其结构可能会发生变化。这些关系保存在一个表中,该表通过在两列(节点ID和父ID)上的自引用来标识。我希望能够运行一个查询来透视数据,以便每一行表示节点的最低单位 例如: 如果我有一张这样的桌子 我希望能够做到这一点 我已经试着做了几次连接,试图让一切都在同一条线上Sql 基于层次结构的透视数据,sql,sql-server,pivot-table,sql-server-2014,hierarchy,Sql,Sql Server,Pivot Table,Sql Server 2014,Hierarchy,我有一个层次结构的数据,其结构可能会发生变化。这些关系保存在一个表中,该表通过在两列(节点ID和父ID)上的自引用来标识。我希望能够运行一个查询来透视数据,以便每一行表示节点的最低单位 例如: 如果我有一张这样的桌子 我希望能够做到这一点 我已经试着做了几次连接,试图让一切都在同一条线上 SELECT L1.NAME AS CITY, L2.NAME AS COUNTY, L3.NAME AS STATE, L4.NAME AS COUNTRY FROM TABLENAME L1 LEFT
SELECT L1.NAME AS CITY, L2.NAME AS COUNTY, L3.NAME AS STATE, L4.NAME AS
COUNTRY
FROM TABLENAME L1
LEFT JOIN TABLENAME AS L2 ON L1.PARENT_NODE_ID = L2.NODE_ID
LEFT JOIN TABLENAME AS L3 ON L2.PARENT_NODE_ID = L3.NODE_ID
LEFT JOIN TABLENAME AS L4 ON L3.PARENT_NODE_ID = L4.NODE_ID
WHERE L1.Type = City
这是问题的核心:我可能并不总是知道层次结构。因此,我需要一个能够处理变化的解决方案。假设业务逻辑的维护者决定我们需要在国家之上添加半球。或州以上的地区(西海岸、中部、东海岸)。然而,城市将始终是最低的节点。我需要一些独立于等级结构的东西
更新
我最初的问题我用了一个简单的例子。在我的实际解决方案中,我必须利用几个连接来获得我需要的层次结构。我正在处理下面的查询,但到目前为止,对于我希望填充的每一列,它都返回null。很可能是案件陈述有问题
;WITH ALLORGS AS( --All Orgs
SELECT ORGS.ID, ORGS.ORG_NAME
, HIER.ID_PARENTORG, TYP.ORG_TYPE_DESCR
FROM ORGANIATIONS AS ORGS
FULL OUTER JOIN HIERARCHYTABLE AS HIER ON ORGS.ID = HIER.ID_ORG
FULL OUTER JOIN ORGANIZATION_TYPES AS TYP ON ORGS.ID_ORG_TYPE = TYP.ID
), CTE AS (
SELECT ID
, ID_PARENTORG
, L1.ORG_NAME
--, ORG_TYPE_DESCR
, CAST('' as varchar(100)) AS UNIT
, CAST('' as varchar(100)) AS REGION
, CAST('' as varchar(100)) AS DDA_POOL
, CAST('' as varchar(100)) AS COUNTY
, CAST('' as varchar(100)) AS STATE
, CAST('' as varchar(100)) AS BUSINESS_UNIT
, CAST('' as varchar(100)) AS PROEPRTY
, CAST('' as varchar(100)) AS DISTRICT
, 1 AS FLAG
FROM ALLORGS L1
WHERE L1.ORG_TYPE_DESCR = 'COST CENTER'
UNION ALL
SELECT T1.ID
,L2.ID_PARENTORG
,T1.ORG_NAME AS COSTCNTR
--, T.ORG_TYPE_DESCR
,CASE WHEN L2.ORG_TYPE_DESCR = 'UNIT' THEN L2.ORG_NAME ELSE NULL END AS UNIT
,CASE WHEN L2.ORG_TYPE_DESCR = 'REGION' THEN L2.ORG_NAME ELSE NULL END AS REGION
,CASE WHEN L2.ORG_TYPE_DESCR = 'DDA_POOL' THEN L2.ORG_NAME ELSE NULL END AS DDA_POOL
,CASE WHEN L2.ORG_TYPE_DESCR = 'COUNTRY' THEN L2.ORG_NAME ELSE NULL END AS COUNTRY
,CASE WHEN L2.ORG_TYPE_DESCR = 'STATE' THEN L2.ORG_NAME ELSE NULL END AS STATE
,CASE WHEN L2.ORG_TYPE_DESCR = 'BUSINESS_UNIT' THEN L2.ORG_NAME ELSE NULL END AS BUSINESS_UNIT
,CASE WHEN L2.ORG_TYPE_DESCR = 'PROPERTY' THEN L2.ORG_NAME ELSE NULL END AS PROPERTY
,CASE WHEN L2.ORG_TYPE_DESCR = 'DISTRICT' THEN L2.ORG_NAME ELSE NULL END AS DISTRICT
,T1.FLAG + 1 AS FLAG
FROM CTE AS T1
INNER JOIN ALLORGS AS L2 ON T1.ID_PARENTORG = L2.ID
)
SELECT a.ID
,a.ORG_NAME AS COSTCNTR
,UNIT
,REGION
,DDA_POOL
,COUNTY
,STATE
,BUSINESS_UNIT
,PROEPRTY
,DISTRICT
FROM CTE AS a
INNER JOIN (SELECT ID, MAX(FLAG) FLAG FROM CTE GROUP BY ID) b ON a.ID = b.ID AND a.FLAG = b.FLAG
试试这个。。。
请在使用前用更多的样本数据进行测试
表格脚本和示例数据
CREATE TABLE [TableName](
[ParentNodeID] [int] NULL,
[NodeID] [int] NULL,
[Type] [nvarchar](50) NULL,
[Name] [nvarchar](50) NULL
)
INSERT [TableName] ([ParentNodeID], [NodeID], [Type], [Name]) VALUES (NULL, 1, N'Country', N'US')
INSERT [TableName] ([ParentNodeID], [NodeID], [Type], [Name]) VALUES (1, 2, N'State', N'Texas')
INSERT [TableName] ([ParentNodeID], [NodeID], [Type], [Name]) VALUES (2, 3, N'County', N'Dallas')
INSERT [TableName] ([ParentNodeID], [NodeID], [Type], [Name]) VALUES (3, 4, N'City', N'Dallas')
INSERT [TableName] ([ParentNodeID], [NodeID], [Type], [Name]) VALUES (NULL, 1, N'Country', N'US')
INSERT [TableName] ([ParentNodeID], [NodeID], [Type], [Name]) VALUES (5, 6, N'State', N'Massachusetts')
INSERT [TableName] ([ParentNodeID], [NodeID], [Type], [Name]) VALUES (7, 8, N'County', N'Suffolk')
INSERT [TableName] ([ParentNodeID], [NodeID], [Type], [Name]) VALUES (9, 10, N'City', N'Boston')
查询
DECLARE @cols AS NVARCHAR(max) = Stuff((SELECT DISTINCT ',' + Quotename([Type])
FROM TableName
FOR xml path(''), type).value('.', 'NVARCHAR(MAX)'), 1, 1, '');
DECLARE @query AS NVARCHAR(max) = 'SELECT max(NodeID) AS NodeID
,max([Country]) AS Country
,max([State]) AS STATE
,max([County]) AS County
,max([City]) AS City
FROM (
SELECT *, Row_Number() OVER (PARTITION BY Type ORDER BY NodeID) rn
FROM TableName
) sq
pivot(max([Name]) FOR [Type] IN ('+ @cols +') ) pvt
GROUP BY rn';
EXECUTE(@query)
输出
+--------+---------+---------------+---------+--------+
| NodeID | Country | STATE | County | City |
+--------+---------+---------------+---------+--------+
| 4 | US | Texas | Dallas | Dallas |
| 10 | US | Massachusetts | Suffolk | Boston |
+--------+---------+---------------+---------+--------+
在线演示:我也实现了同样的情况,我没有测试这个,因为我现在只在这里使用我的手机,但我使用的逻辑基本上是这样的,使用CTE进行抄写,希望这也能起作用
WITH CTE AS (
--Put Initial Value '' to be filled later
SELECT NODE_ID, PARENT_ID, L1.NAME AS CITY, '' AS COUNTY, '' AS STATE, '' AS COUNTRY,
--add hemisphere
'' AS HEMISPHERE,
1 AS FLAG --Only for indication of looping
FROM TABLENAME L1
WHERE L1.TYPE = 'CITY'
UNION ALL
SELECT T1.NODE_ID, L2.PARENT_ID,
T1.NAME AS CITY,
(CASE WHEN L2.TYPE = 'COUNTY' THEN L2.NAME ELSE T1.NAME) AS COUNTY,
(CASE WHEN L2.TYPE = 'STATE' THEN L2.NAME ELSE T1.NAME) AS STATE,
(CASE WHEN L2.TYPE = 'COUNTRY' THEN L2.NAME ELSE T1.NAME) AS COUNTRY
--and can add some more columns here, in case if there is additional column for Hemisphere
(CASE WHEN L2.TYPE = 'Hemisphere' THEN L2.NAME ELSE T1.NAME) AS Hemisphere
T1.FLAG + 1 AS FLAG -- add +1 for n reccuring, only for indication of looping
FROM CTE T1
INNER JOIN TABLENAME L2 ON T1.PARENT_ID =
L2.NODE_ID
)
SELECT a.NODE_ID, CITY, COUNTY, STATE, COUNTRY
FROM CTE a
--to get the last loop which has completely filled data
INNER JOIN (SELECT NODE_ID, MAX(FLAG) FLAG FROM CTE GROUP BY NODE_ID ) b ON a.NODE_ID = b.NODE_ID AND a.FLAG = b.FLAG
查找动态轴或动态交叉表。这是两种处理方法,这里有几十个,如果不是几百个例子的话。您的表格数据似乎不正确,请您再次检查。缺少引用ID。SQL的哪个版本?SQL版本2014这有保证!我正试图将我的实际情况融入其中。我为这个问题提供了一个简单的示例,实际上,我的表是一个组连接,因为层次表的值主要作为编码值存在。换句话说,如上所示的“TABLENAME”实际上是3或4个完整外部联接的聚合。@LCaraway,谢谢,我认为递归将继续执行循环,只要父级和节点id之间存在匹配,但这有点棘手,select语句的列在表的顶部,select语句的列在底部“全体联合“必须具有相似的列名,我们必须小心并注意表的别名,我们将从哪些表中选择哪些列作为正确的列值:”我希望这项工作我刚刚编辑了答案,并在顶部select语句中添加了半球列,在联合后的select语句中都应该是
L2.Name
,而不是L1.Name
或T1.Name