web-dev-qa-db-fra.com

SI EXISTE, ALORS CHOISISSEZ L'AUTRE INSERT, puis SÉLECTIONNEZ

Comment dites-vous ce qui suit dans 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

Ce que j'essaie de faire est de voir s'il existe déjà une valeur de champ vide et, le cas échéant, de renvoyer cette TableID, sinon insérer une valeur de champ vide et renvoyer la clé primaire correspondante.

49
Phillip Senn

Vous devez le faire en transaction pour vous assurer que deux clients simultanés n'inséreront pas deux fois la même valeur de champ:

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

vous pouvez également utiliser Verrouillage vérifié pour réduire les frais généraux de verrouillage

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

En ce qui concerne la raison pour laquelle ISOLATION LEVEL SERIALIZABLE est nécessaire, lorsque vous vous trouvez dans une transaction sérialisable, le premier SELECT qui frappe la table crée un verrou de plage couvrant l'emplacement où devrait se trouver l'enregistrement, afin que personne d'autre ne puisse insérer le même enregistrement jusqu'à la fin de cette transaction.

Sans ISOLATION LEVEL SERIALIZABLE, le niveau d'isolation par défaut (READ COMMITTED) ne verrouillerait pas la table au moment de la lecture. Par conséquent, entre SELECT et UPDATE, une personne serait toujours en mesure d'insérer. Les transactions avec le niveau d'isolement READ COMMITTED n'entraînent pas le verrouillage de SELECT. Les transactions avec REPEATABLE READS verrouillent l’enregistrement (si trouvé) mais pas l’écart.

60
zvolkov
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

Voir ici pour plus d'informations sur IF ELSE

Note: écrit sans installation de SQL Server pratique pour vérifier cela mais je pense que c'est correct

En outre, j'ai modifié le bit EXISTS pour qu'il sélectionne SELECT 1 plutôt que SELECT *, le résultat renvoyé dans un fichier EXISTS étant indifférent, tant que quelque chose a changé, j'ai également modifié le bit SCOPE_IDENTITY () pour renvoyer uniquement l'identité. en supposant que TableID est la colonne d'identité

32
Jane

Tu étais proche:

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

Il vous suffit de changer la structure du if...else..endif quelque peu:

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

Vous pouvez aussi faire:

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

Ou:

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

On dirait que votre table n'a pas de clé. Vous devriez pouvoir simplement essayer le INSERT: s’il s’agit d’un doublon, la contrainte de clé mordra et le INSERT échouera. Pas de soucis: vous devez simplement vous assurer que l'application ne voit pas/ignore l'erreur. Lorsque vous dites "clé primaire", vous voulez probablement dire IDENTITY valeur. C'est très bien, mais vous avez également besoin d'une contrainte de clé (par exemple, UNIQUE) sur votre clé naturelle.

De plus, je me demande si votre procédure en fait trop. Pensez à avoir des procédures distinctes pour les actions 'create' et 'read' respectivement.

2
onedaywhen
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
1
Rob Garrison
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('')
1
Phillip Senn