将自引用条目插入 SQL Server

Rem*_*emy 3 database-design sql-server t-sql

我有一个Person表,其中有一created_by列引用了表本身的主 ID。因此,可能是一名员工将另一名员工添加到数据库中。它工作正常。

People也可以自己添加(注册)。所以created_by列中的值应该是该列的自动递增值id。但该值显然在插入之后才可用。

所以我可以(a)使引用不检查值,(b)在开头添加一个默认值或(c)使列可以为空。所有的选择对我来说都不好。

MySQL的方言是这样的:

SET FOREIGN_KEY_CHECKS = 0;
INSERT INTO EMPLOYEE VALUES('12345','67890'),('67890','12345');
SET FOREIGN_KEY_CHECKS = 1;
Run Code Online (Sandbox Code Playgroud)

...但我找不到 SQL Server 的 T-SQL 类似的东西。

Dav*_*ett 6

将自引用条目插入 SQL Server

对于标题中的一般问题,您可以在简单的插入中添加直接循环引用,例如

INSERT node 
       (id , name  , parent_id) 
VALUES (123, 'Test', 123      )
Run Code Online (Sandbox Code Playgroud)

因为考虑到所有新数据都会强制执行约束:只要语句完成后 FX 引用的值存在,一切都很好。这与在链表中插入多个值相同:

INSERT node 
       (id , name   , parent_id) 
VALUES (101, 'Test1', 100      )
     , (102, 'Test2', 101      )
     , (103, 'Test3', 102      )
Run Code Online (Sandbox Code Playgroud)

或间接循环引用:

INSERT node 
       (id , name   , parent_id) 
VALUES (201, 'Test5', 203      )
     , (202, 'Test6', 201      )
     , (203, 'Test7', 202      )
Run Code Online (Sandbox Code Playgroud)

所以 created_by 列中的值应该是 id 列的自增值

这带来了一个问题,因为在插入之前您不知道生成的是什么,实际上没有可靠的知道方法。对于插入单个行,您可以使用SCOPE_IDENTITY()立即更新新行:

INSERT node 
       (name  ) 
VALUES ('Test')
-- and now make the circular reference
UPDATE node 
SET    parent_id = SCOPE_IDENTITY()  
WHERE  id        = SCOPE_IDENTITY()
Run Code Online (Sandbox Code Playgroud)

在上面的例子中 ifparent_id是一个必需的列(NOT NULL用 no声明DEFAULT)那么你需要提供一个虚拟的临时有效值而不是把它放在初始INSERT语句之外,像这样:

INSERT node 
       (name  , parent_id) 
VALUES ('Test', 0        )
-- and now make the circular reference
UPDATE node 
SET    parent_id = SCOPE_IDENTITY()  
WHERE  id        = SCOPE_IDENTITY()
Run Code Online (Sandbox Code Playgroud)

为了处理多行的插入(在您描述的情况下不太可能,但在其他地方很常见),您可以使用该OUTPUT子句读取为每行创建的 ID 以供进一步参考。我不会在这里讨论,因为它对当前问题来说太过分了,请参阅https://docs.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql了解更多详细信息.

所以 created_by 列中的值应该是 id 列的自增值

事实证明,SEQUENCE如果您拥有 2012 或更高版本,则可以使用 SQL Server 中的a 完成此操作,这是比上述多语句选项更简洁的解决方案,请参阅 SQLing4ever 的答案以获取有效示例。在 2012 年之前,此功能不可用,因此如果您需要支持较旧的 SQL Server 实例,则需要回退到此答案中的方法。


SQL*_*ver 5

看起来您正在为主键使用身份。如果您需要灵活使用主键,我建议您使用序列。它看起来像这样。

CREATE SEQUENCE SQ_temp AS INT INCREMENT BY 1 START WITH 1

CREATE TABLE Users 
( ID INT PRIMARY KEY DEFAULT NEXT VALUE FOR SQ_temp
, UserName VARCHAR(30)
, createdBy INT REFERENCES Users (ID)
)


INSERT INTO dbo.Users
(
    ID
    , UserName
    , createdBy
)
VALUES
(NEXT VALUE FOR SQ_temp,'User1', NEXT VALUE FOR SQ_temp)
, (NEXT VALUE FOR SQ_temp,'User2', NEXT VALUE FOR SQ_temp)
, (NEXT VALUE FOR SQ_temp,'User3', NEXT VALUE FOR SQ_temp)


SELECT *
FROM dbo.Users AS u
Run Code Online (Sandbox Code Playgroud)