插入自引用表

时间:2012-11-10 09:03:02

标签: sql sql-server

如果我有桌子

Table
{
ID int primary key identity,
ParentID int not null foreign key references Table(ID)
}

如何将第一行插入表中?

从业务逻辑的角度来看,不应该删除ParentID上的空约束。

3 个答案:

答案 0 :(得分:5)

在SQL Server中,一个简单的INSERT将执行:

create table dbo.Foo
(
ID int primary key identity,
ParentID int not null foreign key references foo(ID)
)
go

insert dbo.Foo (parentId) values (1)

select * from dbo.Foo

结果

    ID          ParentID
----------- -----------
    1           1

如果您尝试插入的值与您的身份种子不同,则插入将失败。

更新:

关于上下文是什么(即代码应该在实时生产系统中工作还是仅仅是数据库设置脚本)的问题并不是很明确,而且从注释看起来硬编码ID可能不是一个选项。虽然上面的代码通常可以在数据库初始化脚本中正常工作,其中层次结构根ID可能需要知道并且是常量,但是对于林(几个根目录未提前知道ID),以下内容应按预期工作:

create table dbo.Foo
(
ID int primary key identity,
ParentID int not null foreign key references foo(ID)
)
go

insert dbo.Foo (parentId) values (IDENT_CURRENT('dbo.Foo'))

然后可以照常查询最后一个身份(SCOPE_IDENTITY等)。为了解决@ usr的问题,代码实际上是事务安全的,如下例所示:

insert dbo.Foo (parentId) values (IDENT_CURRENT('dbo.Foo'))
insert dbo.Foo (parentId) values (IDENT_CURRENT('dbo.Foo'))
insert dbo.Foo (parentId) values (IDENT_CURRENT('dbo.Foo'))

select * from dbo.Foo

select IDENT_CURRENT('dbo.Foo')
begin transaction   
    insert dbo.Foo (parentId) values (IDENT_CURRENT('dbo.Foo'))
    rollback

select IDENT_CURRENT('dbo.Foo')

insert dbo.Foo (parentId) values (IDENT_CURRENT('dbo.Foo'))

select * from dbo.Foo

结果:

ID          ParentID
----------- -----------
1           1
2           2
3           3

currentIdentity
---------------------------------------
3

currentIdentity
---------------------------------------
4

ID          ParentID
----------- -----------
1           1
2           2
3           3
5           5

答案 1 :(得分:2)

如果您需要为第一个ID使用显式值,则在插入第一个记录时,可以禁用IDENTITY值的检查(请参阅:MSDN: SET IDENTITY_INSERT (Transact-SQL))。

这是一个证明这一点的例子:

CREATE TABLE MyTable
(
  ID int PRIMARY KEY IDENTITY(1, 1),
  ParentID int NOT NULL,
  CONSTRAINT MyTable_ID FOREIGN KEY (ParentID) REFERENCES MyTable(ID)
);

SET IDENTITY_INSERT MyTable ON;
INSERT INTO MyTable (ID, ParentID)
VALUES (1, 1);
SET IDENTITY_INSERT MyTable OFF;

WHILE @@IDENTITY <= 5
BEGIN
    INSERT INTO MyTable (ParentID)
    VALUES (@@IDENTITY);
END;

SELECT *
  FROM MyTable;

IF OBJECT_ID('MyTable') IS NOT NULL
    DROP TABLE MyTable;

答案 2 :(得分:2)

对于树中的根节点,似乎NOT NULL约束不正确。它根本就没有父母。因此,ParentIDNOT NULL的假设从一开始就被打破了。

我建议您将其设为可为空并在ParentID上添加索引,以验证只有一个值NULL

create unique nonclustered index ... on T (ParentID) where (ParentID IS NULL)

在SQL Server中强制实施声音树结构很困难。例如,您可以在图表中获得多个根或循环。很难对所有这些进行验证,目前还不清楚是否值得付出努力。根据具体情况,可能会这样。