简体   繁体   English

如何将自动生成的主键ID保存在同一表的外键列中

[英]How to save auto generated primary key Id in foreign key column in same table

Following is the table structure: 表结构如下:

CREATE TABLE [User] (
        [Id] bigint identity(1,1) not null,
        [FirstName] nvarchar(100) not null,
        [LastName] nvarchar(100) not null,
        [Title] nvarchar(5) null,
        [UserName] nvarchar(100) not null,
        [Password] nvarchar(100) not null,      
        [Inactive] bit null,
        [Created] Datetime not null,
        [Creator] bigint not null,
        [Modified] DateTime null,
        [Modifier] bigint null
        CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED
        (
            [Id] Asc
        )
    );

IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[FK_User_Creator]') AND parent_object_id = OBJECT_ID(N'[User]'))
    ALTER TABLE [User] ADD CONSTRAINT [FK_User_Creator] FOREIGN KEY([Creator]) REFERENCES [User]([Id])
GO


INSERT INTO [User] (Creator) Values ([Id] ?)

This is a case when table is empty and first user is going to add in table. 当表为空并且第一个用户要添加表时,就是这种情况。 Otherwise I don't have issue. 否则我没有问题。

How can I insert Id in creator column with insert statement at the same time? 如何使用插入语句同时在创建者列中插入ID?

One way could be using Sequence instead of identity column. 一种方法是使用序列而不是标识列。 The below script might serve the same purpose: 以下脚本可能达到相同的目的:

CREATE SEQUENCE dbo.useridsequence  
    AS int  
    START WITH 1  
    INCREMENT BY 1 ;  
GO

CREATE TABLE [User] (
        [Id] bigint DEFAULT (NEXT VALUE FOR dbo.useridsequence) ,
        [FirstName] nvarchar(100) not null,
        [LastName] nvarchar(100) not null,
        [Title] nvarchar(5) null,
        [UserName] nvarchar(100) not null,
        [Password] nvarchar(100) not null,      
        [Inactive] bit null,
        [Created] Datetime not null,
        [Creator] bigint DEFAULT NEXT VALUE FOR dbo.useridsequence ,
        [Modified] DateTime null,
        [Modifier] bigint null
        CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED
        (
            [Id] Asc
        )
    );

IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[FK_User_Creator]') AND parent_object_id = OBJECT_ID(N'[User]'))
    ALTER TABLE [User] ADD CONSTRAINT [FK_User_Creator] FOREIGN KEY([Creator]) REFERENCES [User]([Id])
GO


INSERT INTO [User]
(
    -- Id -- this column value is auto-generated
    FirstName,
    LastName,
    Title,
    UserName,
    [Password],
    Inactive,
    Created,
    Creator,
    Modified,
    Modifier
)
VALUES
(
    'Foo',
    'Bar',
    'Title',
    'UserName ',
    'Password',
     0,
    GETDATE(),
    DEFAULT,
    GETDATE(),
    1 
)

SELECT * FROM [User] AS u

Result : 结果: 在此处输入图片说明

The short answer is that you can't do this. 简短的答案是您无法执行此操作。 And I suggest your model is logically flawed in the first place. 我建议您的模型从逻辑上说是有缺陷的。 Do you intend to define all actual database users (eg, create user ... for login ...) as rows in [Users]? 您是否打算将所有实际的数据库用户(例如,为登录创建用户...)定义为[用户]中的行? You need to think about that - but the typical answer is no. 您需要考虑一下-但是典型的答案是“否”。 If the answer is yes, then you don't need the creator column at all because it is redundant. 如果答案是肯定的,那么您根本不需要创建者列,因为它是多余的。 All you need is the created date - for which you probably should have defined a default. 您只需要创建日期-您可能应该为其定义一个默认日期。

But if you want to do this, you will need to do it in two steps (and you will need to make the column nullable). 但是,如果要执行此操作,则需要分两步执行(并且需要使该列可为空)。 You insert a row (or rows) with values for the "real" data columns. 您插入一行(或多行),其中包含“真实”数据列的值。 Then update those same rows with the identity values generated for id. 然后使用为id生成的标识值更新这些相同的行。 An example showing different ways to do this 一个示例,显示了执行此操作的不同方法

use tempdb;
set nocount on;
CREATE TABLE dbo.[user] (
        [user_id] smallint identity(3,10) not null primary key,
        [name] nvarchar(20) not null,
        [active] bit  not null default (1),
        [created] Datetime not null default (current_timestamp),
        [creator] smallint  null  
    );
ALTER TABLE dbo.[user] ADD CONSTRAINT [fk_user] FOREIGN KEY(creator) REFERENCES dbo.[user](user_id);
GO
-- add first row
insert dbo.[user] (name) values ('test');
update dbo.[user] set creator = SCOPE_IDENTITY() where user_id = SCOPE_IDENTITY()

-- add two more rows
declare @ids table (user_id smallint not null);
insert dbo.[user] (name) output inserted.user_id into @ids
values ('nerk'), ('pom');
update t1 set creator = t1.user_id
from @ids as newrows inner join dbo.[user] as t1 on newrows.user_id = t1.user_id;
select * from dbo.[user] order by user_id;

-- mess things up a bit
delete dbo.[user] where name = 'pom';

-- create an error, consume an identity value
insert dbo.[user](name) values (null);

-- add 2 morerows
delete @ids; 
insert dbo.[user] (name) output inserted.user_id into @ids
values ('nerk'), ('pom');
update t1 set creator = t1.user_id
from @ids as newrows inner join dbo.[user] as t1 on newrows.user_id = t1.user_id;

select * from dbo.[user] order by user_id;
drop table dbo.[user];

And I changed the identity specification to demonstrate something few developers realize. 我更改了身份规范,以展示一些开发人员意识到的东西。 It isn't always defined as (1,1) and the next inserted value can jump for many reasons - errors and caching/restarts for example. 它并不总是定义为(1,1),并且下一个插入的值可能由于多种原因而跳转-例如错误和缓存/重新启动。 Lastly, I think you will regret naming a table with a reserved word since references to it will require the use of delimiters. 最后,我想您会后悔用保留字命名表,因为对其的引用将需要使用定界符。 Reduce the pain. 减轻痛苦。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM