我有一个这样的表(SQL Server 2008):
[ATTACHMENTS].[ID]
[ATTACHMENTS].[Name]
[ATTACHMENTS].[DocumentID],
[ATTACHMENTS].[ParentAttachmentID]
如果[ParentAttachmentID]
是NULL
或0
- 则记录最高。否则 - 记录是一个孩子。每个孩子都可以(并且不能成为)其他一些子元素的父母。
我需要计算"路径"对于表的每个记录。路径是:
ParentAttachment.Path + ' > ' + Attachment.Path
(ParentAttachment.Path是递归的)
我尝试这样的事情:
WITH attachments AS (
SELECT *,
[ATTACHMENTS].[Name] AS Path
FROM [ATTACHMENTS]
WHERE [ATTACHMENTS].[ParentAttachmentID] IS NULL
OR [ATTACHMENTS].[ParentAttachmentID] = 0
UNION ALL
SELECT a.*,
c.Path + ' > ' + a.[Name]
FROM [ATTACHMENTS] a
INNER JOIN attachments c
ON a.[ParentAttachmentID] = c.[ID]
)
但它没有正常工作(由于我认为重复,因此某些元素的路径无效)。我犯了哪个错误?请帮我解决这个问题。
UPD 2 :包含来自表格[ATTACHMENTS]的数据的CSV - http://pastebin.com/WMd6HJ7j 带有递归查询结果的CSV:http://pastebin.com/7pqs0dx1
感谢名单!
答案 0 :(得分:0)
你得到的错误是什么?
我认为您没有明确指定数据类型。 Path的大小是锚点起点的大小:ATTACHMENTS.Name。哪个可能是varchar(100)左右。
然后当你继续递增你的路径时,它可能会用完空间。 CTE本身看起来很好。我使用显式尺寸进行操作,然后它工作正常。
CREATE TABLE #Attachments
(
Id INT PRIMARY KEY
NOT NULL ,
ParentId INT NULL ,
Name VARCHAR(50) NOT NULL
);
-- some sample data:
-------------------------------------
-- 100 top level attachments (lvl0)
INSERT INTO #Attachments
( Id ,
ParentId ,
Name
)
SELECT A.Rnum ,
NULL ,
'A' + CAST(rnum AS VARCHAR(10))
FROM ( SELECT TOP 100
ROW_NUMBER() OVER ( ORDER BY ( SELECT
1
) ) AS Rnum
FROM sys.messages
) A;
-- attachments linked to lvl0
INSERT INTO #Attachments
( Id ,
ParentId ,
Name
)
SELECT 1000 + A.Rnum ,
A.Rnum ,
'A' + CAST(( rnum + 1000 ) AS VARCHAR(10))
FROM ( SELECT TOP 1000
ROW_NUMBER() OVER ( ORDER BY ( SELECT
1
) ) AS Rnum
FROM sys.messages
) A;
-- attachments linked to lvl1
INSERT INTO #Attachments
( Id ,
ParentId ,
Name
)
SELECT 10000 + A.Rnum ,
1000 + A.Rnum ,
'A' + CAST(( rnum + 10000 ) AS VARCHAR(10))
FROM ( SELECT TOP 1000
ROW_NUMBER() OVER ( ORDER BY ( SELECT
1
) ) AS Rnum
FROM sys.messages
) A;
-- Query:
-------------------------------------
WITH Att ( Id, Name, ParentId, Lvl, NamePath, IdPath )
AS ( SELECT P.Id ,
P.Name ,
P.ParentId ,
CAST(0 AS INT) AS Lvl ,
CAST(P.[Name] AS VARCHAR(1000)) AS NamePath ,
CAST(P.[Id] AS VARCHAR(1000)) AS IdPath
FROM #ATTACHMENTS P
WHERE P.[ParentId] IS NULL
OR P.[ParentId] = 0
UNION ALL
SELECT A.Id ,
A.Name ,
A.ParentId ,
P.Lvl + 1 ,
CAST(P.NamePath + ' > ' + A.Name AS VARCHAR(1000)) ,
CAST(P.IdPath + '\' + CAST(A.Id AS VARCHAR(10)) AS VARCHAR(1000))
FROM #ATTACHMENTS A
INNER JOIN Att P ON A.ParentId = P.Id
)
SELECT *
FROM Att;
DROP TABLE #Attachments;
答案 1 :(得分:0)
你的陈述似乎对我不对:
DECLARE @t TABLE
(
Code NVARCHAR(MAX) ,
ParentCode NVARCHAR(MAX)
)
INSERT INTO @t
VALUES ( '1', NULL ),
( '2', NULL ),
( '1.1', '1' ),
( '1.1.1', '1.1' ),
( '1.1.2', '1.1' ),
( '1.2', '1' ),
( '1.2.1', '1.2' ),
( '2.1', '2' ),
( '2.2', '2' );
WITH cte
AS ( SELECT Code ,
ParentCode ,
ISNULL(Code, '') AS Path
FROM @t
WHERE ParentCode IS NULL
UNION ALL
SELECT t.Code ,
t.ParentCode ,
Path + ISNULL(' > ' + t.Code, '') AS Path
FROM @t t
JOIN cte c ON c.Code = t.ParentCode
)
SELECT * FROM cte
输出:
Code ParentCode Path
1 NULL 1
2 NULL 2
2.1 2 2 > 2.1
2.2 2 2 > 2.2
1.1 1 1 > 1.1
1.2 1 1 > 1.2
1.2.1 1.2 1 > 1.2 > 1.2.1
1.1.1 1.1 1 > 1.1 > 1.1.1
1.1.2 1.1 1 > 1.1 > 1.1.2
答案 2 :(得分:0)
您必须在WITH
之后为表和名称设置不同的名称,并在连接的第二部分使用第二个名称。请在下面的代码中考虑attachments1
:
WITH attachments1 AS (
-- anchor
SELECT *,
[ATTACHMENTS].[Name] AS [Path]
FROM ATTACHMENTS
WHERE [ATTACHMENTS].[ParentAttachmentID] IS NULL
OR [ATTACHMENTS].ParentAttachmentID = 0
UNION ALL
--recursive member
SELECT a.*,
cast((c.[Path] + N' > ' + a.[Name]) as nvarchar(500)) as [Path]
FROM ATTACHMENTS a
INNER JOIN attachments1 c
ON a.ParentAttachmentID = c.[ID]
)
SELECT * FROM attachments1
您可以从递归成员获取Path
。