Autor Téma: SQLite a TBCD prakticky + Problém  (Přečteno 7482 krát)

Offline miroB

  • Guru
  • *****
  • Příspěvků: 585
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
SQLite a TBCD prakticky + Problém
« kdy: 19-07-2018, 14:50:17 »
Skúšam SQLite + FireDAC + TBCD.
Pôvodne ma vystrašila info z odkazu:
http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Using_SQLite_with_FireDAC#High-Precision_Numbers

Spravil som pokus, viď priložený obrázok.
Tvorba a naplnenie tabuľky v SQL  (qryCreate):
Kód: MySQL [Vybrat]
  1. CREATE TABLE tblBCD ( bcd23 Numeric( 64,23 ), bcd25 Numeric( 64,25 ) );
  2. Insert into tblBCD ( bcd23, bcd25 ) values
  3.  ( '-12345678901234567890,12345678901234567890123456789012345678901',
  4.    '-12345678901234567890,123456789012345678901234567890123456789012' );
  5. select rowid, bcd23, bcd25 from tblBCD
Na výstupe sú však miesto desatinných oddeľovačov znaky "<". Je vidieť aj na obrázku.
Kontrola cez kód:
Kód: Delphi [Vybrat]
  1. uses
  2. Data.FmtBcd
  3. ..
  4. procedure TForm1.FormCreate(Sender: TObject);
  5. var
  6.   x, y, x2, y2 : TBCD;
  7.   s, s2, s3 : String;
  8. begin
  9.   if dbMain.Connected then
  10.     dbMain.Connected   := false;
  11.   dbMain.Connected     := true;
  12.   qryCreate.ExecSQL;
  13.   qryCategories.Active := True;                 // PROBLÉM :
  14.   s   := qryCategories.Fields[ 0 ].AsString; // s : obsahuje kompletný záznam, ale ten znak "<" ..
  15.   s2  := StringReplace( s, '<', ',', [] );   // s2 je už s čiarkou ..
  16.   y   := StrToBcd( s2 );                     // korektné BCD číslo
  17.   s3  := BcdToStr( y - 1 );                  // korektný výsledok
  18.   x2  := qryCategories.Fields[ 0 ].AsBcd;    // toto zdanlivo funguje, ale Precision je poškodená
  19. end;
ešte SQL kód qryCategories:
Kód: MySQL [Vybrat]
  1. select   *,rowid from tblBCD

Naplnenie cez kód sa však správa korektne:
Kód: Delphi [Vybrat]
  1.   s := '-12345678901234567890,12345678901234567890123456789012';
  2.   x2 := StrToBcd( s );                   // BCD číslo
  3.   qryCategories.Edit;                    // vloženie do tabuľky
  4.   qryCategories.Fields[ 0 ].AsBCD := x2; // vloženie do tabuľky
  5.   qryCategories.Post;                    // vloženie do tabuľky
  6.   y2 := qryCategories.Fields[ 0 ].AsBCD; // vloženie do tabuľky
  7.   x := y2 - 1;                           // VYBRATIE z tabuľky + matem operácia
  8.   s3  := BcdToStr( x );                  // koprektný výsledok
  9. //    '-12345678901234567891,12345678901234567890123456789012345678901'
Zdá sa, že SQLite + FireDAC dokážu manažovať BCD
Problém je zatiaľ len s vkladaním pomocou SQL - INSERT INTO
Z kódu Delphi sa BCD čísla vložené a vybrané z tabuľky správajú korektne

Problém je zrejme vo FormatSettings, pretože vo Windows mám decimal separator ako čiarku.

ČIASTKOVÝ ZÁVER: BCD je v SQLite vďaka FireDAC použiteľné bez okľuky cez TEXT, či BLOB.
Funguje typ NUMERIC. Zatiaľ vidím jediný väčší problém: napĺňanie BCD cez INSERT INTO..

Apropos: operácie v databáze sa zrejme prevádzajú na Double, pretože matemat operácie dajú presnosť len v rámci Double.
To by vyzeralo na dodatočné UDF funkcie..


« Poslední změna: 19-07-2018, 14:54:08 od Miroslav Baláž »

Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 1027
  • Karma: 47
    • Verze Delphi: 10.3
Re:SQLite a TBCD prakticky + Problém
« Odpověď #1 kdy: 20-07-2018, 10:52:09 »
A nemá se v tom INSERT použít desetinná tečka?

100749

  • Host
Re:SQLite a TBCD prakticky + Problém
« Odpověď #2 kdy: 20-07-2018, 11:34:58 »
Pôvodne ma vystrašila info z odkazu:
http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Using_SQLite_with_FireDAC#High-Precision_Numbers

Duvod je na miste. Manual rika, ze pro takto precizni hodnoty mas pouzit datovy typ TEXT a mapovani datovych typu FireDAC. SQLite tak precizni hodnotu do datoveho typu REAL neulozi.

Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 1027
  • Karma: 47
    • Verze Delphi: 10.3
Re:SQLite a TBCD prakticky + Problém
« Odpověď #3 kdy: 20-07-2018, 11:51:12 »
Jinak hlodá ve mně zvědavost, z jakého měření padají tak přesná čísla, protože i double by uměl uložit vzdálenost ze Země k Alfa Centauri s přesností na 1 cm 10 m :-)

Offline miroB

  • Guru
  • *****
  • Příspěvků: 585
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:SQLite a TBCD prakticky + Problém
« Odpověď #4 kdy: 20-07-2018, 15:17:10 »
A nemá se v tom INSERT použít desetinná tečka?
S bodkou to nefungovalo vôbec. S čiarkou tak, ako som napísal.
Celkom verím, že sa vo vnútri procesov bije čiarka s bodkou.
Zrejme ak by som mal Win-y nastavene na US, prebehlo by všetko OK. Preskúmam neskôr.
Pri pokuse s čiarkou dáva Delphi túto hlášku:
'-12345678901234567890.12345678901234567890123456789012 is not a valid BCD value'.
Ak zamením bodku za čiarku, je to OK.
Niekde som tu videl tému, kde sa rieši nastavenie FormatSettings, aby Delphi bralo od štartu generálne bodku ako oddelovač. Alebo použiť funkciu
TryStrToBcd(AValue, Result, FormatSettings)

Pôvodne ma vystrašila info z odkazu:
http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Using_SQLite_with_FireDAC#High-Precision_Numbers

Duvod je na miste. Manual rika, ze pro takto precizni hodnoty mas pouzit datovy typ TEXT a mapovani datovych typu FireDAC. SQLite tak precizni hodnotu do datoveho typu REAL neulozi.
Vtip je v tom, že ten Numeric funguje. Je pravdepodobné, že FireDAC interne prehodí typ na blob, alebo text. Funguje nad tým napríklad aj agregačná funkcia SUM. Ibaže zobrazí double. Zatiaľ mi vkladanie funguje len cez Delphi kód. Nie cez SQL. Napríklad pre Numeric( 26,16 ), sa zdá, že pracuje ako s Float/Double.
Ale Numeric( 64,23 ), založí zrejme pole typu Char(34), aspon taky je datasize. Hlási ho však ako ftFMTBcd.

Jinak hlodá ve mně zvědavost, z jakého měření padají tak přesná čísla, protože i double by uměl uložit vzdálenost ze Země k Alfa Centauri s přesností na 1 cm 10 m :-)
Teraz je to ako hobby. Ale plánujem to rozbehnúť. Už viackrát som narazil na problém s nepresnosťou pri sčítaní veľkého množstva údajov. V rámci rôznych štatistík.
Najmä ak ide o rôzne postupy, tak sa výsledky nezhodovali presne.
V bankách furt mali problém s presnosťou. To som však zažil (ako pozorovateľ), pred rokmi, teraz nie:)
PS: Ak by riadne fungoval Extended aj pod 64 bit kódom, ani by ma nenapadlo riešiť nejaké BCD.
Tie obrovské čísla, čo som prezentoval, išli už len pre zaujímavosť až na hranicu toho, čo zvláda BCD.



« Poslední změna: 20-07-2018, 15:48:56 od Miroslav Baláž »

100752

  • Host
Re:SQLite a TBCD prakticky + Problém
« Odpověď #5 kdy: 20-07-2018, 15:58:41 »
Vtip je v tom, že ten Numeric funguje. Je pravdepodobné, že FireDAC interne prehodí typ na blob, alebo text. Funguje nad tým napríklad aj agregačná funkcia SUM. Ibaže zobrazí double. Zatiaľ mi vkladanie funguje len cez Delphi kód. Nie cez SQL. Napríklad pre Numeric( 26,16 ), sa zdá, že pracuje ako s Float/Double.
Ale Numeric( 64,23 ), založí zrejme pole typu Char(34), aspon taky je datasize. Hlási ho však ako ftFMTBcd.

Vecer mrknu na to co se uvnitr deje. Jdu ted vyrobit kure a pak mam nejaky program ;)

Offline miroB

  • Guru
  • *****
  • Příspěvků: 585
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:SQLite a TBCD prakticky + Problém
« Odpověď #6 kdy: 20-07-2018, 23:22:50 »
Vecer mrknu na to co se uvnitr deje..
Rád sa dozviem niečo praktické na túto tému. Alebo zo zákulisia..
« Poslední změna: 20-07-2018, 23:25:53 od Miroslav Baláž »

100767

  • Host
Re:SQLite a TBCD prakticky + Problém
« Odpověď #7 kdy: 21-07-2018, 15:53:59 »
[quote  link=topic=16248.msg100752#msg100752 date=1532095121]
Vecer mrknu na to co se uvnitr deje..
Rád sa dozviem niečo praktické na túto tému. Alebo zo zákulisia..
[/quote]

Kure bylo dobre ;D Omlouvam se za zpozdeni. Lehce se to vcera protahlo ::) :)

Ale k tematu, problem je pravdepodobne ve FireDAC (zatim nevim jaky desetinny separator pouziva SQLite, zda je fixni nebo se jeho nastaveni prebere z operacniho systemu). Tak ci tak, ty do SQLite ukladas text, coz je spravne. FireDAC se pak ridi vychozim datovym typem popsanym SQLite pomoci funkce sqlite3_column_type, coz se deje v metode TSQLiteColumn.InternalGetData.

Pokud ulozis hodnotu s desetinnou teckou, pak SQLite popise datovy typ jako SQLITE_FLOAT. Pokud s desetinnou carkou, pak jako SQLITE_TEXT (tak se to alespon deje na mem stroji s nastavenou desetinnou teckou, zatim nevim zda se tim SQLite ridi).

Problem je v tom, ze pak FireDAC v metode TSQLiteValue.GetData pouzije konverzi na zaklade takto popsaneho datoveho typu (zjednodusene):

Kód: Delphi [Vybrat]
  1. etNumber:
  2. begin
  3.   case iType of // iType je SQLITE_TEXT s desetinnou carkou, SQLITE_FLOAT s desetinnou teckou
  4.     SQLITE_TEXT: FDStr2BCD(PChar(pVal), iSize, PBcd(AValue)^, '.'); // pracuje se s teckou, byt by se sloupec s hodnotou pouzivajici tecku popsal jako SQLITE_FLOAT
  5.     SQLITE_FLOAT: FDDouble2BCD(PDouble(pVal)^, PBcd(AValue)^); // tady prijdes o preciznost
  6.   end;
  7. end;

K tomu, aby FireDAC mohl prevest hodnotu datoveho typu popsaneho SQLite jako SQLITE_TEXT na BCD vsak v tomto pripade potrebuje pracovat s desetinnou carkou, ne teckou. Cili tohle by mohla byt rychla oprava (zatim nevim o moznych negativnich nasledcich, avsak po vcerejsi davce vina teprve prozkoumavam svet ;D):

Kód: Delphi [Vybrat]
  1. etNumber:
  2. begin
  3.   case iType of // iType je SQLITE_TEXT s desetinnou carkou, SQLITE_FLOAT s desetinnou teckou
  4.     SQLITE_TEXT: FDStr2BCD(PChar(pVal), iSize, PBcd(AValue)^, ','); // ted se pracuje s carkou jako desetinnym oddelovacem
  5.     SQLITE_FLOAT: FDDouble2BCD(PDouble(pVal)^, PBcd(AValue)^);
  6.   end;
  7. end;

Podivam se na to jakym zpusobem se ridi SQLite pri popisu sloupcu a pripadne napisu bug report a zpetnou vazbu sem.
« Poslední změna: 21-07-2018, 16:08:53 od 100767 »

100773

  • Host
Re:SQLite a TBCD prakticky + Problém
« Odpověď #8 kdy: 21-07-2018, 17:56:41 »
Na zaklade zbezneho zkoumani mi prijde ze se SQLite snazi konvertovat textove hodnoty do vhodneho formatu aplikaci affinity funkci applyAffinity jenz interne zkousi prevest textove hodnoty desetinnych cisel pomoci funkce sqlite3AtoF:

Kód: C++ [Vybrat]
  1. /* if decimal point is present */
  2. if( *z=='.' ){
  3. ...
  4. }

To by vysvetlovalo proc SQLite popise sloupec s textovou hodnotou cisla s desetinnou teckou jako SQLITE_FLOAT, zatimco s carkou jako SQLITE_TEXT. Pokud je tomu tak a ulozis textovou hodnotu s desetinnou teckou, pak ji SQLite prevede a ulozi jako SQLITE_FLOAT. Pokud s carkou, jako SQLITE_TEXT.

100774

  • Host
Re:SQLite a TBCD prakticky + Problém
« Odpověď #9 kdy: 21-07-2018, 18:17:27 »
Mohl by sis vsak vytvorit vlastni datovy typ, jenz bude mit affinity TEXT, treba FLOATTEXT a pro nej vytvorit mapovani (kdybys pouzil mapovani primo na TEXT, mohlo by dojit ke kolizi hodnot jenz mely byt ulozeny jako text). Pak muzes pouzit desetinnou tecku, protoze se SQLite nebude snazit o konverzi do SQLITE_FLOAT a FireDAC se stavajicim kodem dokaze prevest SQLITE_TEXT s desetinnou teckou. Tedy napr.:

Kód: Delphi [Vybrat]
  1. var
  2.   Value: TBcd;
  3.   MapRule: TFDMapRule;
  4. begin
  5.   FDQuery1.FormatOptions.OwnMapRules := True;
  6.  
  7.   MapRule := FDQuery1.FormatOptions.MapRules.Add;
  8.   MapRule.TypeMask := 'FLOATTEXT';
  9.   MapRule.TargetDataType := dtFmtBCD;
  10.  
  11.   FDQuery1.SQL.Text :=
  12.     'DROP TABLE IF EXISTS MyTable;' +
  13.     'CREATE TABLE MyTable(TheValue FLOATTEXT);' +
  14.     'INSERT INTO MyTable (TheValue) VALUES (''-12345678901234567890.12345678901234567890123456789012345678901'');' +
  15.     'SELECT TheValue FROM MyTable;';
  16.   FDQuery1.Open;
  17.  
  18.   Value := FDQuery1.Fields[0].AsBCD;
  19.   ...
  20. end;

Me se totiz moc nechce FireDAC nutit aby zacal konvertovat textove hodnoty s desetinnou carkou. SQLite pouziva tecku a asi bych to tak i nechal. Cili s vyse uvedenym se provede konverze pomoci:

Kód: Delphi [Vybrat]
  1. SQLITE_TEXT: FDStr2BCD(PChar(pVal), iSize, PBcd(AValue)^, '.');
« Poslední změna: 21-07-2018, 18:25:12 od 100774 »

100775

  • Host
Re:SQLite a TBCD prakticky + Problém
« Odpověď #10 kdy: 21-07-2018, 19:06:37 »
Jinak ve zkratce k cemu doslo. Tim ze jsi vytvoril sloupec typu NUMERIC jsi naznacil SQLite affinity typu NUMERIC. Pri ulozeni hodnoty s desetinnou carkou ji pak SQLite ulozil jako TEXT (pokud bys pouzil tecku, ulozil by SQLite hodnotu jako NUMERIC, cimz bys ztratil presnost).

Vytvorenim sloupce s affinity TEXT docilis toho ze se SQLite prestane snazit o ukladani hodnot s desetinnou teckou jako NUMERIC a zaroven umoznis FireDAC ji korektne prevest na BCD.

Offline miroB

  • Guru
  • *****
  • Příspěvků: 585
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:SQLite a TBCD prakticky + Problém
« Odpověď #11 kdy: 21-07-2018, 21:31:57 »
Super toto funguje:
Mohl by sis vsak vytvorit vlastni datovy typ, jenz bude mit affinity TEXT, treba FLOATTEXT a pro nej vytvorit mapovani (kdybys pouzil mapovani primo na TEXT, mohlo by dojit ke kolizi hodnot jenz mely byt ulozeny jako text). Pak muzes pouzit desetinnou tecku, protoze se SQLite nebude snazit o konverzi do SQLITE_FLOAT a FireDAC se stavajicim kodem dokaze prevest SQLITE_TEXT s desetinnou teckou. Tedy napr.:

Kód: Delphi [Vybrat]
  1. var
  2.   Value: TBcd;
  3.   MapRule: TFDMapRule;
  4. begin
  5.   FDQuery1.FormatOptions.OwnMapRules := True;
  6.  
  7.   MapRule := FDQuery1.FormatOptions.MapRules.Add;
  8.   MapRule.TypeMask := 'FLOATTEXT';
  9.   MapRule.TargetDataType := dtFmtBCD;
  10.  
  11.   FDQuery1.SQL.Text :=
  12.     'DROP TABLE IF EXISTS MyTable;' +
  13.     'CREATE TABLE MyTable(TheValue FLOATTEXT);' +
  14.     'INSERT INTO MyTable (TheValue) VALUES (''-12345678901234567890.12345678901234567890123456789012345678901'');' +
  15.     'SELECT TheValue FROM MyTable;';
  16.   FDQuery1.Open;
  17.  
  18.   Value := FDQuery1.Fields[0].AsBCD;
  19.   ...
  20. end;
..
Samozrejme potrebujem k tomu vlastné Custom funkcie, pretože so stringami SQLite matiku neovláda.
Tu je príklad riešenia.
Ako vidieť dá desatinná čiarka tam furt otravuje. Ale funkcia funguje:
Kód: Delphi [Vybrat]
  1. // Inicializácia funkcie:
  2. FDSQLiteFunction1.DriverLink := FDPhysSQLiteDriverLink1;
  3. FDSQLiteFunction1.FunctionName := 'Rozdiel';
  4. FDSQLiteFunction1.ArgumentsCount := 2;
  5. FDSQLiteFunction1.OnCalculate := FDSQLiteFunction1Calculate;
  6. FDSQLiteFunction1.Active := True;
  7.  
  8. // Event:
  9. procedure TForm1.FDSQLiteFunction1Calculate(AFunc: TSQLiteFunctionInstance;  AInputs: TSQLiteInputs; AOutput: TSQLiteOutput; var AUserData: TObject);
  10.  
  11.   function custStrToBcd( const s: String ): TBCD;
  12.     var
  13.       p: PChar;
  14.     begin
  15.     p        := StrScan( PChar( s ), '.' );
  16.     if p <> nil then
  17.       begin
  18.       p^     := ',';
  19.       result := StrToBcd( s );
  20.       p^     := '.';
  21.       end
  22.     else
  23.       result := StrToBcd( s );
  24.     end;
  25. begin
  26.   AOutput.AsString := BCDToStr( custStrToBcd( AInputs[ 0 ].AsString ) - custStrToBcd( AInputs[ 1 ].AsString ) );
  27. end;
  28.  
Príslušný SQL:
Kód: MySQL [Vybrat]
  1. DROP TABLE IF EXISTS MyTable;
  2. CREATE TABLE MyTable(TheValue FLOATTEXT);
  3. INSERT INTO MyTable (TheValue) VALUES ( '-12345678901234567890.12345678901234567890123456789012345678901' );
  4. SELECT TheValue, Rozdiel( TheValue, '1' ) AS [Odčítané::Text(64)] FROM MyTable;
Výsledok je korektný. Nula pred desatinnou bodkou sa zamení za jedničku. ( prvý vstup je záporné číslo, druhý kladný, preto odčítanie, aby jednička pribudla)
« Poslední změna: 21-07-2018, 21:36:12 od Miroslav Baláž »

100777

  • Host
Re:SQLite a TBCD prakticky + Problém
« Odpověď #12 kdy: 21-07-2018, 21:43:40 »
Samozrejme potrebujem k tomu vlastné Custom funkcie, pretože so stringami SQLite matiku neovláda.

Ano. Jinak to resit nelze. Pokud bys misto TEXT pouzil NUMERIC, ztratil bys v SQLite presnost. Jinak muzes pouzit:

Kód: MySQL [Vybrat]
  1. SELECT TheValue, Rozdiel(TheValue, 1) FROM MyTable;

A funkci upravit na:

Kód: Delphi [Vybrat]
  1. procedure TForm1.FDSQLiteFunction1Calculate(AFunc: TSQLiteFunctionInstance; AInputs: TSQLiteInputs; AOutput: TSQLiteOutput; var AUserData: TObject);
  2. begin
  3.   AOutput.AsNumber := AInputs[0].AsNumber - AInputs[1].AsNumber;
  4. end;
« Poslední změna: 21-07-2018, 21:46:31 od 100777 »

Offline miroB

  • Guru
  • *****
  • Příspěvků: 585
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:SQLite a TBCD prakticky + Problém
« Odpověď #13 kdy: 21-07-2018, 21:46:23 »
AsNumber z toho spraví double a .. som v háji:)
Bolo by to celé zbytočné. Alebo som niečo prehliadol?
Bohužiaľ Custom funkcie nepodporujú cast AsBCD.

Inak, pozitívne je, že Custom funkcie SQLite databázu extra nespomaľujú.
Naopak ak funkcia dokáže nahradiť zložitejšiu SQL konštrukciu, tak výpočet sa až násobne zrýchli.
Zopár takých funkcií už mám.
« Poslední změna: 21-07-2018, 21:51:09 od Miroslav Baláž »

100779

  • Host
Re:SQLite a TBCD prakticky + Problém
« Odpověď #14 kdy: 21-07-2018, 21:48:45 »
AsNumber z toho spraví double a .. som v háji:)
Bolo by to celé zbytočné. Alebo som niečo prehliadol?
Bohužiaľ custom funkcie nepodporujú cast AsBCD 

Podporuji. Property AsNumber je typu TBcd (s AsFloat bys o ni prisel).

Inak, pozitívne je, že Custom funkcie SQLite databázu extra nespomaľujú.

FireDAC vytvori UDF pomoci API funkce sqlite3_create_function jiz preda ukazatele na callback funkce. Ty pak SQLite vola kdy je treba. Je tam samozrejme nejaky zpomalujici obal kdy se vyvolava udalost v Delphi, ten vsak verim ze je zanedbatelny.
« Poslední změna: 21-07-2018, 21:58:18 od 100779 »

Offline miroB

  • Guru
  • *****
  • Příspěvků: 585
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:SQLite a TBCD prakticky + Problém
« Odpověď #15 kdy: 21-07-2018, 21:59:20 »
Hneď idem vyskúšať..
Vyskúšané !
Funkcia ako taká zbehne. Aj výsledok je matematicky správny, lenže..
Pri vyťahovaní výsledku ( x2 := FDQuery1.Fields[ 1 ].AsBcd; )  príde ku chybovej hláške:

'-12345678901234567891.12345678901234567890123456789012345678901 is not a valid BCD value'
Tá nešťastná bodka - čiarka ako decimal separator stále straší.
Nie je lepšie zabezpečiť pre program, aby sa systém rovno tváril ako US a nie SK/CZ ?

Offline miroB

  • Guru
  • *****
  • Příspěvků: 585
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:SQLite a TBCD prakticky + Problém
« Odpověď #16 kdy: 21-07-2018, 22:08:31 »
Inak toto funguje OK:
Kód: Delphi [Vybrat]
  1. AOutput.AsString := BcdToStr( AInputs[ 0 ].AsNumber - AInputs[ 1 ].AsNumber );
Nie je to ideál, ale myslím, že k nejak výraznému spomaleniu nepríde.

100782

  • Host
Re:SQLite a TBCD prakticky + Problém
« Odpověď #17 kdy: 21-07-2018, 22:31:14 »
Potiz je v tom ze sloupec pro tu UDF funkci v SQL prikazu nepopise datovy typ a vytvori se TStringField. Jeho accessor AsBCD se pak pokusi zkonvertovat retezec s ohledem na nastaveni systemu (a pokud mas carku, dojde k vyjimce). Zkousel jsem datovy typ vynutit:

Kód: MySQL [Vybrat]
  1. SELECT TheValue, Rozdiel(TheValue, 1) AS "x::FLOATTEXT" FROM MyTable;

Nicmene tady se zda ze neprobehne mapovani.
« Poslední změna: 21-07-2018, 22:34:08 od 100782 »

Offline miroB

  • Guru
  • *****
  • Příspěvků: 585
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:SQLite a TBCD prakticky + Problém
« Odpověď #18 kdy: 21-07-2018, 22:43:15 »
Žiadny pozitívny efekt proti predošlému textu. Ale to riešenie, čo som upravil posledne, je nanajvýš uspokojivé.

100784

  • Host
Re:SQLite a TBCD prakticky + Problém
« Odpověď #19 kdy: 21-07-2018, 22:48:34 »
Žiadny pozitívny efekt proti predošlému textu. Ale to riešenie, čo som upravil posledne, je nanajvýš uspokojivé.

Tve reseni je bezpecna cesta, protoze jak Ty, tak AsBCD accessor pouzivate konverze vyuzivajici nastaveni systemu. Vrta mi vsak hlavou ta ignorace mapovani.
« Poslední změna: 21-07-2018, 22:50:19 od 100784 »

Offline miroB

  • Guru
  • *****
  • Příspěvků: 585
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:SQLite a TBCD prakticky + Problém
« Odpověď #20 kdy: 21-07-2018, 23:10:07 »
Už sa teším na budúce aggregate bcdSUM, bcdCOUNT, bcdAVG a aj MIN MAX :)
Ono sa to na prvý pohľad nezdá, ale v double po pár matematických operáciách a zaokrúhleniach sa stráca presnosť.
Nevraviac už o tom, že double ako také nie je možné vzájomne porovnávať.
Ten typ numeric, v rozsahu aspoň 20 číslic by pomohol. Ako presné číslo. Nie ako nejak neurčito zaokrúhlený float. A tá zbytočnosť exponentu u float. Tragédia.
Komu treba 45 000 000 000 000 000 000 000. Ten sprostý exponent je tak akurát dobrý na podobnú somarinu..
Leda tak hypoteticky na IBAN. Lenže tam nejde zase CZ/SK, lebo to už je TEXT
Dobre nech si nechajú aj číslo s exponentom, ale nech k nemu dajú ekvivalent na normálne číslo, čo nemusí byť primitívne zaokrúhlené. To je pre databázy pomerne tragédia..
Preto by mal existovať riadny Numeric / Decimal, ktorý by mal 10-12 bytov a bolo by to prevždy vyriešené. Myslím že to mala už prastará dbf-ka. To zase prišiel na svet medzitým nejaký vedecký mudrlant..
Tu nejde o to, že by SQLite nepodporovala taký rozumný formát.
Odklonili sa od neho aj programovacie jazyky. Aspoň do tej miery, že ak ho majú, tak len ako nejakú neplnohodnotnú a neefektívnu exotiku.
Currerncy by bol kandidát. Ale niekedy je treba viac miest za desatinnou bodkou, než má Currency.
Preto Decimal, napríklad aj taký nenáročný ako Decimal(5, 2). Ale presný.


« Poslední změna: 21-07-2018, 23:21:30 od Miroslav Baláž »

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 5108
  • Karma: 41
    • Verze Delphi: W10 + Delphi 10.4 professional
Re:SQLite a TBCD prakticky + Problém
« Odpověď #21 kdy: 22-07-2018, 09:05:28 »
A rozdeliť to desatinné číslo na dve celé čísla? Neviem, ani len netuším, čo by to znamenalo v praxi pri matematických operáciach
W10 64b, Delphi 10.4, FireBird 3.05
Expert na kladenie nejasne formulovaných otázok.

Offline miroB

  • Guru
  • *****
  • Příspěvků: 585
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:SQLite a TBCD prakticky + Problém
« Odpověď #22 kdy: 22-07-2018, 11:51:36 »
Uvažoval som o tom. 2 x 8 bytov (celkom 16), by nádherne stačilo. Keby to bolo napríklad niečo ako Currency, s presnosťou nie na 4, ale aspoň 10 číslic. Aj keď Currency tuším interne počíta so 6 desatinnými miestami, výsledok, zaokrúhľuje na 4. Tá pevná desatinná časť je smola.
Pre dve čísla v databáze by museli byť dve polia a s tým robiť SQL-y.. To by bolo divné.
Preto by to chcelo odkladať ako record do blobu.
Tiež by to chcelo narobiť špeciálne funkcie. Prvé pole by nieslo info o celých číslach. No a desatinná časť by bolo double.
Mimo databázy ( v Delphi ) by sa ten record uložil do BCD. Základné výpočty BCD zvláda.
Lenže to by bolo treba prekopať FireDAC.

Úplnou lahôdkou pre prácu s číslami je knižnica MPArith 1.37.12 https://sslsites.de/wolfgang-ehrhardt.de/
Používa niečo ako veľké celé číslo a k tomu má info o :  1. exponente a 2. bitprecision.
Knižnica pri voliteľnom rozsahu presnosti, umožní nielen základné matem operácie, ale aj mnohé matematické funkcie.
Pri voľbe presnosti 80 bitov spočíta výsledok s presnosťou 24 čísel.
Presnosť sa dá nastaviť napríklad na 512 bitov.
Max presnosť je však až 120000 bitov !!!
Smola je, že každé číslo tam pozostáva z cca 5 Bigint + reťazec číslic podľa presnosti.
Je to veľká "spotreba" najmä kvôli variabilite tej knižnice.

Z toho nejak odkukať, ako využiť 10 bajtov (fixne, bez možnej zmeny rozsahu ), z toho 9 bajtov pre presnosť 21 číslic   + 1 bajt na mantisu.
Resp poskytovať presnosť 19 číslic a posledné dve zaokrúhliť, ako to robí Currency.
10 bajtov by som v databáze na danú tému vďačne obetoval.

Je to určite menej než berie BCD v kombinácii s textom. Tam je to 34 bajtov. Ak taký text uložím ako UTF16, bude to 68 bajtov.



Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 2837
  • Karma: 134
    • Verze Delphi: D2007, XE3, DX10
Re:SQLite a TBCD prakticky + Problém
« Odpověď #23 kdy: 22-07-2018, 11:52:05 »
To je pre databázy pomerne tragédia..
Preto by mal existovať riadny Numeric / Decimal, ktorý by mal 10-12 bytov a bolo by to prevždy vyriešené. Myslím že to mala už prastará dbf-ka. To zase prišiel na svet medzitým nejaký vedecký mudrlant..
Tu nejde o to, že by SQLite nepodporovala taký rozumný formát.
"Normalni" RDBMS samozrejme radnou podporu nejakeho BigDecimal typu maji. Ale kdyz si zkusis na foru SQLite vyhledat podobne pozadavky jako mas, tak odpovedi je, ze SQLite je LITE...

Offline miroB

  • Guru
  • *****
  • Příspěvků: 585
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:SQLite a TBCD prakticky + Problém
« Odpověď #24 kdy: 22-07-2018, 12:28:23 »
..
"Normalni" RDBMS samozrejme radnou podporu nejakeho BigDecimal typu maji. Ale kdyz si zkusis na foru SQLite vyhledat podobne pozadavky jako mas, tak odpovedi je, ze SQLite je LITE...
To beriem.
Lenže ani Delphi k tomu neposkytuje moc podpory, nielen databáza/SQLite.

Ešte tak BCD. Lenže to je doosť veľké a skôr nejaké kostrbaté:
Ako vidíme, FireDAC pre SQLite poskytuje určité možnosti (BCD). Tam sa ale zase výdatne pletie problém desatinná čiarka vs desatinná bodka.
Je to veru nejaké nedotiahnuté.

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 2837
  • Karma: 134
    • Verze Delphi: D2007, XE3, DX10
Re:SQLite a TBCD prakticky + Problém
« Odpověď #25 kdy: 22-07-2018, 13:22:04 »
Ešte tak BCD. Lenže to je doosť veľké a skôr nejaké kostrbaté:
Ako vidíme, FireDAC pre SQLite poskytuje určité možnosti (BCD). Tam sa ale zase výdatne pletie problém desatinná čiarka vs desatinná bodka.
Je to veru nejaké nedotiahnuté.
Tak desetinna tecka/carka a oddelovace tisicu vyzaduji pozornost vzdycky, protoze ruzni useri mohou mit ruzna narodni prostredi a to dokonce v jednom kulturnim prostredi napr. programator serverovych aplikaci vetsinou miva anglicy OS, naopak cizokrajny manager miva svoje materske prostredi atd. Asi jedinym uspokojivym resenim je udelat striktni rozhrani mezi view a modelem, tam se vyfackovat s ruznym formatem a vnitrne uz pouzivat jenom jeden. U SQLite to lze vynutit asi before triggerem - jakmile ti do pole nekdo strci hodnotu v jinem formatu, tak to fuckovat

100798

  • Host
Re:SQLite a TBCD prakticky + Problém
« Odpověď #26 kdy: 22-07-2018, 16:00:51 »
U SQLite to lze vynutit asi before triggerem - jakmile ti do pole nekdo strci hodnotu v jinem formatu, tak to fuckovat

Do sloupce s affinity TEXT muzes ulozit textovou hodnotu s tim ze se ulozi vzdy tak jak je. Ve FireDAC pak muzes vytvorit mapovani trebas na dtFmtBCD diky cemuz se zacnou tvorit sloupce typu TFMTBCDField kde se s hodnotou v bufferu pracuje spravne (pro textovou hodnotu je vsak treba pouzit desetinnou tecku). Problemem jsou UDF, kde se pro vystup vytvori sloupec typu TStringField do nehoz kdyz sahnes pro hodnotu pres AsBcd accessor, pouzije konverzi na zaklade nastaveni systemu.

Offline miroB

  • Guru
  • *****
  • Příspěvků: 585
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:SQLite a TBCD prakticky + Problém
« Odpověď #27 kdy: 22-07-2018, 16:05:12 »
..
Tak desetinna tecka/carka a oddelovace tisicu vyzaduji pozornost vzdycky, protoze ruzni useri mohou mit ruzna narodni prostredi a to dokonce v jednom kulturnim prostredi napr. programator serverovych aplikaci vetsinou miva anglicy OS, naopak cizokrajny manager miva svoje materske prostredi atd. Asi jedinym uspokojivym resenim je udelat striktni rozhrani mezi view a modelem, tam se vyfackovat s ruznym formatem a vnitrne uz pouzivat jenom jeden. U SQLite to lze vynutit asi before triggerem - jakmile ti do pole nekdo strci hodnotu v jinem formatu, tak to fuckovat
Ten trigger, to je dosť zaujímavý nápad. Nad tým sa ešte zamyslím

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 2837
  • Karma: 134
    • Verze Delphi: D2007, XE3, DX10
Re:SQLite a TBCD prakticky + Problém
« Odpověď #28 kdy: 22-07-2018, 16:53:16 »
pouzije konverzi na zaklade nastaveni systemu.
No ale abych to nemusel resit, tak si triggerem zajistim, ze v DB se do pole TEXT nedostane syntaxe, kterou tam nechci videt a pak v te UDF udelam jen konverzi formatu, kterou videt chci za pouziti sveho TFormatSettings

Offline miroB

  • Guru
  • *****
  • Příspěvků: 585
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:SQLite a TBCD prakticky + Problém
« Odpověď #29 kdy: 22-07-2018, 17:07:30 »
..
Do sloupce s affinity TEXT muzes ulozit textovou hodnotu s tim ze se ulozi vzdy tak jak je. Ve FireDAC pak muzes vytvorit mapovani trebas na dtFmtBCD diky cemuz se zacnou tvorit sloupce typu TFMTBCDField kde se s hodnotou v bufferu pracuje spravne (pro textovou hodnotu je vsak treba pouzit desetinnou tecku). Problemem jsou UDF, kde se pro vystup vytvori sloupec typu TStringField do nehoz kdyz sahnes pro hodnotu pres AsBcd accessor, pouzije konverzi na zaklade nastaveni systemu.
O UDF sa postarám. Tam je pri SELECT-e už nutný typecasting.
Aby som však  nemusel večne typecast-ovať, tak by som chcel spraviť takú úpravu FireDAC, že prefix u názvu poľa by znamenal automaticky nejaký typ.
Ono sú s týmto tvarom "FieldName::Integer" trochu obecnejšie problémy.
Mám predstavu asi takto:
"s_"  : String/Text
"n_"  : Numeric( 30,10 ) - to stačí na vytvorenie tfFmtBcd. Numeric(28, 10) ešte nestačí, to by bol len ftBcd a ten prakticky nemá význam, lebo Double.
a tiež "t_", "d_", "dt_" .. atď
Niečo v tom smere sme už preberali. Myslíš, že mám šancu?
Dokonca Float/Double a Text by nemuseli byť v tom zahrnuté, lebo do nich to spravidla padne
samo.

« Poslední změna: 22-07-2018, 17:13:43 od Miroslav Baláž »

100804

  • Host
Re:SQLite a TBCD prakticky + Problém
« Odpověď #30 kdy: 22-07-2018, 20:46:45 »
Niečo v tom smere sme už preberali. Myslíš, že mám šancu?

O tech prefixech v aliasech sloupcu nevim. Nejspis to byl nekdo jiny. Kazdopadne na uzavreni tohoto tematu mi prijde ze zbyva jedina vec[1]. A to jak popsat sloupec s UDF v SELECT listu. Pri pouziti kalkulacni udalosti:

Kód: Delphi [Vybrat]
  1. procedure TForm1.FDSQLiteFunction1Calculate(AFunc: TSQLiteFunctionInstance; AInputs: TSQLiteInputs; AOutput: TSQLiteOutput; var AUserData: TObject);
  2. begin
  3.   AOutput.AsNumber := AInputs[0].AsNumber - AInputs[1].AsNumber;
  4. end;

se v metode TSQLiteValue.SetData pro parametr AOutput nastavi datovy typ SQLITE_TEXT a hodnota se prevede na text s desetinnou teckou. Problem nastane pri cteni hodnoty accessorem AsBcd (jenz pouziva systemovy format), nebo neco vic?

[1]: Ten zbytek s mapovanim sloupcu vlastnim datovym typem jak jsi psal funguje.

Offline miroB

  • Guru
  • *****
  • Příspěvků: 585
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:SQLite a TBCD prakticky + Problém
« Odpověď #31 kdy: 22-07-2018, 21:22:21 »

Kód: Delphi [Vybrat]
  1. procedure TForm1.FDSQLiteFunction1Calculate(AFunc: TSQLiteFunctionInstance; AInputs: TSQLiteInputs; AOutput: TSQLiteOutput; var AUserData: TObject);
  2. begin
  3.   AOutput.AsNumber := AInputs[0].AsNumber - AInputs[1].AsNumber;
  4. end;

se v metode TSQLiteValue.SetData pro parametr AOutput nastavi datovy typ SQLITE_TEXT a hodnota se prevede na text s desetinnou teckou. Problem nastane pri cteni hodnoty accessorem AsBcd (jenz pouziva systemovy format), nebo neco vic?
..
Tá funkcia/event funguje. Len je treba Castnúť funkciu v rámce SELECT.
Napríklad:
Kód: MySQL [Vybrat]
  1. SELECT Rozdiel(MyField, 5.5) AS [rslt::Numeric(30,0)]
Potom sa dá čítať Field.AsBcd a aj Field.AsAnsiString.
Ak sa necastne, dá sa čítať len Field.AsAnsiString.
Ten cast s použitím '::' má však iné negatívne dôsledky. To by bolo na dlhšie. Preto by som nahradil CAST :: radšej niečím ako:
"AS n_Result", čím by SQLite získal info, že to má castnúť na Numeric(30,0). Vďaka prefixu "n_". Taký prefix zaručene nebude páchať žiadnu ďalšiu škodu, ako je pácha "::"

OK. Zatiaľ mám dosť informácií a námetov.
Toto vlákno je už dlhé a stráca prehľadnosť.
Priebežne teda vykonám nejaké operácie, ktoré zvládnem. Aj na základe vecí popísaných tuná. Až sa to celé niekam rozumne posunie a budem mať viac info, ozvem sa s prípadnými ďalšími otázkami.

100807

  • Host
Re:SQLite a TBCD prakticky + Problém
« Odpověď #32 kdy: 23-07-2018, 10:17:57 »
Tá funkcia/event funguje. Len je treba Castnúť funkciu v rámce SELECT.
Napríklad:
Kód: MySQL [Vybrat]
  1. SELECT Rozdiel(MyField, 5.5) AS [rslt::Numeric(30,0)]
Potom sa dá čítať Field.AsBcd a aj Field.AsAnsiString.
Ak sa necastne, dá sa čítať len Field.AsAnsiString.

Ano, protoze FireDAC s nastavenim AOutput.AsNumber pouzije datovy typ SQLITE_TEXT s konverzi na hodnotu s desetinnou teckou. V datasetu se pak o tom UDF sloupci (bez toho ::DataType hintu) vi pouze to ze je typu SQLITE_TEXT. Tudiz pro nej FireDAC vyrobi TStringField a hodnoty ve storage jsou s desetinnou teckou (a pokud mas system nastaveny jinak nez na tecku, AsBcd selze).

Ty vracis hodnotu pres AOutput.AsString (taktez datoveho typu SQLITE_TEXT) s desetinnym separatorem ze systemu (coz Ti tim padem zfunkcni AsBcd). Na stejny problem jako na zacatku vsak v tomto pripade narazis pokud v SQL prikazu pouzijes typecast typu AS [rslt::Numeric(30,0)] (FireDAC pro takovy sloupec ocekava hodnoty s teckou).

Zaverem, v tomto pripade bych se snazil spis prizpusobit a pro konverzi hodnot techto sloupcu na BCD pouzil fixne desetinnou tecku, tedy vytvoril bych napr. helper (cimz se dostanes k implementaci toho co jsem predtim radil):

Kód: Delphi [Vybrat]
  1. type
  2.   TFieldHelper = class helper for TField
  3.   public
  4.     function AsBCD(const Format: TFormatSettings): TBcd;
  5.   end;
  6.  
  7. implementation
  8.  
  9. { TFieldHelper }
  10.  
  11. function TFieldHelper.AsBCD(const Format: TFormatSettings): TBcd;
  12. begin
  13.   if not (Self is TStringField) then
  14.     Result := GetAsBCD
  15.   else
  16.     Result := StrToBcd(GetAsString, Format);
  17. end;

Pouziti pak:

Kód: Delphi [Vybrat]
  1. var
  2.   Value: TBcd;
  3.   Settings: TFormatSettings;
  4. begin
  5.   Settings := TFormatSettings.Create;
  6.   Settings.DecimalSeparator := '.';
  7.  
  8.   Value := FDQuery1.Fields[0].AsBCD(Settings);
  9. end;


Nakonec, ono by vraceni hodnoty s carkou z te UDF mohlo mit negativni vliv napr. pri ukladani hodnoty do sloupce s affinity NUMERIC.
« Poslední změna: 23-07-2018, 10:32:49 od 100807 »

100808

  • Host
Re:SQLite a TBCD prakticky + Problém
« Odpověď #33 kdy: 23-07-2018, 11:20:03 »
Ostatne pro to, aby ses prizpusobil se priklani i vyuziti bindovanych parametru (resp. vyuziti accessoru AsFMTBCD). Tam se taktez pouzije fixne konverze na hodnotu s desetinnou teckou a datovy typ se nastavi na SQLITE_TEXT.

Cili rada na zaver, pouzivej jako separator vzdy desetinnou tecku a pro nacteni hodnoty z pole pouzij namisto vlastnosti AsBCD vlastni helper kde bude mozne zvolit format nezavisly na systemu (kde pro konverzi textu na BCD pouzijes jako separator tecku).
« Poslední změna: 23-07-2018, 11:41:36 od 100808 »

Offline miroB

  • Guru
  • *****
  • Příspěvků: 585
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:SQLite a TBCD prakticky + Problém
« Odpověď #34 kdy: 23-07-2018, 11:56:52 »
Ďakujem. Ten tebou navrhovaný Helper je riešením.
len som ho musel upraviť, aby sa BCD dali aj zapisovať.
Procedúra "getAsBCD_Local" sa zdá byť zbytočná. Ale inak to neviem vyriešiť.
Kód: Delphi [Vybrat]
  1.   TFieldHelper = class helper for TField
  2.     function getAsBCD_Local: TBcd;
  3.     procedure setAsBCD_Local( const Value: TBcd );
  4.   public
  5.     property AsBCD: TBcd read getAsBCD_Local write setAsBCD_Local;
  6.   end;
a funkcie:
Kód: Delphi [Vybrat]
  1. { TFieldHelper }
  2. // ČÍTANIE
  3. function TFieldHelper.getAsBCD_Local: TBcd;
  4. begin
  5.   if Self is TStringField then
  6.     Result := StrToBcd( GetAsString, fmtSettingsDOT )
  7.   else
  8.     Result := GetAsBCD;
  9. end;
  10. // ZÁPIS
  11. procedure TFieldHelper.setAsBCD_Local( const Value: TBcd );
  12. begin
  13. {  if Self is TStringField then
  14.     SetAsString( Data.FmtBcd.BcdToStr( Value, fmtSettingsDOT ) )
  15.   else}
  16.   SetAsBCD( value );
  17. end;
S tou bodkou máš pravdu, študujem:
FireDAC,Phys.SQLiteWrapper :  FDStr2BCD a FDBCD2Str. Obe natvrdo použijú bodku
Kód: Delphi [Vybrat]
  1. function TSQLiteValue.GetData(var AValue: Pointer; var ASize: Integer;
  2.   AByRef: Boolean = False; AExtDataType: TSQLiteExtDataType = etUnknown): Boolean;
  3. ..
  4. case iType of  // etNumber
  5.         SQLITE_TEXT,
  6.         SQLITE_BLOB:
  7.           FDStr2BCD(PChar(pVal), iSize, PBcd(AValue)^, '.'); // Tu je natvrdo bodka
  8. ..
  9. a
  10. procedure TSQLiteValue.SetData(AValue: Pointer; ASize: Integer;
  11.   AExtDataType: TSQLiteExtDataType = etUnknown);
  12. ..
  13. etNumber:
  14.       begin
  15.         pVal := Database.Buffer.Check(C_FD_MaxFixedSize);
  16.         FDBCD2Str(PChar(pVal), iSize, PBcd(AValue)^, '.');  // Tu je natvrdo bodka
  17.         iType := SQLITE_TEXT;
  18.       end;

100810

  • Host
Re:SQLite a TBCD prakticky + Problém
« Odpověď #35 kdy: 23-07-2018, 12:26:27 »
Neni zac! ;) Prave ty metody TSQLiteValue.GetData a TSQLiteValue.SetData slouzi k predavani dat mezi FireDAC a SQLite (coz je napr. pro cteni hodnot z poli a vstupnich parametru UDF a zapisu hodnot poli a vystupniho parametru UDF). Proto jsem na zaver vyrazil s radou se prizpusobit.

Ohledne navrhovaneho helperu, dej pozor na prekryti puvodni property a na to ze pri pouziti puvodni verze zapisu se (u Tebe) pro TStringField ulozi retezec s carkou, zatimco ctes s teckou. Odkomentuj co mas v TFieldHelper.setAsBCD_Local, jinak budes zapisovat jinak nez cist.

Abych se tedy vyhnul rozbiti kodu jenz ocekava puvodni implementaci AsBCD, pouzil jsem unikatni nazev (AsLocale prefix se muze hodit; muzes si pak vyrobit i dalsi jako AsLocaleDateTime atd.) a pridal moznost specifikace formatu:

Kód: Delphi [Vybrat]
  1. type
  2.   TFieldHelper = class helper for TField
  3.   private
  4.     function GetAsLocaleBCD(const Format: TFormatSettings): TBcd;
  5.     procedure SetAsLocaleBCD(const Format: TFormatSettings; const Value: TBcd);
  6.   public
  7.     property AsLocaleBCD[const Format: TFormatSettings]: TBcd read GetAsLocaleBCD write SetAsLocaleBCD;
  8.   end;
  9.  
  10. implementation
  11.  
  12. { TFieldHelper }
  13.  
  14. function TFieldHelper.GetAsLocaleBCD(const Format: TFormatSettings): TBcd;
  15. begin
  16.   if not (Self is TStringField) then
  17.     Result := GetAsBCD
  18.   else
  19.     Result := StrToBcd(GetAsString, Format);
  20. end;
  21.  
  22. procedure TFieldHelper.SetAsLocaleBCD(const Format: TFormatSettings; const Value: TBcd);
  23. begin
  24.   if not (Self is TStringField) then
  25.     SetAsBCD(Value)
  26.   else
  27.     SetAsString(BcdToStr(Value, Format));
  28. end;

Pouziti:

Kód: Delphi [Vybrat]
  1. var
  2.   Value: TBcd;
  3.   Settings: TFormatSettings;
  4. begin
  5.   Settings := TFormatSettings.Create;
  6.   Settings.DecimalSeparator := '.';
  7.  
  8.   Value := FDQuery1.Fields[0].AsLocaleBCD[Settings];
  9.   ...
  10.   FDQuery1.Fields[0].AsLocaleBCD[Settings] := Value;
  11. end;
« Poslední změna: 23-07-2018, 12:47:06 od 100810 »

Offline miroB

  • Guru
  • *****
  • Příspěvků: 585
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:SQLite a TBCD prakticky + Problém
« Odpověď #36 kdy: 23-07-2018, 12:48:53 »
Nie sú to vlastne chyby vo FireDAC? Veď by to malo všetko fungovať..

A.
Ten helper mi však nedovolí zapísať
Kód: Delphi [Vybrat]
  1. Field.AsBcd='-1234567890.0123456789';// Toto hlási chybu konverzie
  2. // Lebo ide hneď v úvode cez funkciu
  3. class operator TBcd.Implicit(const str: string): TBcd;
  4. begin
  5.   if not TryStrToBcd(str, Result) then
  6.     raise EBcdException.Create(SInvalidInput);
  7. end;
  8. //a v nej to zhavaruje
Existuje aj funkcia "TryStrToBcd(AValue, Bcd, FormatSettings)"
Dá sa spraviť nejaký helper aj na class operator TBcd.Implicit ?
Mätie ma ten class operator. Viem aký má význam, ale prácu s týmto prvkom ozaj nepoznám.

B.
..
- dej pozor na prekryti puvodni property  ???  čo sa stane ak ju prekryjem? Veď o to mi ide nie?
a na to ze
- pri pouziti puvodni verze zapisu se (u Tebe) pro TStringField ulozi retezec s carkou.
V mojej verzii som schválne vynechal :
Kód: Delphi [Vybrat]
  1. {  if Self is TStringField then
  2.     SetAsString( Data.FmtBcd.BcdToStr( Value, fmtSettingsDOT ) )
  3.   else}
Pretože tade to snáď nikdy nepôjde.
Ak zapisujem AsBCD, tak Firedac si pamätá, že zapisujem do poľa TNumericField a nie do TStringField
« Poslední změna: 23-07-2018, 13:16:48 od Miroslav Baláž »

100812

  • Host
Re:SQLite a TBCD prakticky + Problém
« Odpověď #37 kdy: 23-07-2018, 13:17:04 »
- dej pozor na prekryti puvodni property  ???  čo sa stane ak ju prekryjem? Veď o to mi ide nie?

To ja netusim :) Mozna bys narazil na problem kdybys soucasne pracoval i s jinymi (napr. pametovymi) dataset objekty (prip. DBMS).

- pri pouziti puvodni verze zapisu se (u Tebe) pro TStringField ulozi retezec s carkou.
V mojej verzii som schválne vynechal :
Kód: Delphi [Vybrat]
  1. {  if Self is TStringField then
  2.     SetAsString( Data.FmtBcd.BcdToStr( Value, fmtSettingsDOT ) )
  3.   else}
Pretože tade to snáď nikdy nepôjde.
Ak zapisujem AsBCD, tak Firedac si pamätá, že zapisujem do poľa TNumericField a nie do TStringField

On si nic nepamatuje. Jen jsi pouzil mapovani a pravdepodobne ted zapisujes do pole typu TFMTBCDField. Kdyby to pole bylo typu TStringField ulozil bys do nej hodnotu s desetinnou carkou. Zkus vypnout mapovani a nacti zaznam s polem s affinity TEXT (napr. ten navrhovany FLOATTEXT), edituj dataset, zaznam znova nacti a zkus precist hodnotu. Skoncis na "not a valid BCD value" protoze do toho sloupce ulozis carku a snazis se cist s teckou.
« Poslední změna: 23-07-2018, 13:20:10 od 100812 »

Offline miroB

  • Guru
  • *****
  • Příspěvků: 585
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:SQLite a TBCD prakticky + Problém
« Odpověď #38 kdy: 23-07-2018, 13:28:59 »
Jasne, už chápem všetky pripomienky.

Ešte k môjmu bodu A.
TBcd.Implicit: pokúšam sa o record helper for TBcd
Lenže chyba: E2123 PROCEDURE, FUNCTION, PROPERTY, or VAR expected
Teda neviem ako modifikovať class operator Implicit, aby som mu podtsrčil
TryStrToBcd(AValue, Bcd, FormatSettings)
Je to v Data.FmtBcd

Predbežne odhadujem, že to nemá riešenie. Viď SO:
https://stackoverflow.com/questions/30126479/is-there-a-workaround-to-make-class-operators-work-for-built-in-types
« Poslední změna: 23-07-2018, 13:47:45 od Miroslav Baláž »

100814

  • Host
Re:SQLite a TBCD prakticky + Problém
« Odpověď #39 kdy: 23-07-2018, 14:05:09 »
Nie sú to vlastne chyby vo FireDAC? Veď by to malo všetko fungovať..

Nerekl bych. Pro tak presne hodnoty potrebujes pro SQLite sloupec s affinity TEXT.

Pro nej (stejne jako pro sloupec s UDF bez ::DataType hintu) se bez mapovani vytvori sloupec TStringField do nejz se da ulozit jakykoli text protoze neexisuje zadna informace o tom ze bylo umyslem ukladat desetinna cisla. Zapis i cteni textove hodnoty accessorem AsBCD probiha dle lokalniho nastaveni systemu. FireDAC pak ulozi data tak jak byla predana o vrstvu vyse.

S mapovanim se da presvedcit FireDAC, aby pro sloupec s affinity TEXT vytvoril TFMTBCDField. Pres accessor AsBCD se predavaji data v numericke podobe s tim ze je pak FireDAC konvertuje z pripadne do textove podoby za fixniho pouziti tecky jako separatoru.

Cili FireDAC Ti nabizi pro tento pripad pomocnou ruku v podobe mapovani (pro UDF je treba hintu, pripadne jak jsi navrhoval prefixy v nazvech). Sloupec s affinity TEXT je treba pouzit v kazdem pripade.

K tomu bodu A.
TBcd.Implicit. Pokúšam sa o record helper for TBcd
Lenže chyba: E2123 PROCEDURE, FUNCTION, PROPERTY, or VAR expected
Teda neviem ako modifikovať class operator Implicit
Je to v Data.FmtBcd

Mam obavu ze s tim nic nenadelas. Helpery zda se nemaji podporu operatoru.
« Poslední změna: 23-07-2018, 14:06:56 od 100814 »

Offline miroB

  • Guru
  • *****
  • Příspěvků: 585
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:SQLite a TBCD prakticky + Problém
« Odpověď #40 kdy: 23-07-2018, 14:10:52 »
Pretože Record helper TBCD pre "class operator" nie je možný, tento rébus:
Kód: Delphi [Vybrat]
  1. Field.AsBcd='-1234567890.0123456789';
Som vyriešil funkciou:
Kód: Delphi [Vybrat]
  1. procedure TFieldHelper.setAsLocaleBCD( const Value: String );
  2. begin
  3.   if Self is TStringField then
  4.     SetAsString( Value )
  5.   else
  6.     SetAsBCD( StrToBcd( Value, fmtSettingsDOT ) );
  7. end;
  8. ..// VOLANIE:
  9. Fields.setAsLocaleBCD( '-1234567890.0123456789' );
  10.  

Offline miroB

  • Guru
  • *****
  • Příspěvků: 585
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:SQLite a TBCD prakticky + Problém
« Odpověď #41 kdy: 23-07-2018, 14:17:09 »
Nie sú to vlastne chyby vo FireDAC? Veď by to malo všetko fungovať..

Nerekl bych. Pro tak presne hodnoty potrebujes pro SQLite sloupec s affinity TEXT.
No mne sa zdá, že tu boli prezentované štandardné metódy. Tie fungujú napr. pre US nastavenie určite bez problémov. Lenže programovací nástroj by mal rešpektovať aj národné nastavenia.
Alebo by mal umožniť pomocou prítomných parametrov situáciu vyriešiť.
Ak by aj všetko ostatné fungovalo len pre US, tak ani Unicode nie je treba .. Obrazne povedané.

100817

  • Host
Re:SQLite a TBCD prakticky + Problém
« Odpověď #42 kdy: 23-07-2018, 14:44:13 »
No mne sa zdá, že tu boli prezentované štandardné metódy. Tie fungujú napr. pre US nastavenie určite bez problémov. Lenže programovací nástroj by mal rešpektovať aj národné nastavenia.
Alebo by mal umožniť pomocou prítomných parametrov situáciu vyriešiť.
Ak by aj všetko ostatné fungovalo len pre US, tak ani Unicode nie je treba .. Obrazne povedané.

To si vyrid s SQLite ;) Sloupce s numerickou affinity vyzaduji v hodnote tecku (a pokud presahnou rozsah, zaokrouhli se). Proto je treba pouzit sloupec s textovou affinity (coz je zkratka jen text). A bez mapovani jde jen o text o nemz FireDAC nevi nic. S mapovanim (pro UDF hintem) jde porad o text, ale jeho cteni a ukladani probiha v rezii FireDAC (s teckou). No a pokud mu primo v SQL prikazu ulozis hodnotu jinak, tak se na nej nemuzes zlobit (porad je to jen neznamy text, ktery se mapovanim nebo sloupcem dle hintu snazi zkonvertovat).
« Poslední změna: 23-07-2018, 14:48:15 od 100817 »

Offline miroB

  • Guru
  • *****
  • Příspěvků: 585
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:SQLite a TBCD prakticky + Problém
« Odpověď #43 kdy: 23-07-2018, 15:23:43 »
A kto tam strká tú čiarku? Nikto iný než systém. Nie užívateľ. Ani SQLite nie. Neviem prečo si BCD berie niečo zo systému, ak mu to prekáža. BCD je vlastne riešené v Data.FmtBcd, takže nie FireDAC, ale Delphi spôsobuje tieto ťažkosti.
Samozrejme aj pri Double je presne ten istý problém, ale ten sa darí niekde po ceste zakryť, na rozdiel od BCD.

100820

  • Host
Re:SQLite a TBCD prakticky + Problém
« Odpověď #44 kdy: 23-07-2018, 15:46:17 »
A kto tam strká tú čiarku? Nikto iný než systém. Nie užívateľ. Ani SQLite nie. Neviem prečo si BCD berie niečo zo systému, ak mu to prekáža. BCD je vlastne riešené v Data.FmtBcd, takže nie FireDAC, ale Delphi spôsobuje tieto ťažkosti.
Samozrejme aj pri Double je presne ten istý problém, ale ten sa darí niekde po ceste zakryť, na rozdiel od BCD.

Delphi vrstva DB. Ale ona nemuze tusit ze si prave ted prejes konvertovat BCD na text s jinym nez systemovym separatorem. Nevi, ze je o nekolik vrstev nize FireDAC a SQLite jenz potrebuji tecku. Nicmene carku muze vlozit i uzivatel konstantni hodnotou v SQL prikazu.

Ano, chybi moznost ten format specifikovat. A to nejspis accessorem. Nebo pridat do TField separatni format settings (vychozi ze systemu) s tim ze kde by se dalo by se volaly konverze podle nej.
« Poslední změna: 23-07-2018, 16:00:18 od 100820 »