2009-09-28 12 views
42

How do you say następujących w Microsoft SQL Server 2005:IF EXISTS, a następnie wybierz ELSE INSERT, a następnie wybierz

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 

Co staram się zrobić, to sprawdzić, czy jest już pusta fieldvalue, i jeśli następnie jest zwracana wartość TableID, należy wstawić pustą wartość pola i zwrócić odpowiedni klucz podstawowy.

+1

Jakiego języka programowania używasz? To może być lepiej wykonane w ramach transakcji. –

+0

możliwy duplikat [Tylko wstawianie wiersza, jeśli jeszcze go tam nie ma] (http://stackoverflow.com/questions/3407857/only-inserting-a-row-if-its-not-already-there) –

+0

To jest starsze pytanie; Pytanie "Tylko wstawianie wiersza" powinno być zamknięte jako duplikat tego, a nie odwrotnie. –

Odpowiedz

6

Byłaś blisko:

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 
28
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 

Zobacz here uzyskać więcej informacji na temat if else

Uwaga: napisany bez SQL Server zainstalować poręczny, aby dokładnie sprawdzić to, ale myślę, że to jest poprawna

Zmieniłem także bit EXISTS, aby wykonać SELECT 1 zamiast SELECT *, ponieważ nie obchodzi cię, co jest zwracane w EXISTS, o ile coś jest Ja również zmienił SCOPE_IDENTITY (bit), aby powrócić tylko tożsamość zakładając, że tableid jest kolumna tożsamości

+2

"WYBIERZ 1" nie ma znaczenia. Czy zmieniasz go, by podkreślić, że nie dbasz o szczegóły? To nie pomaga w wydajności. –

+2

SELCET 1, SELECT NULL, SELECT * są równe w ISTNIEJĄCYCH ... – gbn

+4

Wolę unikać SELECT * w moim kodzie - nie ma w nim dobrego nawyku, więc zwykle robię SELECT 1 podczas robienia an istnieje – Jane

58

trzeba zrobić to w transakcji, aby zapewnić dwa jednoczesne klientów nie będzie wstawić samą fieldValue dwukrotnie:

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

można również użyć Double-checked locking celu zmniejszenia blokowania napowietrznych

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 

Jak, dlaczego IZOLACJA POZIOM SERIALIZABLE jest konieczne, gdy jesteś wewnątrz serializable transakcji najpierw wybrać tha t trafienia tabeli tworzy blokadę zasięgu obejmującą miejsce, w którym powinien być rekord, więc nikt inny nie może wstawić tego samego rekordu, dopóki ta transakcja się nie skończy.

Bez ISOLATION LEVEL SERIALIZABLE, domyślny poziom izolacji (READ COMMITTED) nie blokowałby tabeli w czasie odczytu, więc pomiędzy SELECT i UPDATE ktoś mógłby nadal być w stanie wstawić. Transakcje z poziomem izolacji READ COMMITTED nie powodują blokady SELECT. Transakcje z REPEATABLE READS blokują rekord (jeśli został znaleziony), ale nie lukę.

+6

+1 Nie rozumiem, dlaczego ** tylko ** odpowiedź, która uwzględnia warunki wyścigu i współbieżność, marnieje na zero głosów. –

+2

Czy możesz rozwinąć, aby wyjaśnić, dlaczego "POZIOM ISOLATION SERIALIZABLE" jest konieczny, a co może się stać, jeśli tego nie ustawisz? – binki

+5

@binki, gdy wewnątrz transakcji serializowalnej, pierwszy SELECT, który uderza w tabelę, tworzy * zakres blokady * obejmujący miejsce, w którym powinien być rekord, więc nikt inny nie może wstawić tego samego rekordu, dopóki ta transakcja się nie skończy. Bez funkcji ISOLATION LEVEL SERIALIZABLE, domyślny poziom izolacji (READ COMMITTED) nie blokowałby tabeli w czasie odczytu, więc pomiędzy wyborem i aktualizacją ktoś mógłby nadal móc wstawić. Transakcje z poziomem izolacji READ COMMITTED nie powodują blokowania SELECT. Transakcje z REPEATABLE READS, blokują rekord (jeśli go znaleziono), ale nie lukę. – zvolkov

2

Po prostu trzeba zmienić strukturę if...else..endif nieco:

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 

Można również zrobić:

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

Lub:

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 
1
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 
2

Brzmi jak twoja tabela nie ma klucza. Powinieneś być w stanie po prostu wypróbować INSERT: jeśli jest to duplikat, to kluczowe ograniczenie będzie gryzło, a INSERT zawiedzie. Bez obaw: wystarczy upewnić się, że aplikacja nie wyświetla/ignoruje błędu. Kiedy mówisz "klucz podstawowy", prawdopodobnie oznacza wartość IDENTITY. Wszystko to bardzo dobrze, ale potrzebujesz również klucza kluczowego (np. UNIQUE) na swoim naturalnym kluczu.

Zastanawiam się również, czy twoja procedura robi zbyt wiele. Rozważ osobne procedury odpowiednio dla działań "twórz" i "czytaj".

1
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('') 
Powiązane problemy