2014-11-27 25 views
5

Controllare questo semplice pezzo di codice che utilizza un generatore per creare chiavi primarie univoche in una tabella Firebird:Come faccio a non sprecare i valori del Generatore quando li utilizzo sul lato server con Firebird?

CREATE OR ALTER TRIGGER ON_BEFOREINSERT_PK_BOOKING_ITEM FOR BOOKING_ITEM BEFORE INSERT POSITION 0 
    AS 
    BEGIN 
    IF ((NEW.booking_item_id IS NULL) OR (NEW.booking_item_id = 0)) THEN BEGIN 
     SELECT GEN_ID(LastIdBookingItem, 1) FROM RDB$DATABASE INTO :NEW.booking_item_id; 
    END 
    END! 

Questo trigger palio e incrementi assegna quindi un valore generato per l'elemento Prenotazione id creando così un auto-incrementato chiave per la tabella BOOKING_ITEM. Il trigger controlla anche che all'ID di prenotazione non sia già stato assegnato un valore.

Il problema è che il valore ad incremento automatico verrà perso (sprecato) se, per qualche motivo, il record BOOKING_ITEM non può essere registrato.

Ho un paio di idee su come evitare questo spreco ma ho preoccupazioni su ciascuno. Eccoli:

  1. decrementare il contatore se si verifica un errore di invio. All'interno del trigger ho impostato un blocco try-except (provate, eccetto i blocchi che esistono anche in Firebird PSQL?) Ed eseguite un SELECT GEN_ID(LastIdBookingItem, -1) FROM RDB$DATABASE per le eccezioni dei post. Questo lavoro dovrebbe funzionare? Cosa succede se un'altra transazione si insinua e incrementa il generatore prima di decrementarlo? Questo farebbe davvero casino.

  2. Utilizzare un ID temporaneo. Impostare l'id su un valore temp univoco che modifico sul valore del generatore che voglio sul trigger AFTER INSERT. Questo metodo sembra un po 'forzato e richiede un modo che assicuri che l'id temp sia unico. Ma cosa succede se il booking_item_id è stato fornito lato client, come potrei distinguere da un id temp ?. Inoltre ho bisogno di un altro trigger

  3. Utilizzare il controllo delle transazioni. È come l'opzione 1. tranne che per utilizzare il blocco try-except per reimpostare il generatore, avviare una transazione e quindi ripristinarla se il record non riesce a postare. Non conosco la sintassi per l'utilizzo del controllo delle transazioni. Ho pensato di leggere da qualche parte che SAVEPOINT/SET TRANSACTION non è consentito in PSQL. Inoltre, il rollback dovrebbe avvenire nel trigger AFTER INSERT, quindi ancora una volta ho bisogno di un altro trigger.

Sicuramente questo è un problema per tutti gli sviluppatori Firebird che vuole usare generatori. Altre idee? C'è qualcosa che mi manca?

+2

La risposta è la stessa di tutti gli altri DBMS che utilizzano sequenze: non preoccuparti. Le sequenze non sono progettate per essere prive di spazio e il loro valore non ha senso. Il loro unico scopo è quello di servire come chiave primaria artificiale. Niente di più. Se la tua applicazione si basa su valori gapless, semplicemente non puoi usare una sequenza (cerca in questo sito "sequenza senza pause" per le alternative). –

+0

Sto cercando questo sito per "sequenza senza pause" per le alternative. Grazie. Principalmente hai ragione "chi se ne frega" ma per alcune tabelle sto generando un numero di sequenza e usandolo come, diciamo, un numero di fattura. A volte i nostri revisori ci chiedono di produrre fatture da X a X + n. Certamente non tutte le fatture devono essere attive, alcune possono essere cancellate, ma si chiedono se mancano dei numeri. Fanno domande come "Dov'è la fattura 12345"? –

+0

Esatto, per i numeri di fattura è necessario qualcosa di diverso. Una sequenza non è adatta a questo. –

risposta

8

sequenze sono il controllo delle transazioni al di fuori, e immischiarsi con loro per ottenere i numeri 'gapless' solo causare guai perché un'altra operazione potrebbe incrementare la sequenza e contemporaneamente, portando a lacune + duplicati invece di non lasciare spazi vuoti:

  • inizio: il valore del generatore = 1
  • T1: minimo: il valore è 2
  • T2: minimo: il valore è 3
  • T1: 'rollback', decremento: il valore è 2 (e non 1 come ci si aspetta)
  • T3: minimo: il valore è 3 => valore duplicato

sequenze devono essere utilizzati principalmente per la generazione di chiavi primarie artificiali, e non si dovrebbe preoccuparsi l'esistenza di lacune: non importa più a lungo il numero identifica in modo univoco il record.

Se è necessaria una sequenza di numeri verificabile e il requisito è che non vi siano interruzioni, non è necessario utilizzare una sequenza di database per generarlo. È possibile utilizzare una sequenza per assegnare numeri dopo aver creato e commesso la fattura stessa (in modo che sia sicuro che sia mantenuta). Una fattura senza numero non è ancora definitiva. Tuttavia, anche qui c'è una finestra di opportunità per ottenere una lacuna, ad esempio se si verifica un errore o un altro errore tra l'assegnazione del numero di fattura e l'impegno.

Un altro modo potrebbe essere quello di creare in modo esplicito una fattura zero (contrassegnata come cancellata/numero perso) con i numeri di gap, in modo che l'auditor sappia cosa è successo a quella fattura.

In base alle leggi e ai regolamenti locali, non è necessario "riutilizzare" o riciclare numeri persi perché potrebbero essere interpretati come frodi.

Potresti trovare altre idee su "An Auditable Series of Numbers". Questo contiene anche un progetto Delphi che utilizza IBObjects, ma il documento stesso descrive abbastanza bene il problema e le possibili soluzioni.