Entwickler-Ecke

Datenbanken - Neuen Datensatz anlegen in Firebird Datenbank


SmileySN - So 18.06.06 19:59
Titel: Neuen Datensatz anlegen in Firebird Datenbank
Es kann doch nicht so schwer seine, einen neuen Datensatz anzulegen, oder doch ?

Ich benutze Firebird 1.5 und die Zeos Komponenten.
In meiner DB habe ich die Tabelle Firma mit der PK ID.
Dazu einen Generator und einen Trigger der auf Insert reagiert.

Wenn ich jetzt einen neuen Datensatz anlegen will mache ich das folgendermassen:


Delphi-Quelltext
1:
2:
3:
4:
  
  DM.QFirma.SQL.Clear;
  DM.QFirma.SQL.Add('Insert into Firma (Name,Strasse) Values (''NeuName'',''NeuStrasse'')');
  DM.QFirma.ExecSQL;


Mit diesem Insert Befehl sollte doch der Trigger anspringen und eine eindeutige ID vergeben.
Stattdessen bekomme ich die Meldung:
'no 2 Tables can have duplicate collumn values'

Kann mir mal jemand sagen wie man einen neuen Datensatz anlegt (leer nur mit ID reicht mir schon)


mkinzler - So 18.06.06 20:02

Welchen wert hat der dazugehörige Generator?


SmileySN - So 18.06.06 20:11

Der Generator hat zur Zeit den Wert 13 und das ist auch die ID meines letzten Datensatzes.


mkinzler - So 18.06.06 20:14

Der Generator enthält aber den Wert des nächsten Datensatzes erhöhe den Generator, dann sollte es klappen.


SmileySN - So 18.06.06 20:21

Ok, das hab ich jetzt auch entdeckt, nachdem Du danach gefragt hast, hab den letzten datensatz gelöscht, dann geht es.
War ein Fehler beim Generatorwert.
Jetzt sagt er mir, dass er den Feldnamen 'Name' nicht finden kann, das ist aber eindeutig der Name des Feldes für den Firmennamen. was soll das jetzt ?


SmileySN - So 18.06.06 20:24

Der neue Datensatz wird auch mit den eingetragenen Werten 'Neuname' und NeueStrasse' angelegt


mkinzler - So 18.06.06 20:24

Welchen Namen hat das Feld; 'Name', 'NAME', 'name'?


SmileySN - So 18.06.06 20:36

In der Datenbank sind alle Felder groß geschrieben es heißt hier also 'NAME', das hab ich dann probiert indem ich geschrieben habe:

Delphi-Quelltext
1:
2:
3:
  DM.QFirma.SQL.Clear;
  DM.QFirma.SQL.Add('Insert into Firma (NAME,STRASSE) Values (''NeuName'',''NeuStrasse'')');
  DM.QFirma.ExecSQL;

Er bringt mir aber noch den gleichen Fehler.

QFirma: Das Feld 'Name' wurde nicht gefunden


mkinzler - So 18.06.06 20:40

Ich vermutete eher das es wirklich Name heißt, dann hätte man den feldname Quoten müssen.
Post mal die Metadaten der Tabelle.

Welche Komponenten verwendest du?


SmileySN - So 18.06.06 20:40

Jetzt bin ich etwas weiter, es passiert in der darauffolgenden Procedure DatenlesenFirma;


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
procedure DatenLesenFirma;
begin
  Form1.EdFirmaName.text := DM.QFirma.FieldByName('NAME').AsString;
  Form1.EdFirmaStrasse.Text := DM.QFirma.FieldByName('STRASSE').AsString;
  Form1.EdFirmaPLZ.Text := DM.QFirma.FieldByName('PLZ').AsString;
  Form1.EdFirmaOrt.Text := DM.QFirma.FieldByName('Ort').AsString;
  Form1.EdFirmaTel.Text := DM.QFirma.FieldByName('Tel').AsString;
  Form1.EdFirmaMail.Text := DM.QFirma.FieldByName('Mail').AsString;
  Form1.EdFirmaWeb.Text := DM.QFirma.FieldByName('Web').AsString;
  Form1.EdFirmaStatus.Text := DM.QFirma.FieldByName('Status').AsString;
  Form1.EdFirmaGruendung.Text := DM.QFirma.FieldByName('Gruendung').AsString;
  Form1.EdFirmaLiq.Text := DM.QFirma.FieldByName('Liq').AsString;
  Form1.EdFirmaInfo.Text := DM.QFirma.FieldByName('Memo').AsString;
  AktFirmaID := DM.QFirma.FieldByName('FirmaID').AsInteger;
  Form1.EdFirmaID.Text := DM.QFirma.FieldByName('FirmaID').AsString;
  SpracheLaden(AktSprache);
end;


mkinzler - So 18.06.06 20:42

Dann ist es ja klar, win DDL-Query hat keine Felder.


SmileySN - So 18.06.06 20:45

Kann es sein, dass die Query QFirma nicht mehr richtig geöffnet ist nach dem ExecSQL-Befehl ?

Hier mal die definition aus der Datenbank:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
/******************************************************************************/
/***               Generated by IBExpert 18.06.2006 20:43:06                ***/
/******************************************************************************/

SET SQL DIALECT 3;

SET NAMES NONE;



/******************************************************************************/
/***                                 Tables                                 ***/
/******************************************************************************/


CREATE GENERATOR GEN_FIRMA_ID;

CREATE TABLE FIRMA (
    FIRMAID    INTEGER NOT NULL,
    NAME       VARCHAR(40),
    STRASSE    VARCHAR(40),
    PLZ        VARCHAR(10),
    ORT        VARCHAR(40),
    TEL        VARCHAR(40),
    MAIL       VARCHAR(40),
    WEB        VARCHAR(40),
    STATUS     VARCHAR(20),
    GRUENDUNG  DATE,
    LIQ        DATE,
    MEMO       BLOB SUB_TYPE 2 SEGMENT SIZE 16384
);




/******************************************************************************/
/***                              Primary Keys                              ***/
/******************************************************************************/

ALTER TABLE FIRMA ADD CONSTRAINT PK_FIRMA PRIMARY KEY (FIRMAID);


/******************************************************************************/
/***                                Indices                                 ***/
/******************************************************************************/

CREATE INDEX IDX_FIRMA_NAME ON FIRMA (NAME);


/******************************************************************************/
/***                                Triggers                                ***/
/******************************************************************************/


SET TERM ^ ;


/******************************************************************************/
/***                          Triggers for tables                           ***/
/******************************************************************************/



/* Trigger: FIRMA_BI */
CREATE TRIGGER FIRMA_BI FOR FIRMA
ACTIVE BEFORE INSERT POSITION 0
AS
BEGIN
  IF (NEW.FIRMAID IS NULL) THEN
    NEW.FIRMAID = GEN_ID(GEN_FIRMA_ID,1);
END
^


SET TERM ; ^



/******************************************************************************/
/***                               Privileges                               ***/
/******************************************************************************/


/* Privileges of users */
GRANT ALL ON FIRMA TO DBUSER;


mkinzler - So 18.06.06 20:51

.ExecSQL liefert keinen Cursor. Ich würde in deinem Fall 2 Queries nehmen, Abfrage und DML getrennt.


SmileySN - So 18.06.06 20:52

Zitat:
Dann ist es ja klar, win DDL-Query hat keine Felder.


Das sagt mir jetzt nichts ?????


mkinzler - So 18.06.06 20:55

Eine Abfrage mit Select liefert ein Cursor mit feldern auf dene man mit .FieldByName o.ö. zugreifen kann, eine DML-Abfrage nicht; nach dem .ExecSQL ist der Query geschlössen.


SmileySN - So 18.06.06 20:56

Das hat sich jetzt etwas überschnitten.

Ich kann natürlich auch ein zweites Query für das Insert anlegen, aber wie wird es denn normalerweise gemacht.
Ich muss nach dem INSERT ja die Daten wieder am Bildschirm anzeigen lassen damit der Benutzer die Änderungen sieht.
Ist es wirklich so aufwendig ? Oder gehe ich hier einfach nur einen komplizierten Weg ??


mkinzler - So 18.06.06 21:00

Du kannst natürlich nur einen Query nehmen, ist aber dann nicht optimal, weil du ständig die Abfrage ändern mußt. Besser sind 2. Wobei nach dem Insert ein refresh auf den select-Query gemacht werden muß.


SmileySN - So 18.06.06 21:02

Ich kann auch einfach ein neues Select machen, nur brauche ich dazu die Datensatznummer des neuen Datensatzes und den kann ich ja noch nicht ansprechen.
Oder ich muss auf den letzten Datensatz springen und mir dort die ID holen.


SmileySN - So 18.06.06 21:19

Jetzt bin ich wieder an dem alten Problem mit der ID lesen.
Den letzten Datensatz lesen ist auch nicht so sicher, da könnte ein anderer User ja auch gerade einen Datensatz neu angelegt haben. Dann bekomme ich seinen Datensatz und nicht meinen gerade eingefügten.
Lässt sich das Problem doch nur mit einer StoredProcedure lösen ?


mkinzler - So 18.06.06 21:26

Ja, anstatt des Triggers. In der SP dann zuerst den Generator ermitteln/erhöhen, dann insert inkl ID, ID zurückgeben.


SmileySN - So 18.06.06 21:36

Wäre dies hier so richtig ?


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
CREATE PROCEDURE INSERTFIRMA (
    NAME VARCHAR(40),
    STRASSE VARCHAR(40))
RETURNS (
    ID INTEGER)
AS
begin
  ID=GEN_ID(GEN_FIRMA_ID,1);
  INSERT INTO FIRMA (ID,NAME,STRASSE) values(:ID,:NAME,:STRASSE);
  suspend;
end


mkinzler - So 18.06.06 21:40

Müsste so gehen.


SmileySN - So 18.06.06 22:57

Ich hab die StoredProcedure jetzt in der Datenbank drin, übergebe keine Werte und bekomme nur ID zurück.
Kannst Du mir noch mal helfen, wie ich die Procedure INSERT_FIRMA aufrufe und die ID damit zurückbekomme ??
Ich weiß nicht wie man eine SP von Delphi aus anspricht und den Parameter übergibt.


mkinzler - So 18.06.06 22:59


SQL-Anweisung
1:
Select ID from INSERTFIRMA( <Name>, <Strasse>);                    


SmileySN - So 18.06.06 23:12

Das Select... ist doch sicher ein SQL-Befehl und muss so auch übergeben werden z.B.


Delphi-Quelltext
1:
2:
3:
  DM.QFirma.SQL.Clear;
  DM.QFirma.SQL.Add('Select ID from INSERT_FIRMA()');
  DM.QFirma.Open;


Wie kann ich jetzt den Wert ID auslesen ?


mkinzler - So 18.06.06 23:17


Delphi-Quelltext
1:
2:
3:
4:
DM.QFirma.SQL.Clear;
DM.QFirma.SQL.Add('Select ID from INSERT_FIRMA( <Name>, <strasse>)');
DM.QFirma.Open;
ID :=  DM.QFirma.FieldByName('ID').Value;


SmileySN - So 18.06.06 23:31

So hab ichs hinbekommen:


Delphi-Quelltext
1:
2:
3:
4:
5:
  DM.QFirma.SQL.Clear;
  DM.QFirma.SQL.Add('Select ID From INSERT_FIRMA');
  DM.QFirma.Open;

  AktFirmaID:= DM.QFirma.FieldByName('ID').AsInteger;


Danke für die Kompetente Hilfe


SmileySN - So 18.06.06 23:48

Leider doch noch ein Problem:

Delphi-Quelltext
1:
 AktFirmaID:= DM.QFirma.FieldByName('ID').AsInteger;                    

Hier bekomme ich immer nur 0 zurück

Delphi-Quelltext
1:
 AktFirmaID:= DM.QFirma.FieldByName('ID').Value;                    

Hier bekomme ich die Meldung:
Variante des Typs (Null) konnte nicht in Typ (Integer) konvertiert werden


SmileySN - So 18.06.06 23:49

So sieht meine SP jetzt in der dB aus


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
CREATE PROCEDURE INSERT_FIRMA 
RETURNS (
    ID INTEGER)
AS
begin
  ID=GEN_ID(GEN_FIRMA_ID,1);
  INSERT INTO FIRMA (FIRMAID) values(:ID);
end^

SET TERM ; ^

GRANT INSERT ON FIRMA TO PROCEDURE INSERT_FIRMA;


Den Trigger musste ich rausnehmen, da er mir sonst noch einen leeren Datensatz zusätzlich angefügt hat.


mkinzler - So 18.06.06 23:55

Zitat:
Den Trigger musste ich rausnehmen, da er mir sonst noch einen leeren Datensatz zusätzlich angefügt hat.
Ich hatte ja geschrieben anstatt des Triggers.

Es fehlt nun das SUSPEND; welches die aktuellen werte der Out-Parameter ( in deinem Fall nur ID) in den Cursor schreibt.


SmileySN - Mo 19.06.06 00:02

Unglaublich, es funktioniert wirklich.
Jetzt weiß ich auch wozu das suspend gut ist.

Danke Du bist großartig.