Autor Téma: Získanie práve vytvoreného ID  (Přečteno 15244 krát)

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6156
  • Karma: 44
    • Verze Delphi: W10 + D11
Získanie práve vytvoreného ID
« kdy: 25-10-2012, 08:56:42 »
ID = hodnota pre Primary key

Čo riešim
Vytvorím nový záznam - je už v tabuľke. Aktualizujem si VT - hodnotami z formulára, ale tam nemám ID. Tým pádom pri pohybe vo VT mi nenájde patričný záznam (TIBDataset.Locate('ID', ...)). Ako správne získať hodnotu ID? Predpokladám konkurečný prístup k tabuľke.

Čo som sa dočítal

1) Použiť uloženú procedúru a ziskané ID použiť pre primárny kľúč (to sa môže vykonať priamo v SP) a iné účely. Veľmi to neodporúčajú (Císař). ID by sa malo priraďovať pomocou spúšte.

2) Využiť jednoriadkovú systémovú DB. Vytvoriť si tam jeden záznam s ID a vyzdvihnúť si jeho hodnotu na ďalšie použitie. To mi pripadá dosť komplikované.

Podľa toho čo som čítal, tak predpokladám, že TIBDataset po vytvorení záznamu znova načíta všetky údaje. Tam by sa teorteicky dala zistiť hodnota ID podľa najvyššej hodnoty. Ale je to 100 %? Nezdá sa mi to ako pravé orechové.

Jedná sa o vkladanie jednotlivých záznamov na klientovi. Užívateľ musí minimálne stlačiť tlačidlo "Ulož".

Ako sa to správne robí?

 
W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.

Offline Mi.Chal.

  • Guru
  • *****
  • Příspěvků: 576
  • Karma: 25
Re:Získanie práve vytvoreného ID
« Odpověď #1 kdy: 25-10-2012, 09:33:11 »
měl bys mít někde nějakou logiku, která založí nový záznam a vrátí jeho id. Třeba tak, že si to napíšeš jako SP a ta to id vrátí. Jestli na FB jde nějak nastavit hodnotu v triggeru a vrátit jí v rámci insertu nevím, moc bych tomu ale nevěřil. Jestli tu hodnotu id bude plnit insert nebo si ji vygeneruješ generátorem a použiješ pro vložení insertu je podle mě jedno, pokud máš zaručeno, že ten generátor nebude vracet dvakrát po sobě stejnou hodnotu. Po vložení záznamu si prostě refreshni grid a id záznamu budeš vědět.

2) je blbost, protože to nebude fungovat ve víceuživatelském prostředí. Můžeš dostat tabulku s x záznamy a nevíš, co si vložil ty a co někdo jiný.

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1559
  • Karma: 37
    • Pepak.net
Re:Získanie práve vytvoreného ID
« Odpověď #2 kdy: 25-10-2012, 09:43:32 »
Jestli na FB jde nějak nastavit hodnotu v triggeru a vrátit jí v rámci insertu nevím, moc bych tomu ale nevěřil.
Zničte nevěřící! :-)
INSERT INTO ... RETURNING ...

Citace
pokud máš zaručeno, že ten generátor nebude vracet dvakrát po sobě stejnou hodnotu.
To je u SQL generátorů zaručeno.
Na získání ID předem není nic špatného. Doporučení nebo nedoporučení je spíš věcí zvyklostí autora nebo jím používaného frameworku než nějakých objektivních faktů.

Citace
2) je blbost, protože to nebude fungovat ve víceuživatelském prostředí. Můžeš dostat tabulku s x záznamy a nevíš, co si vložil ty a co někdo jiný.
Plus je blbost pracně emulovat funkci generátorů, které jsou právě pro tohle určené...

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6156
  • Karma: 44
    • Verze Delphi: W10 + D11
Re:Získanie práve vytvoreného ID
« Odpověď #3 kdy: 25-10-2012, 10:23:06 »
// INSERT INTO ... RETURNING ...
Za momentálenj siutácie nemôžem použiť. ID zapisujem pomocou generátora v trigger-te v onBeforeInsert. V Insert nemôžem menovať ID, nakoľko musí mať hodnotu null. Viď podmienka v trigger-te  if (new.id is null) then.

2)
//je blbost, protože to nebude fungovat ve víceuživatelském prostředí. Můžeš dostat tabulku s x záznamy a nevíš, co si vložil ty a co někdo jiný.
Asi ti ušlo, že to je jednoriadková tabuľka - vždy! A slúži na podobné účely. Vložíš a okamžite získaš hodnotu. Či tam niekto stačí vložiť nový záznam neviem

// Plus je blbost pracně emulovat funkci generátorů, které jsou právě pro tohle určené...
Vkladá sa to práve pomocou generátora. Slúži to len na získanie práve vygenerovanej hodnoty.

Takže pravdepodobne mám použiť postup

Vytvorím SP, ktorá vygeneruje ID. Spustím ju v trigger-te. Tam hodnotu ID priamo použijem a pomocou SP si ju pošlem na klienta.
Hm, ale keď SP automaticky spustím v trigger-te, ako sa dostanem k návratovej hodnote? To by som vedel pomocou TIBStoredprocedure. Čosi nesedí  :-\
W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3339
  • Karma: 139
    • Verze Delphi: D2007, XE3, DX10
Re:Získanie práve vytvoreného ID
« Odpověď #4 kdy: 25-10-2012, 10:54:17 »
// INSERT INTO ... RETURNING ...
Za momentálenj siutácie nemôžem použiť. ID zapisujem pomocou generátora v trigger-te v onBeforeInsert. V Insert nemôžem menovať ID, nakoľko musí mať hodnotu null. Viď podmienka v trigger-te  if (new.id is null) then.
Ja to teda nezkousel, protoze nove zaznamy vkladame temer vyhradne pres SP mimo operace s datasetem, ale zkousel jsi .... RETURNING :ID?
U nekterych DB s autoinkrementem jsou platna ID > 0, takze v duchu stejne logiky muzes rict, ze ID=0 ma stejny vyznam jako NULL a pak v tom triggeru muzes testova (new.id is null or new.id=0).  Tim by ti mohlo upadnout rada problemu s datasetem.

Vytvorím SP, ktorá vygeneruje ID. Spustím ju v trigger-te. Tam hodnotu ID priamo použijem a pomocou SP si ju pošlem na klienta.
Hm, ale keď SP automaticky spustím v trigger-te, ako sa dostanem k návratovej hodnote? To by som vedel pomocou TIBStoredprocedure. Čosi nesedí  :-\
Ta SP se vetsinou dela na vlozeni zaznamu a vraci nove ID. A v te SP si ID bud vygenerujes dopredu pomoci gen_id a priradis hodnotu do ID a vratis ji z te SP, nebo to nechas na triggeru, u insertu pouzijes to returning :ID a vratis ho z SP.

A nebo zadnou SP delat nebudes a gen_id si spustis na strane klienta normalnim selectem.

Jak psal Pepak, v zasade je to jedno, pro co se rozhodnes, ale v tom konkretnim projektu to pak delej vsude stejne.

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1559
  • Karma: 37
    • Pepak.net
Re:Získanie práve vytvoreného ID
« Odpověď #5 kdy: 25-10-2012, 11:08:07 »
// INSERT INTO ... RETURNING ...
Za momentálenj siutácie nemôžem použiť. ID zapisujem pomocou generátora v trigger-te v onBeforeInsert. V Insert nemôžem menovať ID, nakoľko musí mať hodnotu null. Viď podmienka v trigger-te  if (new.id is null) then.
Hodnota klíče v okamžiku spouštění INSERTu nijak nesouvisí s možností nebo nemožností použít RETURNING. Aspoň si vygoogluj, co ti někdo poradí, když už nechceš číst všechny ty Release Notes.

Citace
2)
//je blbost, protože to nebude fungovat ve víceuživatelském prostředí. Můžeš dostat tabulku s x záznamy a nevíš, co si vložil ty a co někdo jiný.
Asi ti ušlo, že to je jednoriadková tabuľka - vždy! A slúži na podobné účely. Vložíš a okamžite získaš hodnotu. Či tam niekto stačí vložiť nový záznam neviem
Co budeš dělat v okamžiku, kdy si dva lidi současně vyžádají nové ID?
Co budeš dělat v okamžiku, kdy dva lidi současně udělají INSERT (s různým ID)?

Citace
// Plus je blbost pracně emulovat funkci generátorů, které jsou právě pro tohle určené...
Vkladá sa to práve pomocou generátora. Slúži to len na získanie práve vygenerovanej hodnoty.
Viz druhá otázka výše. Pokud už chceš získávat vygenerovanou hodnotu se zárukou, že ti to čas od času vrátí špatné ID, tak používej SELECT MAX(id) FROM tabulka nebo SELECT GEN_ID(generátor, 0) FROM rdb$database - nebudeš kvůli tomu muset vytvářet extra databázové objekty. Já ovšem doporučuju spíš ten RETURNING, protože preferuji, když mi aplikace funguje vždy a ne jen někdy.

Offline Petr P.

  • Nováček
  • *
  • Příspěvků: 28
  • Karma: 1
    • Verze Delphi: Turbo Delphi 2006
Re:Získanie práve vytvoreného ID
« Odpověď #6 kdy: 25-10-2012, 11:15:32 »
Citace
SELECT GEN_ID(generátor, 0) FROM rdb$database
Tohle nefunguje vždy ?

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3339
  • Karma: 139
    • Verze Delphi: D2007, XE3, DX10
Re:Získanie práve vytvoreného ID
« Odpověď #7 kdy: 25-10-2012, 11:16:32 »
Co budeš dělat v okamžiku, kdy si dva lidi současně vyžádají nové ID?
Co budeš dělat v okamžiku, kdy dva lidi současně udělají INSERT (s různým ID)?
Tyhle problemy vyresi snad transakce s vhodnou izolaci, ne?

Offline Mi.Chal.

  • Guru
  • *****
  • Příspěvků: 576
  • Karma: 25
Re:Získanie práve vytvoreného ID
« Odpověď #8 kdy: 25-10-2012, 11:28:37 »
Jestli na FB jde nějak nastavit hodnotu v triggeru a vrátit jí v rámci insertu nevím, moc bych tomu ale nevěřil.
Zničte nevěřící! :-)
INSERT INTO ... RETURNING ...

Jasně, ale to je vracení z insertu, ne z triggeru - prostě tu hodnotu vrací něco jiného, než ji vygenerovalo. Což tady nijak vadit nebude, jenom není nikde patrná ta logika za tím.

Offline Mi.Chal.

  • Guru
  • *****
  • Příspěvků: 576
  • Karma: 25
Re:Získanie práve vytvoreného ID
« Odpověď #9 kdy: 25-10-2012, 11:30:41 »
Co budeš dělat v okamžiku, kdy si dva lidi současně vyžádají nové ID?
Co budeš dělat v okamžiku, kdy dva lidi současně udělají INSERT (s různým ID)?
Tyhle problemy vyresi snad transakce s vhodnou izolaci, ne?

pak se ale budou "zbytečně" zamykat a čekat na sebe, což zas bude mít dopad na výkon.

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1559
  • Karma: 37
    • Pepak.net
Re:Získanie práve vytvoreného ID
« Odpověď #10 kdy: 25-10-2012, 11:34:23 »
Citace
SELECT GEN_ID(generátor, 0) FROM rdb$database
Tohle nefunguje vždy ?
Ne.

Co budeš dělat v okamžiku, kdy si dva lidi současně vyžádají nové ID?
Co budeš dělat v okamžiku, kdy dva lidi současně udělají INSERT (s různým ID)?
Tyhle problemy vyresi snad transakce s vhodnou izolaci, ne?
No tak může, jistě. Sice mi to připadá jako pronajmout si kamion, když potřebuju přenést tři cihly z jednoho rohu zahrádky do druhého, ale ano, problém to také vyřeší...

Jasně, ale to je vracení z insertu, ne z triggeru - prostě tu hodnotu vrací něco jiného, než ji vygenerovalo. Což tady nijak vadit nebude, jenom není nikde patrná ta logika za tím.
To mi přijde trochu akademické - ten trigger se bez obalujícího INSERTu nespustí, a konec konců ani programátorovi asi tolik nezáleží na tom, kde přesně se to ID vytvoří (jestli v triggeru - a kterém z toho tisíce, které na tabulce má - nebo třeba ještě na straně klienta při přípravě dat), hlavně když dostane to ID, které vytvořil. Ale budiž.

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6156
  • Karma: 44
    • Verze Delphi: W10 + D11
Re:Získanie práve vytvoreného ID
« Odpověď #11 kdy: 31-10-2012, 11:25:16 »
No hurá. Narazil som na nasledovné. Tretí a štvrtý bod ma okamžite zabavili všetkých mukov :)

1. Create a trigger similar to the following.

Kód: [Vybrat]
Create Trigger COUNTRY_INSERT for COUNTRY
active before Insert position 0
as
begin
  if (new.Pkey is NULL) then // Doplnil som o or (new.Pkey = 0)
    new.Pkey = gen_id(COUNTRY_GEN,1);
end^

2. Create a stored procedure that returns the value from the generator:

Kód: [Vybrat]
Create Procedure COUNTRY_Pkey_Gen returns (avalue INTEGER)
as
begin
  avalue = gen_id(COUNTRY_GEN,10);
end^

3. Add a TStoredProccomponent to your application and
associate it with the COUNTRY_Pkey_Genstored procedure.

4. Add a TQuerycomponent to your application and add the following code to
the BeforePostevent:

Kód: [Vybrat]
If(TQuery.state = dsinsert) then
begin
  StoredProc1.ExecProc;
  TQuery.FieldByName('Pkey').AsInteger :=
    StoredProc1.ParamByName('avalue').AsInteger;
end;

This solution allows the client to retrieve the generated value from the server using a
TStoredProccomponent and an InterBase stored procedure. This assures that the
client will know the primary key value when a record is posted.
W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1559
  • Karma: 37
    • Pepak.net
Re:Získanie práve vytvoreného ID
« Odpověď #12 kdy: 31-10-2012, 11:41:30 »
Když ti to přijde jednodušší než:
Kód: Text [Vybrat]
  1. INSERT INTO tabulka(field1, field2, field3) VALUES ('a','b','c') RETURNING field_klic
tak proti gustu...

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6156
  • Karma: 44
    • Verze Delphi: W10 + D11
Re:Získanie práve vytvoreného ID
« Odpověď #13 kdy: 31-10-2012, 12:10:24 »
Predpokladám, že by som to musel dať ako SP na stranu servera. Pre mňa je to jednoduchšie ako to mám(?).
1. bod musím robiť tak či tak.
2. a 3. bod robím len jediný raz.
4. bod musím robiť samostane pre každý prípad čo je jednoduché - malá pravdepodobnosť chyby

Používam TIBDataSet, kde sa mi všetky texty SQL generujú samé a už sa nestarám o "Insert.." na strane servera. Preto sa mi to zdá jednoduchšie riešenie.
W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.

Offline Petr P.

  • Nováček
  • *
  • Příspěvků: 28
  • Karma: 1
    • Verze Delphi: Turbo Delphi 2006
Re:Získanie práve vytvoreného ID
« Odpověď #14 kdy: 31-10-2012, 14:50:46 »
Místo  SP můžeš použít  dotaz v IBQuery:
select gen_id(COUNTRY_GEN,1) from RDB$DATABASE
...
 ID:= IBQuery.Fields[0].AsInteger
« Poslední změna: 31-10-2012, 14:56:22 od Petr P. »

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6156
  • Karma: 44
    • Verze Delphi: W10 + D11
Re:Získanie práve vytvoreného ID
« Odpověď #15 kdy: 01-11-2012, 09:55:16 »
select gen_id(COUNTRY_GEN,1) from RDB$DATABASE

Tu padla poznámka, že to vždy nefunguje.
Mne sa to riešenie so získaním ID v BeforePost veľmi páči. Mimochodom, je to v návode IB6.
W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.

Offline Petr P.

  • Nováček
  • *
  • Příspěvků: 28
  • Karma: 1
    • Verze Delphi: Turbo Delphi 2006
Re:Získanie práve vytvoreného ID
« Odpověď #16 kdy: 01-11-2012, 10:15:22 »
select gen_id(COUNTRY_GEN,1) from RDB$DATABASE

Tu padla poznámka, že to vždy nefunguje.
Mne sa to riešenie so získaním ID v BeforePost veľmi páči. Mimochodom, je to v návode IB6.
Nevím proč by to nemělo fungovat, je to stejný jako volat  v gen_id(COUNTRY_GEN,1) v SP.
Tenhle postup píšou všude včetně Pavla Císaře.

To že vždy nefunguje psal Pepak o získání posledního čísla bez zvýšení o 1  (COUNTRY_GEN,0).
select gen_id(COUNTRY_GEN,0) from RDB$DATABASE.
Myslel tím asi, že mezitím jinej uživatel mezi tím zvýší generátor a u tebe zůstane původní číslo.

Jinak v BeforePost se to použije podobně. Rozdíl je akorát v tom že nepotřebuješ SP.
« Poslední změna: 01-11-2012, 11:12:44 od Petr P. »

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1559
  • Karma: 37
    • Pepak.net
Re:Získanie práve vytvoreného ID
« Odpověď #17 kdy: 01-11-2012, 10:29:02 »
select gen_id(COUNTRY_GEN,1) from RDB$DATABASE

Tu padla poznámka, že to vždy nefunguje.
Ne, to tedy nepadla. Padla poznámka, že
Kód: SQL [Vybrat]
  1. SELECT gen_id(COUNTRY_GEN,0) FROM RDB$DATABASE
volané po provedení INSERTu nefunguje správně.
Kód: SQL [Vybrat]
  1. SELECT gen_id(COUNTRY_GEN,1) FROM RDB$DATABASE
volané před provedením INSERTu je naprosto v pořádku.

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6156
  • Karma: 44
    • Verze Delphi: W10 + D11
Re:Získanie práve vytvoreného ID
« Odpověď #18 kdy: 01-11-2012, 11:02:07 »
Takto som to tam nevidel.  :( Aj my to bolo čudné. S vysvetlením to je jasné.
W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6156
  • Karma: 44
    • Verze Delphi: W10 + D11
Re:Získanie práve vytvoreného ID
« Odpověď #19 kdy: 07-01-2013, 10:43:48 »
A mám problém. Použil som riešenie zo dňa 31-10-2012, 11:25:16. Ďalej úplne zjednodušený kód

actInsertExecute
MasterDataSet.Insert;
DetailDataSet.Insert;

actPostExecute
DetailDataSet.Post;
xx) MasterDataSet.FieldValues['myFKField'] := newPrKey; // Získaný v DetailDataSet.onBeforePost. Je správny
MasterDataSet.Post; // Chyba Hodnota newPrKey sa nenachádza v DetailDataSet

Skutočná hodnota DetailDataSet.IDmyField = newPrKey +1.
Pre IDmyField mám trigger s generátorom s podmienkou na new.IDmyField = null or new.IDmyField = 0. A ten sa spustí, akokeby som tam nemal riadok xx).
 
Všetky moje pokusy zlyhali. Považujem to za zásadný problém. To ešte nemám viacnásobný prístup k DB.

W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1559
  • Karma: 37
    • Pepak.net
Re:Získanie práve vytvoreného ID
« Odpověď #20 kdy: 07-01-2013, 11:28:59 »
Co kdybys postnul skutečný použitý kód se všemi relevantními částmi? Ty máš totiž dost tendenci prasit kód, jak tě napadne, vynechávat si části, které se ti zrovna nelíbí nebo přidávat části, které se ti líbí - tudíž bych tak nějak očekával, že i teď to je stejný případ a prostě sis tam něco "upravil". Protože to, co je popsané v tom "riešeniu zo dňa 31-10-2012, 11:25:16", by ti fungovat mělo.

Btw., nechtěl bys zvážit drobnou časovou investici do naučení se tagů [code=delphi] a [quote], aby tvoje příspěvky byly rozumně čitelné? Pokud je neumíš z hlavy, tak nepiš do "Rychlá odpověď", ale klikni si na "Odpověď" a pak si ty tagy zadávej klikáním myší v editoru. Díky.

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6156
  • Karma: 44
    • Verze Delphi: W10 + D11
Re:Získanie práve vytvoreného ID
« Odpověď #21 kdy: 07-01-2013, 12:00:20 »
Ja som si to doma tak pekne a poriadne prípravil, ale som si skopíroval iný súbor  :(
Kód: [Vybrat]
FB
CREATE OR ALTER TRIGGER ENTRIES_BI FOR ENTRIES
ACTIVE BEFORE INSERT POSITION 0
as
begin
  if ((new.identries is null) or (new.identries = 0)) then
    new.identries = gen_id("GEN_ID",1);
end
create or alter procedure SP_ID
returns (
    ID bigint)
AS
begin
  ID = gen_id("GEN_ID",1);
end
procedure TdmMain.ibdtstFlatBeforePost(DataSet: TDataSet);
begin
  If(DataSet.state = dsinsert) then
  begin
    ibspID.ExecProc;
    NewPrKey := ibspID.ParamByName('ID').AsInteger;
    DataSet.FieldByName('idflats').Value := NewPrKey;
    MasterDataset.FieldByName('FKFlat').Value := NewPrKey;
  end;
end;
// Podľa IB6 Developer's guide str. 34 - 35
procedure TfrmLandlord.actInsertExecute(Sender: TObject);
var
  I, idxcbbCod, idxcbbEntry: integer;
  DateFrom: TDate;
begin
  MasterDataset.Insert;
  FlatDataset.Insert;
// Predvolené údaje a pod.
end
procedure TfrmLandlord.actSaveExecute(Sender: TObject);
begin
  LandlordPost;
  EnableActions(MasterDataset, MasterVST, actmgr1);
end;
procedure TfrmLandlord.LandlordPost;
begin
  with frmLandlord  do
  begin
    with FlatDataset do
    begin
    Kontrola údajov
    end
   
    with MasterDataset do
    begin
    Kontrola údajov
    end;
   
    // MasterDataset.FieldByName('FKFlat').Value := NewPrKey; Skúšal som aj tu
    with FlatDataset do
    begin
      FieldValues['HotArea'] := StrToCurr(cledtHotArea.Text);
      ...
      Post;
    end;
   
    MasterDataset.Post; // Chyba
  end;
W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1559
  • Karma: 37
    • Pepak.net
Re:Získanie práve vytvoreného ID
« Odpověď #22 kdy: 07-01-2013, 14:20:46 »
No, v první řade by asi bylo hezké, kdybys nám tu ujasnil, co je v master datasetu a co v detail datasetu. Tak, jak to máš udělané, to vypadá, jako kdyby k jednomu záznamu v detail datasetu mělo existovat libovolné množství záznamů v master datasetu, a to rozhodně není obvyklý přístup. Kromě toho je krajně nejasné, ke které z těch tabulek se vztahuje ten trigger a ta událost BeforePost.

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6156
  • Karma: 44
    • Verze Delphi: W10 + D11
Re:Získanie práve vytvoreného ID
« Odpověď #23 kdy: 07-01-2013, 15:01:10 »
MasterDataSet = Landlords - vlastník bytu
FlatDataSet = Flats - byt

Landlords = IDLandlords, FKFlat...
Flats = IDFlats...

// ke které z těch tabulek se vztahuje ten trigger a ta událost BeforePost.
Samozrejme, pri kopírovaní som klikol vedľa. Oba (vlastne všetko, celá debata) sa vzťahujú na Flats.
1 - Vyvolám Flats.Post. Vo Flats.onBeforePost získam newPrKey. Napríklad 10.
2 - Vložím ho do Landlords.FKFlat.
3 - Potom nasleduje Landlords.Post s chybou.
Vo (ak vynechám Landlords.Post) Flats.IDFlats je hodnota 11.
Neviem, prečo sa vyvolá trigger, ak som tam predtým použil Landlords.FieldValues['FKFlat'] := newPrKey. Zjavne FB o tom nič nevie. Žeby to bolo poradím vo vykovávaní jednotlivých udalostí? V nastaveniach transakcií som nič nemenil.
Inde problém nemám, ale čo nie je môže byť.
Všetko je v štádiu návrhu a mám teraz iba zopár záznamov 1:1.
 
« Poslední změna: 07-01-2013, 15:02:50 od Stanislav Hruška »
W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1559
  • Karma: 37
    • Pepak.net
Re:Získanie práve vytvoreného ID
« Odpověď #24 kdy: 07-01-2013, 17:52:30 »
2 - Vložím ho do Landlords.FKFlat.
To je samozřejmě blbě. Tím říkáš, že každý byt může mít libovolný počet vlastníků (to by až tak nevadilo) a že každý vlastník může mít nejvýše jeden byt (to už je takové neobvyklé, zvlášť když se pak snažíš dělat vlastníky jako Master tabulku).

Citace
3 - Potom nasleduje Landlords.Post s chybou.
To se nedivím, protože v době, kdy Postuješ do Landlords, ještě neexistuje záznam ve Flats. Tedy aspoň jsi to domýšlím, protože jsi nějak nenapsal nic o tom, kdy nastává skutečný Post do Flats.

Citace
Vo (ak vynechám Landlords.Post) Flats.IDFlats je hodnota 11.
To mě taky nepřekvapuje, protože sekvence jsou mimotransakční a pořád narůstají, bez ohledu na to, jestli se jejich hodnota někde použije.

Citace
Žeby to bolo poradím vo vykovávaní jednotlivých udalostí?
Určitě. Postup by měl být:
1) Uspořádej si pořádně datové struktury. Z toho, co píšeš, je zřejmé, že v Landlords máš ukazatel na Flats, ve Flats nemáš nic (nebo tam možná máš ukazatel na Landlords, aby to bylo zajímavější). Máš to mít tak, že v Landlords nemáš nic a ve Flats máš ukazatel na Landlords.
2) Založ záznam v Landlords. To by mělo být bez problémů.
3) Získej ID v Landlords. Klidně tak, že při zakládání (krok 2) v BeforePost zavoláš tu svou proceduru, která ti to ID vypočítá, a ty si ho kromě do políčka v Landlords uložíš do nějaké proměnné.
4) Použij toto ID pro založení záznamu ve Flats.

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6156
  • Karma: 44
    • Verze Delphi: W10 + D11
Re:Získanie práve vytvoreného ID
« Odpověď #25 kdy: 08-01-2013, 09:41:46 »
2 // Tím říkáš, že každý byt může mít libovolný počet vlastníků.
Áno. Ber to v určitom časovom rozsahu. Jeden byt predá a iný ho kúpi.
// že každý vlastník může mít nejvýše jeden byt
Pre moje potreby áno. Ak mal dva byty, tak som to doteraz riešil 1. byt - Janko Hraško, 2. byt - Janko Hraško 2. Toto si viem riešiť ako potrebujem. Aj vzťahom M:N. Ešte som to tak podrobne neanalyzoval. Hlavne to nemá vplyv na predmet otázky.

3 Citujem sa:
1 - Vyvolám Flats.Post. Vo Flats.onBeforePost získam newPrKey. Napríklad 10.
2 - Vložím ho do Landlords.FKFlat.
3 - Potom nasleduje Landlords.Post s chybou.
Takže záznam by už mal existovať.

// Venujme sa len tomuto
Ja to vidím takto.
LandlordsDataSet i FlatsDataSet sú v stave Insert

a - Vyvolám FlatsDataSet.Post. Neviem ako sa to správa. Nikde som nenarazil na popis.
b - FB spustí trigger Flats Before Insert
c - Vo FlatsDataset.onBeforePost získam v procedúre FB hodnotu IDFlats uložením do newPrKey. Potiaľ to je v priadku.
d - Vložím ju do LandlordDataset.FKFlats := newPrKey;
e - Vyvolám LandlordDataset.Post s danou chybou.
f - FB jednoznačne opäť vyvolá trigger Flats Before Insert. Tu je kameň úrazu. Prečo ho spustí, ak
.  1 - už raz spustený bol pomocou DataSet-u
.  2 - hodnotu pre FKFlats som tam dal?

Mne to vychádza, že je problém v obsluhe udalostí DataSet a transakcii. Ako keby o sebe nevedeli. Čo je nezmysel. To by z FB bol dávno popol.
Ja potrebujem dosiahnuť, aby nedochádzalo k druhému spusteniu trigger-u Flats Before Insert
// Venujme sa len tomuto - Koniec

K poslednému odseku.
// Máš to mít tak, že v Landlords nemáš nic a ve Flats máš ukazatel na Landlords.
To nie je vhodné riešenie. Počas roka dôjde zmene vlastníka a ja ich musím mať k dispozícii oboch - ročné zúčtovanie nákladov.

Nateraz zakladám majiteľa a byt v jednom kroku. Asi bude najrozumnejšie to rozdeliť na dva kroky. Tam problém nebude. Napr. samostatne nadefinujem byty i vlastníkov a až potom ich spárujem.
Ďakujem za čas a ochotu čo mi venuješ.
W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1559
  • Karma: 37
    • Pepak.net
Re:Získanie práve vytvoreného ID
« Odpověď #26 kdy: 08-01-2013, 10:34:55 »
2 // Tím říkáš, že každý byt může mít libovolný počet vlastníků.
Áno. Ber to v určitom časovom rozsahu. Jeden byt predá a iný ho kúpi.
V tom případě máš ale blbě master-detail vztah - Flats by měly být master a Landlords detail.

Citace
3 Citujem sa:
1 - Vyvolám Flats.Post. Vo Flats.onBeforePost získam newPrKey. Napríklad 10.
2 - Vložím ho do Landlords.FKFlat.
3 - Potom nasleduje Landlords.Post s chybou.
Takže záznam by už mal existovať.
Za prvé to záleží na tom, v jakém přesně pořadí ty Posty probíhají (jestli například doběhne celý Flats.Post a teprve potom začne Landlords.Post), a za druhé mám vážné podezření, že vyplňuješ špatná políčka (že si pomocí procedury vytáhneš ID, ale potom ho přepíšeš v triggeru, nebo že si vytáhneš ID, ale zapíšeš ho do jiného pole, než jaké je referencované tím cizím klíčem). Docela dobrá taktika v takových případech je, smazat cizí klíč, zkusit zapsat záznam (teď to proběhne, protože už není nic, co by ho mělo blokovat) a pak se podívat, co jsi kam skutečně zapsal po provedení všech triggerů.

Citace
a - Vyvolám FlatsDataSet.Post. Neviem ako sa to správa. Nikde som nenarazil na popis.
b - FB spustí trigger Flats Before Insert
c - Vo FlatsDataset.onBeforePost získam v procedúre FB hodnotu IDFlats uložením do newPrKey. Potiaľ to je v priadku.
d - Vložím ju do LandlordDataset.FKFlats := newPrKey;
e - Vyvolám LandlordDataset.Post s danou chybou.
Jinými slovy, v tuto chvíli jsi ještě ve stavu FlatsDataset.BeforePost. Skutečný zápis do DB proběhne až později a potom se ti zavolá FlatsDataset.AfterPost.

Citace
f - FB jednoznačne opäť vyvolá trigger Flats Before Insert. Tu je kameň úrazu. Prečo ho spustí, ak
.  1 - už raz spustený bol pomocou DataSet-u
.  2 - hodnotu pre FKFlats som tam dal?
Trigger se spouští v databázi. Databáze neví nic o tom, co dělá nebo nedělá aplikace, a ani ji to nezajímá. Prostě se podívá: "Mám tu trigger pro aktuálně prováděnou událost? Ano. Je ten trigger aktivní? Ano. Tak ho spustím." Je tvoje zodpovědnost, abys trigger napsal tak, aby případně nic nědělal - tedy třeba na začátku provést nějaký vhodný IF a pokračovat, jen pokud operace ještě neproběhla.

Podle mě máš blbě datový model. Jak souvisí hodnota FKFlats (která je v tabulce Landlords) s triggerem na tabulce Flats? Trigger na tabulce Flats se má zabývat výhradně údaji v tabulce Flats, nemá co se zabývat cizími klíči cizích tabulek.

Citace
Ja potrebujem dosiahnuť, aby nedochádzalo k druhému spusteniu trigger-u Flats Before Insert
Jsem si stoprocentně jistý, že nedochází k druhému spuštění Before Insert triggeru na Flats. Before Insert nastane právě jednou, a to v okamžiku, kdy databáze narazí na INSERT INTO tabulka. A to platí i v případě, že to INSERT INTO tabulka neděláš ty, ale dělají ho tvoje komponenty pro přístup k DB.

Takže špatně vyhodnocuješ, co se děje. Možností je víc, ale podle mě děláš jednu z následujících věcí:
a) Vygeneruješ hodnotu procedurou, tu si uložíš, zapíšeš ji do tabulky a pak ji v tabulce přepíšeš hodnotou vygenerovanou v triggeru - tzn. máš blbě podmínku v triggeru.
b) Vygeneruješ hodnotu procedurou, tu si uložíš, zapíšeš ji do tabulky do špatného sloupce a pak trigger pochopitelně vygeneruje novou hodnotu, protože podmínka "hodnota v poli X ještě nebyla vygenerována" je splněna.

Dost blbě se odhaduje, co z toho, protože jsi sice vypsal relevantní části kódu, ale už jsi nenapsal struktury jednotlivých tabulek a vztahy mezi nimi - asi nejlíp příslušný CREATE TABLE příkaz.

Citace
K poslednému odseku.
// Máš to mít tak, že v Landlords nemáš nic a ve Flats máš ukazatel na Landlords.
To nie je vhodné riešenie. Počas roka dôjde zmene vlastníka a ja ich musím mať k dispozícii oboch - ročné zúčtovanie nákladov.
V tom případě prosím dej jako Master tabulku Flats a jako Detail tabulku Landlords. A do všech popisků k tomuto problému zdůrazňuj, že mezi Flats a Landlords je vztah 1:N, protože jinak je to strašně matoucí - běžné bývá spíš to, že jeden majitel má víc bytů, ne že jeden byt má víc majitelů.
(Vůbec by možná bylo nejlepší udělat to jako M:N s tím, že spojovací tabulka bude mít ID bytu, ID vlastníka a období [datum od, datum do], pro které to platí.)

Citace
2. Toto si viem riešiť ako potrebujem. Aj vzťahom M:N. Ešte som to tak podrobne neanalyzoval. Hlavne to nemá vplyv na predmet otázky.
Má. Poměrně podstatný, aspoň podle mě je docela vhodné důkladně pochopit popisovanou realitu a z ní odvozovat řešení, než střílet různé myšlenky od boku a doufat, že se nějaká trefí.

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6156
  • Karma: 44
    • Verze Delphi: W10 + D11
Re:Získanie práve vytvoreného ID
« Odpověď #27 kdy: 08-01-2013, 12:08:44 »
Toto bol popis tabuliek - zjednodušený, ale ostatné je teraz nepodstatné
Landlords = IDLandlords, FKFlat...
Flats = IDFlats...

CREATE TABLE LANDLORDS (
    IDLANDLORDS  PRIMARYKEY /* PRIMARYKEY = BIGINT NOT NULL */,
    FKSVB        FOREIGNKEY /* FOREIGNKEY = BIGINT NOT NULL */,
    FKFLAT       FOREIGNKEY /* FOREIGNKEY = BIGINT NOT NULL */,
Foreign Keys
ALTER TABLE LANDLORDS ADD CONSTRAINT FK_LANDLORDS_1 FOREIGN KEY (FKSVB) REFERENCES SVBS (IDSVBS);
ALTER TABLE LANDLORDS ADD CONSTRAINT FK_LANDLORDS_2 FOREIGN KEY (FKFLAT) REFERENCES FLATS (IDFLATS);
Citace
Za prvé to záleží na tom, v jakém přesně pořadí ty Posty probíhají (jestli například doběhne celý Flats.Post a teprve potom začne Landlords.Post),
To netuším. Je to v správe transakcie. A o tom neviem nič. Preto uvažujem rozložiť to na dva samostatné kroky.
Citace
a za druhé mám vážné podezření, že vyplňuješ špatná políčka
To je na 100% správne.
Citace
Jsem si stoprocentně jistý, že nedochází k druhému spuštění Before Insert triggeru na Flats. Before Insert nastane právě jednou, a to v okamžiku, kdy databáze narazí na INSERT INTO tabulka. A to platí i v případě, že to INSERT INTO tabulka neděláš ty, ale dělají ho tvoje komponenty pro přístup k DB.
Tiež si to myslím, ale všetko mi nasvedčuje, že sa tak deje. Ešte to preskúmam.
Citace
tzn. máš blbě podmínku v triggeru.
if ((new.idflats is null) or (new.idflats = 0)) then
.    new.idflats = gen_id("GEN_ID",1);

To nie je zle. Takúto podmienku mám všade.
Citace
aspoň podle mě je docela vhodné důkladně pochopit popisovanou realitu a z ní odvozovat řešení, než střílet různé myšlenky od boku a doufat, že se nějaká trefí.
Tak toto ma presne vystihuje. Trápim sa niekoľko hodín. Potom vezmem papier a ceruzu a behom 5 minút to je vybavené. Viem o tom a robím to i tak.
Na dnes stačilo.
W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1559
  • Karma: 37
    • Pepak.net
Re:Získanie práve vytvoreného ID
« Odpověď #28 kdy: 08-01-2013, 13:24:13 »
Citace
Za prvé to záleží na tom, v jakém přesně pořadí ty Posty probíhají (jestli například doběhne celý Flats.Post a teprve potom začne Landlords.Post),
To netuším. Je to v správe transakcie. A o tom neviem nič. Preto uvažujem rozložiť to na dva samostatné kroky.
S tím nemá transakce nic společného. Všechny Posty probíhají v Delphi, transakce je až za tím.

Citace
Citace
tzn. máš blbě podmínku v triggeru.
if ((new.idflats is null) or (new.idflats = 0)) then
.    new.idflats = gen_id("GEN_ID",1);

To nie je zle. Takúto podmienku mám všade.
Přesto je to úplně blbě. Ne zápisem (ten je v pořádku*), ale logikou. Vždyť co děláš: "Pokud při insertu do tabulky s detailem zjistím, že nemá vyplněný cizí klíč do master tabulky, tak vygeneruju nové dosud nepoužité ID do master tabulky." Je dobře, žeš to sem takhle vypsal, protože předtím se to utopilo v ostatních informacích, ale když to vypíšeš takhle extra a přeložíš si do slov, co to dělá, tak už je úplně jasné, proč ti to chodí špatně...

Znovu ti radím, vyhoď ten cizí klíč úplně, vynuluj generátor, nech proběhnout jednu sérii INSERTů, a pak si zkontroluj, co se ti do tabulek skutečně zapsalo. Z toho už ti snad bude zřejmé, kde se to pokazilo.

*) Snad. Já obvykle používám GEN_ID(NAZEV_SEKVENCE, 1), ale třeba to jde i s tím názvem sekvence v uvozovlkách nebo apostrofech. Ale když tak jde jen o překlep.
« Poslední změna: 08-01-2013, 13:26:04 od pepak »

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6156
  • Karma: 44
    • Verze Delphi: W10 + D11
Re:Získanie práve vytvoreného ID
« Odpověď #29 kdy: 09-01-2013, 10:26:58 »
Som z toho totálny magor. Zrušil som väzbu medzi tabuľkami. Dvakrát som spustil založenie nových záznamov. Dostal som rad neprerušenej postupnosti ID 601 až 606. Viď obrázok.

Predpoklady:
1) Nie sú žiadne výnimky - vo všeobecnosti
2) Obe tabuľky majú trigger Befor Insert a udalosť OnBeforePost, ktoré sú konštrukčne zhodné. Nič iné. Udalosť OnBeforePost volá procedúru s telom "ID = gen_id("GEN_ID",1);" a zapisuje práve generovanú hodnotu ID do tabuľky ako PK.

Úvaha 1 - tak ako to je napísané (hrám sa na počítač).
Najprv sa dvakrát volá trigger Befor Insert. Nakoľko je podmienka splnená, tak sa 2x spustí generátor. Potom sa dvakrát volá OnBeforePost. Nemá pomienku, tak sa 2x spustí generátor.
Výsledkom je 4x spustený generátor. V skutočnosti sa generátor spustil 3x a v inom poradí. Hľadám ďalej.

Úvaha 2 - poznám výsledok a z neho hľadám riešenie
I. Najprv sa vykoná OnBeforePost pre Detail. Prečo? Veď prvý je INSERT! Generuje sa hodnota 601 - OK.

II. Nalseduje trigger Befor Insert pre Detail. Generuje sa hodnota 602 - OK. Ale prečo sa generuje ID, keď v OnBeforePost sa zapisuje hodnota pre ID?

III. Vykoná sa OnBeforePost pre Master. Generuje sa hodnota 603 - OK.

IV. Nalseduje trigger Befor Insert pre Master. Podľa II. by sa malo generovať ID. Ale negeneruje sa. To zas posdporuje názor, že hodnota pre ID bola zapísaná v OnBeforePost pre Master. To je v rozpore s II.

Je zrejmé, že mi chýba nejaká podstatná vedomosť. Čo to je a kde si o tom môžem prečítať?
Mám k dispozícii túto literatúru:
IB6 Language Ref, IB6 Developer's guide, IB6 API, IBE Book 05-2005, IB Expert Firebird Guide, FB od Císařa
W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1559
  • Karma: 37
    • Pepak.net
Re:Získanie práve vytvoreného ID
« Odpověď #30 kdy: 09-01-2013, 11:36:30 »
Proboha tě prosím, udělej si konečně pořádek v názvech tabulek, názvech polí a názvech datasetů. Ty tam házíš "Master", "Detail", "ID", "FK", "idLandlords", "idFlats", "FKFlat", "Flats", "Landlords" atd. bez jakéhokoliv upozornění, co patří kam. V tom se nedá vyznat, nebo aspoň já se v tom nevyznám - jestli je pole ID v datasetu Detail totožné s polem idLandlords v tabulce Flats nebo jak to vůbec je. Připadá mi, že v každém svém příspěvku to přeházíš, aby to bylo zase úplně jinak. Co kdybys to udělal podle osvědčeného zvyku, že pokud se tabulka jmenuje "Flats", tak její dataset se bude jmenovat "DatasetFlats" (nebo "FlatsDataset", to je mi fuk), a pokud má pole "ID", tak se to pole bude jmenovat "ID" jak v databázi, tak v Delphi? (Pokud to pole má samostatnou komponentu, tak ať se jmenuje FlatsIDField nebo tak nějak, aby bylo jasné, do které tabulky patří.)

Úvaha 1 - tak ako to je napísané (hrám sa na počítač).
Najprv sa dvakrát volá trigger Befor Insert. Nakoľko je podmienka splnená, tak sa 2x spustí generátor. Potom sa dvakrát volá OnBeforePost. Nemá pomienku, tak sa 2x spustí generátor.
Ne, takhle to určitě není. BEFORE INSERT trigger se spouští až v okamžiku, kdy databáze narazí na příkaz INSERT INTO tabulka, což je až poté, co kompletně proběhne BeforePost v Delphi.

Citace
Výsledkom je 4x spustený generátor. V skutočnosti sa generátor spustil 3x a v inom poradí. Hľadám ďalej.
3x spuštěný generátor, v souvislosti s posloupností ID v obrázku, značí, že operace proběhnou v tomto pořadí:
1) Proběhne BeforePost na té tabulce, která obsahuje pole FKFlat, ať už to je kterákoliv (v tom aby se prase vyznalo, viz výše). Procedurou se vygeneruje nové ID, které se do toho pole FKFlat zapíše. Primární klíče obou zúčastněných tabulek zůstanou nevyplněné.
2) Proběhne InternalPost do tabulky Flats (odhaduji, viz výše; možná se místo toho jmenuje úplně nesmyslně Detail, fakt nevím). Protože je její primární klíč prázdný, tak se triggerem vygeneruje nová hodnota.
3) Proběhne InternalPost do tabulky Landlords (nebo Master, v tom se prostě nedá vyznat). Protože je její primární klíč prázdný, tak se triggerem vygeneruje nová hodnota.

Citace
Úvaha 2 - poznám výsledok a z neho hľadám riešenie
I. Najprv sa vykoná OnBeforePost pre Detail. Prečo? Veď prvý je INSERT! Generuje sa hodnota 601 - OK.
Protože už jsem ti nejméně jednou psal, že BeforePost probíhá ještě předtím, než databázová komponenta vůbec začne uvažovat o tom, že by třeba mohla oslovit databázi - tzn. ještě předtím, než vůbec někdo vymyslí, že výsledkem postu bude nějaký příkaz INSERT INTO tabulka. Ten INSERT se začne řešit až poté, co skončí celý BeforePost - to teprve komponenta vygeneruje příkaz, v tomto případě INSERT, a pošle ho databázi. Databáze přijme INSERT, a protože je to INSERT, tak se podívá, jestli jsou nějaké BEFORE INSERT, které by mohla zavolat. Ty provede, pak spustí samotný insert, a pak spustí všechny AFTER INSERT (pokud nějaké jsou). Pak vrátí řízení volajícímu (tzn. komponentě) a ta zavolá AfterPost. A teprve až AfterPost skončí, tak program bude pokračovat dalším příkazem za Dataset.Post.

Citace
II. Nalseduje trigger Befor Insert pre Detail. Generuje sa hodnota 602 - OK. Ale prečo sa generuje ID, keď v OnBeforePost sa zapisuje hodnota pre ID?
Protože to máš blbě pojmenované, tak se v tom nikdo nevyzná. Já si z tvých kódů myslím, že sice vygeneruješ nové ID, ale pak ho zapisuješ do špatného políčka. Varianta 2 je, že ho sice zapisuješ do správného políčka, ale ve špatném pořadí - pokud používáš cizí klíče, tak musíš napřed zapsat do tabulky, na kterou se budeš odkazovat, a teprve potom zapisovat do tabulky, ze které se odkazuješ (prostě proto, že ten odkaz musí mít platný cíl, což dost dobře mít nemůže, pokud napřed zapíšeš odkaz a teprve potom začneš vytvářet cíl). A ten cíl musí existovat v databázi - je úplně jedno, že jsi třeba vyplnil DatasetX.FieldY.Value, protože o tom se databáze nedozví, dokud neproběhne Post, a pokud se databáze o něčem nedozví, tak to v té databázi neexistuje!

Aby to tu bylo explicitně: Když zavoláš XXX.Post (kde XXX je TDataset napojený na nějakou SQL databázi), tak proběhnou následující operace (tedy ještě některé další, ale budu psát jen ty, co jsou pro tento problém relevantní):
0) [Aplikace) XXX.Post.
1) [Aplikace] XXX.BeforePost.
2) [Aplikace] XXX.InternalPost. Jeho součástí je:
   2.1) [Aplikace] Vygenerovat SQL příkaz do databáze a poslat ho.
   2.2) [DBServer] Všechny BEFORE triggery pro operaci v SQL příkazu.
   2.3) [DBServer] Vlastní provedení operace v SQL příkazu.
   2.4) [DBServer] Všechny AFTER triggery pro operaci v SQL příkazu.
3) [Aplikace] XXX.AfterPost.
4) [Aplikace] Teprve teď se začne zpracovávat další příkaz po XXX.Post.

Citace
Je zrejmé, že mi chýba nejaká podstatná vedomosť. Čo to je a kde si o tom môžem prečítať?
Základy jazyka SQL (tohle není Firebirdový problém, tak, jak to děláš, by ti to přesně stejně nefungovalo i s jakoukoliv jinou databází) a nastudovat si trochu, jak funguje posloupnost v TDataset.Post - ideálně tak, že si pěkně prokrokuješ tu komponentu, kterou pro přístup k databázi používáš, ale když na to přijde, tak to klidně můžeš udělat s prehistorickou TTable.

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6156
  • Karma: 44
    • Verze Delphi: W10 + D11
Re:Získanie práve vytvoreného ID
« Odpověď #31 kdy: 09-01-2013, 13:20:01 »
Citace
Proboha tě prosím, udělej si konečně pořádek v názvech...
Ja to mám presne tak ako to píšeš  :) . Aby som to tu neplietol, tak som v poslednom príspevku používal jedine výrazy Master a Detail. Zdalo sa mi to prehľadnejšie. Ale procedúry, trigger a pod. som kopíroval zo zdrojov. A tam je Landlords = Master a Flats = Detail. Už to nebudeme potrebovať.
Citace
3x spuštěný generátor, v souvislosti s posloupností ID v obrázku, značí, že operace proběhnou v tomto pořadí:
1) Proběhne BeforePost na té tabulce, která obsahuje pole FKFlat, ať už to je kterákoliv (v tom aby se prase vyznalo, viz výše). Procedurou se vygeneruje nové ID, které se do toho pole FKFlat zapíše. Primární klíče obou zúčastněných tabulek zůstanou nevyplněné.
2) Proběhne InternalPost do tabulky Flats (odhaduji, viz výše; možná se místo toho jmenuje úplně nesmyslně Detail, fakt nevím). Protože je její primární klíč prázdný, tak se triggerem vygeneruje nová hodnota.
3) Proběhne InternalPost do tabulky Landlords (nebo Master, v tom se prostě nedá vyznat). Protože je její primární klíč prázdný, tak se triggerem vygeneruje nová hodnota.
Bod 1) mi to kompletne vysvetľuje. Hlavne posledná veta!
Citace
Protože už jsem ti nejméně jednou psal, že BeforePost probíhá ještě předtím, než...
To mi úplne ušlo.
Citace
...jak funguje posloupnost v TDataset.Post
To mi chýbalo. Za to veľké ĎAKUJEM. Nie je niekde o tom pokec?
W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3339
  • Karma: 139
    • Verze Delphi: D2007, XE3, DX10
Re:Získanie práve vytvoreného ID
« Odpověď #32 kdy: 09-01-2013, 13:54:55 »
Nie je niekde o tom pokec?
Google mi namatkou vyhodil tohle: http://podgoretsky.com/ftp/Docs/Delphi/D5/dg/5_datset.html.
a zda se mi, ze tam leccos pisou

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1559
  • Karma: 37
    • Pepak.net
Re:Získanie práve vytvoreného ID
« Odpověď #33 kdy: 09-01-2013, 15:27:19 »
Ja to mám presne tak ako to píšeš  :) . Aby som to tu neplietol, tak som v poslednom príspevku používal jedine výrazy Master a Detail. Zdalo sa mi to prehľadnejšie.
Ono by to přehlednější bylo, kdybys to nenastavil tak, že Master je dataset detailové tabulky a Detail je dataset masterové tabulky. A kdybych nepotřeboval koukat i na zdrojáky triggerů, kde samozřejmě používáš skutečné názvy tabulek.

Citace
Už to nebudeme potrebovať.
To chápu tak, že už jsi na chybu přišel a opravil ji.

Citace
Citace
...jak funguje posloupnost v TDataset.Post
To mi chýbalo. Za to veľké ĎAKUJEM. Nie je niekde o tom pokec?
Nevím, ale připadá mi vcelku samozřejmé, že se napřed kousek udělá v Delphi, pak spousta věcí v databázi, a pak zase kousek věcí v Delphi. Už jen z toho titulu, že TDataset je univerzální a musí umět fungovat s každým myslitelným datovým zdrojem - tedy třeba takovým, který triggery vůbec nemá, nebo takovým, který sice triggery má, ale neumí jejich provádění oddělit tak, aby si aplikace mohla říct, "teď chci první BEFORE TRIGGER, teď chci druhý, teď mi to zastav, ať můžu zavolat událost, dobrý, pokračuj vložením o tabulky, zase zastav, já si udělám událost a pak budeš moct pokračovat s AFTER TRIGGERama" (btw., neznám databázi, která by to uměla oddělit; a přece by to oddělení bylo nutnou podmínkou, pokud bys chtěl dosáhnout chování, kdy napřed proběhne BEFORE TRIGGER a teprve potom BeforePost).

Nejlepší samozřejmě máš, když si to prokrokuješ, ať v tom máš úplně jasno, protože různé implementace datasetu můžou to hotohle schématu doplnit ještě některé další věci, třeba další mezilehlé události, které se ti někdy mohou hodit (nebo ti naopak dělat velké problémy). Ale to už bude specifické pro tu kterou komponentu, takže bude dobré, když si to vyzkoušíš s tou, kterou používáš.

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6156
  • Karma: 44
    • Verze Delphi: W10 + D11
Re:Získanie práve vytvoreného ID
« Odpověď #34 kdy: 09-01-2013, 16:12:15 »
// To chápu tak, že už jsi na chybu přišel a opravil ji.
To nie. Na tom pracujem až večer doma. Ale si myslím, že už mám dosť informácií aby som vedel o čo ide. Koniec koncov záver podľa obrázka sme urobili zhodný.

Používam TIBDataSet. Prejdem help v D či tam niečo nie je a samozrejme aj internet. Mám aj originál knihu k D7. Len je ťažká :)

Poznámka: nesedelo mi aby aplikácia poslala požiadavku napr. DataSet.Insert a užívateľ išiel na obed. Celý čas by tam viselo otvorené spojenie/požiadavka s/na DB. Logické je to urobiť až po DataSet.Post v aplikácii a všetko vybaviť naraz. Ak by som na to niekde narazil, tak to sem dám.
W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1559
  • Karma: 37
    • Pepak.net
Re:Získanie práve vytvoreného ID
« Odpověď #35 kdy: 09-01-2013, 17:10:23 »
Poznámka: nesedelo mi aby aplikácia poslala požiadavku napr. DataSet.Insert a užívateľ išiel na obed. Celý čas by tam viselo otvorené spojenie/požiadavka s/na DB.
Však taky ne. Zjednodušeně řečeno, všechno, co se děje mezi Dataset.Open a Dataset.BeforePost (včetně), je kompletně off-line, a co se děje po Dataset.AfterPost už je zase offline. Databáze by v tu dobu vůbec nemusela existovat a stejně by to fungovalo. (Ono to tak ve skutečnosti úplně přesně není, protože třeba záznamy se můžou donačítat dynamicky, až když jsou potřeba, třeba když uděláš Dataset.Next, ale jako zjednodušená představa to funguje docela dobře.)

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6156
  • Karma: 44
    • Verze Delphi: W10 + D11
Re:Získanie práve vytvoreného ID
« Odpověď #36 kdy: 10-01-2013, 15:32:08 »
Včera som pozeral do kníh a bavia sa o DataSet-e samotnom. Nie o tom čo hľadám. Nepomohol ani internet.

Pri DataSet.Insert sa neviem dostať do IBCustomDataSet.pas. Shift+F7 i BreakPoint v IBCustomDataSet.pas nepomohol a Shift+F8 ma hodí do CPU okna, ktoré je pre mňa španielskou dedinou. Ako sa tam dostanem? Krokovanie bude asi jediná cesta ako niečo zistiť.
« Poslední změna: 10-01-2013, 15:39:55 od Stanislav Hruška »
W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1559
  • Karma: 37
    • Pepak.net
Re:Získanie práve vytvoreného ID
« Odpověď #37 kdy: 10-01-2013, 16:29:43 »
Project Options -> Compiler Options -> Use Debug DCUs.

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6156
  • Karma: 44
    • Verze Delphi: W10 + D11
Re:Získanie práve vytvoreného ID
« Odpověď #38 kdy: 11-01-2013, 09:21:49 »
Tu je výlsedok krokovania. Zvolil som prácu s jedinou tabuľkou.
Kód: [Vybrat]
MasterDataset.Insert;
  TDataSet.Insert;
    BeginInsertAppend;
      CheckBrowseMode;
        CheckActive;
        DataEvent(deCheckBrowseMode, 0);
      CheckCanModify;
      DoBeforeInsert;
        inherited DoBeforeInsert;
          TDataSet.DoBeforeInsert;
            TIBCustomDataSet.DoBeforeInsert; override;
              inherited DoBeforeInsert;
      CheckParentState;
      DoBeforeScroll;  // if Assigned(FBeforeScroll) then FBeforeScroll(Self);
    InternalInsert; = TIBCustomDataSet.InternalInsert; // virtual
      CursorPosChanged; = FCurrentRecord := -1;  // TIBCustomDataSet.InternalInsert;
    EndInsertAppend;
      DoAfterInsert; = if Assigned(FAfterInsert) then FAfterInsert(Self);
      DoAfterScroll; = if Assigned(FAfterScroll) then FAfterScroll(Self);
MasterDataset.Post;
  TIBCustomDataSet.Post;
    UpdateRecord;
      DataEvent(deUpdateRecord, 0);
    inherited Post;
      TDataSet.Post;
        UpdateRecord;
          DataEvent(deUpdateRecord, 0); // TDataSet.DataEvent
        DataEvent(deCheckBrowseMode, 0);
        DoBeforePost; = if Assigned(FBeforePost) then FBeforePost(Self); *
          TdmMain.ibdtstSVBBeforePost(DataSet: TDataSet); // MasterDataset
        CheckOperation(InternalPost, FOnPostError); // InternalPost - CheckRequiredFields;
          TIBCustomDataSet.InternalPost;
            inherited InternalPost;
            CheckEditState;
            AdjustRecordOnInsert(Buff);
            WriteRecordCache(FRecordCount, Buff);
            InternalPostRecord(Qry, Buff);
              TIBSQL.GetRowsAffected
// Tu predpokladám odovzdanie činnosti do FB
// To znamená, že sa najprv vykoná všetko predtým a až teraz "SQLInsert" + MasterDataset.onAfterPost (viď DoAfterPost)
// SQLInsert = spustenie trigger Befor Insert
{
CREATE OR ALTER TRIGGER SVBS_BI FOR SVBS
ACTIVE BEFORE INSERT POSITION 0
as
begin
  if ((new.idsvbs is null) or (new.idsvbs = 0)) then
    new.idsvbs = gen_id("GEN_ID",1);
end
}
    case SQLType of
      SQLUpdate:   Result := Database.GDSLibrary.isc_vax_integer(@result_buffer[6], 4);
      SQLDelete:   Result := Database.GDSLibrary.isc_vax_integer(@result_buffer[13], 4);
      SQLInsert:   Result := Database.GDSLibrary.isc_vax_integer(@result_buffer[27], 4);
    else
      Result := -1;
    end ;
// End TIBSQL.GetRowsAffected
              SetModified(False);
              WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Buff);
        FreeFieldBuffers;
        SetState(dsBrowse);
        Resync([]);
        DoAfterPost; = if Assigned(FAfterPost) then FAfterPost(Self);
// * Pre úplnosť
procedure TdmMain.ibdtstSVBBeforePost(DataSet: TDataSet);
begin
  If(DataSet.state = dsinsert) then
  begin
    ibspID.ExecProc;
    NewPrKey := ibspID.ParamByName('ID').AsInteger;
    DataSet.FieldByName('IDSVBS').Value := NewPrKey;
  end;
end;

Zhrniem čo mám. Budem používať označenie podľa skutočného názvu tabuliek - snáď nebudú zmätky. Čo najjednoduchšie.

Landlord.Insert;
Flat.Insert;
Flat.Post;
Landlord.FieldValues['PK'] := newPrKey;
Landlord.Post;
Kompletne, včítane udalostí by to malo byť takto:
Kód: [Vybrat]
Landlord.Insert; // Žiadna udalosť
Flat.Insert; // Žiadna udalosť
procedure TdmMain.ibdtstFlatBeforePost(DataSet: TDataSet);
begin
  If(DataSet.state = dsinsert) then
  begin
    ibspID.ExecProc;
    NewPrKey := ibspID.ParamByName('ID').AsInteger;
    DataSet.FieldByName('idflats').Value := NewPrKey;
  end;
end;
// new.idflats má priradenú hodnotu, ale nespráva sa tak. Žeby bolo iné poradie uadlostí?
CREATE OR ALTER TRIGGER FLATS_BI FOR FLATS
ACTIVE BEFORE INSERT POSITION 0
as
begin
  if ((new.idflats is null) or (new.idflats = 0)) then
    new.idflats = gen_id("GEN_ID",1);
end
Flat.Post;
Landlord.FieldValues['FKFlats'] := newPrKey;
procedure TdmMain.ibdtstLandlordBeforePost(DataSet: TDataSet);
begin
  If(DataSet.state = dsinsert) then
  begin
    ibspID.ExecProc;
    NewPrKey := ibspID.ParamByName('ID').AsInteger;
    DataSet.FieldByName('IDLandlords').Value := NewPrKey;
  end;
end;
CREATE OR ALTER TRIGGER LANDLORDS_BI FOR LANDLORDS
ACTIVE BEFORE INSERT POSITION 0
as
begin
  if ((new.idlandlords is null) or (new.idlandlords = 0)) then
    new.idlandlords = gen_id("GEN_ID",1);
end
Landlord.Post;
Z uvedeného vyplýva, že nerobím zápis do nesprávneho poľa.
Citace
3x spuštěný generátor, v souvislosti s posloupností ID v obrázku, značí, že operace proběhnou v tomto pořadí:
1) Proběhne BeforePost na té tabulce, která obsahuje pole FKFlat, ať už to je kterákoliv (v tom aby se prase vyznalo, viz výše). Procedurou se vygeneruje nové ID, které se do toho pole FKFlat zapíše. Primární klíče obou zúčastněných tabulek zůstanou nevyplněné.
Tu mi nesedí posledná veta. Boli spustené oba trigger-ty. Takže new.IDxx má hodnotu. Len pri tom Flats mi to nejako blbne.
Večer vyskúšam krokovanie na kontrétnom prípade. Včera som už nestíhal.
 
Ešte uvažujem, že procedúru pre získanie newPrKey by som hodil do AFterPost s parametrom "0"
« Poslední změna: 11-01-2013, 10:10:00 od Stanislav Hruška »
W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6156
  • Karma: 44
    • Verze Delphi: W10 + D11
Re:Získanie práve vytvoreného ID
« Odpověď #39 kdy: 12-01-2013, 09:18:02 »
Tak som to našiel. Už som sa tým stretol. Že to nikoho netrklo.
Vymazal som daný komponent, nahodil znova a všetko ide ako má.
Aspoň som si naštudoval niečo, čomu by som sa ináč nevenoval.
W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.