如何在插入期间将标识用作列值

时间:2009-05-20 15:06:02

标签: sql sql-server tsql

我有一个包含SQL语句的存储过程,类似于:

CREATE PROCEDURE SaveUser
@UserName nvarchar(10),
@FirstName nvarchar(150),
@LastName nvarchar(150)

AS
BEGIN

    INSERT INTO Users (UserName, FirstName, LastName)
    VALUES (@UserName, @FirstName, @LastName)

    SELECT SCOPE_IDENTITY()

END

有些用户无法登录系统,也没有用户名;但是,UserName字段上有一个唯一索引,所以我希望能够将用户ID#(一个自动增量字段)作为UserName插入。

是否有人知道MS SQL Server方法或变量可以让我做类似的事情?

INSERT INTO Users (UserName, FirstName, LastName)
VALUES (ISNULL(@UserName, SCOPE_IDENTITY()),@FirstName,@LastName)

编辑我现在的意图是使用类似下面的内容

DECLARE @NextID int
SET @NextID = IDENT_CURRENT(Users) + IDENT_INCR(Users)


INSERT INTO Users (UserName, FirstName, LastName)
VALUES (ISNULL(@UserName, @NextID), @FirstName, @LastName)

我想知道是否有内置方法可以保证这是一致的。

7 个答案:

答案 0 :(得分:3)

INSERT NULL或随机字符串,然后使用scope_identity进行更新

INSERT INTO Users (UserName, FirstName, LastName)
VALUES (ISNULL(@UserName, CAST(NEWID() AS varchar(50)),@FirstName,@LastName)

UPDATE Users
SET @UserName = CAST(SCOPE_IDENTITY() AS int)
WHERE UserId = SCOPE_IDENTITY()

(道歉,如果newid没有直接投射......现在无法测试)

可以使用聚合进行一体化但快速连续不可靠(2次调用)等。

答案 1 :(得分:2)

您可以在检索时解决此问题,而不是复制ID列。例如,假设您为任意用户使用用户名为NULL。然后,您可以检索用户名,如:

select 
   UserName = IsNull(UserName,UserId)
from WebUsers

编辑:如果您喜欢名称上的UNIQUE约束,则可以使用计算列:

create table WebUsers (
    id int identity,
    name varchar(12),
    uniqueid as isnull(name,id)
)

create unique index ix_webusers_uniqueid on WebUsers (uniqueid)

列uniqueid是一个计算列,只要您使用它就会转换为isnull(name,id)。使用此设置,您可以在一个查询中插入用户:

insert into WebUsers (name) values ('Dark Lord')
insert into WebUsers (name) values ('The Ring')
insert into WebUsers (name) values (NULL)
insert into WebUsers (name) values (NULL)

但不是重复用户;以下将挽救错误:

insert into WebUsers (name) values ('The Ring')

您可以在uniqueid上查询以查找指定用户的用户名,或查找任意用户的ID:

select uniqueid from WebUsers

答案 2 :(得分:1)

对于使用SQL Server 2008或更高版本的任何人,有一项新功能可以非常轻松地解决此问题,无需进行任何应用程序或Proc更改:过滤索引。过滤的索引允许您在索引上放置WHERE条件,以便索引将忽略这些值。在这种情况下,即使在唯一索引中,也可以忽略空的UserName值。

语法如下:

CREATE UNIQUE INDEX IX_Users_UserName ON Users (UserName) 
                         WHERE UserName is not null

使用Filtered Index解决此特定问题也意味着您不需要将垃圾数据放入UserName字段。

答案 3 :(得分:1)

如何将它包装在事务中,并将spid用于UserName。像这样:

CREATE PROC BlahBlah
...
BEGIN
BEGIN TRAN UserInsert
BEGIN TRY

    INSERT INTO Users (UserName, FirstName, LastName)    
    VALUES (COALESCE(@UserName,@@SPID), @FirstName, @LastName)

    IF @UserName IS NULL
    BEGIN
    DECLARE @MyUserID int  
    SET @MyUserID = (SELECT SCOPE_IDENTITY())

    UPDATE Users
    SET UserName = @MyUserID
    WHERE UserID = @MyUserID
    END
END TRY
BEGIN CATCH
    ROLLBACK TRAN UserInsert
    RETURN
END CATCH
COMMIT TRAN UserInsert
END

答案 4 :(得分:1)

您可以添加AFTER INSERT触发器,使用插入的表格中的Identity列值更新UserName字段。

答案 5 :(得分:0)

必须分两步完成。在这种情况下,我只需使用NEWID()函数生成用于用户名的随机GUID值,然后使用update语句对其进行更新。由于您使用存储过程,因此在这种情况下使用的步骤数量不会导致重大问题。

有关NEWID及其使用的详细信息,请访问here

答案 6 :(得分:0)

如何将它包装在事务中,并将spid用于UserName。像这样:

CREATE PROC BlahBlah
...
BEGIN
BEGIN TRAN UserInsert
BEGIN TRY

    INSERT INTO Users (UserName, FirstName, LastName)    
    VALUES (COALESCE(@UserName,@@SPID), @FirstName, @LastName)

    IF @UserName IS NULL
    BEGIN
    DECLARE @MyUserID int  
    SET @MyUserID = (SELECT SCOPE_IDENTITY())

    UPDATE Users
    SET UserName = @MyUserID
    WHERE UserID = @MyUserID
    END
END TRY
BEGIN CATCH
    ROLLBACK TRAN UserInsert
    RETURN
END CATCH
COMMIT TRAN UserInsert
END

其他几点:    - 这似乎是一个非常奇怪的要求。您可能想看看您的设计。    - 小心SCOPE_IDENTITY() issues with parallel query plans。 <{3}}子句目前更安全,直到bug得到解决。