Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   Delphi Auto-Insertwert abfragen (https://www.delphipraxis.net/121619-auto-insertwert-abfragen.html)

emsländer 1. Okt 2008 12:03

Datenbank: MSSQL • Version: 2005 • Zugriff über: ADO

Auto-Insertwert abfragen
 
Moin,

wie kann ich in diesem Konstrukt den Autoinc-Wert abfragen?

Delphi-Quellcode:
  _stmp := 'select * from Akten';
  _query := Tadoquery.Create(nil);
  with _query do begin
    connection := mus_Service.ADOConnMUS;
    try
      _query.sql.Text := _stmp;
      _query.open;
      insert;
      FieldValues['Datum_und_Zeit'] := Datum_und_Zeit;
      FieldValues['SenderIP'] := SenderIP;
      FieldValues['eMail'] := eMail;
      FieldValues['Telefon'] := Telefon;
      FieldValues['Mobile'] := Mobile;
      FieldValues['Art'] := Art;
      post;
      result := xxx; // Hier die Abfrage  
    finally
      freeandnil(_query);
      result := 0;
    end;
  end;
Die Table hat ein Feld mit dem Namen Aktenr, welches AUTOINC ist.

Gruss

EL

DeddyH 1. Okt 2008 12:16

Re: Auto-Insertwert abfragen
 
Ich habe mal ein bisschen gegockelt und das Einzige, was ich finden konnte ist, sofort nach dem Insert die Identity abzufragen. Beispiel in PHP (von hier):
Code:
$result = mssql_query("INSERT INTO STUFF(gaga,otherGaga) VALUES ('hello','apple'); SELECT @@IDENTITY as insertId;");

emsländer 1. Okt 2008 12:21

Re: Auto-Insertwert abfragen
 
Zitat:

Zitat von DeddyH
Ich habe mal ein bisschen gegockelt und das Einzige, was ich finden konnte ist, sofort nach dem Insert die Identity abzufragen. Beispiel in PHP (von hier):
Code:
$result = mssql_query("INSERT INTO STUFF(gaga,otherGaga) VALUES ('hello','apple'); SELECT @@IDENTITY as insertId;");

Das ist mir bekannt. Ich muss in diesem Fall aber mit diesem ADO-Konstrukt arbeiten.

Gruss

EL

MorphieX 1. Okt 2008 12:58

Re: Auto-Insertwert abfragen
 
Nur ne idee, kenn mich mit MSSQL nicht aus, aber:
steht sowas nicht in ner System-tabelle?

nahpets 1. Okt 2008 13:02

Re: Auto-Insertwert abfragen
 
Hallo,

mir ist da nur eine Möglichkeit bekannt, ein Select auf genau den Datensatz, den Du soeben eingefügt hast.

Ins Unreine ungefähr so:
SQL-Code:
select AutoIncSpalte from Akten
where Datum_und_Zeit = :Datum_und_Zeit
and  SenderIP      = :SenderIP
and  eMail         = :eMail
and  Telefon       = :Telefon
and  Mobile        = :Mobile
and  Art           = :Art
Sofern Du "alleine" auf der Datenbank unterwegs bist, also keiner parallel oder fast parallel Daten einfügen kann, dann könntest Du dank AutoInc auch mit
SQL-Code:
select max(AutoInc_Spalte) from Akten
zum Ziel kommen. Fügen mehrere Personen, Programm... Daten ein, so scheitert das, da nicht sichergestellt ist, dass Dein Satz der Neueste ist und damit über diesen Wert verfügt.

Stephan

Jelly 1. Okt 2008 13:09

Re: Auto-Insertwert abfragen
 
Einfach nach dem Post den Wert in einer getrennten Abfrage auslesen.
SQL-Code:
select Scope_Identity()
(Scope_Identity() ist besser geeignet weil @@identity den wirklich letzten Auto Wert der aktuellen Session zurückgibt. Führt ein Insert in eine Tabelle einen InsertTrigger aus, der selbst wieder in eine andere Tabelle mit Autoinc Wert was einfügt, so gibt dir @@identity diesen letzten Wert zurück.

Angst um parallelen Zugriff muss man übrigens nicht haben, weil dieses Ganze Identity Auslesen Session bezogen ist, sich also auf eine Connection bezieht.

Persönlich allerdings bevorzuge ich das Reservieren eines eindeutigen Werte bereits vor dem Insert (In anderen Datenbank als Sequenzen bekannt). Hier muss man allerdings darauch achten, dass die Ganze Spielerei transaktionssicher ist (sobald es mehr als eine SQL Anweisung ist kann es passieren, dass eine andere Session dazwischenfunkt).

Hier z.B. mal der Code zum Nachahmen von Sequenzen unter MSSQL. Dazu noch eine geeignete Delphi Klasse zum Kapseln, und schon erhält man ganz easy bereits vorm Insert einen eindeutigen ID Wert auch ohne diese Identity Gedöhns (was einem z.B. bei .NET tierisch auf den Sack geht weil connectionslos gearbeitet wird).

SQL-Code:
CREATE TABLE [dbo].[Sequence] (
   [SequenceName] [varchar] (255) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
   [CurrentValue] [bigint] NOT NULL ,
   [Increment] [int] NOT NULL ,
   [CreationDate] [datetime] NOT NULL
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[Sequence] WITH NOCHECK ADD
   CONSTRAINT [PK_Sequence] PRIMARY KEY CLUSTERED
   (
      [SequenceName]
   ) ON [PRIMARY]
GO

ALTER TABLE [dbo].[Sequence] ADD
   CONSTRAINT [DF_Sequence_CurrentValue] DEFAULT (0) FOR [CurrentValue],
   CONSTRAINT [DF_Sequence_Increment] DEFAULT (1) FOR [Increment],
   CONSTRAINT [DF_Sequence_CreationDate] DEFAULT (getdate()) FOR [CreationDate],
   CONSTRAINT [CK_Sequence] CHECK ([Increment] <> 0)
GO




SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO




CREATE PROCEDURE spNextSequence (@SequenceName varchar(255), @SequenceId bigint OUTPUT) AS
DECLARE @Increment int


SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION SP_GEN_ID


SET @SequenceId = (Select CurrentValue from Sequence where SequenceName = @SequenceName)

IF @SequenceId is null BEGIN
    insert into Sequence (SequenceName) values (@SequenceName)
   SET @SequenceId = (Select CurrentValue from Sequence where SequenceName = @SequenceName)
END
 

SET @Increment = (Select Increment from Sequence where SequenceName = @SequenceName)
update Sequence set CurrentValue=CurrentValue+@Increment where SequenceName = @SequenceName
SET @SequenceId = (Select CurrentValue from Sequence where SequenceName = @SequenceName)


COMMIT TRANSACTION SP_GEN_ID

GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
Man beachte hier in der Stored Procedure das
SQL-Code:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
Damit wird sichergestellt, dass immer nur einer diese Prozedur gleichzeitig ausführen kann.

emsländer 1. Okt 2008 13:15

Re: Auto-Insertwert abfragen
 
Zitat:

Zitat von Jelly
Einfach nach dem Post den Wert in einer getrennten Abfrage auslesen.
SQL-Code:
select Scope_Identity()
(Scope_Identity() ist besser geeignet weil @@identity den wirklich letzten Auto Wert der aktuellen Session zurückgibt. Führt ein Insert in eine Tabelle einen InsertTrigger aus, der selbst wieder in eine andere Tabelle mit Autoinc Wert was einfügt, so gibt dir @@identity diesen letzten Wert zurück.

Angst um parallelen Zugriff muss man übrigens nicht haben, weil dieses Ganze Identity Auslesen Session bezogen ist, sich also auf eine Connection bezieht.


Blöde Frage: Wie baue ich diesen: select Scope_Identity() in dieses Konstrukt ein?

Delphi-Quellcode:
  with _query do begin
    connection := mus_Service.ADOConnMUS;
    try
      sql.Text := _stmp;
      open;
      insert;
      FieldValues['Datum_und_Zeit'] := Datum_und_Zeit;
      FieldValues['SenderIP'] := SenderIP;
      FieldValues['eMail'] := eMail;
      FieldValues['Telefon'] := Telefon;
      FieldValues['Mobile'] := Mobile;
      FieldValues['Art'] := Art;

      post;
      result := ????? <======== HIER
      _query.Close;
    finally
      freeandnil(_query);
      result := 0;
    end;
  end;


Gruss

EL

Jelly 1. Okt 2008 13:24

Re: Auto-Insertwert abfragen
 
Delphi-Quellcode:
with _query do begin
    connection := mus_Service.ADOConnMUS;
    try
        sql.Text := _stmp;
        open;
        insert;
        FieldValues['Datum_und_Zeit'] := Datum_und_Zeit;
        FieldValues['SenderIP'] := SenderIP;
        FieldValues['eMail'] := eMail;
        FieldValues['Telefon'] := Telefon;
        FieldValues['Mobile'] := Mobile;
        FieldValues['Art'] := Art;

        post;
        result := LastIdentity;
        _query.Close;
    finally
        freeandnil(_query);
        //result := 0;    --> Sonst wird dir nach aussen immer 0 ausgegeben
    end;
end;

function LastIdentity : integer;
begin
    with _query do begin
        connection := mus_Service.ADOConnMUS;
        try
           sql.Text := 'Select Scope_Identity() as id';
           open;
           if not(FielByName('id').IsNull) then
              result := FieldByName('id').asInteger;
           else
              result := 0;
           _query.Close;
           exit;
        finally
           freeandnil(_query);
       end;
    end;
end ;

emsländer 1. Okt 2008 13:45

Re: Auto-Insertwert abfragen
 
Zitat:

Zitat von Jelly
Delphi-Quellcode:
with _query do begin
    connection := mus_Service.ADOConnMUS;
    try
        sql.Text := _stmp;
        open;
        insert;
        FieldValues['Datum_und_Zeit'] := Datum_und_Zeit;
        FieldValues['SenderIP'] := SenderIP;
        FieldValues['eMail'] := eMail;
        FieldValues['Telefon'] := Telefon;
        FieldValues['Mobile'] := Mobile;
        FieldValues['Art'] := Art;

        post;
        result := LastIdentity;
        _query.Close;
    finally
        freeandnil(_query);
        //result := 0;    --> Sonst wird dir nach aussen immer 0 ausgegeben
    end;
end;

function LastIdentity : integer;
begin
    with _query do begin
        connection := mus_Service.ADOConnMUS;
        try
           sql.Text := 'Select Scope_Identity() as id';
           open;
           if not(FielByName('id').IsNull) then
              result := FieldByName('id').asInteger;
           else
              result := 0;
           _query.Close;
           exit;
        finally
           freeandnil(_query);
       end;
    end;
end ;

Hi Jelly,

der liefert mir immer 0 als result :?

Gruss

EL

DeddyH 1. Okt 2008 13:50

Re: Auto-Insertwert abfragen
 
Und wenn Du in der Funktion eine eigene lokale Query erzeugst?

emsländer 1. Okt 2008 13:53

Re: Auto-Insertwert abfragen
 
Zitat:

Zitat von DeddyH
Und wenn Du in der Funktion eine eigene lokale Query erzeugst?

Wie meinst Du das?

Gruss

EL

DeddyH 1. Okt 2008 13:55

Re: Auto-Insertwert abfragen
 
Delphi-Quellcode:
function LastIdentity : integer;
begin
    with TADOQuery.Create do begin //hier
        connection := mus_Service.ADOConnMUS;
        try
           sql.Text := 'Select Scope_Identity() as id';
           open;
           if not(FielByName('id').IsNull) then
              result := FieldByName('id').asInteger;
           else
              result := 0;
           Close;          
        finally
          Free;
       end;
    end;
end ;

emsländer 1. Okt 2008 14:01

Re: Auto-Insertwert abfragen
 
Zitat:

Zitat von DeddyH
Delphi-Quellcode:
function LastIdentity : integer;
          if not(FieldByName('id').IsNull) then  
              result := FieldByName('id').asInteger;
           else
              result := 0;  <====== Bedingung landet immer hier


DeddyH 1. Okt 2008 14:04

Re: Auto-Insertwert abfragen
 
Und mit @@IDENTITY statt Scope_Identity()? Aber ich möchte Jelly nicht in den Rücken fallen.

emsländer 1. Okt 2008 14:19

Re: Auto-Insertwert abfragen
 
Zitat:

Zitat von DeddyH
Und mit @@IDENTITY statt Scope_Identity()? Aber ich möchte Jelly nicht in den Rücken fallen.

Gibts denn nichts, was die ganze ADO-Automatik da vorgibt?

Gruss

EL

nahpets 1. Okt 2008 14:22

Re: Auto-Insertwert abfragen
 
Hallo,
SQL-Code:
Select Scope_Identity() as id
scheint erst dann einen Wert ungleich Null zu liefern, wenn mindestens ein Datensatz in der Tabelle ist. Liegt da eventuell das Problem?

Stephan

emsländer 1. Okt 2008 14:24

Re: Auto-Insertwert abfragen
 
Zitat:

Zitat von nahpets
Hallo,
SQL-Code:
Select Scope_Identity() as id
scheint erst dann einen Wert ungleich Null zu liefern, wenn mindestens ein Datensatz in der Tabelle ist. Liegt da eventuell das Problem?

Stephan


nein - auch bei mehreren (im Moment 12 in der Testumgebung) Datensätzen immer 0

Gruss

EL

nahpets 1. Okt 2008 14:31

Re: Auto-Insertwert abfragen
 
Hallo,

Frage: Auch wenn Du die Abfrage im Management Studio machst?
Wenn da ein anderes Ergebnis kommt, als im Programm, geht Dir "unterwegs" der Scope verloren, was ich nicht ausschließen möchte.

Was liefert Dir denn
SQL-Code:
select IDENT_CURRENT('Akten') as ID
der holt wohl den höchsten vergebenen Wert zur ID-Spalte, der als Parameter übergebenen Tabelle.

Stephan

emsländer 1. Okt 2008 14:51

Re: Auto-Insertwert abfragen
 
Zitat:

Zitat von nahpets
Hallo,

Frage: Auch wenn Du die Abfrage im Management Studio machst?
Wenn da ein anderes Ergebnis kommt, als im Programm, geht Dir "unterwegs" der Scope verloren, was ich nicht ausschließen möchte.

Was liefert Dir denn
SQL-Code:
select IDENT_CURRENT('Akten') as ID
der holt wohl den höchsten vergebenen Wert zur ID-Spalte, der als Parameter übergebenen Tabelle.

Stephan

ich habe mir jetzt einen Workaround gebaut:


zunächst habe ich der Table ein Feld "neu" mit automatischen Inhalt 1 (nur 1, kein Increment) hinzugefügt. Obenauf einen Index drauf, damit die Suche ggfs. schneller geht.
Somit hat immer genau der neue Datensatz neu=1.
hier die Abfrage mit sofortiger Änderung neu=0;
Delphi-Quellcode:
function getaktennr : integer;
  var _stmp : string;
  _query : Tadoquery;
begin
  _stmp := 'select neu, Aktennr from Akten where neu=1';
  _query := Tadoquery.Create(nil);
  with _query do begin
    connection := mus_Service.ADOConnMUS;
    _query.sql.Text := _stmp;
    _query.open;   open;
    if (not eof) then begin
      result := FieldByName('Aktennr').asinteger;
      edit;
      FieldValues['neu'] := 0;
      post;
    end else result := 0;
  end;
end;
Danke an alle, die geholfen haben :shock: - Mit diesem Workaround kann ich gut leben. Zumal dieses die einzige Stelle ist, an der der Datenbank neue Datensätze hinzugefügt werden.

Gruss

EL

Jelly 1. Okt 2008 15:44

Re: Auto-Insertwert abfragen
 
Deine Lösung ist unschön wegen dem Extrafeld.

Lass doch mal in meinem Code dir Prüfung auf IsNull weg und versuche einfach den mit AsInteger zurückzugeben. Mit dem Scope_Identity() hatte ich noch nie Probleme, aber auch wohl deshalb vielleicht weil ich das so in der Form gar nicht mehr nutze.

Ich plädiere definitiv zu der Lösung mit der Sequence Tabelle und der Stored Procedure aus #6. Damit bist du einfach viel flexibler, und du hast die nötige Id schon bevor du überhaupt den Insert losschickst. Dafür müsstest du allerdings die Identity Eigenschaft der DB-Spalte wieder kicken.

Elvis 2. Okt 2008 13:43

Re: Auto-Insertwert abfragen
 
Habe jetzt nicht alles durchgelesen, aber in MSSQL 2005 gibt es auch etwas, ähnlich wie die returning clause aus Oracle oder Firebird.
Ich versuche normalerweise MSSQL nicht anzufassen, aber laut meinem SQL-Generator sollte das so aussehen:
SQL-Code:
INSERT INTO DeineTabelle(Feld1, Feld2)
OUTPUT inserted.ID
VALUES(:Feld1, :Feld2)
Das ganze ist wie bei einem Returning aus Firebird eine Abfrage, die die Spalten in der Output clause als Ergebnismenge enthält.


Alle Zeitangaben in WEZ +1. Es ist jetzt 03:28 Uhr.

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