Как использовать идентификатор в качестве значения столбца при вставке - PullRequest
4 голосов
/ 20 мая 2009

У меня есть сохраненный процесс, содержащий оператор 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 имеет уникальный индекс, поэтому я хотел бы иметь возможность вставить идентификатор пользователя #, который является полем с автоматическим приращением, в качестве имени пользователя.

Кто-нибудь знает метод или переменную 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 ]

3 голосов
/ 20 мая 2009

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 вызова) в быстрой последовательности и т. Д.)

2 голосов
/ 20 мая 2009

Вместо дублирования столбца ID, вы также можете решить это во время поиска. Например, допустим, вы позволили имени пользователя NULL для любого пользователя. Затем вы можете получить имя пользователя, например:

select 
   UserName = IsNull(UserName,UserId)
from WebUsers

РЕДАКТИРОВАТЬ: Если вам нравится УНИКАЛЬНОЕ ограничение на имена, вы можете использовать вычисляемый столбец:

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 (имя, идентификатор) всякий раз, когда вы его используете. С помощью этой настройки вы можете вставить пользователей в один запрос:

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, чтобы найти имя пользователя для именованных пользователей или идентификатор для любых пользователей:

select uniqueid from WebUsers
1 голос
/ 18 декабря 2013

Как насчет того, чтобы обернуть его в транзакцию и просто использовать свой spid для имени пользователя. Как это:

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
1 голос
/ 21 января 2011

Для тех, кто использует SQL Server 2008 или более позднюю версию, есть новая функция, которая решает эту проблему довольно легко и без необходимости вносить какие-либо изменения в приложение или Proc: Фильтрованные индексы . Отфильтрованные индексы позволяют вам поставить условие WHERE на индекс, чтобы индекс игнорировал эти значения. В этом случае вы можете игнорировать нулевые значения UserName даже в уникальном индексе.

Синтаксис следующий:

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

Использование фильтрованного индекса для решения этой конкретной проблемы также означает, что вам не нужно помещать ненужные данные в поле UserName.

1 голос
/ 20 мая 2009

Можно добавить триггер AFTER INSERT, обновив поле UserName значением столбца Identity из вставленной таблицы.

0 голосов
/ 20 мая 2009

Как насчет того, чтобы обернуть его в транзакцию и просто использовать свой spid для имени пользователя. Как это:

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 () с параллельными планами запросов . Предложение OUTPUT в настоящее время намного безопаснее, пока ошибка не будет устранена.

0 голосов
/ 20 мая 2009

Это должно быть сделано в два этапа. В этом случае я просто использовал бы функцию NEWID (), чтобы сгенерировать случайное значение GUID для использования в качестве имени пользователя и впоследствии обновить его с помощью оператора update. Поскольку вы используете хранимую процедуру, количество шагов, которые вы используете в этом случае, не вызовет серьезных проблем.

Подробности о NEWID и его использовании можно найти здесь

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...