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

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ů: 6163
  • 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ů: 3343
  • 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ů: 6163
  • 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ů: 6163
  • 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ů: 6163
  • 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ů: 6163
  • 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.