ЕСЛИ СУЩЕСТВУЕТ, ТОГДА ВЫБРАТЬ ПОЛНОСТЬЮ ВСТАВИТЬ И ТОГДА ВЫБРАТЬ
Как вы говорите следующее в 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('')