我有一个递归CTE,它给出了一组父子键的列表,如下所示,让我们在一个名为[#relationtree]的临时表中说明:
Parent | Child
--------------
1 | 3
3 | 5
5 | 6
5 | 9
我想将这些关系的副本创建到一个表中,例如,使用以下结构:
CREATE TABLE [dbo].[Relations]
(
[Id] int identity(1,1)
[ParentId] int
)
如何插入上述记录,但递归获取先前插入的标识值,以便能够为我插入的子项的每个副本插入该值作为ParentId列?
我期待在[dbo]中结束这个。[关系](鉴于我们当前的种子价值,比如50)
Id | ParentId
-------------
... other rows present before this query ...
50 | NULL
51 | 50
52 | 51
53 | 51
我不确定scope_identity在这种情况下是否可以工作,或者创建一个包含新ID列表并手动插入标识列的新临时表是正确的方法吗?
我可以编写一个游标/循环来执行此操作,但必须有一种很好的方法来做一些递归选择魔法!
答案 0 :(得分:1)
由于您尝试将树放入表格的某个部分,因此无论如何您似乎都需要使用SET IDENTITY_INSERT ON
。您需要确保新树有空间。在这种情况下,我假设49是表中当前的最大id
,因此我们不需要担心超出表中后面的树。
您需要能够将ID从旧树映射到新树。除非有一些围绕id的规则,否则确切的映射应该是无关紧要的,只要它是准确的,所以在这种情况下,我只是做这样的事情:
SET IDENTITY_INSERT dbo.Relations ON
;WITH CTE_MappedIDs AS
(
SELECT
old_id,
ROW_NUMBER() OVER(ORDER BY old_id) + 49 AS new_id
FROM
(
SELECT DISTINCT parent AS old_id FROM #relationtree
UNION
SELECT DISTINCT child AS old_id FROM #relationtree
) SQ
)
INSERT INTO dbo.Relations (Id, ParentId)
SELECT
CID.new_id,
PID.new_id
FROM
#relationtree RT
INNER JOIN CTE_MappedIDs PID ON PID.old_id = RT.parent
INNER JOIN CTE_MappedIDs CID ON CID.old_id = RT.parent
-- We need to also add the root node
UNION ALL
SELECT
NID.new_id,
NULL
FROM
#relationtree RT2
INNER JOIN CTE_MappedIDs NID ON NID.old_id = RT2.parent
WHERE
RT2.parent NOT IN (SELECT DISTINCT child FROM #relationtree)
SET IDENTITY_INSERT dbo.Relations OFF
我没有对此进行过测试,但如果它没有按预期工作,那么希望它会指向正确的方向。
答案 1 :(得分:0)
我知道你已经有了一个合适的答案,但我认为你可以更简单地完成同样的事情(并不是Tom H的回答有任何错误)使用LAG函数来检查上一行,假设您有SQL Server 2012或更高版本。
设定:
CREATE TABLE #relationtree (
Parent INT,
Child INT
)
CREATE TABLE #relations (
Id INT IDENTITY(1,1),
ParentId INT
)
INSERT INTO #relationtree (Parent, Child) VALUES(1,3), (3,5), (5,6), (5,9)
INSERT INTO #relations (ParentId) values(1), (3), (5)
解决方案:
DECLARE @offset INT = IDENT_CURRENT('#relations')
;WITH relationtreeids AS (
SELECT *,
ROW_NUMBER() OVER(ORDER BY Parent, Child) - 2 AS UnmodifiedParentId -- Simulate an identity field
FROM #relationtree
)
INSERT INTO #relations
-- The LAG window function allows you to inspect the previous row
SELECT CASE WHEN LAG(Parent) OVER(ORDER BY Parent) IS NULL
THEN NULL
WHEN LAG(Parent) OVER(ORDER BY Parent) = Parent
THEN UnmodifiedParentId + @offset ELSE UnmodifiedParentId + @offset + 1
END AS ParentId
FROM relationtreeids
输出:
Id ParentId
1 1
2 3
3 5
4 NULL
5 4
6 5
7 5