Autor Téma: Parametrizované príkazy  (Přečteno 1150 krát)

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 3082
  • Karma: 29
    • Verze Delphi: XE7 professional
Parametrizované príkazy
« kdy: 20-12-2017, 11:16:20 »
Tému zakladám na základe tejto diskusiehttp://forum.delphi.cz/index.php/topic,15942.msg98307.html#new{http://forum.delphi.cz/index.php/topic,15942.msg98307.html#new
Je pre mňa zaujímavá a má mať vlastné vlákno. Mám tu zmätok, nedostatok vedomostí a pod.
Citace
Vždycky když někde vidím, jak má někdo v kódu SQL příkaz INSERT a kousek dál jiný kód s SQL UPDATE, a tam všude nastříkané parametry, tak se zarazím.
Ja si to bez parametrov predstaviť neviem. Mám k dispozícii dva varianty.
Kód: [Vybrat]
INSERT INTO MYTABLE
(FIELD1, ...)
VALUES(:PARAM1,...)
alebo
Kód: [Vybrat]
INSERT INTO MYTABLE
SELECT * FROM TABLE1
WHERE ...
Toto si bez časti WHERE neviem predstaviť. Pri UPDATE je to tak isto.
Citace
Až na vyjimky, kdy opravdu musím cpát data opravdu rychle, nebo kde je ve where řetezec, si nechávám generovat update a insert přes následníka TDataset (Firedac, ADO) ze sikovneho jednoducheho SQL přikazu a používám společný kód s FieldByName a Edit nebo Append a Post.
Už tu viackrát zaznelo, že mám mať nejaký generátor SQL textov. Žiaľ, doteraz sa mi nepodarilo vytvoriť si o ňom žiadnu predstavu. Sem tam sa mi niečo opakuje. V mnohých prípadoch o tom ani netuším. Lenže v princípe je každé SQL unikátne. A na tom končím :'( 
Ako sa to dá robiť pomocou potomka TDataset je pre mňa záhadou.
Rád by som už konečne vedel, ako sa to generovanie SQL textov dá generovať bez toho, aby som sa z toho zbláznil.

Ak si na to vytvorím triedu, tak jej musím poslať nejaký parameter. Tých parametrov budú stovky. To podľa mňa znamená "nekonečne" dlhý case/if. Či... ??
« Poslední změna: 20-12-2017, 11:18:55 od Stanislav Hruška »
Delphi XE7, FireBird
Expert na kladenie nejasne formulovaných otázok.

Offline raul

  • Plnoletý
  • ***
  • Příspěvků: 178
  • Karma: 11
    • Verze Delphi: FPC :D
Re:Parametrizované príkazy
« Odpověď #1 kdy: 20-12-2017, 11:28:56 »
Excellent
Rated 1 time
Pouzival jsem pred lety PostgresDAC. Rozdil rychlosti pouziti primo parametrizovanych dotazu a klasiky byl propastny - pro me. Samozrejme ne vzdy tomu tak je, ale rozhodne bych se k tomu nestavel jako Radek, ze zpozornim, kdyz to nekdo pouzije. Naopak mi to rekne, ze ten clovek dela kod sviznejsi - pokud ho teda jen nechce zobrazit na formu apod. GUI aplikace nedelam, spise serverove veci.

 Jemne podobnou diskusi jsme vedli pred lety s jednim kolegou - jeho nazor, ze nechape k cemu je dnes pointerova aritmetika - nechapu ja. Treba pri zpracovani obrazu - idealni a nejrychlejsi.

Obecne myslim, ze zalezi na tom, jak z vejsky na to clvoek kouka - chce-li rychle neco napsat, pak klido. Chce-li psat dele, ale mit kod rychlejsi, pak jednoznacne parametricky.

Generator se da napsat velmi jednoduchy - treba neco jako : genInsert(string tablename; fields : array of string) : string; - kterej udela INSERT INTO <tablename> (<field1>,<field2>) VALUES(:<field1>,:<field2>) - pripadne libovolne upravit s pridanim automatickych polozek apod.
Lazarus 1.6.3:), FPC, Intel/Arm, Windows/Linux

Online Delfin

  • Guru
  • *****
  • Příspěvků: 546
  • Karma: 25
  • SW konzultant
    • Verze Delphi: 2009, Tokyo
Re:Parametrizované príkazy
« Odpověď #2 kdy: 20-12-2017, 12:19:52 »
Tady se, alespon jak jsem to pochopil, micha vice veci dohromady. Radek ma nejspis na mysli konstrukci podobnou teto - pisu z hlavy do web browseru a tuto konstrukci radu let nepouzivam (raul ji oznacil jako "klasiku"):

Kód: Delphi [Vybrat]
  1. Query.SQL.Text := 'SELECT ID, MyColumn FROM MyTable';
  2. Query.Open;
  3.  
  4. Query.Append;
  5. Query.FieldByName('MyColumn').AsInteger := 666;
  6. Query.Post;
  7.  
  8. Query.Locate('ID=1'); // tady by mela byt navic konstrukce s ulozenim a restore bookmark kurzoru
  9. Query.Edit;
  10. Query.FieldByName('MyColumn').AsInteger := 777;
  11. Query.Post;
  12.  

Zatimco parametrizovany ekvivalent by mohl byt nejak takto:

Kód: Delphi [Vybrat]
  1. Query.SQL.Text := 'SELECT MyColumn FROM MyTable';
  2. Query.Open;
  3.  
  4. Query.SQL.Text := 'INSERT INTO MyTable (MyColumn) VALUES (:MyColumn)';
  5. Query.ParamByName('MyColumn').AsInteger := 666;
  6. Query.ExecSQL;
  7.  
  8. Query.SQL.Text := 'UPDATE MyTable SET MyColumn = :MyColumn WHERE ID = :ID';
  9. Query.ParamByName('ID').AsInteger := 1;
  10. Query.ParamByName('MyColumn').AsInteger := 777;
  11. Query.ExecSQL;

Prvni blok kodu vygeneruje interne dotazy podobne tem co jsou v druhem bloku kodu. Mozna tim byl myslen ten generator. Tezko rict... Jinak souhlas s raulem, osobne bych spis zpozornil kdyby nekdo pouzil to co je k videni v prvnim bloku.
« Poslední změna: 20-12-2017, 12:21:33 od Delfin »
Shiny disco balls! I don't like :)

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 3082
  • Karma: 29
    • Verze Delphi: XE7 professional
Re:Parametrizované príkazy
« Odpověď #3 kdy: 20-12-2017, 13:08:00 »
viď raul
Citace
Generator se da napsat velmi jednoduchy - treba neco jako : genInsert(string tablename; fields : array of string) : string; - kterej udela INSERT INTO <tablename> (<field1>,<field2>) VALUES(:<field1>,:<field2>) - pripadne libovolne upravit s pridanim automatickych polozek apod.
Pre tieto typy SQL (INSERT s VALUES) to ide. Vďaka. To si prerobím.
A ako na ostatné?
viď Delfin: zásadne to robím podľa druhého bloku. Akonáhle mám WHERE, CASE či niečo iné, čo má dostať niečo zvonka, tak to robím jedine cez parameter. Okrem iného mi to kontroluje správnosť typov a zbavuje potreby používať CAST. A že som sa už neraz sekol :)
« Poslední změna: 20-12-2017, 13:12:36 od Stanislav Hruška »
Delphi XE7, FireBird
Expert na kladenie nejasne formulovaných otázok.

Online Delfin

  • Guru
  • *****
  • Příspěvků: 546
  • Karma: 25
  • SW konzultant
    • Verze Delphi: 2009, Tokyo
Re:Parametrizované príkazy
« Odpověď #4 kdy: 20-12-2017, 13:13:36 »
Citace
Generator se da napsat velmi jednoduchy - treba neco jako : genInsert(string tablename; fields : array of string) : string; - kterej udela INSERT INTO <tablename> (<field1>,<field2>) VALUES(:<field1>,:<field2>) - pripadne libovolne upravit s pridanim automatickych polozek apod.
Pre tieto typy SQL (INSERT s VALUES) to ide. Vďaka. To si prerobím.
A ako na ostatné?
Delfin: zásadne to robím podľa druhého bloku. Akonáhle mám WHERE, CASE či niečo iné, čo má dostať niečo zvonka, tak to robím jedine cez parameter. Okrem iného mi to kontroluje správnosť typov a zbavuje potreby používať CAST. A že som sa už neraz sekol :)

No, a chces stavet "entity framework"? Nebo pro co chces vyrabet generator SQL prikazu (FireDAC totiz v sobe jeden ma)?
Shiny disco balls! I don't like :)

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 3082
  • Karma: 29
    • Verze Delphi: XE7 professional
Re:Parametrizované príkazy
« Odpověď #5 kdy: 20-12-2017, 13:16:42 »
Citace
Nebo pro co chces vyrabet generator SQL prikazu (FireDAC ma jeden v sobe)?
Lebo tu každý do mňa hučí, že tak to je správne ;D Niečo pravdy na tom je. Viď opakované pasáže. A že formulár má byť hlúpy. A robiť inteligentnú jednotku pre každý formulár sa mi nezdá to pravé.
« Poslední změna: 20-12-2017, 13:19:19 od Stanislav Hruška »
Delphi XE7, FireBird
Expert na kladenie nejasne formulovaných otázok.

Offline raul

  • Plnoletý
  • ***
  • Příspěvků: 178
  • Karma: 11
    • Verze Delphi: FPC :D
Re:Parametrizované príkazy
« Odpověď #6 kdy: 20-12-2017, 13:26:10 »
Ostatne podobne -
genUpdate(string tablename; fields : array of string; ID : boolean; native_where : string) : string;
kterej udela
pokud je ID true:
UPDATE <tablename> SET <field1>=:<field1>,<field2>=:<field2> WHERE ID=:ID
pokud je ID false:
UPDATE <tablename> SET <field1>=:<field1>,<field2>=:<field2> WHERE <native_WHERE>

mySQL tusim ma prikaz REPLACE, neco podobneho ma uz i snad pgSQL, kde pokud primarni klic neni nalezen, udela se insert. Jinak samozrejme je mozno udelat hafo dalsich, realne pouzivam slozitejsi konstrukce, ale ty jsou odvisle nad db schematem, ktere pouzivam leta. Tyto konstrukce pak zajisti i spravne generovani tabulek vcetne historie atd atd atd - moznosti je velmi. Pokud clovek delat ciste GUI klikatko, tak klido pouzit klasicke komponenty -  od dob BDE. Pokud vsak chce rychlost, pak je toto rychlejsi - cokoliv univerzalniho je proste pomale a mnbohdy zbytecne (Treba ziskavat barvu obrazku pres getPixel ci jak se to jmenuje (overuje abys nekoukal za roh atd - mozna uz to tam neni, ale kdysi kdysi snad bylo - mozna v delphi, mozna ve VB, bavim se obecne).

Pokud se bavime o slozitejsich insertech apod, pak zasadne pres SP, kterou proste jen zavolam - na coz mam i generator (opet navazany na ekosystem). Takze zavolani metody delphi je stejne jako metody na SQL vcetne typu. Jo, musim mit precompiler. A jo, je to rychle a elegantni.
Za ty leta jsem si proste napsal generator db, a pak tyhle drobotiny kolem, nekdy slozite, nekdy jen jednoduche byt praci ulehcujici.
Nejak jsem moc neprivyk tem internim vymysleninam - coz nebrat zle, jen proste vzdy tam bylo cosik - opakuji GUI skoro nepisu. Ono je prima, ze se to kolikrat da naklikat jednoduse, a pak k tomu pustej zhuverilce (kterej nevi, ze existuje index v db), kterej naklika takovou sracku, ze se z toho server pos... (Resil jsem optimalizaci import dat jedne automobilky - prichazeji denne, import trval 8-16h (jednak diky dodavateli dat, druhak kvuli kokotovi co to psal)).
Lazarus 1.6.3:), FPC, Intel/Arm, Windows/Linux

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 3082
  • Karma: 29
    • Verze Delphi: XE7 professional
Re:Parametrizované príkazy
« Odpověď #7 kdy: 20-12-2017, 13:45:45 »
Pracujem s FB, ale je to obecná téma.
Ja som mal na mysli SELECT-y. INSERT A UPDATE sú dosť priamočiare, aj si to vysvetlil, ale SELECT...
SP nemám ani jednu. Z dôvodu, aby som neodkryl logiku programu. Dnes už viem, že to až tak veľa neprezradí.
Delphi XE7, FireBird
Expert na kladenie nejasne formulovaných otázok.

Offline raul

  • Plnoletý
  • ***
  • Příspěvků: 178
  • Karma: 11
    • Verze Delphi: FPC :D
Re:Parametrizované príkazy
« Odpověď #8 kdy: 20-12-2017, 13:53:47 »
Aha :) Selecty generuju primo ze schematu tim prekompilerem, takze tam me ted z patra nenapada nic. Ono to zalezi i na stavbe aplikace, jak se k datum pristupuje apod. Nicmene opet je to tak, ze jednoduche dotazy se mi vygeneruji primo - dle zadani prekompileru a slozitejsi zapouzdrim do view ci SP, kterou opet prozenu prekompilerem, takze pripravi patricne metody. Rucne bych to uz delat ani nechtel, prece jen mam to schema relativne slozite atd. V pripade, ze stavim cosik slozitejsiho, tak nad objektem SQL mam i adaptivni cache (jednak umoznuje ulozit vice variant dat, druhak neni zavisla na case, ale koeficientu pouziti s moznosti ji flushout automaticky pri insertu), proto me pak netrapi pustit dotaz x-krat treba atd.
Lazarus 1.6.3:), FPC, Intel/Arm, Windows/Linux

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 1966
  • Karma: 101
    • Verze Delphi: D2007, XE3, DX10
Re:Parametrizované príkazy
« Odpověď #9 kdy: 20-12-2017, 14:01:55 »
Ja som mal na mysli SELECT-y. INSERT A UPDATE sú dosť priamočiare, aj si to vysvetlil, ale SELECT...
No vzdyt to chapes spravne: se SELECTem se moc podstatneho udelat neda, pokud se nejedna o flat tabulku nebo tahani vysledku vracenych SP nebo view...

I pri pouziti treba toho Entity Frameworku:
- kdyz vkladam do DB tak vytvarim instance objektu a ty propojuju, prirazuju jim hodnoty a nakonec udelam SaveChanges() a EF se mi postara o vlozeni do DB vcetne vygenerovani umely PK a vyreseni referenci
- kdyz menim strom objektu, tak ho nejakym dotazem natahnu, EF ho ma pripojeny k trackeru, ktery sleduje, co menim, prirazuju hodnoty objektum a dam SaveChanges() EF se postara o aktualizaci dat v DB

Ale u tech selectu, tam nezbyde nez napsat dotazy a jak pises, skoro kazdy je jiny - taha jina data, z jinak provazane sady entit atd. Takze jediny rozdil je, ze ja to pisu ve vyspelem jazyku LINQ pro SQL, ktery operuje s objekty a ma plnou podporu prekladace se silnou typovou kontrolou (zadna jmena sloupcu v uvozovkach jako text, zadne sestavovani SQL prikazu) a je zaclenen do jazyk C#, takze spoustu chyb zachyti pri psani/prekladani kodu a ty to musis zastarale matlat nekde ve stringach na urovni sr*ckoidniho SQL mimo kontrolu prekladace a na prvni chyby narazis az spi spusteni toho SQL, coz je zdlouhave, pracne a tudiz nakladne...
« Poslední změna: 20-12-2017, 14:04:00 od pf1957 »

Online Delfin

  • Guru
  • *****
  • Příspěvků: 546
  • Karma: 25
  • SW konzultant
    • Verze Delphi: 2009, Tokyo
Re:Parametrizované príkazy
« Odpověď #10 kdy: 20-12-2017, 14:04:47 »
Citace
Nebo pro co chces vyrabet generator SQL prikazu (FireDAC ma jeden v sobe)?
Lebo tu každý do mňa hučí, že tak to je správne ;D Niečo pravdy na tom je. Viď opakované pasáže. A že formulár má byť hlúpy.

To jo, s tim urcite souhlas. Ja jen nevedel o jakem generatoru se bavime :) A porad to nejak nevim. Ty chces sestavit model z Delphi trid a vygenerovat pro nej schema databaze a vsechny potrebne dotazy? Nebo obracene? Nebo jakou mas predstavu?
« Poslední změna: 20-12-2017, 14:10:21 od Delfin »
Shiny disco balls! I don't like :)

Offline Radek Červinka

  • Administrátoři
  • Padawan
  • *****
  • Příspěvků: 1823
  • Karma: 73
    • Verze Delphi: D5,D2007, DXE, DXE2 + 2 poslední (Tokyo)
    • O Delphi v češtině
Re:Parametrizované príkazy
« Odpověď #11 kdy: 20-12-2017, 14:09:30 »

Kód: Delphi [Vybrat]
  1. Query.SQL.Text := 'SELECT ID, MyColumn FROM MyTable';
  2. if GenerateInsert then
  3.   Query.SQL.Add('WHERE 1 = 0')  // vrati prazdny zaznam, jen s fieldy
  4. else
  5.   Query.SQL.Add('WHERE ID = xxx'); // to je priklad, vrati prave jeden zaznam pro edit nebo delete
  6. // konec generatoru
  7. Query.Open;
  8.  
  9. if Query.isEmpty then
  10. begin
  11.    Query.Append;
  12.    // primarni ID
  13. end
  14. else
  15.    Query.Edit; // nebo delete podle toho co potrebuji
  16. Query.FieldByName('MyColumn').AsInteger := 666;
  17. Query.Post;
  18.  

Je to SILNE zjednoduseni, ale podle parametru mi to vygenruje na jednom miste prikaz pro SELECT, INSERT, UPDATE nebo DELETE. Pricemz pro SELECT mi to pripoji JOIN tabulky.

Interne pak DB komponenty vygeneruji parametrizovany INSERT nebo Update, nebo DELETE pro zmenene pole.  Takze nejen ze mam veskere zapisy na jednom miste, ale taky nemusim resit zda se nejake pole zmenilo atd.
Nevyhoda je, ze prvni Open je dotaz do DB navic (protoze to mam generovane vzdy dynamicky). Ale to mi za to stoji, hlavne z hlediska udrzitelnosti a prehlednosti kodu.

Update: ten generator je v samostatne tride (modulu), takze v celem programu ani v zadnem data modulu nejsou SQL prikazy.

« Poslední změna: 20-12-2017, 14:12:31 od Radek Červinka »
Embarcadero MVP - Czech republic

Offline Radek Červinka

  • Administrátoři
  • Padawan
  • *****
  • Příspěvků: 1823
  • Karma: 73
    • Verze Delphi: D5,D2007, DXE, DXE2 + 2 poslední (Tokyo)
    • O Delphi v češtině
Re:Parametrizované príkazy
« Odpověď #12 kdy: 20-12-2017, 14:13:50 »
Nez mne zacnete lincovat :-), je to jen koncept toho co pouzivam.
Embarcadero MVP - Czech republic

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 1966
  • Karma: 101
    • Verze Delphi: D2007, XE3, DX10
Re:Parametrizované príkazy
« Odpověď #13 kdy: 20-12-2017, 14:18:19 »
Nez mne zacnete lincovat :-), je to jen koncept toho co pouzivam.
Me neni jasne, kdyz nedefinujes joiny, jak "neco" pozna, jak je spravne vytvorit, vzdyt muze jit o ruzne typy joinu, self-joiny provazane nekdy pres prosty FK, jindy pres vice poli atd...

Offline Radek Červinka

  • Administrátoři
  • Padawan
  • *****
  • Příspěvků: 1823
  • Karma: 73
    • Verze Delphi: D5,D2007, DXE, DXE2 + 2 poslední (Tokyo)
    • O Delphi v češtině
Re:Parametrizované príkazy
« Odpověď #14 kdy: 20-12-2017, 14:33:51 »
Nez mne zacnete lincovat :-), je to jen koncept toho co pouzivam.
Me neni jasne, kdyz nedefinujes joiny, jak "neco" pozna, jak je spravne vytvorit, vzdyt muze jit o ruzne typy joinu, self-joiny provazane nekdy pres prosty FK, jindy pres vice poli atd...


Generator je vzdy pro konkretni tabulku. Pro aktualizaci (resp. zapis) to vzdy vrati konkretni zaznam , tzn. ze pripadne joiny (ciselniky) jsou v transakci vytvareny predtim. Ale jelikoz si je uzivatel vetsinou vybere v GUI tak je jasne ze jsou v DB.

napr. jednoducha tabulka pro staty
Kód: [Vybrat]
procedure TSQLManagerTables.OpenQState(DataSet: TDataSet; eOperation: TEnum_OpenOperation;
  iID: Integer = 0; iCurrency: Integer = 0);
var
  bJoinTables: Boolean;
  sMainFilter, sSecondFilter: string;
begin
  with (DataSet as TBaseDataset) do
  begin
    if Active then
      Close;

// podle typu SQL vygeneruje hlavni where
    gPrepareMainFilter(eOperation, 's.idState', iID, bJoinTables, sMainFilter);

// filtry na strane serveru
    sSecondFilter := gsPrepareSecondFilter('s.flCurrency', iCurrency);

// prima cast
    SQL.Clear;
    SQL.Add('SELECT s.idState, s.dcName, s.dcCode, s.dcCodeISO, s.flCurrency, ');
    SQL.Add(' dcCodeIso3, dlNumeric, dcNameLocFull, ');
    SQL.Add(' dcNameLocCut, dcNameIntFull, dcNameIntCut ');

// bJoinTables je nastaven dle typu operace pri generovani hlavniho where
// pripoj sloupce z join tabulek, nebo ruzne pocitane sloupce
    if bJoinTables then
      SQL.Add(',c.dcName as dcCurrencyName');

    SQL.Add('FROM tCRM_State s');
// potrebujeme to pro zobrazeni, OK, joini co muzes
    if bJoinTables then
      SQL.Add('LEFT JOIN tCRM_Currency c ON s.flCurrency = c.idCurrency');

// inteligentne postpojuj pripadne fitry
    SQL.Add(WhereJoin(True, [sMainFilter, sSecondFilter]));

    if bJoinTables then
      SQL.Add(gsOrderBy('s.dcName'));
   end;
end;


Kdekoliv potrebuji v programu pracovat s patricnou tabulkou tak zavolam
SQLManager.OpenQState(ds, eoDelete, 100);
ds.Open;
ds.Delete;
ds.Close;
nebo

SQLManager.OpenQState(ds, eoSelect, 0, iCZK);
ds.Open;
a  zobraz

nebo proste co potrebuji.

Je to muj zpusob jak drzet sql na jednom miste a trsku modularne. Variantou je entity framework atd.

« Poslední změna: 20-12-2017, 14:36:16 od Radek Červinka »
Embarcadero MVP - Czech republic

 

S rychlou odpovědí můžete používat BB kódy a emotikony jako v běžném okně pro odpověď, ale daleko rychleji.

Jméno: E-mail:
Ověření:
Datový typ v Delphi, který má True a False: