Jeg kører Delphi 2007 og bruger Firebird som database.
Jeg har i en del tabeller oprettet et felt ID af typen integer som primær nøgle til at identifisere de enkelte poster.
Jeg har ved samme lejlighed oprettet en generator, der i Firebird kan erstatte den manglende felttype autoinc som flere andre databaser har.
Dette spiller fint, når jeg eksempelvis har en query og den vej igennem læser, retter, tilføjer, slette data til min database.
Jeg er dog i den situation, at jeg i nogle tilfælde vil gøre brug af TClientDataSet til at gemme mine data i buffer under afvikling af applikationen.
Problemet er så, at jeg her får en exception "Field ID must have a value" når jeg forsøger at poste en nyindsat linie.
Det er jeg sådan set enig i, da feltet som primær nøgle ikke må have en null værdi. Generatoren fanger det ikke, måske fordi der ikke kaldes til databasen, men dataene ligger i mit clientdataset indtil jeg kalder applyUpdates.
Er der andre eksperter, der har en løsning eller forslag.
Jeg så en løsning for lang tid siden hvor man i Master-detail konstruktion i CDS lavede negative nøgler som blev erstattet med de rigtige når ApplyChanges blev kørt. Ved ikke om det kan bruges. Skal gerne se om jeg kan finde linket (det ligger på arbejdet).
Alternativt må du bruge CDS'ets OnNewRecord til at kalde generatoren så du får ID'et der. Eneste ulempe med den løsning er at du risikerer huller i rækkefølgen hvis du vælger CancelChanges. Det bør ikke have stor betydning.
Hvis du reelt ikke ønsker at bruge generator, så lad være. Og lad applikationen generere keys. Der er flere forskellige måder at gøre det på: GUID, high-low etc..
Det jeg har gjort er at jeg bruger en Trigger til at indsætte ID fra en Generator hvis (og kun hvis) dette felt ikke er sat. Desuden har jeg også en StoredProcedure der er i stand til at give mig en værdi fra Generatoren. Denne stored procedure bruger jeg så hvis jeg ønsker at bruge et ClientDataSet til at buffer poster før jeg sender dem videre til databasen
Trigger (Before Insert): <SNIP> AS BEGIN /* Set RECID if not allready set */ if ((new.F_RECID is null) or (new.F_RECID = 0)) then new.F_RECID = gen_id(G_SUPPLIERS_RECID, 1); END </SNIP>
StoreProcedure: <SNIP> CREATE PROCEDURE P_G_SUPPLIERS_RECID (NUMBERS_TO_GENERATE INTEGER) RETURNS (ID INTEGER) AS BEGIN ID = Gen_id(G_SUPPLIERS_RECID, NUMBERS_TO_GENERATE); END </SNIP>
I dataset'ets "BeforeInsert" Event kan du så kalde den stored proceudre til at hente værdier til generatoren. Alternativt sæt bare værdien på ID feltet til "0" så tager triggeren sig af det.
Mit forslag med at sætte en værdi i OnNewRecord burde kunne fungere sammen med din trigger; den har jo en værdi og vil ikke blive ændret.
arne: Jeg kan ikke se hvorfor man skulle undlade at bruge generatorer. Det virker fint og jeg må antage at kaldet er atomarisk. Især i forhold til at skulle bruge et GUID som nøgle. Det kan ikke undgå at blive langsomt med sådan en nøgle - og bagvedliggende indeks, kan det?
Det har ikke helt lykkedes for mig. Jeg har taget udgangspunkt i Pellelils lækre kodestumper.
Jeg får stadig samme fejl - "Field ID must have a value"
Jeg har i Firebird:
/* GENERATOR */
CREATE SEQUENCE GEN_POSTERINGER_ID; ALTER SEQUENCE GEN_POSTERINGER_ID RESTART WITH 0;
/* TRIGGER */
CREATE OR ALTER TRIGGER POSTERINGER_BI FOR POSTERINGER ACTIVE BEFORE INSERT POSITION 0 AS BEGIN IF ((NEW.ID IS NULL) or (NEW.ID = 0)) THEN NEW.ID = GEN_ID(GEN_POSTERINGER_ID,1); END
/* STORED PROCEDURE */
CREATE OR ALTER PROCEDURE POSTERINGER_RECID ( numbers_to_generate integer) returns ( id integer) as begin /* Procedure Text */ ID = Gen_id(GEN_POSTERINGER_ID, NUMBERS_TO_GENERATE); suspend; end^
I Delphi har jeg udover mine data aware komponenter samt min database, IBquery, datasetprovider, clientdataset og datasource også tilføjet en IBStoredProc.
I såvel clientdatasetets BeforeInsert og BeforePost events har jeg forsøgt med at kalde den ovenstående oprettede procedure via IBStoredProc.
Jeg får ingen "direkte" fejlmeddelelse på denne del af koden, men jeg får stadig fejlmeddelelse om, at ID ikke er fyldt ud.
skindbeni: Har du prøvet at sætte værdien i CDS'ets OnNewRecord? Sådan plejer vi at gøre det (via et kald til en SProc der tæller en generator op). Kan det eventuelt være andre felter i recorden du mangler at udfylde?
arne: Tingene skal køre det ene eller det andet sted. Det er jeg helt med på - det var bare ikke det jeg spurgte om. Mit spørgsmål gik på om ikke GUID som primærnøgle var en sløv og uhensigtsmæssig løsning.
Det er selvfølgelig ingen tvivl om, at jeg et eller andet sted har lavet en fejl eller mangler noget.
Nu er det iøvrigt mit allerførste bekendtskab med en "stored procedure" og "trigger" og det er muligvis der, det løber løbsk formig.
Når jeg kalder min StoredProc, så bliver variablen ID tilføjet en værdi, som jeg tolker proceduren.
Som jeg har gjort det i min kode indtil nu, så har jeg udelukkende nedenståënde kode, som jeg har forsøgt i såvel CDS'ens BeforeInsert, BeforePost og nu OnNewRecord:
Den første linie eller noget lignende har jeg fundet ud er en nødvendighed. Fjerner jeg den får jeg nedenstående fejl, når jeg forsøger at indsætte en ny linie:
"Required Param Value not set"
Tilbage til emnet:
Har jeg ikke ret i, at jeg mangler en linie ala 3. linie, som er den nye linie jeg har indsat i OnNewRecord eventen.
Er det korrekt anvendelse? Det løser i hvert fald problemet, men genererer et nyt problem.
Når jeg så forsøger at poste (ikke indsætte) en linie 2 i min grid (eller for den sags skyld lukker applikationen og starter op igen og forsøger at indlæse poste første linie), så får jeg en "key violation" exception.
Jeg har kigget i min database, og generatoren har IKKE talt 1 værdi op. Det giver, at der må være noget i min trigger, generator eller storedProc, der ikke spiller. Igen er det mit første bekendtskab med disse og jeg har svært ved at gennemskue, hvori fejlen evt. ligger.
arne: Tjaa, det lyder jo meget godt (har du SSD-disk? :-)) men 16 bytes pr nøgle bliver til en del i et indeks. Har du prøve at oprette tilsvarende for int-nøgler (med og uden identity)?
Ingen der har et bud på, hvorfor jeg får "Key Violation" fejl, når jeg indsætter en (ny) linie.
Det er som om generatoren ikke registerer, at den skal tælle 1 op?
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.