Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   SQL UPDATE will nicht (https://www.delphipraxis.net/171853-sql-update-will-nicht.html)

Privateer3000 28. Nov 2012 14:39

Datenbank: Firebird • Version: 2.1 • Zugriff über: ZEOS7

SQL UPDATE will nicht
 
Hallöchen,
ich steh grad irgendwie aufm Schlauch:
folgendes update:
Code:
try
    v_ma:='Passwort1';
    v_ta:='Passwort2';
     ug:=TStringList.Create;
     systemquer.Active:=True;
     systemquer.Edit;
      ug.Add('update SYSTEM set MPASSWORT='+v_ma+',TAPASS='+v_ta+' where ID=1');
      sysupdate.ModifySQL:=ug; { TODO 1 -oIch -cSQL : Update vervollständigen }
       try
         systemquer.Post;
         systemquer.Refresh;
       except
         Showmessage('Speichern fehlgeschlagen!');
       end;
  finally
    ug.Free;
    systemquer.Active:=False;
  end;
produziert folgende Fehlermeldung:
Code:
Execute, proto: firebird-2.1, msg: Dynamic SQL Error SQL error code = -206 Column unknown Passwort1 At line 1, column 29, errcode: -206, error: Column does not belong to referenced table The SQL: update SYSTEM set MPASSWORT=Passwort1,TAPASS=Passwort2 where ID=1
Wieso soll Passwort1 "Column unknown" sein? :evil:
Ich hab es auch ohne Update-Kompo probiert (dyn. Query)

Was passiert da?

DeddyH 28. Nov 2012 14:42

AW: SQL UPDATE will nicht
 
Da fehlen die Anführungszeichen, es handelt sich ja wohl um Strings. Wieso benutzt Du keine SQL-Parameter, abgesehen von den Sicherheitsaspekten sind solche Probleme damit auch gleich vom Tisch?

Privateer3000 28. Nov 2012 15:06

AW: SQL UPDATE will nicht
 
Weil ich nicht weiß wie bei der TZUpdateSQL von ZEOS
Parameter übergeben werden.
Params.ParamByName('XY') werden nicht akzeptiert.

Wie diese Parameter eingesetzt werden, hab ich keine Dok gefunden.
Man kan zur Laufzeit wie auch zu Entwicklerzeit diese setzen.
aber wie?
Bei der TZQuery geht das einwandfrei
Code:
with ug do begin
     Close; SQL.Clear;
     SQL.Text:='update SYSTEM set MPASSWORT = :MA,TAPASS = :TA where ID=1';
      ParamByName('MA').Value:=v_ma;
      ParamByName('TA').Value:=v_ta;
       try
         ExecSQL;
       except
         Showmessage('Speichern fehlgeschlagen!');
       end;
      end;
das funktioniert auch.

mkinzler 28. Nov 2012 15:09

AW: SQL UPDATE will nicht
 
Die Parameter werden automatisch mit den Werten, welche du in die Felder schreibst, belegt.

Privateer3000 28. Nov 2012 15:13

AW: SQL UPDATE will nicht
 
Zitat:

Zitat von mkinzler (Beitrag 1193441)
Die Parameter werden automatisch mit den Werten, welche du in die Felder schreibst, belegt.

Sondern?
Ich seh da einfach nicht durch.
Die Doks zu ZEOS sind so lückenhaft.
Wie muss denn die Paramter-gedingse zur Laufzeit denn aussehen?
:roll::roll:

DeddyH 28. Nov 2012 15:14

AW: SQL UPDATE will nicht
 
Mit ZEOS kenne ich mich nicht aus, aber laut Tutorial im DT soll es auch beim TZUpdateSQL Parameter geben.

mkinzler 28. Nov 2012 15:16

AW: SQL UPDATE will nicht
 
Delphi-Quellcode:
DataSet.Edit;
DataSet.FieldByName(<Feld>).Value := <Wert>;
->
Entsprechender Wert in Parameter des UpdateSQL wird gesetzt.
Delphi-Quellcode:
DataSet.Post;
->
Delphi-Quellcode:
UpdateSQL.ExecuteSQL;
wird ausgeführt

http://www.delphi-treff.de/tutorials...d/tzupdatesql/

Lemmy 28. Nov 2012 15:23

AW: SQL UPDATE will nicht
 
Was dir Markus sagen will: Verwende TUpdateSQL nicht direkt sondern immer in Verbindung mit einer TQUery. Dort solltest DU im Objektinspektor die Möglichkeit haben eine TUpdateSQL-Instanz zu hinterlegen.

WEnn Du nun in der TQuery deinen Select hast (Select * from X where ID=:ID) und in der TUpdateSQL die dazugehörigen DML-Befehle (die ggf. auch automatisch anhand der Select erzeugt werden), dann arbeitest Du ganz einfach mit der Query:

Delphi-Quellcode:
  Query.OPen;
  Query.Update;
  Query.FieldByName('eins').AsString := 'eins';
  Query.Post;
und die Query und TUpdateSQL übernehmen den REst.

p80286 28. Nov 2012 15:32

AW: SQL UPDATE will nicht
 
Abgesehen von dem bisher geschriebenen,
was haben systemquer und sysupdate miteinander zu tun?

Delphi-Quellcode:
try
  v_ma:='Passwort1';
  v_ta:='Passwort2';
  ug:=TStringList.Create;
  systemquer.Active:=True;
  systemquer.Edit;
  ug.Add('update SYSTEM set MPASSWORT='+v_ma+',TAPASS='+v_ta+' where ID=1');

  sysupdate.ModifySQL:=ug; { TODO 1 -oIch -cSQL : Update vervollständigen }

  try
    systemquer.Post;
    systemquer.Refresh;
  except
     Showmessage('Speichern fehlgeschlagen!');
  end;
  finally
    ug.Free;
    systemquer.Active:=False;
  end;
Gruß
K-H

Privateer3000 28. Nov 2012 17:26

AW: SQL UPDATE will nicht
 
Vielen Dank für Eure Antworten!

sysupdate ist eine TZUpdateSQL und bammelt
an der systemquer (TZQuery) dran.
Das funktioniert normalerweise.
Nur das Parameterdingenskirchen verstehe ich nicht.
Ich werde die Posts mal durchgehen.

Privateer3000 28. Nov 2012 21:16

AW: SQL UPDATE will nicht
 
[QUOTE=mkinzler;1193446]
Delphi-Quellcode:
DataSet.Edit;
DataSet.FieldByName(<Feld>).Value := <Wert>;
->
Entsprechender Wert in Parameter des UpdateSQL wird gesetzt.
Delphi-Quellcode:
DataSet.Post;
->
Delphi-Quellcode:
UpdateSQL.ExecuteSQL;
wird ausgeführt

Hallo MArkus,
ich kenne das Tut von Delphi-Treff.
Es ist nur irgendwie verwirrend.
Code:
UpdateSQL.ModifySql: UPDATE namenSET   recno = :RecNo,      name = :nameWHERE recno = :old_recno
Ich habe die Paras auch w.o. mit Doppelpunkt gekennzeichnet.
Die Updatekomponente hab ich auch schon im Gebrauch, nur halt ohne Paras.
Weil ich es nicht verstehe.
Wie sollte der nachstehende (funktionierende) Source richtig aussehen:
Code:
 
vquery.Edit;
  sqltext1:=TStringList.Create;
  sqltext1.Add('UPDATE tabelle SET NEU='+IntToStr(1)+' WHERE SP1='+IntToStr(salt1)+' AND SP2='+IntToStr(salt2));
  vupdate.ModifySQL:=sqltext1;
vquery.Post;
vquery : TZQuery
vupdate: TZUpdateSQL

EgonHugeist 28. Nov 2012 22:23

AW: SQL UPDATE will nicht
 
@Privateer3000

Ich versuchs dir mal zu erklären, wie die Komponenten arbeiten.

Im Grundsatz stellt sie nichts weiter dar als einen Holder für drei verschieden Statements: Inser, Update, Delete.

Aufgabe der Compo ist es den anderen DataSet-Komonenten das erstellen eben dieser benötigten Statements abzunehmen. Deshalb solltest du z.B. die TZQuery.UpdateObject := TZUpdateSQL zuweisen. Die Komponente bietet einen Editor, um eben die drei Statments zusammenzubasteln. (Doppelclick auf jene.) Du kannst dort den Feldnamen einen Parameter zuweisen, wie es im Delphi-Treff beschrieben ist oder schau mal hier: http://sourceforge.net/projects/zeos...hots/95422.jpg manchmal sagt ein Bild mehr als tausend Worte.

Ebenfalls kannst du dort default-values und die Parameter-Typen hinterlegen. Mit Hilfe dieser Komponente legt Zeos den Erfolg der Statments in die Hände der Benutzer. Es gab in der Vergangenheit einfach zu viele Bugreport wegen NOT_NULL Felder oder Feldern mit default-values mit Queries, welch sich über mehrere Tables erstrecken. Benützt man nun queries mit joins, welche sich ins Unendliche Erstrecken können, verweigert Zeos mit den DataSet-Komenten jeglich Art der Updates. Siehe: http://www.delphipraxis.net/171396-m...-more-tha.html ! Einfach weil das Parsen solcher Queries nun nicht wirklich Aufgabe von Zeos ist, Geschwindigkeiten verlohren gehen und vielmehr die der Datenbank und Zeos für jeden Provider extra Anpassungen bräuchte und sich die Frage stellt: wo anfangen und wo aufhöhren.

Zurück zum Thema:

Also du hast die TZDataSet-Komponente mit der TZUpdateSQL-Kompo verknüpft und die drei nötigen Statements generiert.

Die Komponente ist jedoch NICHT für das ausführen der Statements oder zuweisen der Parameter-Values gedacht, kann es jedoch. Dies tun die TZDataSet-Komponenten für dich. Wie bekannt, kannst du in der TZQuery etc. Felder und auch Parameter zuweisen. Die Felder werden in der Regel von den Controls genutzt. Entspricht ein Feld einem zugewiesenem Parameter nutzt Zeos automatisch den Parameter um die geänderten Daten der Controls dem Parameter zuzuweisen. Jedoch kannst auch du die Parameter selber füllen. JEDOCH die Parameter der TZDataSet-Komponente und nicht die der TZUpdateSQL-Komponente.

Simples Beispiel:

ZUpdateSQL1.ModifySQL := 'update tabelle1 set tabelle1.Feld1 = :Feld1; //funktioniert auch! Feld1 sollte aber im selct der TZQuery entahlten sein.
ZQuery1.UpdateObject := ZUpdateSQL1;
ZQuery1.Edit;
ZQuery1.ParamByName('Feld1'').AsString := 'Test';
ZQuery1.Post;

Ich hoffe dies war etwas verständlicher.

Gruß Michael

Privateer3000 29. Nov 2012 09:16

AW: SQL UPDATE will nicht
 
Vielen Dank Michael,
für deine Erklärungen.
Langsam kommt Licht ins Helle :)
Denn an sich scheint das eine durchaus komfortable Sache zu sein.
Dass der Feldname als Paramter fungiert erschloss sich mir garnicht.
Ich kannte bisher nur im SQL-String übergebene Parameter die angesprochen
werden können.
Diese ZEOS (ehem. BDE?) Komponenten sind halt noch neu für mich
das ich bisher nur mit ADO handtiert hatte.

Vielen Dank nochmals
Grüße

EgonHugeist 29. Nov 2012 21:53

AW: SQL UPDATE will nicht
 
Kein Problem. Leider fehlt mir die Zeit überall eine "sinnvolle" Antwort zu geben, da ich eigentlich nur auf der Suche nach noch offenen Problemen für das Zeos7 Stable-Release 7.0.2 zu Weihnachten/Neujahr bin.

Ob Zeos jehmals irgendwas mit der BDE zu tun hatte kann ich dir auch nicht sagen, wage dieses jedoch zu bezweifeln. Ich nutze Zeos erst das zweite Jahr aktiv und seit fast einem Jahr hab ich da mal angefangen alles wieder auf den neuesten Stand der Dinge zu bringen. Ich denke vielmehr das Zeos ehemals eine Pascal-Protierung der JDBC Klassen gewesen ist und sich dann einfach in eine eigene Richtung entwickelt hat.

Vieleicht würde anderen die vorherige kurze Erklärung auch helfen? Im LCL-Forum mein ich?

Schau ich selber kannte auch die gesamten Möglichkeiten vorher nicht. Jedoch im Zuge der ehrenamtlichen Arbeit am Projekt entdeckt man immer wieder Dinge und Möglichkeiten, welche man stehenden Fußes und mangels Dokumentation kaum ersehen kann. Und Zeos ist mittleweile wieder ein echter Mitstreiter gewurden (nach 3Jahren durchhängens), hat die FPC-SQLDB Komponenten wieder weit abgeschüttelt und steht gegen comerziell hochpreisige Produkte auch nicht wirklich schlecht da, denke ich jedenfalls. Dennoch sollten wir realistisch bleiben, daß eine vollständig Bug-freie und 100% Dokumentierte Version wohl nie möglich sein wird, solange nicht einmal eine Hand voll Leute daran mitarbeiten und nur Fehler behoben werden können, welch nachvollziehbar sind.

Falls Interesse besteht, Zeos7.1.0-alpha ist schon in Arbeit. Sollten irgenwelche Probleme auftauchen, sei so gut und melde dich im Zeos-Forum.

Ich hoffe du kommst nun mit der Komponente und deiner Arbeit zurecht. Oft erschließt sich alles ein wenig klarer, wenn der erste Schritt getan wurde. Und ja das Arbeiten mit der Komponente ist eine tolle Sache und garantiert ein Fehler-freie Arbeit, wenn man erst einmal verstanden hat, wie das Dingenskirchen rockt. :cyclops:

PS: versuche ADO zu vermeiden, da beim Wrappen/anpassen zu ODBC dann zu OLEDB und vice versa 'ne Menge Performance liegen bleibt. Nativer zugriff ist immernoch das schnellste.

Privateer3000 30. Nov 2012 07:28

AW: SQL UPDATE will nicht
 
Zitat:

Zitat von EgonHugeist (Beitrag 1193629)

PS: versuche ADO zu vermeiden, da beim Wrappen/anpassen zu ODBC dann zu OLEDB und vice versa 'ne Menge Performance liegen bleibt. Nativer zugriff ist immernoch das schnellste.

Das war auch ein Grund warum ich mich von ADO verabschieded hatte, sowie die Multiuser Problematik.
Nebenher muss ich mit Filemaker arbeiten, was zugegebenermaßen ganz lustig ist.

Vielen Dank Michael für deine ausführliche Antwort. Es tauchen immer neue Fragen auf je mehr man sich damit Beschäftigt.
zB TZStoredProc und TZQuery. Was ist der tatsächliche Unterschied? Beide geben Ergebnismengen zurück und führen Inserts aus.
Bis auf ein unnötiges Prepare, sehe ich keinen Unterschied.

Das ZEOS nichts nativ mit BDE zu tun hat, ist mir bekannt. Es ging mir um parallele Eigenschaften mit BDE.
Ich hab mal ne Zeit mit BDE gearbeitet, ich fands zum Haareraufen.

Sollte ich irgendwann Fehler entdecken, die nicht meiner Unfähigkeit entspringen :) werde ich das im ZEOS Forum posten.
Aber bis dahin wird noch einiges an Wasser den Niagara herunterplätschern.
Grüße

p80286 30. Nov 2012 09:27

AW: SQL UPDATE will nicht
 
Zitat:

Zitat von EgonHugeist (Beitrag 1193629)
PS: versuche ADO zu vermeiden, da beim Wrappen/anpassen zu ODBC dann zu OLEDB und vice versa 'ne Menge Performance liegen bleibt. Nativer zugriff ist immernoch das schnellste.

Da muß ich leider widersprechen. Dank genialer Treiber und meiner eigenen Genialität, nicht zu vergessen einiger ebenso genialer Tips aus diesem Forum, ist es mir gelungen, DB-Zugriffe zu realisieren, die originären Zugriffen nicht nachstehen.

Zumindestens das Oracle-Original ist nicht besser.

Aber wie oben schon zart angedeutet, das liegt nur an meiner Genialität.

Gruß
K-H

EgonHugeist 30. Nov 2012 20:22

AW: SQL UPDATE will nicht
 
@privateer3000

Neue Frage, neuer Thread.
Kurze Andeutung: Ja es gibt da Unterschiede, obwohl TZStoredProc ein TZDataSet descendant ist. Ob du jene Unterschiede mit FireBird ermessen kannst, glaub ich nicht. Jedoch sieht's da bei anderen Anbietern schon ganz anders aus.

Privateer3000 1. Dez 2012 20:34

AW: SQL UPDATE will nicht
 
Zitat:

Zitat von EgonHugeist (Beitrag 1193499)
@Privateer3000

Simples Beispiel:

ZUpdateSQL1.ModifySQL := 'update tabelle1 set tabelle1.Feld1 = :Feld1; //funktioniert auch! Feld1 sollte aber im selct der TZQuery entahlten sein.
ZQuery1.UpdateObject := ZUpdateSQL1;
ZQuery1.Edit;
ZQuery1.ParamByName('Feld1'').AsString := 'Test';
ZQuery1.Post;

Ich hoffe dies war etwas verständlicher.

Gruß Michael

Ich nehm das nochmal auf:
bedeutet das, die where Klausel wird von dem Query übernommen?!
denn ich muss ja die Zeile festelegen können wo das Update gemacht wird...

EgonHugeist 1. Dez 2012 23:59

AW: SQL UPDATE will nicht
 
Sorry, ging nicht aus meinem quick and dirty code vor.

Nein für die where clausel mußt du selbstverständlich auch sorgen, da die drei update Statements unabhängig sind.

Das sollte sich beim öffnen des Editors eigentlich auch von selbst ergeben (hoffe ich).

Gruß Michael

Privateer3000 4. Dez 2012 04:32

AW: SQL UPDATE will nicht
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von EgonHugeist (Beitrag 1193950)
Das sollte sich beim öffnen des Editors eigentlich auch von selbst ergeben (hoffe ich).

Gruß Michael

äh, nö :)

EgonHugeist 4. Dez 2012 06:40

AW: SQL UPDATE will nicht
 
:?:Grad mal selber gecheckt, et voila: Oija, doch doch :P Funzt wunderbar. :o

Gugst du: :cyclops:
Zitat:

Die Komponente bietet einen Editor, um eben die drei Statments zusammenzubasteln. (Doppelclick auf jene.)
Doppelclick auf die KOMPONENTE welche sich auf dem Formular befindet. Ist ja klar, wenn du auf die TStrings-prop clickst, das da ja der standart StringListen-Editor öffnet.

Grüßle

Privateer3000 4. Dez 2012 18:29

AW: SQL UPDATE will nicht
 
Zitat:

Zitat von EgonHugeist (Beitrag 1194217)

Doppelclick auf die KOMPONENTE welche sich auf dem Formular befindet. Ist ja klar, wenn du auf die TStrings-prop clickst, das da ja der standart StringListen-Editor öffnet.

Grüßle

Krass
ich sollte mal auf alles doppelklicken was mir übern Weg hoppelt.
Danke für den Tipp!

Grüße

Laaeris 3. Okt 2013 20:30

AW: SQL UPDATE will nicht
 
Zitat:

Zitat von EgonHugeist (Beitrag 1193499)
@Privateer3000

[...SNIP...]
Simples Beispiel:

ZUpdateSQL1.ModifySQL := 'update tabelle1 set tabelle1.Feld1 = :Feld1; //funktioniert auch! Feld1 sollte aber im selct der TZQuery entahlten sein.
ZQuery1.UpdateObject := ZUpdateSQL1;
ZQuery1.Edit;
ZQuery1.ParamByName('Feld1'').AsString := 'Test';
ZQuery1.Post;

Ich hoffe dies war etwas verständlicher.

Gruß Michael

Ich habe jetzt stundenlang alles Mögliche probiert und auch im Internet gesucht, aber so richtig kriege ich es nicht hin.
Ich mag einfach eine beliebige Tabelle ändern wollen (baue gerade eine immense Applikation um und mag nicht für jede Tabelle etwas Eigenes bauen an u-i-d) und hatte die Idee, den U-Fall über eine Query zu tun.
Nutze Delphi XE2 Starter, mySQL 5.5, Zeos 7

Wie mache ich das am Schlauesten? Mein bisheriger Code funktioniert nicht, egal wie ich es anstelle.
Mein momentaner Code ist wieder möglichst einfach und orientiert an Deinem Beispiel:

Delphi-Quellcode:
  //DataModul.ZQuery_Updater.SQL.Clear;
  //DataModul.ZQuery_Updater.SQL.Add('Select * from '+Schema+Tabellenname);
  DataModul.ZUpdateSQL_Updater.ModifySQL.Clear;
  DataModul.ZUpdateSQL_Updater.ModifySQL.Add('UPDATE '+Schema+Tabellenname);
  DataModul.ZUpdateSQL_Updater.ModifySQL.Add('SET '+Schema+Tabellenname+'.'+Feldname+'= :Val1');
  DataModul.ZUpdateSQL_Updater.ModifySQL.Add('WHERE '+Schema+Tabellenname+'.'+Indexfeld+'=:'+CHR(39)+inttostr(ID_Wert)+CHR(39)+';');
  DataModul.ZQuery_Updater.UpdateObject:=DataModul.ZUpdateSQL_Updater;
  DataModul.ZQuery_Updater.Edit;
  DataModul.ZQuery_Updater.Params.ParamByName('Val1').AsString:=neuer_Wert;
  DataModul.ZQuery_Updater.Post;
a) Wenn ich nun starte, ist die Fehlermeldung sinngemäss, dass die Komponente nicht aktiv ist.
b) Aktiviere ich sie vorher mit ACTIVE oder OPEN, heisst es, der SQL-Code wäre leer.
c) Fülle ich den SQL-Code (siehe die beiden ersten Zeilen) wird es zum einen sehr langsam, zum anderen findet er den Parameter nicht,
obwohl ich den in beiden Komponenten auch mal deklariert habe.
d) Ich habe es mit EXECSQL und dem obigen Code einfach in der Query (ohne ZUpdateSQL) auch schon geschafft, dass das fehlerfrei durchläuft, aber dann wird der Wert in der DB nicht geändert - trotz Autocommit TRUE der Connection.

Was mache ich falsch oder:

Gibt es ein einfaches Beispiel, das ich nutzen kann, bei dem ich mit einer Komponente Query eine Tabelle ändern kann, bei der ich einen Indexfeldwert habe und einen Wertfeldnamen (und einen Wert)?

Viele Grüße,

David

Laaeris 3. Okt 2013 20:38

AW: SQL UPDATE will nicht
 
...manchmal ist es so einfach... ich hatte da einen Doppelpunkt zuviel, und beim posten hab ich's gemerkt. :-)

Ich nutze jetzt erst mal das Folgende:

Delphi-Quellcode:
  DataModul.ZQuery_Updater.SQL.Clear;
  DataModul.ZQuery_Updater.SQL.Add('UPDATE '+Schema+Tabellenname);
  DataModul.ZQuery_Updater.SQL.Add('SET '+Schema+Tabellenname+'.'+Feldname+'= :Val1');
  DataModul.ZQuery_Updater.SQL.Add('WHERE '+Schema+Tabellenname+'.'+Indexfeld+'='+CHR(39)+inttostr(ID_Wert)+CHR(39)+';');
  DataModul.ZQuery_Updater.Params.ParamByName('Val1').AsString:=neuer_Wert;
  DataModul.ZQuery_Updater.ExecSQL;
Viele Grüße & Sorry,

David

DeddyH 4. Okt 2013 08:42

AW: SQL UPDATE will nicht
 
Wieso nutzt Du einmal SQL-Parameter und einmal nicht? Wie ist es so (ungetestet)?
Delphi-Quellcode:
const
  UPDATE_SQL = 'UPDATE %s%s SET %s%s.%s = :Val1 WHERE %s%s.%s = :Val2';
begin
  DataModul.ZQuery_Updater.SQL.Text := Format(UPDATE_SQL,
    [Schema,
     Tabellenname,
     Schema,
     Tabellenname,
     Feldname,
     Schema,
     Tabellenname,
     Indexfeld]);
  DataModul.ZQuery_Updater.Params.ParamByName('Val1').Value := neuer_Wert;
  DataModul.ZQuery_Updater.Params.ParamByName('Val2').Value := ID_Wert;
  DataModul.ZQuery_Updater.ExecSQL;
end;


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:06 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz