Newsgroup: it.comp.lang.delphi
Titolo: Gestire Autoincrement con Delphi

Ciao ragazzi.
Ho un db mysql 5 con la tabella CLIENT con i campi ID, IP, HOST, PATH
ID č chiave primaria autoincrement.
Sulla form ho solo un TSimpleDataSet, un TDataSource ed una TDBGrid.
Nel TSimpleDataSet.DataSet.CommandText č presente la query select * from 
client
Se eseguo una query da codice di tipo insert tutto ok ma se utilizzo il 
seguente codice
SimpleDataSet1.append;
SimpleDataSet1.FieldByName('ip').AsString := edit2.Text;
SimpleDataSet1.FieldByName('host').AsString:=edit3.Text;
SimpleDataSet1.FieldByName('path').AsString:=edit4.Text;
SimpleDataSet1.Post;
mi viene sollevata l'eccezione che manca il valore per il campo ID.
Per evitarla č sufficiente indicare nel TSimpleDataSet.DataSet.CommandText 
una select che escluda il campo ID ma in questo caso la TDBGrid non mostra 
piů tale campo che perň io vorrei visualizzare.
In sostanza, che voi sappiate, non esiste un'istruzione che permetta a 
Delphi di passare il valore ID come quello che č, cioč un Autoincrement? Una 
cosa stile
SimpleDataSet1.FieldByName('iid').AsInteger := IS_Autoincrement;
Anche altre soluzioni...
Grazie
Valter

Premetto che detesto usare campi autoinc.
Devi reinterrogare il db per sapere che valore è stato assegnato al
campo, cosa che serve se hai le classiche relazioni testata/righe.
In genere preferisco usare lato client generators o sequences e
buonanotte e passare IO il valore al db assegnandolo al campo,
A parte cio', se gli passi uno zero (non un null, ovviamente..!) lui
non lo ignora?
A.

non lo ignora?
Ci avevo già provato constatando che nel db l'autoincrement era corretto ma 
la dbgrid visualizzava lo zero passato. Procedendo con altre operazioni si 
sollevavano errori causa valori nella dbgrid non allineati con quelli del 
db. Così ho lasciato perdere, non essendo certo si trattasse di 
un'operazione corretta.
Ma data la tua risposta ci sono tornato ed ho aggiunto subito dopo 
ApplyUpdate(0) un Refresh del dataset, i valori della dbgrid vengono 
riaggiornati ed il tutto pare ok.
Grazie
V 

 V

L'idea del refresh è una pezza.
Effettivamente già solo l'idea di passare un valore non rispondente alla 
realtà mi mette male.
Grazie
V

 ID è chiave primaria autoincrement.
Fai doppio click sulla TAdoTable e verifica che hai aggiunto tutti i
campi della tabella.
Altrimenti clicca con il DX e e fai "Add all fields..."
Fatto ciò verifica che nelle property del field ID sia impostato
READONLY.
Poi scrivi due righe in button che facciano semplicemente:
  SimpleDataSet1.append;
  SimpleDataSet1.Post;
e vedi se funzia tutto.
Se funzia (e a me funzia) prova cosi':
  SimpleDataSet1.append;
  SimpleDataSet1.Post;
  SimpleDataSet1.Edit;
  SimpleDataSet1.FieldByName('ip').AsString := edit2.Text;
  SimpleDataSet1.FieldByName('host').AsString:=edit3.Text;
  SimpleDataSet1.FieldByName('path').AsString:=edit4.Text;
  SimpleDataSet1.Post;
Un'altra cosa: nel field ID c'e' la property AutoGenerateValue.
A me è settata a arNone, ma ha anche altri valori. Provali.
Gp

 buonanotte e passare IO il valore al db assegnandolo al campo
Tu ragioni da uomo single-thread a con un solo client che accede in
esclusiva sul DB.
Prova a pensare con una decina di client, magari web che fanno insert
parallele sul tuo DB e ne parliamo.
Detto questo, ovviamente di risposta non ce n'e' una sola: tutte le
tecniche hanno vantaggi e svantaggi. Sappi che questo è uno dei
problemi che più ha tormentato i programmatori di database e di
framework di accesso ai DB. Spesso i migliori framework mettono a
disposizione entrambe le possibilità, ma ce ne sono anche di ibride!
Ciao,
Gp

 parallele sul tuo DB e ne parliamo.
Magari devo studiare, ma non vedo l'autoinc come soluzione migliore.
Mi spieghi meglio?
A.

 Gp

 
Ma in questo modo perdi tutti i controlli su eventuali campi 
obbligatori/constraint in quanto devono essere disabilitati per fare 
questa operazione (append-post), e se hai qualche controllo nell'evento 
Before insert/post, datachange ect ect vengono scatenati a vuoto,
<IMHO>
  strada impraticabile
</IMHO>
^_Goblin_^
-- 
Ibis et redibis non morieris in bello
http://goblinland.myminicity.com/

 nulli.
io del guid non mi fido: è un hash.
E come tale, anche se poco probabile, il rischio di duplicazione
esiste.
In  piu, è troppo lungo.
Se hai relazioni master/detail portarsi dietro per ogni riga di
dettaglio 32 caratteri di primary key del master la vedo pesante.
Ma continuo a non capire xche l'uso di una sequence dovrebbe essere
scartato a favore di un autoinc.
Se poi si parla di web, con certi siti che ho visto un rallentamento
(se esiste!) dovuto a cio non si nota di certo...
A.

   SimpleDataSet1.Post;
.. e l'applyupdates quando la fai? Mica sei su file cds che ti basta
la post.
A.

 parallele sul tuo DB e ne parliamo.
Io mi schiero dalla parte di quelli che evitano il campo autoInc. :)
Ma non capisco cosa c'e' di sbagliato nel generare un numero lato client 
per mezzo di un generator, in quanto ci pensa il server a gestire il 
tutto e al tuo client ritorna un numerino univoco, anche se ci sono 10 
client che fanno la stessa richiesta simultaneamente.


 client che fanno la stessa richiesta simultaneamente.
infatti.....
A.

 Mi spieghi meglio?
Il problema secondo me ï¿œ che non vi siete capiti.
Tu in un post di questo thread dici che preferisci calcolarti gli id 
lato client, e lui ti faceva notare che se piu' client voglio scrivere 
nello stesso momento, la concorrenza di accesso e' un problema visto 
che gli ID sono generati dai client.
Dovresti approfondire il discorso perchï¿œ non ï¿œ chiaro: io ti ho capito 
quando hai parlato di master detail, ï¿œ ovvio che se devi fare una 
insert di un detail, devi prima conoscere l'ID master,e questo quindi 
sei costretto a calcolartelo prima dividendo cosï¿œ le scritture in due 
sessioni distinte, prima il master poi i details (e cosi' sei costretto 
a ricorrere alle transazioni..)
Questo thread ï¿œ interessante, approfondiamo.
ciao
-- 
Morde

 Questo thread è interessante, approfondiamo.
Ok. Cerco di spiegarmi.
La app chiede al server un id.
Il server lo calcola in qualche modo, tipicamente una sequence/
generator.
Il client riceve l'id e scrive il record master
cicla sui record di dettaglio
per ognuno chiede un id al server
il server lo calcola sempre tramite sequence e lo manda indietro
il client riceve e scrive la riga di dettaglio
Questo a grandi linee.
Ovviamente esiste spazio per ottimizzazioni, ma quelle vanno valutate
in base al contesto.
A.

 gli ID sono generati dai client.
Io per lato client intendo dire che eseguo una procedura che chiama un 
generator (o adirittura il generator o sequences) il quale ti ritorna il 
numerino ID da assegnare al tuo field.
Nell'evento OnNewrecord inserisco la chiamata:
  CDS.FieldByName('ID').AsInteger := AssegnaID('NextID');
function AssegnaID(sProc: String): Integer;
begin
   SQLStoredProc1.StoredProcName := sProc;
   SQLStoredProc1.ExecProc;
   Result := SQLStoredProc1.Params[0].AsInteger;
end;
Io uso FB ...
CREATE PROCEDURE NextID
returns (
     id_next integer)
as
begin
   ID_next = gen_id(gid_NextID,1);
   suspend;
end
(Creazione del generator)
CREATE SEQUENCE gid_NextID;
ALTER SEQUENCE gid_NextID RESTART WITH 1;
Questa è la strada che di solito seguo per avere un ID


 Questo a grandi linee.
Ottimo, grazie ;)
-- 
Morde

 numerino ID da assegnare al tuo field.
Ecco ï¿œ lï¿œ che non capivo.. l'espressione "lato client" quindi non ï¿œ del 
tutto appropriata, perchï¿œ l'ID viene calcolato "lato db" sempre e 
comunque (come ï¿œ logico)
Questo tipo di ottenimento dell'ID diventa molto utile se devi gestire 
l'approccio master-detail (vedi follow-up di alberto in questo fork) 
con classi business e non con componenti VCL db aware.
Ciao
-- 
Morde

campi della tabella.
Ciao al gruppo.
Non utilizzo la TAdoTable.
Cmq, impostando nel SimpleDataSet.FieldDefs il campo come ReadOnly (ma anche 
lasciandolo di default) e la propriet� DataType a ftAutoInc genera comunque 
l'errore che � necessario un valore per il campo ID.
La propriet� AutoGenerateValue non pare disponibile..
Attualmente l'unico modo per far gestire correttamente l'autoincrement a 
MySql � passargli il valore "0".
Ciao
V

 comunque (come è logico)
Si... è piu' corretto dire "il client chiede al server un id e poi lo
usa".
Comunque, ci sono contesti nei quali questa logica non è applicabile
in quanto si basa sul seguente presupposto:
"esiste sempre un server db al quale chiedere gli id".
Ma questa cosa non è ovviamente sempre vera.
In tal caso il calcolo della pk è TOTALMENTE  a carico del client, e
per garantire l'univocita' ad ogli client deve essere associato un
identificativo univoco a livello di sistema che diventa parte della
pk.
A.

 classi business e non con componenti VCL db aware.
Con classi di business dipende dal tipo di framework che si utilizza. 
tiOPF prevede direttamente nelle classi il tipo TOID che, in base alla 
unit scelta da aggiungere alla clausola uses, genera un integer,  un 
int64, un GUID ecc..
Es:
MyCustomer = class(TtiObject)
.......
end;
MyCustomer := TCustomer.CreateNew('','')
Il costruttore CreateNew crea un nuovo id per l'oggetto appena creato e 
setta lo stato dell'oggetto a PosCreate (ObjectState property)
Vero ï¿œ quello che dice Alberto, i GUID sono di 36 caratteri stringa, 
quindi alcune volte lenti da gestire nelle tabelle del db, ma per db non 
troppo estesi la trovo una soluzione comoda e pratica.
Antonio

 (se esiste!) dovuto a cio non si nota di certo...
Sono d'accordo sugli autoinc, vanno evitati accuratamente ;)
Antonio

 master/detail, come dici.
quello che mi lascia molto perplesso è il fatto che alcuni db non
abbiamo qualcosa come le sequence.
Almeno, fino a qlc versione fa, ne Sybase ne SqlServer hanno questa
cosa.
Su SqlServer, visto chi lo fa, non mi stupisco....Che tu puoi
aspettare quando nel cf2.0 manca la gestione dei radiogroup...
Ma Sybase...bho??!
A.

 esiste.
Sai che non sono cos� sicuro che sia un hash ?
Credo sia piuttosto calcolato randomicamente usando degli
"ammucchiamenti numerici" notevoli ;). Sto dando un'occhiata ai sorgenti
di tiOPF2 per la generazione. Sotto win32 invoca la classica
CoCreateGuid ma la parte relativa a Linux fa dei giri pazzeschi andando
a pescare nel cuore di FPC per tirar fuori sto GUID :O.
Per cui credo che sia pi� probabile che camminando per le strade di
Londra si venga colpiti in testa da una tartaruga lasciata cadere da
un'aquila che l'aveva nel becco che di avere una violazione di chiave
primaria per la generazione di 2 guid identici sullo stesso db ;).
Rimane fermo il concetto delle lunghezza di una stringa di 36 caratteri.
Antonio

 In  piu, è troppo lungo.
Questo puo' essere un problema.
Ciao!
Gp

 Questo puo' essere un problema.
su coding horror
http://www.codinghorror.com/blog/archives/000817.html
si parla proprio del *costo* del guid come *pk*.
-- 
Morde

 questa operazione (append-post),
Scusa, mi era sfuggito un copia-incolla.
  SimpleDataSet1.append;
  SimpleDataSet1.Post;
mi serviva come test per verificare che tutto è ok per il problema
descritto nella mail iniziale. Ma poi, nel programma si usa
direttamente
  SimpleDataSet1.append;
  SimpleDataSet1.FieldByName('ip').AsString := edit2.Text;
  SimpleDataSet1.FieldByName('host').AsString:=edit3.Text;
  SimpleDataSet1.FieldByName('path').AsString:=edit4.Text;
  SimpleDataSet1.Post;
Gp

 la post.
Come detto, non avevo letto che era sy mysql.
Il concetto però non cambia: a me funzia l'ID assegnato dal DB e non
capisco perché non debba funzionare a lui.
Ciao,
Gp

 che gli ID sono generati dai client.
E' esattamente quello che volevo dire. Scusate se non mi ero spiegato
bene.
Grazie Morde!

 l'ID viene calcolato "lato db" sempre e comunque (come è logico)
"come è logico" non è condiviso da tutti, come dimostra questo
thread. :-)
Gianpiero

 Credo sia piuttosto calcolato randomicamente usando degli
Per qui vuole approfondire, qui è spiegato abbastanza bene:
 
http://en.wikipedia.org/wiki/Globally_Unique_Identifier
Ricordavo abbastanza bene il discorso delle stelle e della probabilità
Gp :-)

capisco perch� non debba funzionare a lui.
[..]
Non lo capisco nemmeno io, azzardo, forse perch� utilizziamo componenti 
diversi? O forse il driver di accesso al db, uso dbxopenmysql50...
In tutti i casi se eseguo una query di update l'operazione va a buon fine, 
diversamente solleva eccezione se all'ID non passo il valore zero.
V

 Rimane fermo il concetto delle lunghezza di una stringa di 36 caratteri.
con 4 byte, puoi spaziare su oltre 4 miliardi di id....sicuri e
affidabili per quanto puo essere affidabile un db.
xche farsi del male?
A.

 colpisca il nostro pianeta nel prossimo anno.
1) quale sarebbe questa "da qualche parte"?
2) in base a quali dati questa "parte" fa queste affermazioni?
3) questa "parte" ha anche pubblicato una dimostrazione matematica di
quanto ha scritto?
In assenza di queste 3 cose questa "parte" la metto da parte.
Sono STUFO di sentire o leggere cose di questo genere spesso campate
per aria, prive di qualsiasi fondamento oggettivo in grado di
dimostrare la fondatezza di quanto si scrive.
Troppo spesso chi scrive di queste cose non le fa in pratica.
Qualcuno segue il "profeta" di turno e poi quando non funziona un
cazzo i cazzi sono i NOSTRI e non di chi ha scritto le cose...
Pax vobiscum.
A.

> Tranquillo, tranquillo. Mi ero perso il fatto che non usava ADO.
xche? se usava ado era lecitot usare una TAdoTable su un db c/s?
A.


Ciao,
scusa il casino, ma avevo letto velocemente la mail e non mi ero
accorto di DB usavi, quindi ti ho espresso la mia esperienza con
Delphi (limitata ad ADO) nella quale le tecniche che ti ho spiegato
funzionano.
Gp

 Pax vobiscum.
Cum spiritu tuo
Gianpiero

 quanto ha scritto?
Calma, calma. La dimostrazione non è poi tanto complessa, per lo meno


 funzionano.
ok. cmq, giusto x chiarire:
usare gli oggetti tabella come TTable, TAdoTable e TSqlTable con
database client/server è SBAGLIATO.
A.

 che ci siamo detti nelle precedenti mail).
azz, sei laureato in statistica?? :-D
Comunque, la differenza tra queste cose che hai citato e la storia del
gui è semplice:
Io posso SCEGLIERE di non usare il guid e mandare afanculo le
probabilità.
Sono basse, ok, ma certo + alte del rischio di duplicazione di una
sequence o di una coppia idclient+idrecord.
Viceversa, non ho modo di sapere se e quando saro sfigato.
Percio, ripeto: potendo evitare la pur minima possibilità di avere
casini, xche non farlo?
Cia'!
A.

 dalla IEEE. Lo trovi online con un po' di ricerche.
Di Linux ci si pu� fidare sempre proprio perch� il codice sorgente �
liberamente accessibile e scrutabile ;)
Antonio

 liberamente accessibile e scrutabile ;)
e ti pareva.... mica ti perdi un occasione per sottolineare gli
aspetti positivi di linux, eh???
:-D
A.

 aspetti positivi di linux, eh???
Eh gi� :)
Antonio

 lavoro con client misti, sia web che non web..
Mi piace. Anche se io con questo alg. aggiungeei alla tabella sempre
un timestamp per conservare l'ordine degli inserimenti.
Ciao,
gp