Erstellen von Tabellen

Für Themen zu Datenbanken und Zugriff auf diese. Auch für Datenbankkomponenten.
paweld
Beiträge: 91
Registriert: So 11. Jun 2023, 16:01
OS, Lazarus, FPC: Lazarus trunk, FPC fixes

Re: Erstellen von Tabellen

Beitrag von paweld »

af0815 hat geschrieben: So 6. Okt 2024, 13:17 Das ist sehr stark SQl-Dialekt abhängig. MS-SQL hat da wiederum nichts vergleichbares. Wenn man es relativ unabhängig von einem speziellen SQl-Server machen will, sollte man es vermeiden.
Dies kann in MSSQL auf folgende Weise geschehen

Code: Alles auswählen

SQLQuery1.SQL.Text:='insert into tablexy (feld1, feld2) values (1, 2) select scope_identuity()';
SQLQuery1.SQL.Open;
ShowMessage('ID: ' + SQLQuery1.Fields[0].AsString);
Grüße / Pozdrawiam
paweld

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6848
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Erstellen von Tabellen

Beitrag von af0815 »

Vergleichbares bezog sich auf gleiche Syntax. Ja es lässt sich vermutlich auf jedem DB System irgendwie abbilden, nur ist die Syntax sehr DB-System abhängig. Frei nach dem Motto-wehe der Kunde wechselt die Systeme.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

wennerer
Beiträge: 609
Registriert: Di 19. Mai 2015, 20:05
OS, Lazarus, FPC: Linux Mint 20 Cinnamon,Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-linux-
CPU-Target: x86_64-linux-gtk2

Re: Erstellen von Tabellen

Beitrag von wennerer »

Hallo,
ich bräuchte nochmal Hilfe zum Thema not null.
Zunächst habe ich im DB Browser eine ganz einfache Tabelle angelegt:
TabPerson.png
TabPerson.png (18.27 KiB) 3007 mal betrachtet
und das Feld Name auf Not Null gesetz. In Lazarus habe ich ein Miniprogramm geschrieben das nur die Caption eines Editeingabefeldes in die Datenbanktabelle einlest.

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
begin
 SQLite3Connection1.Open;
 SQLTransaction1.Active := True;

 SQLQuery1.SQL.Text := 'Insert into TabPerson (Name) values (:Name)';
 SQLQuery1.Params.ParamByName('Name').AsString := Edit1.Text;
 SQLQuery1.ExecSQL;

 SQLTransaction1.Commit;
end;                 
Bei SQLTransaction1.Commit wird meiner Meinung nach in die Datenbanktabelle geschrieben. Aber ein leeres Editfeld entspricht offenbar nicht Null. Ich hab natürlich zu dem Thema etliches im Netz gelesen und denke ich muss irgendwie Definieren welche Zeichenfolge Null entspricht.
Sehe ich das Richtig? Und falls ja wie geht das?

Viele Grüße
Bernd
Dateianhänge
NotNull_Test.zip
(71.29 KiB) 97-mal heruntergeladen

Sieben
Beiträge: 292
Registriert: Mo 24. Aug 2020, 14:16
OS, Lazarus, FPC: Ubuntu Xenial 32, Lazarus 2.2.0, FPC 3.2.2
CPU-Target: i386

Re: Erstellen von Tabellen

Beitrag von Sieben »

Etwa so:

Code: Alles auswählen

SQLQuery1.SQL.Text := 'Insert into TabPerson (Name) values (:Name)';
if (Edit1.Text = EmptyStr) then
  SQLQuery1.Params.ParamByName('Name').Clear
else   
  SQLQuery1.Params.ParamByName('Name').AsString := Edit1.Text;
SQLQuery1.ExecSQL; 

icho2099
Beiträge: 48
Registriert: Fr 21. Feb 2020, 19:17
OS, Lazarus, FPC: Win10/64
CPU-Target: 64 Bit
Wohnort: Osterholz-Scharmbeck

Re: Erstellen von Tabellen

Beitrag von icho2099 »


Benutzeravatar
Zvoni
Beiträge: 396
Registriert: Fr 5. Jul 2024, 08:26
OS, Lazarus, FPC: Windoof 10 Pro (Laz 2.2.2 FPC 3.2.2)
CPU-Target: 32Bit
Wohnort: BW

Re: Erstellen von Tabellen

Beitrag von Zvoni »

paweld hat geschrieben: So 6. Okt 2024, 13:56
af0815 hat geschrieben: So 6. Okt 2024, 13:17 Das ist sehr stark SQl-Dialekt abhängig. MS-SQL hat da wiederum nichts vergleichbares. Wenn man es relativ unabhängig von einem speziellen SQl-Server machen will, sollte man es vermeiden.
Dies kann in MSSQL auf folgende Weise geschehen

Code: Alles auswählen

SQLQuery1.SQL.Text:='insert into tablexy (feld1, feld2) values (1, 2) select scope_identuity()';
SQLQuery1.SQL.Open;
ShowMessage('ID: ' + SQLQuery1.Fields[0].AsString);
Doofe Frage:
Wieso nicht einfach GetInsertID von TSQLite3Connection benutzen?!?!?!?
Das funktioniert nämlich auch mit ExecSQL.
Es gibt keinen Grund bei SQLite solche Klimmzüge zu machen, um die eingefügte ID zurückzubekommen

Generell: Es sollte jedem klar sein, dass nur die LETZTE eingefügte ID zurückkommt.
Bekanntlich kann man ja MEHRERE Zeilen auf einen Schlag mit nur einem INSERT-Statement in eine Tabelle einfügen
Ein System sie alle zu knechten, ein Code sie alle zu finden,
Eine IDE sie ins Dunkel zu treiben, und an das Framework ewig zu binden,
Im Lande Redmond, wo die Windows drohn.

charlytango
Beiträge: 1087
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz stable (2.2.6, 3.x)
CPU-Target: Win 32/64, Linux64
Wohnort: Wien

Re: Erstellen von Tabellen

Beitrag von charlytango »

Zvoni hat geschrieben: Do 10. Okt 2024, 09:12 Doofe Frage:
Wieso nicht einfach GetInsertID von TSQLite3Connection benutzen?!?!?!?
Das funktioniert nämlich auch mit ExecSQL.
Ja klar geht das mit TSQLite3Connection und auch noch mit TMySQL40Connection und Konsorten, aber nicht mehr mit TSQLConnector.
Und bei ZEOS geht es gar nicht.
Wenn ich mich also nur auf SQLite unter SQLDB beschränke, dann würde es für mich Sinn machen.
Zvoni hat geschrieben: Do 10. Okt 2024, 09:12 Es gibt keinen Grund bei SQLite solche Klimmzüge zu machen, um die eingefügte ID zurückzubekommen
Für mich schon.
Weitgehende Unabhängigkeit von anderen Komponenten die verwendet werden ist mir einfach etwas wert. Eine einfache SQL Query wird mit einiger Sicherheit immer und mit jedem Zugriffspaket. KLar ist das auch immer eine Frage der Balance und der persönlichen Einschätzung. Ich habe einfach nur schon einiges an Geld und Zeit an solche Situationen verloren, das macht vorsichtiger.

In jedem Fall würde ich die Abfrage der ID in eine eigene Funktion stecken, die auch auf den Connectortyp zugreifen kann, um flexibel zu sein.

Anders formuliert:
Obwohl ich weiß, dass es Unterschiede zwischen Datenbanken und Zugriffskomponenten (SQLDB/ZEOS) gibt, versuche ich kompatibel zu bleiben um im Fall des Falles eine Ausweichmöglichkeit zu haben.
Und dann ist da noch die Situation bei der man zb auf eine Netzwerkgestütze Datenbank umstellen mag oder der Kunde einfach sein DBMS wechselt. (was mir natürlich auch schon passiert ist)

Sieht dann bei mir (ohne große Erklärung) etwa so aus:

Code: Alles auswählen

function TSQLConnectParent.RunSqlGetLastID: integer;
var
  sSQL:string;
begin

  {$ifdef USEZEOS}
  if SQLConn.Protocol = cPRO_SQLITE then
    sSQL:='SELECT last_insert_rowid()'
  else if SQLConn.Protocol = 'mssql' then
    sSQL:='SELECT SCOPE_IDENTITY()'
  else
     sSQL:='SELECT LAST_INSERT_ID()';

  {$else}
  if SQLConn.ConnectorType = cPRO_SQLITE then
    sSQL:='SELECT last_insert_rowid()'
  else if SQLConn.ConnectorType = 'mssql' then
    sSQL:='SELECT SCOPE_IDENTITY()'
  else
     sSQL:='SELECT LAST_INSERT_ID()';
  {$endif}

  if RunSQL(sSQL,'RunSqlGetLastID') then
    begin
      if SQLQry.RecordCount=0 then exit;    
      SQLQry.FindFirst;
      result := SQLQry.Fields[0].AsInteger;
    end;
end;
Sollte sich da an den Statements irgend etwas ändern weil die Datenbankhersteller das so wollen oder SQLDB bzw ZEOS gerade mal wieder keinen Maintainer hat oder die Komponente auf links gedreht wird, ist der Aufwand für Fehlersuche und Wartung minimal.

Just my 3 cents....

Benutzeravatar
Zvoni
Beiträge: 396
Registriert: Fr 5. Jul 2024, 08:26
OS, Lazarus, FPC: Windoof 10 Pro (Laz 2.2.2 FPC 3.2.2)
CPU-Target: 32Bit
Wohnort: BW

Re: Erstellen von Tabellen

Beitrag von Zvoni »

charlytango hat geschrieben: Do 10. Okt 2024, 11:39
Zvoni hat geschrieben: Do 10. Okt 2024, 09:12 Doofe Frage:
Wieso nicht einfach GetInsertID von TSQLite3Connection benutzen?!?!?!?
Das funktioniert nämlich auch mit ExecSQL.
Ja klar geht das mit TSQLite3Connection und auch noch mit TMySQL40Connection und Konsorten, aber nicht mehr mit TSQLConnector.
Und bei ZEOS geht es gar nicht.
Wenn ich mich also nur auf SQLite unter SQLDB beschränke, dann würde es für mich Sinn machen.
Zvoni hat geschrieben: Do 10. Okt 2024, 09:12 Es gibt keinen Grund bei SQLite solche Klimmzüge zu machen, um die eingefügte ID zurückzubekommen
Für mich schon.
Weitgehende Unabhängigkeit von anderen Komponenten die verwendet werden ist mir einfach etwas wert. Eine einfache SQL Query wird mit einiger Sicherheit immer und mit jedem Zugriffspaket. KLar ist das auch immer eine Frage der Balance und der persönlichen Einschätzung. Ich habe einfach nur schon einiges an Geld und Zeit an solche Situationen verloren, das macht vorsichtiger.

In jedem Fall würde ich die Abfrage der ID in eine eigene Funktion stecken, die auch auf den Connectortyp zugreifen kann, um flexibel zu sein.

Anders formuliert:
Obwohl ich weiß, dass es Unterschiede zwischen Datenbanken und Zugriffskomponenten (SQLDB/ZEOS) gibt, versuche ich kompatibel zu bleiben um im Fall des Falles eine Ausweichmöglichkeit zu haben.
Und dann ist da noch die Situation bei der man zb auf eine Netzwerkgestütze Datenbank umstellen mag oder der Kunde einfach sein DBMS wechselt. (was mir natürlich auch schon passiert ist)

Sieht dann bei mir (ohne große Erklärung) etwa so aus:

Code: Alles auswählen

function TSQLConnectParent.RunSqlGetLastID: integer;
var
  sSQL:string;
begin

  {$ifdef USEZEOS}
  if SQLConn.Protocol = cPRO_SQLITE then
    sSQL:='SELECT last_insert_rowid()'
  else if SQLConn.Protocol = 'mssql' then
    sSQL:='SELECT SCOPE_IDENTITY()'
  else
     sSQL:='SELECT LAST_INSERT_ID()';

  {$else}
  if SQLConn.ConnectorType = cPRO_SQLITE then
    sSQL:='SELECT last_insert_rowid()'
  else if SQLConn.ConnectorType = 'mssql' then
    sSQL:='SELECT SCOPE_IDENTITY()'
  else
     sSQL:='SELECT LAST_INSERT_ID()';
  {$endif}

  if RunSQL(sSQL,'RunSqlGetLastID') then
    begin
      if SQLQry.RecordCount=0 then exit;    
      SQLQry.FindFirst;
      result := SQLQry.Fields[0].AsInteger;
    end;
end;
Sollte sich da an den Statements irgend etwas ändern weil die Datenbankhersteller das so wollen oder SQLDB bzw ZEOS gerade mal wieder keinen Maintainer hat oder die Komponente auf links gedreht wird, ist der Aufwand für Fehlersuche und Wartung minimal.

Just my 3 cents....
OK, einverstanden. Macht in dem Kontext natürlich Sinn.
Ich für meinen Teil bau die Business-Logik halt anderst auf (SQL-Statements stehen bei mir nicht im Frontend-"Quelltext", sondern selbst in der Datenbank)
Ein System sie alle zu knechten, ein Code sie alle zu finden,
Eine IDE sie ins Dunkel zu treiben, und an das Framework ewig zu binden,
Im Lande Redmond, wo die Windows drohn.

wennerer
Beiträge: 609
Registriert: Di 19. Mai 2015, 20:05
OS, Lazarus, FPC: Linux Mint 20 Cinnamon,Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-linux-
CPU-Target: x86_64-linux-gtk2

Re: Erstellen von Tabellen

Beitrag von wennerer »

Ich danke euch allen für die Beiträge. Ist sehr interessant für mich.
@sieben: ja

Code: Alles auswählen

SQLQuery1.Params.ParamByName('Name').Clear
löst den Fehler aus. Vielen Dank!
exception.png
exception.png (22.98 KiB) 2970 mal betrachtet
Viele Grüße
Bernd

Sieben
Beiträge: 292
Registriert: Mo 24. Aug 2020, 14:16
OS, Lazarus, FPC: Ubuntu Xenial 32, Lazarus 2.2.0, FPC 3.2.2
CPU-Target: i386

Re: Erstellen von Tabellen

Beitrag von Sieben »

TField.Clear macht das Gleiche auf einer anderen Ebene. Beide, TParam wie TField kann man mit IsNull abfragen, ebenso kann man beide auch mit Value := NULL auf 'Nulldiät' setzen. Ich wollte dir noch ein Beispiel aus den DBCtrls zeigen, aber - für mich überraschenderweise - scheinen die einen Leerstring als not Null zu akzeptieren. Daher etwas aus einer eigenen Komponente, die ich gerade überarbeite:

Code: Alles auswählen

procedure TDBPxPicEdit.UpdateData(Sender: TObject);
begin
  FValType := vaRequestPost;
  if (FDatalink.DataSource.State in dsEditModes) then
  try
    if (Text > EmptyStr) then
      FDataLink.Field.Text := Text
    else
      FDataLink.Field.Clear;
  except
    on E:EConvertError do
    begin
      FFailType := ftTypeFail;
      HandleFailure(Text, E.Message, FValType, FFailType);
      SysUtils.Abort;
    end else raise;
  end;
end; 

charlytango
Beiträge: 1087
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz stable (2.2.6, 3.x)
CPU-Target: Win 32/64, Linux64
Wohnort: Wien

Re: Erstellen von Tabellen

Beitrag von charlytango »

wennerer hat geschrieben: Do 10. Okt 2024, 18:56

Code: Alles auswählen

SQLQuery1.Params.ParamByName('Name').Clear
löst den Fehler aus.

exception.png
Warum der Fehler datenbankseitig auftritt scheint ja klar zu sein -- Du dürftest das Feld TabPerson.Name als "Not Null" definiert haben.
Spannend ist f mich dass alleine die Zuweisung bzw Löschung des Parameters eine Exception auslöst.

charlytango
Beiträge: 1087
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz stable (2.2.6, 3.x)
CPU-Target: Win 32/64, Linux64
Wohnort: Wien

Re: Erstellen von Tabellen

Beitrag von charlytango »

Zvoni hat geschrieben: Do 10. Okt 2024, 11:48 Ich für meinen Teil bau die Business-Logik halt anderst auf (SQL-Statements stehen bei mir nicht im Frontend-"Quelltext", sondern selbst in der Datenbank)
Oh... auf die Idee wäre ich jetzt nicht gekommen, hat aber was.
Flexibilität gegen mehr Traffic. Aber ein spannender Ansatz.

Benutzeravatar
Zvoni
Beiträge: 396
Registriert: Fr 5. Jul 2024, 08:26
OS, Lazarus, FPC: Windoof 10 Pro (Laz 2.2.2 FPC 3.2.2)
CPU-Target: 32Bit
Wohnort: BW

Re: Erstellen von Tabellen

Beitrag von Zvoni »

charlytango hat geschrieben: Fr 11. Okt 2024, 10:02
Zvoni hat geschrieben: Do 10. Okt 2024, 11:48 Ich für meinen Teil bau die Business-Logik halt anderst auf (SQL-Statements stehen bei mir nicht im Frontend-"Quelltext", sondern selbst in der Datenbank)
Oh... auf die Idee wäre ich jetzt nicht gekommen, hat aber was.
Flexibilität gegen mehr Traffic. Aber ein spannender Ansatz.
Jepp.
Ich hab im Prinzip im Frontend immer nur ein und dasselbe SQL-Statement
(und natürlich im DB-Backend auch immer diesselbe Tabelle).
Der ID-Parameter ist im Frontend eine lange Liste von Konstanten.
Der Trick dabei ist, dass unten gezeigtes Statement von JEDEM DBMS verstanden wird (Und NEIN, DBF ist kein DB-System :P :P )

Code: Alles auswählen

SELECT SQLStatement FROM tbl_SQL_Statements WHERE ID=:pID;
Heisst: Ich feuer genanntes Statement an die angeschlossene DB, und hole mir DAMIT erst das eigentliche Statement aus der DB selbst im jeweils SPEZIFISCHEN SQL-Dialekt ab.

Vorteil: Ich kann das SQL-Statement selbst in der DB ändern (bsp. MySQL-Workbench, DB Browser for SQLite, SSMS, was auch immer),
SOLANGE das Interface des Statements gleich bleibt --> Bsp. 5 Spalten als Ergebnis mit ihren jeweiligen Datentypen, 2 In-Parameter usw.
Ist mir sogar tatsächlich passiert:
Das ursprüngliche SQL sollte eine Übersicht für unsere Top10-Kunden sein, mit Summe der gelieferten Tonnage
Nach nem Monat kam mein Chef und sagt: "Tonnage ist irgendwie uninteressant. Kannst du stattdessen Summe des Umsatzes machen?"
Antwort: "Kein Problem" --> SQL im Backend geändert. Feuer frei.

Ich musste nicht mein Frontend neu kompilieren!!!

Und ja: ich hab ne Dynamik eingebaut, damit die Spalten-Überschriften entsprechend dann auch geändert wurden. War ein anderes SQL-Statement.....
Ein System sie alle zu knechten, ein Code sie alle zu finden,
Eine IDE sie ins Dunkel zu treiben, und an das Framework ewig zu binden,
Im Lande Redmond, wo die Windows drohn.

Sieben
Beiträge: 292
Registriert: Mo 24. Aug 2020, 14:16
OS, Lazarus, FPC: Ubuntu Xenial 32, Lazarus 2.2.0, FPC 3.2.2
CPU-Target: i386

Re: Erstellen von Tabellen

Beitrag von Sieben »

charlytango hat geschrieben: Fr 11. Okt 2024, 09:36 Spannend ist für mich dass alleine die Zuweisung bzw Löschung des Parameters eine Exception auslöst.
Tut es nicht, die Exception wird erst ausgelöst, wenn man das Statement mit dem NULL-Parameter tatsächlich an die Datenbank schickt. Auch TField.Clear würde erst mit ApplyUpdates eine auslösen.

Benutzeravatar
Zvoni
Beiträge: 396
Registriert: Fr 5. Jul 2024, 08:26
OS, Lazarus, FPC: Windoof 10 Pro (Laz 2.2.2 FPC 3.2.2)
CPU-Target: 32Bit
Wohnort: BW

Re: Erstellen von Tabellen

Beitrag von Zvoni »

Sieben hat geschrieben: Fr 11. Okt 2024, 11:28
charlytango hat geschrieben: Fr 11. Okt 2024, 09:36 Spannend ist für mich dass alleine die Zuweisung bzw Löschung des Parameters eine Exception auslöst.
Tut es nicht, die Exception wird erst ausgelöst, wenn man das Statement mit dem NULL-Parameter tatsächlich an die Datenbank schickt. Auch TField.Clear würde erst mit ApplyUpdates eine auslösen.
Korrekt.
Der Screenshot zeigt eindeutig es ist ein ESQLDatabaseError, und im Kontext des Codes, ist ganz klar der NOT NULL-Constraint für das Feld der Schuldige
Ein System sie alle zu knechten, ein Code sie alle zu finden,
Eine IDE sie ins Dunkel zu treiben, und an das Framework ewig zu binden,
Im Lande Redmond, wo die Windows drohn.

Antworten