Mange mennesker vil foreslå, at du bruger MERGE
, men jeg advarer dig imod det. Som standard beskytter det dig ikke mod samtidighed og raceforhold mere end flere udsagn, men det introducerer andre farer:
- Vær forsigtig med SQL Servers MERGE-erklæring
- Hvad skal du undgå, hvis du vil bruge FLOT
- SQL Server UPSERT-mønstre og antimønstre
Selv med denne "simpelere" syntaks til rådighed, foretrækker jeg stadig denne tilgang (fejlhåndtering udeladt for kortheds skyld):
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
UPDATE dbo.table SET ... WHERE PK = @PK;
IF @@ROWCOUNT = 0
BEGIN
INSERT dbo.table(PK, ...) SELECT @PK, ...;
END
COMMIT TRANSACTION;
Flere oplysninger om denne UPSERT
henvendelse her:
- Hold op med at bruge dette UPSERT-antimønster
Mange vil foreslå denne måde:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
IF EXISTS (SELECT 1 FROM dbo.table WHERE PK = @PK)
BEGIN
UPDATE ...
END
ELSE
BEGIN
INSERT ...
END
COMMIT TRANSACTION;
Men alt dette opnår er at sikre, at du muligvis skal læse tabellen to gange for at finde rækken/rækkerne, der skal opdateres. I den første prøve behøver du kun at finde rækken/rækkerne én gang. (I begge tilfælde, hvis der ikke findes nogen rækker fra den første læsning, sker der en indsættelse.)
Andre vil foreslå denne måde:
BEGIN TRY
INSERT ...
END TRY
BEGIN CATCH
IF ERROR_NUMBER() = 2627
UPDATE ...
END CATCH
Dette er dog problematisk, hvis det af ingen anden grund end at lade SQL Server fange undtagelser, som du kunne have forhindret i første omgang, er meget dyrere, undtagen i det sjældne scenarie, hvor næsten hver indsættelse fejler. Det beviser jeg her:
- Kontrollerer for potentielle overtrædelser af begrænsninger, før du går ind i TRY/CATCH
- Ydeevnepåvirkning af forskellige fejlhåndteringsteknikker
Ikke sikker på, hvad du tror, du vinder ved at have et enkelt udsagn; Jeg tror ikke, du vinder noget. MERGE
er et enkelt udsagn, men det skal stadig virkelig udføre flere operationer alligevel - selvom det får dig til at tro, at det ikke gør det.