2009-09-28 14 views
42

¿Cómo se dice lo siguiente en Microsoft SQL Server 2005:si existe, seleccione Insertar y luego seleccione ELSE

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 

Lo que estoy tratando de hacer es ver si hay un espacio en blanco ya ValorDelCampo, y si hay, entonces devuelva ese TableID, de lo contrario inserte un valor de campo en blanco y devuelva la clave primaria correspondiente.

+1

¿Qué lenguaje de programación está utilizando? Esto podría hacerse mejor allí dentro de una transacción. –

+0

posible duplicado de [Solo insertando una fila si no está ya allí] (http://stackoverflow.com/questions/3407857/only-inserting-a-row-if-its-not-already-there) –

+0

Esta es la una pregunta más antigua; la pregunta 'Solo insertando una fila' debe cerrarse como un duplicado de esto, y no al revés. –

Respuesta

6

estaban cerca:

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 

Ver here para más información sobre IF ELSE

Nota: escrito sin una instalación de SQL Server a mano para comprobar esto, pero creo que es correcto

Además, he cambiado el bit EXISTS para hacer SELECCIONAR 1 en lugar de SELECCIONAR * ya que no le importa lo que se devuelve dentro de un EXISTO, siempre que algo sea también he cambiado el bit SCOPE_IDENTITY() para devolver sólo la identidad suponiendo que TableID es la columna de identidad

+2

El 'SELECCIONAR 1' no importa. ¿Lo estás cambiando solo para señalar que no te importan los detalles? No ayuda al rendimiento. –

+2

SELCET 1, SELECCIONAR NULO, SELECCIONAR * son todos iguales en EXISTS ... – gbn

+4

Prefiero evitar SELECCIONAR * entre mi código, no me parece una buena costumbre entrar, así que generalmente hago un SELECCIONAR 1 cuando hago existe – Jane

58

que tiene que hacer esto en la transacción para asegurar dos clientes simultáneos no insertará misma ValorDelCampo dos veces:

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 

también se puede utilizar para reducir el bloqueo Double-checked locking sobrecarga

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 

cuanto a por qué AISLAMIENTO dE NIVEL sERIALIZABLE es necesario, cuando se está dentro de una transacción serializable, la primera tha SELECT t golpea la mesa crea un bloqueo de rango que cubre el lugar donde debe estar el registro, para que nadie más pueda insertar el mismo registro hasta que finalice esta transacción.

Sin ISOLATION LEVEL SERIALIZABLE, el nivel de aislamiento predeterminado (READ COMMITTED) no bloquearía la tabla en el momento de la lectura, por lo que entre SELECT y UPDATE, alguien aún podría insertar. Las transacciones con nivel de aislamiento LECTURA COMPROMETIDA no provocan que SELECCIONAR se bloquee. Las transacciones con READATABLE READS bloquean el registro (si se encuentra) pero no el espacio.

+6

+1 No entiendo por qué la ** única ** respuesta que considera las condiciones de carrera y concurrencia languidece en cero votos. –

+2

Puede expandirse para explicar por qué 'ISOLATION LEVEL SERIALIZABLE' es necesario y ¿qué puede pasar si no establece eso? – binki

+5

@binki, cuando se encuentra dentro de una transacción serializable, el primer SELECT que golpea la tabla crea un * range lock * que cubre el lugar donde debe estar el registro, para que nadie más pueda insertar el mismo registro hasta que finalice la transacción. Sin NIVEL DE AISLAMIENTO SERIALIZABLE, el nivel de aislamiento predeterminado (LECTURA COMPROMETIDA) no bloquearía la tabla en el momento de la lectura, por lo que entre seleccionar y actualizar, alguien aún podría insertar. Las transacciones con nivel de aislamiento LECTURA COMPROMETIDA no causan SELECCIÓN para bloquear. Las transacciones con READATABLE READS, bloquean el registro (si se encuentra) pero no el espacio. – zvolkov

2

sólo hay que cambiar la estructura de la if...else..endif un tanto:

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 

También puede hacer:

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

O:

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

Suena como tu mesa no tiene llave Debería poder simplemente probar el INSERT: si es un duplicado, la restricción de clave morderá y el INSERT fallará. No se preocupe: sólo tiene que garantizar la aplicación no ver/omite el error. Cuando dice 'clave principal', se supone que quiere decir IDENTITY valor. Eso está muy bien, pero también necesita una restricción clave (por ejemplo, UNIQUE) en su clave natural.

Además, me pregunto si su procedimiento está haciendo demasiado. Considere tener procedimientos separados para las acciones 'crear' y 'leer', respectivamente.

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('') 
Cuestiones relacionadas