2009-09-28 12 views
42

Come si dice quanto segue in Microsoft SQL Server 2005:SE ESISTE, quindi selezionare Inserisci ELSE e quindi selezionare

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 

Quello che sto cercando di fare è di vedere se c'è un vuoto fieldvalue già, e se vi è quindi restituire tale TableID, altrimenti inserire un valore di campo vuoto e restituire la chiave primaria corrispondente.

+1

Quale linguaggio di programmazione stai utilizzando? Questo potrebbe essere fatto meglio all'interno di una transazione. –

+0

possibile duplicato di [Inserire solo una riga se non è già lì] (http://stackoverflow.com/questions/3407857/only-inserting-a-row-if-its-not-already-there) –

+0

Questo è il vecchia domanda; la domanda "Solo inserendo una riga" dovrebbe essere chiusa come duplicato di questo, non il contrario. –

risposta

6

Eri vicino:

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 

Vedi here per ulteriori informazioni su IF ELSE

Nota: scritto senza una installazione di SQL Server a portata di mano per ricontrollare questo, ma penso che sia corretto

Inoltre, ho modificato il bit EXISTS per eseguire SELECT 1 anziché SELECT * poiché non ti interessa cosa viene restituito all'interno di un EXISTS, a condizione che qualcosa sia Ho anche cambiato il bit SCOPE_IDENTITY() per tornare solo l'identità assumendo che TableID è la colonna di identità

+2

'SELECT 1' non ha importanza. Stai cambiando solo per sottolineare che non ti interessa i dettagli? Non aiuta le prestazioni. –

+2

SELCET 1, SELECT NULL, SELECT * sono tutti uguali in EXISTS ... – gbn

+4

Preferisco evitare SELECT * tra il mio codice - non mi sembra una buona abitudine, quindi di solito faccio una SELECT 1 quando faccio esiste – Jane

58

Hai bisogno di fare questo in un'operazione per garantire due connessioni simultanee non inserirà stesso fieldValue due volte:

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 

è anche possibile utilizzare Double-checked locking per ridurre il blocco sovraccarico

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 

per quanto riguarda il motivo per cui ISOLATION LEVEL SERIALIZABLE è necessario, quando si è all'interno di una transazione serializzabile, la prima tha SELEZIONA t colpisce la tabella crea un blocco dell'intervallo che copre il punto in cui deve essere il record, in modo che nessun altro possa inserire lo stesso record fino al termine della transazione.

Senza LIVELLO DI ISOLAMENTO SERIALIZZABILE, il livello di isolamento predefinito (READ COMMITTED) non bloccava la tabella in fase di lettura, quindi tra SELECT e UPDATE, qualcuno potrebbe ancora essere in grado di inserire. Le transazioni con il livello di isolamento READ COMMITTED non causano il blocco di SELECT. Le transazioni con REPEATABLE READS bloccano il record (se trovato) ma non il gap.

+6

+1 Non capisco perché la ** sola ** risposta che considera condizioni di competizione e concorrenza sta languendo su zero voti. –

+2

Si può espandere per spiegare perché 'ISOLATION LEVEL SERIALIZABLE' è necessario e cosa può accadere se non lo si imposta? – binki

+5

@binki, quando all'interno di una transazione serializzabile, il primo SELECT che raggiunge la tabella, crea un * intervallo di blocco * che copre il punto in cui deve essere il record, quindi nessun altro può inserire lo stesso record, fino a quando questa transazione non termina. Senza ISOLATION LEVEL SERIALIZABLE, il livello di isolamento predefinito (READ COMMITTED) non bloccherebbe la tabella in fase di lettura, quindi tra select e update, qualcuno sarebbe ancora in grado di inserirlo. Le transazioni con il livello di isolamento READ COMMITTED non causano il blocco di SELECT. Le transazioni con REPEATABLE READS, bloccano il record (se trovato) ma non il gap. – zvolkov

2

Non vi resta che cambiare la struttura del if...else..endif un po ':

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 

Si potrebbe anche fare:

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

Oppure:

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

Suona come il tuo tavolo non ha chiave Dovresti essere in grado di provare semplicemente lo INSERT: se si tratta di un duplicato, il vincolo della chiave si morde e lo INSERT avrà esito negativo. Nessun problema: devi solo assicurarti che l'applicazione non veda/ignori l'errore. Quando si dice "chiave primaria" si presume presumibilmente il valore IDENTITY. Tutto molto bene, ma è necessario anche un vincolo di chiave (ad esempio UNIQUE) sulla chiave naturale.

Inoltre, mi chiedo se la procedura sta facendo troppo. Prendi in considerazione di avere procedure separate per le azioni "crea" e "leggi" rispettivamente.

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('') 
Problemi correlati