Forum: PC-Programmierung SQL : Automatisch erzeugte ID erkennen;


von interrupt (Gast)


Lesenswert?

Hallo,
in mnachen SQL-Tabellen sind ja Spalten enthalten, die automatisch eine 
laufende ID erzeugen, wenn ein neuer Eintrag vorgenommen wird.

Gibt es eine Möglichkeit, diese automatisch erzeugte ID direkt nach dem 
Insert-Befehl ohne eine spezielle zusätzliche Abfrage zu bekommen ?
(manchmal sind die neu eingetragenen Daten ja nicht eindeutig abfragbar, 
sondern die beste Referenz ist tatsächlich die automatisch angelegte ID)

Danke, Interrupt

von Peter II (Gast)


Lesenswert?

interrupt schrieb:
> Gibt es eine Möglichkeit, diese automatisch erzeugte ID direkt nach dem
> Insert-Befehl ohne eine spezielle zusätzliche Abfrage zu bekommen ?
> (manchmal sind die neu eingetragenen Daten ja nicht eindeutig abfragbar,
> sondern die beste Referenz ist tatsächlich die automatisch angelegte ID)

ganz ohne Abfrage geht es nicht. Beim SQL-Server musst du die Variabel 
@@IDENTITY abfragen.

insert into test ....
select @@IDENTITY

es gibt nur ein paar Fallstricke, wenn noch Trigger mit beteiligt sind.

von SR (Gast)


Lesenswert?

For example, after inserting a row that generates an AUTO_INCREMENT 
value, you can get the value like this:

mysql> SELECT LAST_INSERT_ID();
        -> 195

von S. R. (svenska)


Lesenswert?

Zumindest in Perl gibt es die Möglichkeit, da kommt dann als Teil der 
API die ID zurück. Der Haken ist, dass das nicht mit jeder Datenbank und 
jedem Treiber funktioniert.

Daher vermute ich mal, dass das kein allgemeines SQL ist, sondern eine 
Eigenschaft der verwendeten Datenbank.

von Toni Tester (Gast)


Lesenswert?

Um welches SQL und welchen Treiber geht es?

http://nl1.php.net/manual/en/mysqli-stmt.insert-id.php
http://nl1.php.net/manual/en/pdo.lastinsertid.php
https://mariadb.com/kb/en/mariadb/mysql_stmt_insert_id/

Nur die ersten Beispiele, die mir so in den Sinn kommen, auf den 
unterschiedlichsten Abstraktionsebenen.

von interrupt (Gast)


Lesenswert?

Das ganze soll mit einem Programm, das in C++ geschrieben wurde und mit 
einer Microsoft SQL -Datenbank laufen, Trigger sind in dem Zusammenahng 
nicht vorgesehen.

von Peter II (Gast)


Lesenswert?

interrupt schrieb:
> Das ganze soll mit einem Programm, das in C++ geschrieben wurde und mit
> einer Microsoft SQL -Datenbank laufen, Trigger sind in dem Zusammenahng
> nicht vorgesehen.

dann verwende das "select @@IDENTITY" nach dem Insert.

von Chris .. (dechavue)


Lesenswert?

Du kannst auch mit OUTPUT die betroffenen Datensätze eines Insert/Update 
abfragen (https://msdn.microsoft.com/en-us/library/ms177564.aspx)

Das hat den vorteil, dass du wirklich alle betroffenen Zeilen bekommts. 
Mit dem select @@IDENTITY bekommst du nur den letzten Eintrag (im 
Folgenden Beispiel den Eintrag mit dem Text 'Bar')
1
/*
2
CREATE TABLE [dbo].[Test](
3
  [ID] [int] IDENTITY(1,1) NOT NULL,
4
  [Text] [nvarchar](50) NOT NULL,
5
 CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED 
6
(
7
  [ID] ASC
8
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
9
) ON [PRIMARY]
10
*/
11
12
DECLARE @tmp TABLE(ID INT, Text NVARCHAR(50))
13
14
INSERT INTO Test (Text)
15
OUTPUT Inserted.ID, Inserted.Text INTO @tmp
16
VALUES
17
('Foo'),
18
('Bar')
19
20
SELECT * FROM @tmp

von interrupt (Gast)


Lesenswert?

Danke soweit, werde ich testen.

Noch eine Anschlussfrage :

Bisher (bis incl. Win7) erfolgt der Zugriff auf die MSSQL-Datenbank über 
eine ODBC-Schnittstelle.
Funktioniert das auch mit Win10 und / oder gibt es da eine bessere 
Lösung für Programme, die mit C++ (GNU C-Compiler mit Eclipse) 
geschrieben sind ?

von Peter II (Gast)


Lesenswert?

interrupt schrieb:
> Bisher (bis incl. Win7) erfolgt der Zugriff auf die MSSQL-Datenbank über
> eine ODBC-Schnittstelle.
> Funktioniert das auch mit Win10 und / oder gibt es da eine bessere
> Lösung für Programme, die mit C++ (GNU C-Compiler mit Eclipse)
> geschrieben sind ?

Für C++ ist ODBC das richtige. Wenn auch manchmal etwas umständlich.

Es gibt zwar noch den NativeClient, aber das würde ich nicht empfehlen.

von Andy (Gast)


Lesenswert?

LASTINSERTID ist etwas, wenn du nur einen client oder nur wenige 
gleichzeitige Zugriffe hast. Andernfalls solltestdudas anders machen 
denn, oft sieht man so einen Unsinn auch in DB-Anwendungen, wo jemand 
auf die Idee kam, dann nachträglich das ganze multiuserfähig zu machen.

Beispiel:
dein c++Programm als DB-client wird auf zwei PCs ausgeführt, beide User 
arbeiten völlig parallel und unabhängig voneinander.
Zeitstempel     PC  SQL
09:05:23.00001 PC1  "INSERT INTO ..."
09:05:23.00004 PC2  "INSERT INTO...."

nun kommt die Rückfrage wie oben angegeben

09:05:23.00005 PC1 "LASTINSERTID...."
und bekommt die ID vom Insert des PC2. Es wird zwar behauptet, daß 
lastInsertId connectionabhängig sein soll, aber das habe ich bislang 
nicht feststellen können.

Achja: ich habe hier auch schon "kommerzielle Software" erwischt, bei 
denen zwischen den SQL-befehlen INSERT und lastInsertID ca 0,8sek 
vergingen...


Sicherer ist:
SELECT id FROM table WHERE <viele Daten des insert> ORDER BY id DESC 
LIMIT 1;

Funktioniert natürlich nur, wenn die Summe der daten in INSERT unique 
genug sind. Solltest du aber immer wieder diesselben Daten per INSERT 
eintragen wollen, wäre wohl eine DB eh kaum der richtige Speicherort.

von Peter II (Gast)


Lesenswert?

Andy schrieb:
> Sicherer ist:
> SELECT id FROM table WHERE <viele Daten des insert> ORDER BY id DESC
> LIMIT 1;

nur ist das das langsamste. Da es hier um MS-SQL geht ist @@IDENTITY das 
richtige das ist 100% Session abhängig und macht keine Probleme.

In mysql ist es LAST_INSERT_ID().


deine Lösung ist sehr schlecht. Sie ist langsam und braucht zu viele 
Resourcen. Könnte man verschmerzen wenn sie wenigsten 
Plattformunabhängig währe das ist sie aber auch nicht. Bringt also nur 
Nachteile gegenüber LAST_INSERT_ID():

von Clemens L. (c_l)


Lesenswert?

Andy schrieb:
> multiuserfähig

... ist MS SQL (wie auch praktisch jede andere Datenbank), wenn man 
Transaktionen korrekt benutzt.

von Peter II (Gast)


Lesenswert?

Clemens L. schrieb:
> Andy schrieb:
>> multiuserfähig
>
> ... ist MS SQL (wie auch praktisch jede andere Datenbank), wenn man
> Transaktionen korrekt benutzt.

richtig, aber für arbeiten mit ident spalten braucht man keine 
Transaktionen.

von Andy (Gast)


Lesenswert?

ein Select auf ein PrimKey, mit einer Datenmenge von max 1 Datensatz, 
der eben erst eingesetzt wurde. Wenn ein Db-Server das nicht aus seinen 
Cache bedienen kann, dann ist der ... suboptimal konfiguriert.

Wenn man das richtig macht, sollte das kaum langsamer als last_insertID 
sein. Und wie gesagt: es ist defninitv sicherer nach meinr Erfahrung. 
YMMW.

von Peter II (Gast)


Lesenswert?

Andy schrieb:
> ein Select auf ein PrimKey, mit einer Datenmenge von max 1 Datensatz,
> der eben erst eingesetzt wurde. Wenn ein Db-Server das nicht aus seinen
> Cache bedienen kann, dann ist der ... suboptimal konfiguriert.

schon das parsen vom SQL braucht resourcen. Dann müssen die Daten noch 
sortiert werden. Dann brauchst du noch zwingend ein Index auf der ID 
spalte.

> Wenn man das richtig macht, sollte das kaum langsamer als last_insertID
> sein. Und wie gesagt: es ist defninitv sicherer nach meinr Erfahrung.
> YMMW.
man liest einfach die Doku, darin steht wie man so etwas zu verwenden 
hat. Bei Mysql steht eindeutig drin:


[...]
The ID that was generated is maintained in the server on a 
per-connection basis. This means that the value returned by the function 
to a given client is the first AUTO_INCREMENT value generated for most 
recent statement affecting an AUTO_INCREMENT column by that client. This 
value cannot be affected by other clients, even if they generate 
AUTO_INCREMENT values of their own. This behavior ensures that each 
client can retrieve its own ID without concern for the activity of other 
clients, and without the need for locks or transactions.
[...]

Vermutlich war das Problem bei dir an einer anderen stelle.

von Jan H. (j_hansen)


Lesenswert?

Ich verwende bei MSSQL meist scope_identity(): 
http://stackoverflow.com/a/1920640

Die "Lösung" von Andy kann man höchstens noch als Hack durchgehen 
lassen.

von Peter II (Gast)


Lesenswert?

Jan H. schrieb:
> Ich verwende bei MSSQL meist scope_identity():
> http://stackoverflow.com/a/1920640

Danke, selbst nach 10 Jahren aktiven SQL lernt man nicht aus. Aber wie 
sind eh weg von Identity (hat bei uns Nachteile) und damals gab es nur 
@@IDENTITY (SQL Server < 2008)

von Andy (Gast)


Lesenswert?

Peter II schrieb:
> Vermutlich war das Problem bei dir an einer anderen stelle.

Schön wärs, denn das, was da im Manual drinsteht, kenn ich auch so. Ich 
trau dem ganzen nicht mehr 100%tig (ich habe halt in der freien Wildbahn 
auch gegenteiliges gesehen)
Am häufigsten habe ich Auswirkungen von falschen Rückgaben durch 
LastInsertID bei einer WaWi gesehen (per Mitschnitt der SQL-Verbindung), 
allerdings war das eine ins win7-Zeitalter gehobene Delphi-Anwendung mit 
ODBC-treiber älter Art.
Hier habe ich für die Zeitspanne von INSERT zu  Last_insert_ID schonmal 
0,6sek beobachten können.
Vermutlich wegen der langsamen programminternen Ausführungszeit (nein, 
selbst Zugriffe durch PHP-Scripte auf die DB laufen getesteterweise um 
Größenordnungen schneller!) und damit der langen Zeitabstände fiel das 
hier besonders häufig auf.
Ein anderer (test-) MSSQL mit quasi fast leeren Tabellen half nicht und 
brachte gleiche Ergebnisse.

Wenn ihr das so einsetzen wollt, bitteschön. Wenn ihr dann Probleme 
bekommt, denkt ggf an meine Erfahrungen und zieht auch dies als mögliche 
Fehlerquelle in Erwägung. An sowas denkt doch keiner, der das 
"SQL-Handbuch" kennt. Schaden kanns nicht.

von Peter II (Gast)


Lesenswert?

Andy schrieb:
> Wenn ihr das so einsetzen wollt, bitteschön. Wenn ihr dann Probleme
> bekommt, denkt ggf an meine Erfahrungen und zieht auch dies als mögliche
> Fehlerquelle in Erwägung. An sowas denkt doch keiner, der das
> "SQL-Handbuch" kennt. Schaden kanns nicht.

deine Lösung bringt doch viel mehr Probleme. Der Datensatz kann schon 
lange vom nächsten gelöscht wurden sein. Also muss  man mit 
Transaktionen arbeiten - was sinnlos lang die Tabelle blockiert.

von deiner Lösung kann man nur abraten.

von Andy (Gast)


Lesenswert?

Peter II schrieb:
>... Der Datensatz kann schon
> lange vom nächsten gelöscht wurden sein.

Dann findet das SELECT den Datensatz auch nicht. Wenn der Datensatz 
berechtigerweise gelöscht oder geändert wurde, dann ist das halt ein 
normaler Betriebsstatus, mit dem das Programm klarzukommen hat.
(im einfachsten Fall üblicherweise eine Fehlerbehandlung)

Das kann dir auch bei deiner Lösung passieren: Wer garantiert, daß nach 
Übergabe des PrimKeys aus @@Identity nicht schon das DELETE in der Queue 
steht (oder gar durch eine andere Session abgearbeitet wird)?
Noch bevor ich sinnvoll (außer vllt als debug-ausgabe) mit dem primkey 
arbeiten kann, hab ich einen PrimKey zur Weiterverarbeitung, der in der 
tabelle nicht mehr existiert. Wiederum die frage, ob das eingeplant ist.

So oder so ist das Beispiel unglücklich gewählt. Ich verstehe, worauf du 
hinauswillst und ich sage: verlaß dich nicht zu 100% drauf.

von ui (Gast)


Lesenswert?

Ich würde das ganze evtl. von der anderen Seite mal betrachten.
Du willst in ne DB einen Eintrag hinzufügen, der dann später nicht mehr 
eindeutig referenzierbar ist? (Über seine Daten, ohne die ID). Für was 
solltest du dann die ID benötigen?
Für mich bedeutet das nur, dass du u.U. 2 gleiche Datensätze in der DB 
hast, die sich nur anhand der ID unterscheiden.
Dann gibt es 2 Ansätze:
- Dein DB-Schema ist scheisse und du solltest dir Gedanken darüber 
machen ob du in der falschen Normalform bist.
- Es kann einfach auf Grund deiner Daten passieren, dass du 2 oder mehr 
gleiche Datensätze hast. Wofür musst du die Daten unmittelbar nach dem 
Einfügen mit der ID referenzieren? Bzw. wofür brauchst du diese ID? Was 
willst du weiterhin mit der ID machen?

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.