Soluzioni per INSERT O AGGIORNAMENTO su SQL Server


599

Assumi una struttura di tabella di MyTable(KEY, datafield1, datafield2...).

Spesso voglio aggiornare un record esistente o inserire un nuovo record se non esiste.

Essenzialmente:

IF (key exists)
  run update command
ELSE
  run insert command

Qual è il modo migliore per scrivere questo?



28
Per chiunque incontri questa domanda per la prima volta, assicurati di leggere tutte le risposte e i loro commenti. L'età a volte può portare a informazioni fuorvianti ...
Aaron Bertrand,

1
Prendi in considerazione l'utilizzo dell'operatore EXCEPT, introdotto in SQL Server 2005.
Tarzan,

Risposte:


370

non dimenticare le transazioni. Le prestazioni sono buone, ma l'approccio semplice (SE ESISTE ..) è molto pericoloso.
Quando più thread tenteranno di eseguire l'inserimento o l'aggiornamento, è possibile ottenere facilmente la violazione della chiave primaria.

Le soluzioni fornite da @Beau Crawford e @Esteban mostrano un'idea generale ma soggetta a errori.

Per evitare deadlock e violazioni di PK puoi usare qualcosa del genere:

begin tran
if exists (select * from table with (updlock,serializable) where key = @key)
begin
   update table set ...
   where key = @key
end
else
begin
   insert into table (key, ...)
   values (@key, ...)
end
commit tran

o

begin tran
   update table with (serializable) set ...
   where key = @key

   if @@rowcount = 0
   begin
      insert into table (key, ...) values (@key,..)
   end
commit tran

1
Domanda posta per la soluzione più performante piuttosto che la più sicura. Mentre una transazione aggiunge sicurezza al processo, aggiunge anche un overhead.
Luke Bennett,

31
Entrambi questi metodi possono ancora fallire. Se due thread simultanei fanno lo stesso sulla stessa riga, il primo avrà esito positivo, ma il secondo inserimento avrà esito negativo a causa di una violazione della chiave primaria. Una transazione non garantisce che l'inserimento avrà esito positivo anche se l'aggiornamento non è riuscito perché il record esiste. Per garantire che un numero qualsiasi di transazioni simultanee avrà successo DEVI usare un lucchetto.
Jean Vincent,

7
@aku qualsiasi motivo hai usato i suggerimenti per la tabella ("with (xxxx)") anziché "IMPOSTA LIVELLO DI ISOLAMENTO TRANSAZIONE SERIALIZZABILE" appena prima del tuo TRAN INIZIO?
EBarr,

4
@CashCow, l'ultimo vince, ecco cosa dovrebbe fare INSERT o UPDATE: il primo inserisce, il secondo aggiorna il record. L'aggiunta di un blocco consente che ciò avvenga in un arco di tempo molto breve, prevenendo un errore.
Jean Vincent,

1
Ho sempre pensato che l'uso dei suggerimenti per il blocco fosse negativo e dovremmo lasciare che il motore Microsoft Internal imponga i blocchi. È questa l'apparente eccezione alla regola?

382

Vedi la mia risposta dettagliata a una domanda precedente molto simile

@Beau Crawford's è un buon metodo in SQL 2005 e in basso, anche se se stai concedendo un rep, dovrebbe andare dal primo a farlo . L'unico problema è che per gli inserti sono ancora due operazioni di I / O.

MS Sql2008 introduce mergedallo standard SQL: 2003:

merge tablename with(HOLDLOCK) as target
using (values ('new value', 'different value'))
    as source (field1, field2)
    on target.idfield = 7
when matched then
    update
    set field1 = source.field1,
        field2 = source.field2,
        ...
when not matched then
    insert ( idfield, field1, field2, ... )
    values ( 7,  source.field1, source.field2, ... )

Ora è davvero solo un'operazione di I / O, ma un codice orribile :-(


10
@Ian Boyd - sì, questa è la sintassi dello standard SQL: 2003, non quella upsertche invece tutti gli altri provider di database hanno deciso di supportare. La upsertsintassi è un modo molto più efficace per farlo, quindi almeno MS avrebbe dovuto supportarlo - non è come se fosse l'unica parola chiave non standard in T-SQL
Keith

1
qualche commento sul suggerimento di blocco in altre risposte? (lo scoprirò presto, ma se è il modo consigliato, consiglio di aggiungerlo sulla risposta)
eglasius,

25
Vedi qui weblogs.sqlteam.com/dang/archive/2009/01/31/… per la risposta su come evitare che le condizioni di gara possano causare errori che possono verificarsi anche quando si utilizza la MERGEsintassi.
Seph

5
@Seph è una vera sorpresa - un po 'un fallimento da parte di Microsoft lì: - Immagino che ciò significhi che hai bisogno di un HOLDLOCKper operazioni di fusione in situazioni di elevata concorrenza.
Keith,

11
Questa risposta deve davvero essere aggiornata per tenere conto del commento di Seph sul fatto che non è thread-safe senza HOLDLOCK. Secondo il post collegato, MERGE elimina implicitamente un blocco di aggiornamento, ma lo rilascia prima di inserire le righe, il che può causare una condizione di competizione e violazioni della chiave primaria durante l'inserimento. Usando HOLDLOCK, i blocchi vengono mantenuti fino a dopo l'inserimento.
Triynko,

169

Fai un UPSERT:

UPDATE MyTable SET FieldA = @ FieldA WHERE Key = @ Key

IF @@ ROWCOUNT = 0
   INSERT INTO MyTable (FieldA) VALUES (@FieldA)

http://en.wikipedia.org/wiki/Upsert


7
Le violazioni della chiave primaria non dovrebbero verificarsi se sono stati applicati i vincoli di indice univoci appropriati. L'intero punto del vincolo è impedire che si verifichino righe duplicate. Non importa quanti thread stanno tentando di inserire, il database serializzerà come necessario per imporre il vincolo ... e in caso contrario, il motore non ha valore. Ovviamente, il wrapping di questo in una transazione serializzata renderebbe questo più corretto e meno suscettibile a deadlock o inserimenti non riusciti.
Triynko,

19
@Triynko, credo @ Sam Zafferano significava che se due + discussioni interleave nella giusta sequenza poi sql server sarà lanciare un errore che indica una violazione di chiave primaria si sarebbe verificato. Il wrapping in una transazione serializzabile è il modo corretto per prevenire errori nel set di istruzioni sopra riportato.
EBarr

1
Anche se hai una chiave primaria che è un auto-incremento, la tua preoccupazione saranno eventuali vincoli univoci che potrebbero essere presenti nella tabella.
Seph

1
il database dovrebbe occuparsi dei problemi chiave primari. Quello che stai dicendo è che se l'aggiornamento fallisce e un altro processo arriva prima con un inserto, l'inserimento fallirà. In quel caso hai comunque una condizione di gara. Il blocco non cambierà il fatto che la post-condizione sarà che uno dei processi che tenta di scrivere otterrà il valore.
CashCow,

93

Molte persone ti suggeriranno di usarlo MERGE, ma ti avverto di non farlo. Per impostazione predefinita, non ti protegge dalle condizioni di concorrenza e di gara più di più dichiarazioni e introduce altri pericoli:

http://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/

Anche con questa sintassi "più semplice" disponibile, preferisco ancora questo approccio (gestione degli errori omessa per brevità):

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;

Molte persone suggeriranno in questo modo:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
IF EXISTS (SELECT 1 FROM dbo.table WHERE PK = @PK)
BEGIN
  UPDATE ...
END
ELSE
  INSERT ...
END
COMMIT TRANSACTION;

Ma tutto ciò è garantire che potrebbe essere necessario leggere due volte la tabella per individuare le righe da aggiornare. Nel primo esempio, dovrai sempre individuare le righe una sola volta. (In entrambi i casi, se non viene trovata alcuna riga dalla lettura iniziale, si verifica un inserimento.)

Altri suggeriranno in questo modo:

BEGIN TRY
  INSERT ...
END TRY
BEGIN CATCH
  IF ERROR_NUMBER() = 2627
    UPDATE ...
END CATCH

Tuttavia, ciò è problematico se non altro per il fatto che lasciare che SQL Server rilevi le eccezioni che si sarebbe potuto prevenire in primo luogo è molto più costoso, tranne nel raro scenario in cui quasi tutti gli inserimenti falliscono. Dimostro tanto qui:


3
Che dire dell'inserimento / aggiornamento di una tabella tem che inserisce / aggiorna molti record?
user960567,

@ user960567 Bene,UPDATE target SET col = tmp.col FROM target INNER JOIN #tmp ON <key clause>; INSERT target(...) SELECT ... FROM #tmp AS t WHERE NOT EXISTS (SELECT 1 FROM target WHERE key = t.key);
Aaron Bertrand,

4
bella risposta dopo più di 2 anni :)
user960567

12
@ user960567 Spiacenti, non ricevo sempre notifiche di commenti in tempo reale.
Aaron Bertrand,

60
IF EXISTS (SELECT * FROM [Table] WHERE ID = rowID)
UPDATE [Table] SET propertyOne = propOne, property2 . . .
ELSE
INSERT INTO [Table] (propOne, propTwo . . .)

Modificare:

Purtroppo, anche a mio svantaggio, devo ammettere che le soluzioni che lo fanno senza una scelta sembrano essere migliori dal momento che svolgono il compito con un passo in meno.


6
Mi piace ancora questo meglio. L'upert sembra più simile alla programmazione per effetto collaterale, e non ho mai visto l'individuo piccolo indice cluster cercare quella selezione iniziale per causare problemi di prestazioni in un vero database.
Eric Z Beard,

38

Se si desidera UPSERT più di un record alla volta, è possibile utilizzare l'istruzione DML ANSI SQL: 2003 MERGE.

MERGE INTO table_name WITH (HOLDLOCK) USING table_name ON (condition)
WHEN MATCHED THEN UPDATE SET column1 = value1 [, column2 = value2 ...]
WHEN NOT MATCHED THEN INSERT (column1 [, column2 ...]) VALUES (value1 [, value2 ...])

Dai un'occhiata all'imitazione dell'istruzione MERGE in SQL Server 2005 .


1
In Oracle, l'emissione di una dichiarazione MERGE penso che blocchi la tabella. Lo stesso accade in SQL * Server?
Mike McAllister,

13
MERGE è suscettibile alle condizioni di gara (vedi weblogs.sqlteam.com/dang/archive/2009/01/31/… ) a meno che tu non lo faccia detenere blocchi di sicurezza. Inoltre, dai un'occhiata alle prestazioni di MERGE in SQL Profiler ... Trovo che sia tipicamente più lento e generi più letture di soluzioni alternative.
EBarr,

@EBarr - Grazie per il link sui lucchetti. Ho aggiornato la mia risposta per includere il suggerimento di blocco suggerito.
Eric Weilnau,


10

Anche se è abbastanza tardi per commentare questo, voglio aggiungere un esempio più completo usando MERGE.

Tali istruzioni Insert + Update sono in genere chiamate istruzioni "Upsert" e possono essere implementate utilizzando MERGE in SQL Server.

Un ottimo esempio è dato qui: http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx

Quanto sopra spiega anche gli scenari di blocco e concorrenza.

Citerò lo stesso per riferimento:

ALTER PROCEDURE dbo.Merge_Foo2
      @ID int
AS

SET NOCOUNT, XACT_ABORT ON;

MERGE dbo.Foo2 WITH (HOLDLOCK) AS f
USING (SELECT @ID AS ID) AS new_foo
      ON f.ID = new_foo.ID
WHEN MATCHED THEN
    UPDATE
            SET f.UpdateSpid = @@SPID,
            UpdateTime = SYSDATETIME()
WHEN NOT MATCHED THEN
    INSERT
      (
            ID,
            InsertSpid,
            InsertTime
      )
    VALUES
      (
            new_foo.ID,
            @@SPID,
            SYSDATETIME()
      );

RETURN @@ERROR;

1
Ci sono altre cose di cui preoccuparsi con MERGE: mssqltips.com/sqlservertip/3074/…
Aaron Bertrand,

8
/*
CREATE TABLE ApplicationsDesSocietes (
   id                   INT IDENTITY(0,1)    NOT NULL,
   applicationId        INT                  NOT NULL,
   societeId            INT                  NOT NULL,
   suppression          BIT                  NULL,
   CONSTRAINT PK_APPLICATIONSDESSOCIETES PRIMARY KEY (id)
)
GO
--*/

DECLARE @applicationId INT = 81, @societeId INT = 43, @suppression BIT = 0

MERGE dbo.ApplicationsDesSocietes WITH (HOLDLOCK) AS target
--set the SOURCE table one row
USING (VALUES (@applicationId, @societeId, @suppression))
    AS source (applicationId, societeId, suppression)
    --here goes the ON join condition
    ON target.applicationId = source.applicationId and target.societeId = source.societeId
WHEN MATCHED THEN
    UPDATE
    --place your list of SET here
    SET target.suppression = source.suppression
WHEN NOT MATCHED THEN
    --insert a new line with the SOURCE table one row
    INSERT (applicationId, societeId, suppression)
    VALUES (source.applicationId, source.societeId, source.suppression);
GO

Sostituisci i nomi di tabelle e campi con qualsiasi cosa ti serva. Prenditi cura della condizione ON . Quindi impostare il valore appropriato (e digitare) per le variabili sulla riga DECLARE.

Saluti.


7

È possibile utilizzare MERGEStatement, questa istruzione viene utilizzata per inserire dati se non esistono o aggiornare se esistono.

MERGE INTO Employee AS e
using EmployeeUpdate AS eu
ON e.EmployeeID = eu.EmployeeID`

@RamenChef Non capisco. Dove sono le clausole WHEN MATCHED?
Mi piace il

@likejudo Non ho scritto questo; L'ho solo rivisto. Chiedi all'utente che ha scritto il post.
RamenChef

5

Se si aggiorna AGGIORNAMENTO if-no-righe-aggiornato, quindi INSERT route, prendere in considerazione prima di eseguire INSERT per evitare una condizione di competizione (presupponendo che non sia intervenuta DELETE)

INSERT INTO MyTable (Key, FieldA)
   SELECT @Key, @FieldA
   WHERE NOT EXISTS
   (
       SELECT *
       FROM  MyTable
       WHERE Key = @Key
   )
IF @@ROWCOUNT = 0
BEGIN
   UPDATE MyTable
   SET FieldA=@FieldA
   WHERE Key=@Key
   IF @@ROWCOUNT = 0
   ... record was deleted, consider looping to re-run the INSERT, or RAISERROR ...
END

Oltre a evitare una condizione di competizione, se nella maggior parte dei casi il record esisterà già, ciò causerà il fallimento dell'insert, con conseguente spreco di CPU.

L'uso di MERGE è probabilmente preferibile per SQL2008 in poi.


Idea interessante, ma sintassi errata. SELECT ha bisogno di FROM <table_source> e TOP 1 (a meno che la table_source selezionata non abbia solo 1 riga).
jk7,

Grazie. L'ho cambiato in NOT EXISTS. Ci sarà sempre una sola riga corrispondente a causa del test per "chiave" come da O / P (anche se potrebbe essere necessario essere una chiave multi-parte :))
Kristen,

4

Dipende dal modello di utilizzo. Bisogna guardare il quadro generale sull'uso senza perdersi nei dettagli. Ad esempio, se il modello di utilizzo prevede aggiornamenti del 99% dopo la creazione del record, "UPSERT" è la soluzione migliore.

Dopo il primo inserimento (hit), saranno tutti gli aggiornamenti a singola istruzione, senza se e senza ma. La condizione 'dove' sull'inserto è necessaria, altrimenti inserirà i duplicati e non vorrai occuparti del blocco.

UPDATE <tableName> SET <field>=@field WHERE key=@key;

IF @@ROWCOUNT = 0
BEGIN
   INSERT INTO <tableName> (field)
   SELECT @field
   WHERE NOT EXISTS (select * from tableName where key = @key);
END

2

MS SQL Server 2008 introduce l'istruzione MERGE, che credo faccia parte dello standard SQL: 2003. Come molti hanno dimostrato, non è un grosso problema gestire casi di una riga, ma quando si tratta di set di dati di grandi dimensioni, è necessario un cursore, con tutti i problemi di prestazioni che si presentano. L'affermazione MERGE sarà molto apprezzata quando si tratta di set di dati di grandi dimensioni.


1
Non ho mai avuto bisogno di usare un cursore per farlo con grandi set di dati. Hai solo bisogno di un aggiornamento che aggiorni i record corrispondenti e un inserimento con una clausola select anziché di valori che ha lasciato join alla tabella.
HLGEM,

1

Prima che tutti saltino a HOLDLOCK per paura di questi utenti disonesti che eseguono direttamente i tuoi sprocs :-) lasciami sottolineare che devi garantire l'unicità dei nuovi PK in base alla progettazione (chiavi di identità, generatori di sequenze in Oracle, indici univoci per ID esterni, query coperte da indici). Questo è l'alfa e l'omega del problema. Se non lo possiedi, nessun HOLDLOCK dell'universo ti salverà e, se lo possiedi, non avrai bisogno di altro oltre UPDLOCK alla prima selezione (o di utilizzare prima l'aggiornamento).

Gli Sprocs normalmente funzionano in condizioni molto controllate e con l'ipotesi di un chiamante di fiducia (livello intermedio). Ciò significa che se un semplice schema di upsert (aggiornamento + inserimento o unione) vede mai un PK duplicato significa un bug nella progettazione di livello intermedio o tabella ed è positivo che SQL urlerà un errore in questo caso e rifiuterà il record. Posizionare un HOLDLOCK in questo caso equivale a mangiare eccezioni e a prendere dati potenzialmente difettosi, oltre a ridurre il tuo perf.

Detto questo, utilizzando MERGE o UPDATE, INSERT è più semplice sul tuo server e meno soggetto a errori poiché non devi ricordare di aggiungere (UPDLOCK) per selezionare prima. Inoltre, se si stanno effettuando inserimenti / aggiornamenti in piccoli lotti, è necessario conoscere i propri dati per decidere se una transazione è appropriata o meno. È solo una raccolta di record non correlati, quindi ulteriori transazioni "avvolgenti" saranno dannose.


1
Se fai solo un aggiornamento, quindi inserisci senza alcun blocco o isolamento elevato, quindi due utenti potrebbero provare a restituire gli stessi dati (non lo considererei un bug nel livello intermedio se due utenti provassero a inviare esattamente le stesse informazioni in allo stesso tempo - dipende molto dal contesto, no?). Entrano entrambi nell'aggiornamento, che restituisce 0 righe per entrambi, quindi entrambi tentano di inserire. Uno vince, l'altro ottiene un'eccezione. Questo è ciò che le persone di solito cercano di evitare.
Aaron Bertrand,

1

Le condizioni di gara sono davvero importanti se provi prima un aggiornamento seguito da un inserto? Diciamo che hai due thread che vogliono impostare un valore per chiave chiave :

Discussione 1: valore = 1
Discussione 2: valore = 2

Esempio di scenario delle condizioni di gara

  1. la chiave non è definita
  2. Il thread 1 non riesce con l'aggiornamento
  3. Il thread 2 non riesce con l'aggiornamento
  4. Esattamente uno dei thread 1 o 2 ha esito positivo con l'inserimento. Ad es. Filo 1
  5. L'altro thread ha esito negativo con insert (con chiave duplicata errore) - thread 2.

    • Risultato: il "primo" dei due gradini da inserire, decide il valore.
    • Risultato desiderato: l'ultimo dei 2 thread per scrivere i dati (aggiornare o inserire) dovrebbe decidere il valore

Ma; in un ambiente multithread, lo scheduler del sistema operativo decide l'ordine di esecuzione del thread: nello scenario sopra, dove abbiamo questa condizione di competizione, è stato il sistema operativo a decidere la sequenza di esecuzione. Vale a dire: è sbagliato dire che "thread 1" o "thread 2" è stato "primo" dal punto di vista del sistema.

Quando il tempo di esecuzione è così vicino per il thread 1 e il thread 2, il risultato delle condizioni di gara non ha importanza. L'unico requisito dovrebbe essere che uno dei thread dovrebbe definire il valore risultante.

Per l'implementazione: se l'aggiornamento seguito da insert genera un errore "chiave duplicata", questo dovrebbe essere considerato un successo.

Inoltre, ovviamente non si dovrebbe mai presumere che il valore nel database sia lo stesso del valore scritto per ultimo.


1

In SQL Server 2008 è possibile utilizzare l'istruzione MERGE


11
questo è un commento in assenza di un vero codice di esempio, questo è proprio come molti altri commenti sul sito.
cambio

Molto vecchio, ma un esempio sarebbe carino.
Matt McCabe,

0

Avevo provato la soluzione di seguito e funziona per me, quando si verifica una richiesta simultanea per l'istruzione insert.

begin tran
if exists (select * from table with (updlock,serializable) where key = @key)
begin
   update table set ...
   where key = @key
end
else
begin
   insert table (key, ...)
   values (@key, ...)
end
commit tran

0

È possibile utilizzare questa query. Funziona in tutte le edizioni di SQL Server. È semplice e chiaro. Ma devi usare 2 query. Puoi usare se non puoi usare MERGE

    BEGIN TRAN

    UPDATE table
    SET Id = @ID, Description = @Description
    WHERE Id = @Id

    INSERT INTO table(Id, Description)
    SELECT @Id, @Description
    WHERE NOT EXISTS (SELECT NULL FROM table WHERE Id = @Id)

    COMMIT TRAN

NOTA: si prega di spiegare le risposte negative


Sto indovinando la mancanza di blocco?
Zeek2,

Nessuna mancanza di blocco ... Uso "TRAN". Le transazioni sql-server predefinite hanno il blocco.
Victor Sanchez,

-2

Se si utilizza ADO.NET, DataAdapter gestisce questo.

Se vuoi gestirlo da solo, questo è il modo:

Assicurati che ci sia un vincolo chiave primaria nella colonna chiave.

Allora lei:

  1. Fai l'aggiornamento
  2. Se l'aggiornamento non riesce perché esiste già un record con la chiave, eseguire l'inserimento. Se l'aggiornamento non fallisce, hai finito.

Puoi anche farlo al contrario, ovvero esegui prima l'inserimento e esegui l'aggiornamento se l'inserimento non riesce. Normalmente il primo modo è migliore, perché gli aggiornamenti vengono eseguiti più spesso degli inserti.


... e fare prima l'inserimento (sapendo che a volte fallirà) è costoso per SQL Server. sqlperformance.com/2012/08/t-sql-queries/error-handling
Aaron Bertrand

-3

Fare un if esiste ... altrimenti ... comporta fare almeno due richieste (una per verificare, una per agire). L'approccio seguente richiede solo uno in cui esiste il record, due se è richiesto un inserimento:

DECLARE @RowExists bit
SET @RowExists = 0
UPDATE MyTable SET DataField1 = 'xxx', @RowExists = 1 WHERE Key = 123
IF @RowExists = 0
  INSERT INTO MyTable (Key, DataField1) VALUES (123, 'xxx')

-3

Di solito faccio quello che hanno detto molti altri manifesti in merito alla verifica prima che sia esistente e poi a fare qualunque sia il percorso corretto. Una cosa che dovresti ricordare quando fai questo è che il piano di esecuzione memorizzato nella cache da sql potrebbe non essere ottimale per un percorso o per l'altro. Credo che il modo migliore per farlo sia chiamare due diverse procedure memorizzate.

FirstSP:
Se esiste
   Chiama SecondSP (UpdateProc)
Altro
   Chiama ThirdSP (InsertProc)

Ora, non seguo il mio consiglio molto spesso, quindi prenderlo con un granello di sale.


Questo potrebbe essere stato rilevante nelle versioni antiche di SQL Server, ma le versioni moderne hanno una compilazione a livello di istruzione. Forks ecc. Non sono un problema e l'utilizzo di procedure separate per queste cose non risolve nessuno dei problemi inerenti la scelta tra un aggiornamento e un inserto ...
Aaron Bertrand,

-10

Fai una selezione, se ottieni un risultato, aggiornalo, altrimenti crealo.


3
Sono due chiamate al database.
Chris Cudmore,

3
Non vedo problemi con questo.
Clint Ecker,

10
Il problema è due chiamate al DB, finendo per raddoppiare il numero di roundtrip verso il DB. Se l'app colpisce il db con molti inserimenti / aggiornamenti, ne risentiranno le prestazioni. UPSERT è una strategia migliore.
Kev

5
crea anche una condizione di gara no?
Niico,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.