Comment insérer ou mettre à jour en utilisant une seule requête?

26

J'ai un test de table ayant des colonnes id dont la clé primaire et auto incrémenté et le nom. Je veux insérer un nouvel enregistrement si annd seulement s'il n'y a pas d'enregistrements. Par exemple

l'entrée est id = 30122 et nom = john

s'il y a des enregistrements avec l'ID 30122, j'ai mis à jour la colonne de nom en john, s'il n'y a pas d'enregistrements, j'ai inséré un nouvel enregistrement.

Je peux faire en utilisant 2 requêtes comme

select * from test where id=30122

s'il a des enregistrements, je peux utiliser update test set name='john' where id=3012

ou s'il n'a pas d'enregistrements, je peux utiliser

insert into test(name) values('john')

Mais je voulais utiliser une seule requête?

Quelqu'un peut-il dire si c'est possible?

SpringLearner
la source
1
But I wanted to use single query?Pourquoi?
Aaron Bertrand
@AaronBertrand Mon back-end est développé en utilisant java.So si j'utilise 2 requêtes alors je dois frapper la base de données 2 fois.Si cela peut être fait en utilisant une seule requête, alors pourquoi utiliser 2 requêtes
SpringLearner
1
Java ne prend pas en charge une procédure stockée ou un seul lot avec deux instructions nécessitant un seul accès à la base de données?
Aaron Bertrand
@AaronBertrand pourriez-vous donner un exemple de la façon dont vous géreriez cela avec SQL Server 2008 ou version ultérieure?
eaglei22
1
@ eaglei22 J'utiliserais le 2ème exemple dans la réponse de vijayp ci-dessous. Je ne choisirais toujours MERGEaucune version, même SQL Server 2019. Quelques informations à ce sujet ici .
Aaron Bertrand

Réponses:

41

Vous pouvez essayer ceci

IF EXISTS(select * from test where id=30122)
   update test set name='john' where id=3012
ELSE
   insert into test(name) values('john');

Une autre approche pour de meilleures performances est

update test set name='john' where id=3012
IF @@ROWCOUNT=0
   insert into test(name) values('john');

et aussi lire ces mauvaises habitudes pour lancer le préfixe de schéma

vijayp
la source
4
Le premier exemple est un gaspillage et peut souvent conduire à des blocages - je ne le suggérerais pas du tout.
Aaron Bertrand
@AaronBertrand vous voulez élaborer? Merci
Hexo
5
@SlapY Bien sûr, dans le premier exemple, vous dites: "Hé, SQL Server, y a-t-il une ligne avec cet ID?" SQL Server part pour rechercher la ligne, peut-être à l'aide d'une analyse, puis revient avec la réponse. "Pourquoi, oui, utilisateur, j'ai une rangée avec cet ID!" Ensuite , vous dites: « D' accord, SQL Server, allez trouver cette ligne à nouveau , mais cette fois, mettre à jour! » Voyez-vous comment effectuer deux fois la recherche ou l'analyse est un gaspillage? Pouvez-vous imaginer ce qui se passe si un autre utilisateur pose la même question à SQL Server sur l'existence d'une ligne, avant de passer à autre chose?
Aaron Bertrand
Merci, je ne vois tout simplement pas pourquoi le premier menace de bloquer tandis que le second ne l'est pas. Les deux se composent de plusieurs instructions qui peuvent être interceptées si elles ne sont pas exécutées avec un verrouillage complet. Ai-je tort?
Hexo
2
@ 0x25b3 Ce n'est pas que l'un est menacé par des blocages et que l'autre ne l'est pas, c'est que le premier exemple leur est beaucoup plus sujet. Vous devriez conclure une transaction complète et appropriée dans les deux cas, mais les gens ne le font pas, alors ...
Aaron Bertrand
17

En supposant que SQL Server 2008 ou version ultérieure, vous pouvez utiliser MERGE:

Table

CREATE TABLE dbo.Test
(
    id integer NOT NULL,
    name varchar(30) NULL,

    CONSTRAINT PK_dbo_Test__id
        PRIMARY KEY CLUSTERED (id)
);

Question

MERGE dbo.Test WITH (SERIALIZABLE) AS T
USING (VALUES (3012, 'john')) AS U (id, name)
    ON U.id = T.id
WHEN MATCHED THEN 
    UPDATE SET T.name = U.name
WHEN NOT MATCHED THEN
    INSERT (id, name) 
    VALUES (U.id, U.name);

L' SERIALIZABLEastuce est requise pour un fonctionnement correct sous une concurrence élevée .

Vous pouvez trouver une comparaison des méthodes courantes de Michael J. Swart ici:

Mythbusting: Solutions de mise à jour / insertion simultanées

Evaldas Buinauskas
la source
8
La fusion a quelques problèmes .
vonPryz
le lien mythique est excellent. Joli!
JonnyRaa