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

Online miroB

  • Guru
  • *****
  • Příspěvků: 540
  • 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ů: 847
  • Karma: 45
    • 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?

Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 847
  • Karma: 45
    • Verze Delphi: 10.3
Re:SQLite a TBCD prakticky + Problém
« Odpověď #2 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 :-)

Online miroB

  • Guru
  • *****
  • Příspěvků: 540
  • 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ěď #3 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áž »

Online miroB

  • Guru
  • *****
  • Příspěvků: 540
  • 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, 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áž »

Online miroB

  • Guru
  • *****
  • Příspěvků: 540
  • 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ěď #5 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áž »

Online miroB

  • Guru
  • *****
  • Příspěvků: 540
  • 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: 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áž »

Online miroB

  • Guru
  • *****
  • Příspěvků: 540
  • 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ěď #7 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 ?

Online miroB

  • Guru
  • *****
  • Příspěvků: 540
  • 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ěď #8 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.

Online miroB

  • Guru
  • *****
  • Příspěvků: 540
  • 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ěď #9 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é.

Online miroB

  • Guru
  • *****
  • Příspěvků: 540
  • 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ěď #10 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ů: 4584
  • Karma: 40
    • Verze Delphi: XE7 professional
Re:SQLite a TBCD prakticky + Problém
« Odpověď #11 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
Delphi XE7, FireBird
Expert na kladenie nejasne formulovaných otázok.

Online miroB

  • Guru
  • *****
  • Příspěvků: 540
  • 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ěď #12 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ů: 2625
  • Karma: 133
    • Verze Delphi: D2007, XE3, DX10
Re:SQLite a TBCD prakticky + Problém
« Odpověď #13 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...

Online miroB

  • Guru
  • *****
  • Příspěvků: 540
  • 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ěď #14 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é.