ЕСЛИ СУЩЕСТВУЕТ, ТОГДА ВЫБРАТЬ ПОЛНОСТЬЮ ВСТАВИТЬ И ТОГДА ВЫБРАТЬ

Как вы говорите следующее в Microsoft SQL Server 2005:

IF EXISTS (SELECT * FROM Table WHERE FieldValue='') THEN
   SELECT TableID FROM Table WHERE FieldValue=''
ELSE
   INSERT INTO TABLE(FieldValue) VALUES('')
   SELECT TableID FROM Table WHERE TableID=SCOPE_IDENTITY()
END IF

То, что я пытаюсь сделать, это проверить, есть ли еще пустое полевое значение, и если есть тогда возврат этого TableID, иначе вставьте пустое поле и верните соответствующий первичный ключ.

Ответ 1

Вам нужно сделать это в транзакции, чтобы гарантировать, что два одновременных клиента не будут вставлять одинаковое значение fieldValue дважды:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
    DECLARE @id AS INT
    SELECT @id = tableId FROM table WHERE [email protected]
    IF @id IS NULL
    BEGIN
       INSERT INTO table (fieldValue) VALUES (@newValue)
       SELECT @id = SCOPE_IDENTITY()
    END
    SELECT @id
COMMIT TRANSACTION

вы также можете использовать Двойную проверку блокировки, чтобы уменьшить накладные расходы на блокировку

DECLARE @id AS INT
SELECT @id = tableID FROM table (NOLOCK) WHERE [email protected]
IF @id IS NULL
BEGIN
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
    BEGIN TRANSACTION
        SELECT @id = tableID FROM table WHERE [email protected]
        IF @id IS NULL
        BEGIN
           INSERT INTO table (fieldValue) VALUES (@newValue)
           SELECT @id = SCOPE_IDENTITY()
        END
    COMMIT TRANSACTION
END
SELECT @id

Что касается того, почему необходим SERIALIZABLE ISOLATION LEVEL, когда вы находитесь внутри сериализуемой транзакции, первый SELECT, который попадает в таблицу, создает блокировку диапазона, охватывающую место, где должна быть запись, поэтому никто другой не может вставить одну и ту же запись, пока это завершение транзакции.

Без ISOLATION LEVEL SERIALIZABLE уровень изоляции по умолчанию (READ COMMITTED) не будет блокировать таблицу во время чтения, поэтому между SELECT и UPDATE кто-то все равно сможет вставить. Транзакции с уровнем изоляции READ COMMITTED не блокируют блокировку SELECT. Транзакции с REPEATABLE READS блокируют запись (если она найдена), но не разрыв.

Ответ 2

IF EXISTS (SELECT 1 FROM Table WHERE FieldValue='') 
BEGIN
   SELECT TableID FROM Table WHERE FieldValue=''
END
ELSE
BEGIN
   INSERT INTO TABLE(FieldValue) VALUES('')
   SELECT SCOPE_IDENTITY() AS TableID
END

См. здесь для получения дополнительной информации о IF ELSE

Примечание: написано без установки SQL Server, чтобы дважды проверить это, но я думаю, что это правильно.

Кроме того, я изменил бит EXISTS, чтобы сделать SELECT 1, а не SELECT *, поскольку вам все равно, что возвращается в EXISTS, если что-то есть Я также изменил бит SCOPE_IDENTITY(), чтобы вернуть только идентификатор, предполагая, что TableID является столбцом идентификации

Ответ 3

Вы были близки:

IF EXISTS (SELECT * FROM Table WHERE FieldValue='')
   SELECT TableID FROM Table WHERE FieldValue=''
ELSE
BEGIN
   INSERT INTO TABLE (FieldValue) VALUES ('')
   SELECT TableID FROM Table WHERE TableID=SCOPE_IDENTITY()
END

Ответ 4

Вам просто нужно немного изменить структуру if...else..endif:

if exists(select * from Table where FieldValue='') then begin
  select TableID from Table where FieldValue=''
end else begin
  insert into Table (FieldValue) values ('')
  select TableID from Table where TableID = scope_identity()
end

Вы также можете сделать:

if not exists(select * from Table where FieldValue='') then begin
  insert into Table (FieldValue) values ('')
end
select TableID from Table where FieldValue=''

Или:

if exists(select * from Table where FieldValue='') then begin
  select TableID from Table where FieldValue=''
end else begin
  insert into Table (FieldValue) values ('')
  select scope_identity() as TableID
end

Ответ 5

Похоже, у вашей таблицы нет ключа. Вы должны просто попробовать INSERT: если его дубликат, то ограничение ключа будет укусить, а INSERT завершится с ошибкой. Не стоит беспокоиться: вам просто нужно убедиться, что приложение не видит/игнорирует ошибку. Когда вы говорите "первичный ключ", вы, вероятно, имеете в виду значение IDENTITY. Это очень хорошо, но вам также нужно ограничение ключа (например, UNIQUE) на вашем естественном ключе.

Кроме того, я задаюсь вопросом, делает ли ваша процедура слишком много. Рассмотрите отдельные процедуры для действий "создать" и "прочитать" соответственно.

Ответ 6

DECLARE @t1 TABLE (
    TableID     int         IDENTITY,
    FieldValue  varchar(20)
)

--<< No empty string
IF EXISTS (
    SELECT *
    FROM @t1
    WHERE FieldValue = ''
) BEGIN
    SELECT TableID
    FROM @t1
    WHERE FieldValue=''
END
ELSE BEGIN
    INSERT INTO @t1 (FieldValue) VALUES ('')
    SELECT SCOPE_IDENTITY() AS TableID
END

--<< A record with an empty string already exists
IF EXISTS (
    SELECT *
    FROM @t1
    WHERE FieldValue = ''
) BEGIN
    SELECT TableID
    FROM @t1
    WHERE FieldValue=''
END
ELSE BEGIN
    INSERT INTO @t1 (FieldValue) VALUES ('')
    SELECT SCOPE_IDENTITY() AS TableID
END

Ответ 7

create schema tableName authorization dbo
go
IF OBJECT_ID ('tableName.put_fieldValue', 'P' ) IS NOT NULL 
drop proc tableName.put_fieldValue
go
create proc tableName.put_fieldValue(@fieldValue int) as
declare @tableid int = 0
select @tableid = tableid from table where fieldValue=''
if @tableid = 0 begin
   insert into table(fieldValue) values('')
   select @tableid = scope_identity()
end
return @tableid
go
declare @tablid int = 0
exec @tableid = tableName.put_fieldValue('')