Newsgroup: it.comp.lang.perl
Titolo: last_insert_id

La funzione dovrebbe servire ad ottenere l'ultimo id inserito con un
comando insert. Il manuale DBI-CPAN mostra la seguente sintassi che
dipende dal driver in uso:
$rv = $dbh->last_insert_id($catalog, $schema, $table, $field);
nella pratica non ottengo mai il valore corretto, non in sintonia con
i valori inseriti precedentemente e controllati attraverso la finestra
del command client.
Al fine di evitare di perdere troppo tempo nella ricerca del motivo
per il quale non riesco ad ottenere un valore affidabile dell'ultimo
id inserito, anche perché i post in lingua inglese nei vari forum e
newsgroup legati a MySQL sono pessimisti, ho aggirato i problema nel
modo seguente:
my @riga_nuovo_id = $dbh->selectrow_array('SELECT MAX(id) FROM tab');
my $nuovo_id = (@riga_nuovo_id[0]=='') ? 1: @riga_nuovo_id[0];
Dato che il campo 'id' è impostato come auto_increment suppongo che il
valore restituito dalle due righe di programma sia sempre corretto,
tenendo conto che i valori siano registrati in tabella con 'commit'
quando necessario.
Il dubbio però è una brutta malattia, non per 'insicurezza' ma per
'diffidenza'.
Secondo voi posso stare tranquillo e fidarmi del valore restituito
dalle due righe di programma?

 valore restituito dalle due righe di programma sia sempre corretto,
Non mi sembra affidabile. Se tra la precedente INSERT e questa SELECT
un'altra sessione fa INSERT e 'commit', avrai un MAX(id) che non
corrisponde all'id della tua INSERT.
Max

 Max
Hai ragione, nel mio caso la riga del select è immediatamente
successiva a quella di insert.

Oreste wrote:

Oreste, Max intendeva dire (giustamente) che un altro processo/utente (o 
anche più di uno) potrebbe fare una insert e committarla proprio in 
mezzo (in senso temporale) alla tua insert e alla tua successiva select, 
per cui otterresti un id scorretto (maggiore).
In altre parole, anche se nel tuo codice compaiono una dietro l'altra, 
la tua insert e la successiva select non costituiscono un blocco atomico 
dal punto di vista del db server, che viceversa le vede come operazioni 
separate e pertanto tra l'una e l'altra potrebbe eseguire qualsiasi cosa.
Per rendere affidabile quel codice, dovresti wrappare le due istruzioni 
in una transazione con livello di isolamento perlomeno read committed.
Ma in realtà forse il problema non esiste: nella tua tabella dovresti 
avere comunque una chiave secondaria (ovvero un campo o una combinazione 
di campi al di fuori di id con vincolo UNIQUE), altrimenti rischieresti 
di avere due record identici che differiscono solo per il campo seriale, 
che sarebbe piuttosto strano...
Perciò puoi usare quest'altra chiave (di cui conosci il valore) per 
recuperare il record e quindi l'id generato dal db server.
(Se non hai altre chiavi sulla tabella, è probabile che il disegno del 
db non sia corretto).
Ciao,
Emanuele.

 my $nuovo_id = (@riga_nuovo_id[0]=='') ? 1: @riga_nuovo_id[0];
Comunque questo andrebbe scritto così:
my $nuovo_id = ( $riga_nuovo_id[0] eq '' ) ? 1 : $riga_nuovo_id[0];
Inoltre non riesco a capire in che caso il db possa restituire la 
stringa vuota: se non ci sono record restituisce NULL, che poi da DBI 
viene tradotto in undef, quindi in realtà dovrebbe essere:
my $nuovo_id = $riga_nuovo_id[0] || 1;
considerato che il campo serial parte da 1.
(Che per inciso funzionerebbe anche con la stringa vuota).
Infine non capisco neanche perché setti $nuovo_id ad 1, se non ci sono 
record (ma avrai i tuoi buoni motivi, presumo ;-)
Ciao,
Emanuele.

 Emanuele.
cari Max e Emanuele,
vi ringrazio per i preziosi suggerimenti, rispondo solo ora perchè nel
mezzo del lavoro di programmazione ho ricevuto visite e devo
interrompere altrimenti mi viene a mancare la tranquillità necessaria
che mi permette di concentrarmi nelle centinaia di righe di programma.
Il programma è parte di un applicativo internet, composto quindi da
più moduli scritti in nei vari linguaggi e meta linguaggi utili, in
particolare i soliti html, css, javascript. Uso molto XMLHttpRequest
che mi risolve parecchi problemi. Inoltre i codici specifici del
modulo HTML::Template contribuisce da una parte a rendere il
minestrone di codici ancora più ricco (leggi incasinato) ma anche a
separare meglio i vari linguaggi ed evitare di inserire html nel mezzo
di routine Perl. Da parte mia è un modo per mantenere un minimo di
sanità mentale.
Le due righe
my @riga_nuovo_id = $dbh->selectrow_array('SELECT MAX(id) FROM tab');
my $nuovo_id = (@riga_nuovo_id[0]=='') ? 1: @riga_nuovo_id[0];
vengono richiamate da un tasto in una pagina html, di conseguenza
costituiscono un blocco di istruzioni che sono generate da un
collegamento http. In pratica, anche se nel server due blocchi di
istruzioni possono sovrapporsi, essene vengono comunque inviate per
sessioni di domanda/risposta al server. Certo esiste la possibilità
che una sessione venga interrotta, ma due condizioni sono previste 1)
autocommit è impostato, inoltre non è previsto più di un operatore per
questo tipo di operazione, si tratta di data entry con dati che
necessitano di conoscenza avanzate sulla materia specifica che
riguarda i dati. Il personale specializzato in loco si conta sulla
punta delle dita. A proposito si tratta di dati relativi a collezioni
museali.
Perché acquisire l'ultimo id? In effetti una volta che si preparano i
dati per l'invio l'id non dovrebbe essere una preoccupazione. Il mio
problema è dovuto al fatto che il record, la scheda è estremamente
ricca di dati, così tanti da costringere a dividerla in più pagine per
evitare di creare un paginone che avrebbe come risultato quello di
confondere l'utente ed aumentare la probabilità di errore durante
l'inserimento dei dati.
Mi piacerebbe mostrare il lavoro, ma le pagine si trovano in un'area
protetta a richiesta del cliente.
Dunque procedo nel modo seguente: nel momento in cui l'operatore
decide di inserire i dati relativi ad un oggetto museale, creo un
record blank, acquisisco l'id e lo "aggangio" con un campo hidden
all'interno del form html. Il form viene creato al volo da
HTML::Template.
Cerco, per quanto possibile, di evitare l'uso di cookie, comunque solo
quando assolutamente necessario.
Per completare una scheda, i form da creare al volo saranno sei,
ognuno richiamabile da un click su un tab, questi ultimi permettono di
organizzare la scheda completa in modo accettabile da un punto di
vista grafico. Quando una sezione è completa viene registrata con un
comando update
$sql = 'UPDATE obj_pt SET '.$sub_sql.' WHERE id='.$id_obj;
ecc..
notare l'uso delle singole virgolette, una mania acquisita quando
programmavo in Delphi Pascal.
Dopo l'update il tab scompare, solo le sezioni incomplete sono
visibili.
La riga
my $nuovo_id = (@riga_nuovo_id[0]=='') ? 1: @riga_nuovo_id[0];
che però modificherò secondo il tuo suggerimento, serve solo quando la
tabella è vuota, dunque una sola volta. Certo che anche a me appare
come uno spreco, non è escluso che aggiunga un record arbitrario nella
tabella in fase di setup, così risparmio una antipatica riga di
programma.
Ciao
oreste.parlatano.com