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

时间:2017-10-14 09:48:27

标签: sql sql-server sql-server-2008 sql-server-2008-r2

以下是表格结构:

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] ?)

这是表格为空且第一个用户要在表格中添加的情况。否则我没有问题。

如何在同时使用insert语句在创建者列中插入Id?

2 个答案:

答案 0 :(得分:1)

一种方法可能是使用Sequence而不是identity列。以下脚本可能起到同样的作用:

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

结果:     enter image description here

答案 1 :(得分:0)

简短的回答是你不能这样做。我建议你的模型在逻辑上有缺陷。您是否打算将所有实际数据库用户(例如,创建用户...用于登录...)定义为[用户]中的行?你需要考虑一下 - 但典型的答案是否定的。如果答案是肯定的,那么您根本不需要创建者列,因为它是多余的。您只需要创建日期 - 您可能应该为其定义默认日期。

但是如果你想这样做,你需要分两步完成(你需要让列可以为空)。您插入一行(或多行),其中包含“实际”数据列的值。然后使用为id生成的标识值更新这些相同的行。显示不同方法的示例

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];

我改变了身份规范,以展示一些开发人员意识到的东西。它并不总是定义为(1,1),并且下一个插入的值可以跳转的原因有很多 - 例如错误和缓存/重启。最后,我认为你会后悔命名一个带有保留字的表,因为引用它将需要使用分隔符。减轻痛苦。