none
bloccare un contatore in un campo che non è auto-incrementale RRS feed

  • Domanda

  • buon giorno, ho una tabella di un sistema documentale, che ha un campo che è il numeratore progressivo, ma non è stata usata una identity e il campo non è auto-incrementale.

    L'applicazione del documentale inserisce regolarmente i suoi documenti, ed io devo fare in modo che da una mia applicazione venga aggiunto un record a quella tabella, ovviamente incrementando quel contatore. Il problema è che l'applicazione documentale inserisce records in continuazione, quindi io non faccio in tempo a leggere l'ultimo contatore ed inserire un nuovo records, che il documentale ha già fatto altrettanto occupando il contatore che avevo rilevato e quindi la mia insert non va a buon fine perché quel campo è una chiave univoca.

    Qualcuno ha idea come poter "bloccare" il contatore in questione?

    (spero di essermi spiegato al meglio).

    Grazie in anticipo

    mercoledì 6 novembre 2019 12:14

Risposte

  • Ciao,

    puoi provare una cosa di questo tipo:

    DECLARE @MyTable table( id smallint)
    
    INSERT INTO dbo.Test (ID) OUTPUT Inserted.ID INTO @MyTable
    VALUES ( 	(SELECT isnull(MAX(ID),0) FROM dbo.Test with(tablock) ) + 1 
    )
    
    SELECT id FROM @MyTable

    usando la clausola OUTPUT riesci a recuperare i valori del record appena inserito.



    HTH,

    Cristiano Gasparotto, MCSE Data Management and Analytics

    You can find me working hard every day at Datamaze!

    Please Mark This As Answer if it solved your issue or Vote This As Helpful if it helps to solve your issue. Thank you!

    giovedì 7 novembre 2019 10:15

Tutte le risposte

  • Ciao,

    se esegui la insert in questo modo, non dovresti ricevere errori:

    INSERT INTO dbo.Test 
    (
    	ID
    	, Price
    )
    VALUES 
    (
    	(SELECT MAX(ID) FROM dbo.Test) + 1 --aggiungi uno al valore massimo del campo ID
    	, 100 
    )
    Vedo difficile bloccare il contatore perché, anche se fosse possibile, rischi di impattare sull'applicativo documentale.


    HTH,

    Cristiano Gasparotto, MCSE Data Management and Analytics

    You can find me working hard every day at Datamaze!

    Please Mark This As Answer if it solved your issue or Vote This As Helpful if it helps to solve your issue. Thank you!


    mercoledì 6 novembre 2019 12:47
  • Grazie mille Cristiano.

    Se io dopo aver fatto la insert volessi recuperare il valore inserito, come faccio? Anche se faccio una select immediatamente, potrebbe essere che sia stato inserito qualche altro record, quindi prendendo il MAX() di quel campo non è detto che sia quello che ho inserito io.

    Grazie ancora e buona giornata!

    giovedì 7 novembre 2019 08:49
  • Ciao,

    la situazione è molto problematica e potrebbe non essere risolvibile; nell'ipotesi di un server con più processori e dal momento che più processi paralleli leggono il max incrementandolo e poi lo scrivono, è assai probabile (dipende dal numero e dalla velocità con cui i processi scrivono, dal numero di cpu) che su thread differenti avvenga la lettura dello stesso max con conseguente tentativo di scrittura di chiavi duplicate e relativa eccezione.

    E' indispensabile quindi che al momento della lettura del max venga apposto un lock di tipo esclusivo a livello di tabella che verrà tolto dopo la insert; seguendo l'esempio del post precedente di Cristiano G.:

    INSERT INTO dbo.Test (ID)
    VALUES ( 	(SELECT isnull(MAX(ID),0) FROM dbo.Test with(tablock) ) + 1 
    )

    Questo non basta a proteggerti dall'errore chiavi duplicate sugli altri scrittori; se loro p.es. fanno lettura e scrittura con due statement distinti e non impongono un livello transazionale adeguato (non il default "read committed") avrai sempre, statisticamente, la possibilità che loro vadano in errore.

    Il fatto che attualmente questo non avvenga lascia pensare che o gli attuali scrittori già utilizzano un livello transazionale adeguato - in tal caso hai risolto il problema - oppure che l'applicativo in qualche modo serializza la loro attività. In quest'ultimo caso non puoi evitare l'errore.

    Aiuterebbe sapere come lìapplicativo opera, magari profilando i suoi statement di insert.

    Giorgio

    giovedì 7 novembre 2019 09:18
  • grazie mille Giorgio!!!!

    cercherò di fare qualche indagine in merito.

    Per caso hai qualche suggerimento per come potrei recuperare il dato inserito in quel campo subito dopo la insert?

    grazie ancora

    giovedì 7 novembre 2019 09:25
  • Ciao,

    puoi provare una cosa di questo tipo:

    DECLARE @MyTable table( id smallint)
    
    INSERT INTO dbo.Test (ID) OUTPUT Inserted.ID INTO @MyTable
    VALUES ( 	(SELECT isnull(MAX(ID),0) FROM dbo.Test with(tablock) ) + 1 
    )
    
    SELECT id FROM @MyTable

    usando la clausola OUTPUT riesci a recuperare i valori del record appena inserito.



    HTH,

    Cristiano Gasparotto, MCSE Data Management and Analytics

    You can find me working hard every day at Datamaze!

    Please Mark This As Answer if it solved your issue or Vote This As Helpful if it helps to solve your issue. Thank you!

    giovedì 7 novembre 2019 10:15
  • Ciao,

    puoi provare una cosa di questo tipo:

    DECLARE @MyTable table( id smallint)
    
    INSERT INTO dbo.Test (ID) OUTPUT Inserted.ID INTO @MyTable
    VALUES ( 	(SELECT isnull(MAX(ID),0) FROM dbo.Test with(tablock) ) + 1 
    )
    
    SELECT id FROM @MyTable

    usando la clausola OUTPUT riesci a recuperare i valori del record appena inserito.



    HTH,

    Cristiano Gasparotto, MCSE Data Management and Analytics

    Please Mark This As Answer if it solved your issue or Vote This As Helpful if it helps to solve your issue. Thank you!

    perfetto, grazie mille Cristiano!!!
    giovedì 7 novembre 2019 14:52