Newsgroup: it.comp.lang.delphi
Titolo: Interbase, generators e triggers

Ho un DB fatto in Interbase 6.0, le tabelle contengono alcuni contatori
realizzati con un generator, e funziona tutto (o quasi...) alla perfezione.
Il problema che mi sorge e' questo: io lancio una finestra di dialogo, e
vorrei come valore di ritorno l'ID dell'ultimo elemento inserito, vorrei
sapere se esiste un metodo piu' appropriato del brutale SELECT MAX ID (che
tra l'altro funziona solo in caso di singolo utente e singola istanza
dell'applicazione.)
Grazie mille.

dell'applicazione.)
Cosa c'e' nella finestra di dialogo, a cosa ti serve?
Ti chiedo questo perche' non capisco perche la SELECT MAX ID non va bene.
Mi emebra la query piu' adatta invece, e funziona a nche in multiutenza.
Se dici che non funziona per via degli accessi concorrenti, questo 
riguarda il db, non la tua applicazione che fa la select.
Spero di essermi spiegatro..
ciao
mOrde

cioč, tu fai una select dopo ogni inserimento per leggere l'ultimo id
calcolato?
Ci sono 2 soluzioni.
1) Invece di usare un trigger crei una stored procedure che richiami sulla
OnNewRecord del dataset.
In questo modo, ad ogni inserimento la pk viene assegnata dal generatore e
la hai pronta e disponibile, ad esempio per relazxioni master-detail
Ad esempio, se lo fai sulla BeforePost hai il problema che se l'utente deve
salvare prima di poter inserire record sul detail
2) esiste una select su una tabella di sistema che ti ritorna l'ultimo
valore calcolato da un generatore.
Questo metodo a me non piace molto in quanto non si sa mai cosa potrebbe
cambiare nelle tabelle di sisetma di un database facendo un aggiornamento.
Se non vado errato, il solito zio billy mi sembra abbia modificato tali
tabelle nel passaggio tra alcune evr di M$Sql e gli utenti che si
appoggiavano a queste tabelle sono stati, ovviamente, molto felici.
Inoltre, siccome in ib mi sembra non esista il concetto di sessione, non
puoi sapere neanche con il tuo metodo se l'ultimo id calcolato dal
generatore sia effettivamente quello che hai calcolato TU e non un altro
utente da un altra postazione sulla rete.
Bye
A.

select first 1 ID from ATABLE order by ID desc
Duilio


Non conoscevo "first 1", e' sql ansi oppure e' un dialetto di interbase?
Poi, curiosita' mia, vorrei cercare di capire se la select proposta da te 
sia piu' performante di una "select max(campo) from tabella".
Credo che la differenza tre le due sia determinata dal campo che si vuole
estrarre, se e' indicizzato o no. O sbaglio ? Sono tutt'e due equivalenti?
Cioa
Morde

 pianger d'amore:

Ok. E quindi, nel caso altamente improbabile (ma purtroppo le cose meno
probabili sono quelle che ti mettono nei guai piu' spesso) che 2 utenti
inseriscano un record nello stesso DB, i 2 utenti avranno lo stesso ID
leggendolo con SELECT MAX.
Per l'applicazione che sto sviluppando adesso questo non e' un problema (e'
sempre un solo utente su una sola macchina), ma la domanda era per il caso
piu' generale (che dovro' affrontare a breve).

leggendolo con SELECT MAX.
Quindi avevo intuito giusto, c'e' il post di Salvati che ti puo' aiutare,
dal mio punto di vista ti posso suggerire di impostare un lock exclusive sulla
tabella quando ci scrivi sopra (e' questione di millisecondi). 
A proposito di accessi concorrenti, ho lavorato diversi anni su DB2, 
mi ricordo che aveva un sistema di accessi concorrenti che ragionava per 
isolasion level e cursor stabilty, riconoscendo 9 diversi livelli di isolamento.
Ora non so se interbase ha simili servizi, cmq Salvati ti suggerisce di utilizzare 
le risorse del db con una stored procedure,a mio modestissimo parere e' sempre meglio 
far lavorare il db che il tuo programma.
Prendi con le molle questi miei suggerimenti, io interbase non lo conosco e attualmente
sto usando access per i programmi che sto scrivendo.
(col vantaggio che gli id di tipo contatore sono gestiti totalmente in automatico, alemno quello... :-)
Ciao
Morde

A differenza di altri db, ib6 non ha:
lock esplicito
sessione di lavoro
non avendo la sessione, un qualcosa che ti ritorna l'ultimo valore assegnato
ad un autoinc per la sessione attiva
un tipo di campo autoinc
Inoltre, una cosa vale, credo, x tutti i database, salvo metodi di accesso
dedicati come gli ibx o i DOA per papa Oracle:
i dati assegnati al db lato server non sono visibili lato clienti salvo un
requery.
Che differenza esiste tra il calcolare un id  via una stored procedure da
codice o rileggere sempre da codice l'ultimo id calcolato dopo un
inserimento?
O meglio, chiamare una sp PRIMA dell'insert o chiamare un qualcosa DOPO
l'insert?
In ogni caso è il database che calcola il nuovo id
A.

In ogni caso č il database che calcola il nuovo id
Ah, allora non avevo capito...
Ignora le mie affermazioni.
Ciao
morde

piu' generale (che dovro' affrontare a breve).
Dai un'occhio qui, se ti puo' servire, presa da 
http://www.delphifaq.com/faq_da.htm
Ciao Morde
----
Using InterBase generators for AutoIncrement fields
InterBase doesn't offer the convenient AutoIncrement datatype as some desktop database systems (MS-Access, Parados) do. 
In a project I simulated this for a unique index field by using a trigger combined with a generator.
The example below assumes that there is a table CUSTOMER with a uniquely indexed field CUST_HASH.
The generators' name is GEN_CUSTOMER.
The traditional technique would be to detect the current maximum number max and then insert a value of [max+1]:
SELECT MAX(cust_hash)+1 FROM customer
INSERT INTO customer (...) values (...)
The risk with this approach is that a parallel user could theoretically do the same thing 
before you write the determined value and end the transaction. 
The parallel user would try to post the same number and either 
cause a unique-index violation or post a duplicated value!
The trick with the generator is also faster since you don't have to do the max() query for each insert.
 
CREATE GENERATOR gen_customer; 
SET GENERATOR gen_customer TO 100;
CREATE TRIGGER customer_autoinc FOR customer 
BEFORE INSERT AS 
BEGIN 
  IF (NEW.cust_hash is NULL) THEN
    NEW.cust_hash = GEN_ID(gen_customer, 1); 
END;
 

 On Fri, 25 Oct 2002 09:15:03 +0200, "Gabriele Boccone"

E' quello che ho gia' fatto, grazie... :-)
Il problema era diverso: una volta che ho il campo autoincrementato, come
posso tenere "aperto" un record dopo averlo inserito nel DB?

mi sono perso nel th.. :((
Vediamo se ho capito.
A te serve vedere dalla tua app l'id dell'ultimo record inserito?
Se usi il trigger non puoi senza rileggere il record dopo averlo salvato.
Se vuoi questo, vedi il mio 1迺ntervento su quest th.
Bye
A.

posso tenere "aperto" un record dopo averlo inserito nel DB?
Scusami, ma non riesco a capire cosa intendi per record aperto......
Morde

 On Fri, 25 Oct 2002 14:34:49 +0200, "Gabriele Boccone"

Intendo, per esempio, in una DBGrid: se usi un generator su una tabella che
modifichi con una DBGrid, devi usare un Commit e un Refresh, altrimenti una
volta inserita la riga nella tabella, non puoi piu' modificarla finche' il
Dataset collegato non viene "chiuso e riaperto"...
Stessa cosa con qualsiasi data control standard (ad esempio scorrendo i
record con un DBNavigator).

Qualic omponenti usi per accedere ai dati?
Come gestisci l'aggiornamento?
Bye
A.

 Come gestisci l'aggiornamento?
In che senso? Se capisco cosa intendi dire: su ogni "afterpost" ho un
bookmark, un commit, una riapertura della tabella e un riposizionamento.

usi TIbTable, TIbquery o cosa?
A.

 usi TIbTable, TIbquery o cosa?
Entrambi piu' IBDataSet.

se usi TIbQuery, a questa devi associare TIbUpdateSql.
In questo modo le modifiche le vedi senza dover chiduere e riaprire, salvo i
dati assegnati al record dai trigger.
Sui db client/server meglio evitare oggetti tabella.
Bye
A.

sia piu' performante di una "select max(campo) from tabella".
si', se esiste l'indice giusto
Ciao
Duilio

DB2                   select * from table fetch first 10 rows only 
Grazie per i chiarimenti.
Pensa che in tre anni di sql statico con db2, non ho visto ne' mi hanno
mai consigliato una select fetch first.
Neanche i DBA, ed i programmatori host, i quali vidimavano qualsiasi select
venisse bindata ad host, hanno mai accennato alla fetch first.
Ciao
Morde