Visualizzazione risultati 1 fino 9 di 9
Like Tree1Likes
  • 1 Post By phollia

Discussione: Record registrati "Doppi"

  1. #1
    phollia non è connesso Utente giovane
    Data registrazione
    05-02-2015
    Messaggi
    68

    Predefinito Record registrati "Doppi"

    Salve a tutti.

    Visto che mi è capitato più di una volta, la cosa passa da caso isolato e fortuito a problema.

    La situazione è questa, ho uno script che si occupa di registrare dei dati on una tabella MySQL.
    Prima di procedere però verifico che quello specifico dato non sia già stato registrato (generando un errore).

    Il fatto però è che più di una volta mi ritrovo col dato registrato due volte (due record consecutivi in cui anche data e ora di registrazione sono identici).

    Cosa può essere successo e come posso rimediare?
    Ho pensato pure di LOCKare la tabella fino a conclusione dell'INSERT, ma mi sorge un dubbio. LOCK non blocca a livello di utente e quindi essendo lo stesso utente (utente PHP) a chiedere di utilizzare la tabella non è che possa ugualmente scrivere?


    Grazie
    Ultima modifica di phollia : 27-10-2021 alle ore 16.58.52

  2. #2
    L'avatar di alemoppo
    alemoppo non è connesso Staff AV
    Data registrazione
    24-08-2008
    Residenza
    PU / BO
    Messaggi
    22,141

    Predefinito

    Poi mostrare parte del codice che esegue la INSERT? È dentro qualche ciclo? Quel codice è incluso in più pagine?

    Ciao!

  3. #3
    phollia non è connesso Utente giovane
    Data registrazione
    05-02-2015
    Messaggi
    68

    Predefinito

    Citazione Originalmente inviato da alemoppo Visualizza messaggio
    Poi mostrare parte del codice che esegue la INSERT? È dentro qualche ciclo? Quel codice è incluso in più pagine?

    Ciao!
    Allora, tutto lo script è nella stessa pagina e molto lineare.

    Prima verifico che il dato (una risposta) non sia stata già inserita per la stessa opzione e dallo stesso utente:

    Codice PHP:
    //Verifico che non si sia già inviata un'altra risposta
    $sql = "SELECT * FROM risposte WHERE opzione = 'XXXX' AND user = 'XXX'";

    $result = $con->query($sql);
    $c = $con->affected_rows;
    if(
    $c > 0 ) {
    $error = true;
    $errors[] = "Una risposta risulta gia' inviata!";
    }
    Quindi, se non ci sono errori procedo con l'INSERT

    Codice PHP:
    if(!$error) {
    $sql = "INSERT INTO risposte (campo1, campo2, ...) VALUES ('uno', due, ...)";

    $con->query($sql);

    if(
    $con->errno == 0){
    echo
    "<div>";
    echo
    "<p>Dati salvati.</p>\n";
    echo
    "</div>";
    }else{
    echo
    "<div>";
    echo
    "<p>Si e' verificato un errore durante il salvataggio,<br />";
    echo
    "per favore riprova.<br />";
    echo
    "Se il problema dovesse ripresentarsi contattare l'amministratore</p>";
    echo
    "</div>";
    }
    }else{
    echo
    "<div>";
    echo
    "<p>Si sono verificati errori:<br />";
    foreach(
    $errors as $e){
    echo
    $e . "<br />";
    }
    echo
    "</p></div>";
    }

  4. #4
    mzanella non è connesso AlterGuru
    Data registrazione
    29-12-2015
    Messaggi
    1,954

    Predefinito

    Puoi aggiungere un vincolo di unicità nella tabella, sulle colonne (opzione, user). Così sarà il database a gestire questo vincolo, senza ulteriori azioni da parte tua. Puoi anzi rimuovere la query di verifica e il relativo controllo.

    I suggerimenti che do più spesso:


  5. #5
    phollia non è connesso Utente giovane
    Data registrazione
    05-02-2015
    Messaggi
    68

    Predefinito

    Citazione Originalmente inviato da mzanella Visualizza messaggio
    Puoi aggiungere un vincolo di unicità nella tabella, sulle colonne (opzione, user). Così sarà il database a gestire questo vincolo, senza ulteriori azioni da parte tua. Puoi anzi rimuovere la query di verifica e il relativo controllo.
    Uno dei campi è di tipo TEXT per cui una verifica di questo tipo mi porta a dover dichiarare il numero di caratteri da verificare e non è proprio comodo ma neanche tanto funzionale.

    Mi interesserebbe invece capire COSA succede nei casi in cui i record arrivano a duplicarsi e quindi COME poter intervenire.

  6. #6
    mzanella non è connesso AlterGuru
    Data registrazione
    29-12-2015
    Messaggi
    1,954

    Predefinito

    Uno dei campi è di tipo TEXT per cui una verifica di questo tipo mi porta a dover dichiarare il numero di caratteri da verificare e non è proprio comodo ma neanche tanto funzionale.
    Non hai bisogno di conoscere in anticipo il numero esatto di caratteri, è sufficiente una limitazione superiore a tale valore per dichiarare una colonna di tipo VARCHAR. E una limitazione superiore è necessariamente disponibile, fosse anche 65535 (la dimensione massima di un TEXT).
    I problemi di prestazioni ci sono, ma sono inevitabili, quindi non rappresentano un motivo per escludere questa soluzione che anzi, a ben vedere, è in effetti la più efficiente: considerato un qualsiasi approccio alternativo, ad un certo punto dovrai necessariamente andare a verificare la presenza di un coppia (opzione, user) uguale a quella che stai cercando di inserire, quindi pagherai comunque il prezzo del confronto tra dati testuali. Per lo stesso motivo avrai bisogno di un meccanismo di lock, da qualche parte. Entrambi possono essere effettuati a livello applicazione, pagando quindi degli overhead, o dentro il database, dunque nel modo più efficiente possibile. Molto meglio lasciar lavorare il database!

    Mi interesserebbe invece capire COSA succede nei casi in cui i record arrivano a duplicarsi e quindi COME poter intervenire.
    Il cosa è relativamente semplice: nel lasso di tempo che intercorre tra la SELECT di controllo e la relativa INSERT si inserisce un'altra chiamata che inserisce un dato. È il classico problema di race condition. Difficilmente risolvibile "a mano" in un ambiente web, dove le diverse richieste sono servite da processi indipendenti e tra loro non comunicanti.
    L'unica soluzione è disporre di una terza entità con cui tutti i processi che stanno servendo le richieste siano in grado di comunicare e che possa fungere da semaforo. Potresti usare un lock sulla tabella, ma potrebbe presentarsi il problema di cui parlavi all'inizio (e non ricordo se il LOCK TABLE sia per utente o per sessione), oppure usare un meccanismo simile ai file pid di GNU/Linux: quando devi inserire un dato cerchi prima di creare un file: se il file esiste già vuol dire che un altro processo è entrato nella sessione critica, e devi aspettare, se il file non esiste allora lo crei e acquisisci accesso alla sezione critica. In questo caso il funzionamento si basa sul fatto che disponi di un'operazione atomica per creare un file e controllarne l'esistenza, cosa che non hai in SQL.

    I suggerimenti che do più spesso:


  7. #7
    NLSweb non è connesso Altervistiano Junior
    Data registrazione
    17-01-2014
    Messaggi
    658

    Predefinito

    Scusate, ma non potrebbe essere più semplice utilizzare ON DUPLICATE KEY UPDATE ?
    Per cui o inserisci un nuovo elemento o lo sovrascrivi.

  8. #8
    phollia non è connesso Utente giovane
    Data registrazione
    05-02-2015
    Messaggi
    68

    Predefinito

    Citazione Originalmente inviato da NLSweb Visualizza messaggio
    Scusate, ma non potrebbe essere più semplice utilizzare ON DUPLICATE KEY UPDATE ?
    Per cui o inserisci un nuovo elemento o lo sovrascrivi.
    No. La PK non viene duplicata in questo caso.
    mzanella likes this.

  9. #9
    darbula non è connesso AlterGuru 2500
    Data registrazione
    24-04-2011
    Messaggi
    2,894

    Predefinito

    Strict comparisons with ===
    Codice PHP:
    if(0 === $c) {
    echo
    'stai aggiungendo i dati';
    } elseif(-
    1 === $c) {
    echo
    'errore';
    } else {
    if(
    is_string($c) || $c < 0 || $c > 0)
    echo
    'già presente';
    else
    echo
    'altro';
    }
    https://www.php.net/manual/en/mysqli.affected-rows.php
    Ultima modifica di darbula : 01-11-2021 alle ore 22.01.52

Regole di scrittura

  • Non puoi creare nuove discussioni
  • Non puoi rispondere ai messaggi
  • Non puoi inserire allegati.
  • Non puoi modificare i tuoi messaggi
  •