假设我有一个看起来像这样的表(例如使用 C#):
public class Table {
public Guid Id {get; set;}
public string Name {get; set;}
public Guid ParentId {get; set;}
}
这是一个递归表,其中行可以在同一个表中具有链接的父级。
假设我从头开始(即没有数据可供从其他表中选择),通过 TSQL 插入深度为 X 的新行的最佳方法是什么?
假设我希望数据看起来像这样:
此脚本的目的是进行负载测试 - 因此我没有任何可以选择的数据。数据会不断地被添加、删除和再次添加。我发现的大部分内容要么是 SELECT CTE,要么是 SELECT 之后插入。
我确信另一种选择是编写一个控制台应用程序来为我执行此操作(诚然,我的 SQL 游戏不是那么好),但我想看看是否有办法在 SQL 中执行此操作。
这个数据没有什么特别的(必然的)。您可以将插入脚本编写为 sql 插入语句。例如,为简单起见,此处使用
int
而不是 guid
ID 值:
create table Scratch (
id int,
[name] nvarchar(100),
parentId int,
primary key (id))
alter table Scratch add constraint fk_temp_id
foreign key (parentId) references Scratch(id)
insert into Scratch (id, [Name], ParentId)
values
(1, 'a', null),
(2, 'b', 1),
(3, 'c', 1),
(4, 'd', 2),
(5, 'e', 2),
(6, 'f', 3),
(7, 'g', 3)
话虽这么说,以下是如何使用高达
N
级别深度的循环创建数据 - 但超过 63 级别将会溢出 bigint
序列值。
create table dbo.Scratch (
id uniqueidentifier,
[name] nvarchar(100),
parentId uniqueidentifier,
seq int,
parentSeq int,
primary key (id));
alter table dbo.Scratch add constraint fk_temp_id
foreign key (parentId) references Scratch(id);
-- seed first row
insert into dbo.Scratch (id, [name], parentId, seq, parentSeq) values (newid(), '1', null, 1, null);
-- build binary tree with int ids
declare @I int = 0;
declare @J int = 0;
declare @K int;
declare @next_node bigint = 2;
while @I < 3
begin
set @I += 1;
set @J = 0;
set @K = cast(power(2, @I) as bigint)
while @J < @K / 2
begin
set @J += 1
insert into dbo.Scratch(id, [name], ParentId, seq, parentSeq)
values
(newid(), cast(@next_node as nvarchar(100)), null, @next_node, @next_node / 2),
(newid(), cast(@next_node+1 as nvarchar(100)), null, @next_node+1, @next_node / 2);
set @next_node += 2;
end
end
-- update the parent guids
update b
set b.parentId = a.id
from dbo.Scratch a
inner join dbo.Scratch b
on a.seq = b.ParentSeq
-- drop the temporary columns needed for creating the data
--alter table dbo.Scratch drop column parentSeq;
--alter table dbo.Scratch drop column seq;
结果:
id | 名字 | 家长ID | 序列 | 父序列 |
---|---|---|---|---|
46b16f14-20b9-4388-a7f2-832e2c56215d | 1 | 1 | ||
a613b692-138d-4207-8e84-43cccd0d2476 | 2 | 46b16f14-20b9-4388-a7f2-832e2c56215d | 2 | 1 |
0ff8b67a-63fe-4125-b2b9-bb962d67f526 | 3 | 46b16f14-20b9-4388-a7f2-832e2c56215d | 3 | 1 |
f27f82b6-d1c1-4cb2-810d-4fa21ffef5bc | 4 | a613b692-138d-4207-8e84-43cccd0d2476 | 4 | 2 |
346fd62a-6727-4414-975b-2b1659ee40c9 | 5 | a613b692-138d-4207-8e84-43cccd0d2476 | 5 | 2 |
a8d3928a-6357-47ef-b1d0-0be76fa9233f | 6 | 0ff8b67a-63fe-4125-b2b9-bb962d67f526 | 6 | 3 |
1c669bdf-63e1-44af-858b-752003d9a980 | 7 | 0ff8b67a-63fe-4125-b2b9-bb962d67f526 | 7 | 3 |
6e7ac3b1-921e-4ddc-a0c7-ad9499992689 | 8 | f27f82b6-d1c1-4cb2-810d-4fa21ffef5bc | 8 | 4 |
6786d550-7df8-4ecd-b7ca-6b7658b91b68 | 9 | f27f82b6-d1c1-4cb2-810d-4fa21ffef5bc | 9 | 4 |
d3113177-cc37-4d55-8b2d-b603345837a8 | 10 | 346fd62a-6727-4414-975b-2b1659ee40c9 | 10 | 5 |
bb32c1c2-9f75-455e-9aa1-fc38ddd25a6c | 11 | 346fd62a-6727-4414-975b-2b1659ee40c9 | 11 | 5 |
1a218245-b6d4-41a7-8117-1b3d0bd2fdd9 | 12 | a8d3928a-6357-47ef-b1d0-0be76fa9233f | 12 | 6 |
fe8f1882-a89a-4201-83f4-b72e528ba9da | 13 | a8d3928a-6357-47ef-b1d0-0be76fa9233f | 13 | 6 |
edab3434-c038-406e-bcf0-b6139feff6c0 | 14 | 1c669bdf-63e1-44af-858b-752003d9a980 | 14 | 7 |
19007b10-e86f-4fe4-9c0f-8e739f4a5c89 | 15 | 1c669bdf-63e1-44af-858b-752003d9a980 | 15 | 7 |
我最终做了这样的事情(WHILE循环示例):
DECLARE @TopLevelCount BIGINT = 1
DECLARE @parentFolderId UNIQUEIDENTIFIER = NULL
WHILE @TopLevelCount < 2
BEGIN
DECLARE @TopLevelId UNIQUEIDENTIFIER = NEWID()
INSERT INTO Table(Id, [Name], ParentId)
VALUES (@TopLevelId, 'Item ' + CAST(@TopLevelCount AS NVARCHAR(5)), @parentFolderId)
DECLARE @TopLevel2Count BIGINT = @TopLevelCount * 10
WHILE @TopLevel2Count < (@TopLevelCount * 10) + 2
BEGIN
DECLARE @TopLevel2Id UNIQUEIDENTIFIER = NEWID()
INSERT INTO Table(Id, [Name], ParentId)
VALUES (@TopLevel2Id, 'Item ' + CAST(@TopLevel2Count AS NVARCHAR(MAX)), @TopLevelId)
DECLARE @TopLevel3Count BIGINT = @TopLevel2Count * 10
WHILE @TopLevel3Count < (@TopLevel2Count * 10) + 2
BEGIN
DECLARE @TopLevel3Id UNIQUEIDENTIFIER = NEWID()
INSERT INTO Table(Id, [Name], ParentId)
VALUES (@TopLevel3Id, 'Item ' + CAST(@TopLevel3Count AS NVARCHAR(MAX)), @TopLevel2Id)
DECLARE @TopLevel4Count BIGINT = @TopLevel3Count * 10
WHILE @TopLevel4Count < (@TopLevel3Count * 10) + 2
BEGIN
DECLARE @TopLevel4Id UNIQUEIDENTIFIER = NEWID()
INSERT INTO Table(Id, [Name], ParentId)
VALUES (@TopLevel4Id, 'Item ' + CAST(@TopLevel4Count AS NVARCHAR(MAX)), @TopLevel3Id)
DECLARE @TopLevel5Count BIGINT = @TopLevel4Count * 10
WHILE @TopLevel5Count < (@TopLevel4Count * 10) + 2
BEGIN
DECLARE @TopLevel5Id UNIQUEIDENTIFIER = NEWID()
INSERT INTO Table(Id, [Name], ParentId)
VALUES (@TopLevel5Id, 'Item ' + CAST(@TopLevel5Count AS NVARCHAR(MAX)), @TopLevel4Id)
DECLARE @TopLevel6Count BIGINT = @TopLevel5Count * 10
WHILE @TopLevel6Count < (@TopLevel5Count * 10) + 2
BEGIN
DECLARE @TopLevel6Id UNIQUEIDENTIFIER = NEWID()
INSERT INTO Table(Id, [Name], ParentId)
VALUES (@TopLevel6Id, 'Item ' + CAST(@TopLevel6Count AS NVARCHAR(MAX)), @TopLevel5Id)
SET @TopLevel6Count = @TopLevel6Count + 1
END
SET @TopLevel5Count = @TopLevel5Count + 1
END
SET @TopLevel4Count = @TopLevel4Count + 1
END
SET @TopLevel3Count = @TopLevel3Count + 1
END
SET @TopLevel2Count = @TopLevel2Count + 1
END
SET @TopLevelCount = @TopLevelCount + 1
END
深度为 6。我将标记 @topsail 的答案是这个问题的正确答案,因为实际上没有人可能需要这么大的深度(在我的例子中我需要 6)。