Pagina 1 di 2 1 2 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 11
  1. #1

    [MySQL] Fattura: Lock, Transazioni, SubQuery, AUTO_INCREMENT Multi-Colonna

    Salve a tutti, durante la progettazione di un modulo fatturazione per un CMS mi sono trovato ad affrontare il problema delle fatture, in particolare della loro numerazione:
    • Numero univoco
    • Azzeramento della numerazione a fine anno
    • Problemi di concorrenza


    Il semplice autoincrement non posso usarlo, perchè la numerazione non si può azzerare facilmente a fine anno.

    Prelevare il "MAX(numero) FROM fatture" (per fare +1) potrebbe causare problemi di concorrenza se contemporaneamente viene aggiunto un altro record.

    Una prima soluzione che mi è venuta in mente è stata quella di effettuare un LOCK (di tipo write) sulla tabella fatture, prelevare l'ultimo numero, inserire la fattura successiva e sbloccare la tabella.
    Con questa soluzione non dovrebbero esserci particolari problemi (o mi sbaglio?), però cosa succede se durante il blocco della tabella deve essere generata una nuova fattura? Se il tutto avviene "abbastanza" velocamente la richiesta viene eseguita subito dopo lo sblocco della tabella? Avviene un errore? Va in timeout lo script?
    Dalla documentazione
    If the LOCK TABLES statement must wait due to locks held by other sessions on any of the tables, it blocks until all locks can be acquired.
    Però non saprei cose succede nella pratica.

    Con le transazioni invece dovrei poter gestire eventuali errori (uso codeigniter quindi uso la sua classe per gestire le transazioni):
    Codice PHP:
    $this->db->trans_start();
    $this->db->query('SELECT MAX(numero)+1 AS new FROM fatture WHERE anno=YEAR(CURDATE())');

    $new = ...;

    $this->db->query('INSERT INTO fatture (anno,numero,..) VALUES (YEAR(CURDATE(), '.$new.', '...')');
    $this->db->trans_complete();

    if (
    $this->db->trans_status() === FALSE)
    {
        
    // ovviamente (anno,numero) sarebbe univoco, quindi in caso ci concorrenza verrebbe generato un errore

    Questa soluzione non mi convince molto perchè a quanto so io le transazioni sono utilizzate per evitare che una serie di query collegate tra loro vengano eseguite solo in parte, in questo caso però ho solo una insert, quindi non penso faccia al caso mio, meglio la LOCK, almeno credo.

    Altra soluzione che fa uso di una subquery:
    Codice PHP:
    INSERT INTO fatture (
        
    anno
        
    numero
        ...

    VALUES (
        
    YEAR(CURDATE()),
        (
            
    SELECT MAX(numero)+1
             FROM fatture 
            WHERE anno
    =YEAR(CURDATE())
        ),
        
    '...'
    )'; 
    In questo modo faccio tutto in colpo (almeno spero che le subquery vengano eseguite contestualmente alla query principale e quindi non credo che qualcuno posso inserire una nuova fattura tra la query e la subquery).


    Infine un ultimo metodo che a me sembra più elegante (e funzionante), utilizzo una chiave primaria composta da (anno,numero) dove numero è auto_increment.
    Codice PHP:
    CREATE TABLE fatture (
        
    anno INT(4NOT NULL,
        
    numero INT(10NOT NULL AUTO_INCREMENT,
        
    totale DECIMAL(8,2NOT NULL,
        
    PRIMARY KEY (anno,numero)
    ENGINE=MyISAM;

    INSERT INTO `fatture` (
    `
    anno` ,
    `
    numero` ,
    `
    emissione` ,
    `
    totale`
    )
    VALUES (
    '2010'NULL '2010-09-56''101.00'
    );


    INSERT INTO `fatture` (
    `
    anno` ,
    `
    numero` ,
    `
    emissione` ,
    `
    totale`
    )
    VALUES (
    '2010'NULL '2010-09-16''101.00'
    );


    INSERT INTO `fatture` (
    `
    anno` ,
    `
    numero` ,
    `
    emissione` ,
    `
    totale`
    )
    VALUES (
    '2011'NULL '2011-01-01''103.00'
    ); 
    Il risultato finale è il seguente:
    codice:
    2010 	1 	2010-09-15 	100.00
    2010 	2 	2010-09-16 	101.00
    2011 	1 	2011-01-01 	103.00
    Secondo voi quale di queste soluzioni è la migliore? Ne conoscete altre?
    Fatemi sapere cosa ne pensate, in particolare dell'ultima.

  2. #2
    up

  3. #3
    Utente di HTML.it L'avatar di dottwatson
    Registrato dal
    Feb 2007
    Messaggi
    3,012
    le fatture hanno di particolare che devono essere gestite con un progressivo numerico, il quale giustamente come dici te deve essere azzerato ad inizio anno.


    il tuo problema nasce dal fatto (mi è sembrato di capire) che possono essere generate in maniera multipla, da diversi accessi effettuati contemporaneamente.

    innanzitutto la tabella deve avere 3 colonne:

    id_fattura
    num_fatt
    data (timestamp)

    1) fai in modo che il numero fattura sia dato dal sistema, e non dall' utente. Lo comunicherai a fine inserimento il numero assegnato

    2) l'azzeramento del numero fattura lo stabilisci al momento dell' inserimento in quanto la data parla chiaro....

    3) semplifichi la l'inserimento in quanto l'utente potrebbe non avere idea di che progressivo assegnare

    spero di esserti stato di aiuto

    Non sempre essere l'ultimo è un male... almeno non devi guardarti le spalle

    il mio profilo su PHPClasses e il mio blog laboweb

  4. #4
    Originariamente inviato da dottwatson
    innanzitutto la tabella deve avere 3 colonne:

    id_fattura
    num_fatt
    data (timestamp)

    1) fai in modo che il numero fattura sia dato dal sistema, e non dall' utente. Lo comunicherai a fine inserimento il numero assegnato

    ehm, in tutte le soluzioni postate da me il numero della fattura viene generato dal sistema, l'utente non deve fare proprio niente.

    id_fattura mi pare superfluo, dato che le fatture non possono essere eliminate, al massimo potrebbe servire per utilizzarlo come codice univoco da non azzerare a fine anno al contrario del num_fatt, anche se secondo me (anno,num_fatt) è più che sufficiente.

    Il mio post serviva solo per aiutarmi a scegliere la soluzione migliore tra quelle proposte:

    - LOCK
    - SUBQUERY
    - CHIAVE PRIMARIA MultiColonna con AutoIncrement su una delle due colonne. (quella che al momento preferisco).

    O a indicarmi eventuali problemi.

    Ad ogni modo grazie per l'up

  5. #5

    Re: [MySQL] Fattura: Lock, Transazioni, SubQuery, AUTO_INCREMENT Multi-Colonna

    Originariamente inviato da LoRdCoStE
    Salve a tutti, durante la progettazione di un modulo fatturazione per un CMS mi sono trovato ad affrontare il problema delle fatture, in particolare della loro numerazione:
    • Numero univoco
    • Azzeramento della numerazione a fine anno
    • Problemi di concorrenza


    Infine un ultimo metodo che a me sembra più elegante (e funzionante), utilizzo una chiave primaria composta da (anno,numero) dove numero è auto_increment.
    Codice PHP:
    CREATE TABLE fatture (
        
    anno INT(4NOT NULL,
        
    numero INT(10NOT NULL AUTO_INCREMENT,
        
    totale DECIMAL(8,2NOT NULL,
        
    PRIMARY KEY (anno,numero)
    ENGINE=MyISAM;

    INSERT INTO `fatture` (
    `
    anno` ,
    `
    numero` ,
    `
    emissione` ,
    `
    totale`
    )
    VALUES (
    '2010'NULL '2010-09-56''101.00'
    );


    INSERT INTO `fatture` (
    `
    anno` ,
    `
    numero` ,
    `
    emissione` ,
    `
    totale`
    )
    VALUES (
    '2010'NULL '2010-09-16''101.00'
    );


    INSERT INTO `fatture` (
    `
    anno` ,
    `
    numero` ,
    `
    emissione` ,
    `
    totale`
    )
    VALUES (
    '2011'NULL '2011-01-01''103.00'
    ); 
    Il risultato finale è il seguente:
    codice:
    2010 	1 	2010-09-15 	100.00
    2010 	2 	2010-09-16 	101.00
    2011 	1 	2011-01-01 	103.00
    Secondo voi quale di queste soluzioni è la migliore? Ne conoscete altre?
    Fatemi sapere cosa ne pensate, in particolare dell'ultima.
    Se la key è su due valori, al cambio del primo l'auto increment si azzera da solo??
    Ciao!

  6. #6
    Utente di HTML.it L'avatar di luca200
    Registrato dal
    Apr 2002
    Messaggi
    4,120

    Re: [MySQL] Fattura: Lock, Transazioni, SubQuery, AUTO_INCREMENT Multi-Colonna

    Originariamente inviato da LoRdCoStE
    Secondo voi quale di queste soluzioni è la migliore? Ne conoscete altre?
    Fatemi sapere cosa ne pensate, in particolare dell'ultima.
    OVVIAMENTE la soluzione è l'ultima, e visto che l'avevi trovata da solo, non capisco il senso del thread

  7. #7
    CREATE TABLE fatture (
    anno INT(4) NOT NULL,
    numero INT(10) NOT NULL AUTO_INCREMENT,
    totale DECIMAL(8,2) NOT NULL,
    PRIMARY KEY (anno,numero)
    ) ENGINE=MyISAM;

    Questa non la sapevo, ma creare una tabella cosi con phpmyadmin è impossibile. si può fare solo con una query.

    Se metti il campo auto_increment, lo imposta da solo come indice primario, se non lo metti e crei l'indice sui due campi, poi non puoi modificare il campo ad auto_increment



    Cmq confermo che funziona.
    e anche secondo me è la soluzione più ovvia.
    Ciao!

  8. #8

    Re: Re: [MySQL] Fattura: Lock, Transazioni, SubQuery, AUTO_INCREMENT Multi-Colonna

    Originariamente inviato da luca200
    OVVIAMENTE la soluzione è l'ultima, e visto che l'avevi trovata da solo, non capisco il senso del thread
    Il senso di un forum penso sia quello di condividere idee e soluzioni, anche a me la soluzione migliore è sembrata l'ultima ma data la particolarità mi interessava un secondo parere.


    Questa non la sapevo, ma creare una tabella cosi con phpmyadmin è impossibile. si può fare solo con una query.

    Se metti il campo auto_increment, lo imposta da solo come indice primario, se non lo metti e crei l'indice sui due campi, poi non puoi modificare il campo ad auto_increment
    Eh si, bisogna crearla a mano, però vabbè tanto sono poche le situazioni in cui serve una cosa del genere

  9. #9
    Utente di HTML.it
    Registrato dal
    Jan 2011
    Messaggi
    2
    Scusatemi, non se qualcuno segue ancora questo topic...ma io ci sono capitato e fa proprio al caso mio.
    MI servirebbe sapere come avviene l'azzeramento dell'opazione 3: avviene completamente in automatico? Cioe quando cambia l'altro attributo facente parte della chiave multipla si azzera l'attributo autoincrement?
    oppure c'è da considerare qualcosa nell'insert? perche provando nel mio DB non mi funziona! =S

    codice:
    create table venditabottiglie(
    anno int ,
    noperazione int(11) auto_increment,
    nbottiglie int,
    nlotto int references imbottigliamento(lotto),
    primary key (anno,noperazione))ENGINE=InnoDB;
    codice:
    insert into venditabottiglie(anno,nbottiglie,nlotto) values (2010,50,2);
    Questo codice non è equivalente all'opzione 3 sopra riportata? =)
    Purtroppo, a me, cambiando l'anno l'attributo autoincrement continua ad andare per i fatti suoi...probabile che faccio qualche pecca imperdonabile, scusatemi sono alle prime armi...ma è qualche ora che ci sbatto la testa senza soluzione!

    Grazie in anticipo.

  10. #10
    A parte che con InnoDB non mi fa creare quella tabella, ma devo invertire le due colonne della chiave primaria in questo modo:

    codice:
    CREATE TABLE venditabottiglie(
    anno int,
    noperazione int( 11 ) AUTO_INCREMENT ,
    nbottiglie int,
    nlotto int REFERENCES imbottigliamento( lotto ) ,
    PRIMARY KEY ( noperazione, anno )
    ) ENGINE = InnoDB;
    Però una volta creata la tabella effettivamente la cosa non funziona come dovrebbe, "noperazione" continua ad essere incrementato anche cambiando anno.

    Con engine MyISAM la cosa funziona perfettamente, vedi tu se ti servono funzioni specifiche di InnoDB o se puoi passare a MyISAM.

    Saluti

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.