2014-12-15 11 views
7

Ich habe eine BEFORE INSERT TRIGGER, die verwendet wird, um den AUTO_INCREMENT Wert einer Spalte (id_2) zu berechnen.Ändern der LAST_INSERT_ID() aus einem TRIGGER in MySQL

id_1 | id_2 | data 
1 | 1 | 'a' 
1 | 2 | 'b' 
1 | 3 | 'c' 
2 | 1 | 'a' 
2 | 2 | 'b' 
2 | 3 | 'c' 
2 | 4 | 'a' 
3 | 1 | 'b' 
3 | 2 | 'c' 

Ich habe PRIMARY (ID_1, ID_2) und ich bin mit InnoDB. Vorher verwendete die Tabelle MyISAM und ich hatte keine Probleme: id_2 wurde auf AUTO_INCREMENT festgelegt, so dass jeder neue Eintrag für id_1 neue id_2 allein erzeugen würde. Jetzt, nach InnoDB Schalt, ich habe diesen Trigger, das Gleiche zu tun:

SET @id = NULL; 
SELECT COALESCE(MAX(id_2) + 1, 1) INTO @id FROM tbl WHERE id_1 = NEW.id_1; 
SET NEW.id_2= @id; 

Es funktioniert perfekt, außer jetzt die LAST_INSERT_ID() falschen Wert hat (es gibt 0) gewonnen. Eine Menge Code hängt davon ab, dass LAST_INSERT_ID() korrekt ist. Seit MySQL 5.0.12 haben jedoch alle Änderungen, die innerhalb von TRIGGERS an LAST_INSERT_ID vorgenommen werden, keinen Einfluss auf den globalen Wert. Gibt es eine Möglichkeit, dies zu umgehen? Ich kann leicht den AFTER UPDATE TRIGGER eingestellt, die die LAST_INSERT_ID ändert durch LAST_INSERT_ID(NEW.id_2) Aufruf jedoch jeder Client-Seite würde LAST_INSERT_ID auf 0 gesetzt

erhalten Gibt es eine Arbeits Behelfslösung MySQL zu zwingen, den Zustand der LAST_INSERT_ID zu halten, die im Inneren verändert wurde der Auslöser? Gibt es eine andere Alternative, als zurück zu MyISAM zu wechseln, das dieses sofort unterstützt oder ein anderes SELECT max(id_2) FROM tbl WHERE id_1 = :id als Teil der Transaktion ausführt, um sicherzustellen, dass die gefundene Zeile diejenige ist, die früher eingefügt wurde?

> SHOW CREATE TABLE tbl; 

CREATE TABLE `tbl` (
    `id_1` int(11) NOT NULL, 
    `id_2` int(11) NOT NULL, 
    `data` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    PRIMARY KEY (`id_1`,`id_2`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci 

Beispiel:

INSERT INTO tbl (id_1, id_2, data) VALUES (1, NULL, 'd'); 
SELECT LAST_INSERT_ID(); 

Die erste Anweisung wird die Zeile 1 | 4 | 'd' in die Tabelle einfügen. Die zweite Anweisung gibt 0 zurück, aber ich brauche es, um 4 zurückzugeben.

Wie Ravinder Reddy gefragt, die kurze Erklärung über das System und fügte hinzu:

ich eine Tabelle, die Körbe, und ich habe eine andere Tabelle enthält (tbl), dieElemente enthält. Der Basket wird von der Anwendung erstellt und erhält eine ID von AUTO_INCREMENT auf Basket-Tabelle. Die Aufgabe besteht darin, Elemente in den Korb mit ID = id_1 in tbl einzufügen und ihnen eine eindeutige ID innerhalb des Geltungsbereichs dieses Korbs zuzuordnen. Jedes Element hat etwas data damit verbunden, das innerhalb desselben Korbes wiederholen kann. In der Praxis versuche ich also, alle data Einträge in einem einzigen Korb zu speichern, und dann in der Lage zu sein, auf diese einzelnen Einträge durch ihre id_1 - id_2 Paare verweisen (und abrufen).

+1

Ich bin ein wenig verwirrt, aber wie hätten Sie eine LAST_INSERT_ID() _before_ die INSERT ausgeführt wird? Ich glaube, LAST_INSERT_ID() ist nur dann zuverlässig, wenn ein Insert auf derselben Client-Verbindung ausgeführt wurde. Wenn die Anwendung eine Verbindung öffnet, eine Einfügung ausführt und dann die Verbindung schließt, können Sie nicht einfach eine neue Verbindung öffnen und auf LAST_INSERT_ID() zugreifen, ohne zuvor eine Einfügung für diese Verbindung ausgeführt zu haben, bevor die Funktion aufgerufen wird. –

+0

Ich machte den Aufruf von 'LAST_INSERT_ID()' nachdem das INSERT abgeschlossen war, also nachdem der Trigger beendet wurde. Mit anderen Worten hatte der Auslöser, um den Wert für die zweite Spalte des zweispaltigen Primärschlüssels zu generieren, keine Möglichkeit, den neu festgelegten Wert zu kommunizieren. Ich habe der Frage ein Beispiel hinzugefügt. – Xeos

+0

@Xeos: Können Sie bitte 'show create table tbl' output zum Beitrag hinzufügen? –

Antwort

1

Mit Ihrer Tabellenstrukturbeschreibung ist klar, dass es kein Primärschlüsselfeld hat, dessen Werte automatisch generiert werden können. Die information_schema.tables von MySQL enthält keinen auto_increment Wert, sondern null für die Felder, die nicht definiert sind auto_increment.

Trigger-Ausgabe:

Der Codeblock in Ihrem Triggerkörper verwendet scheint abhängig von expliziter Berechnung und Eingabe für die ID-Felder. Es hat das Standardverhalten eines auto_increment-Felds nicht verwandt.

Wie pro MySQL's documentation on LAST_INSERT_ID:

LAST_INSERT_ID() eine BIGINT UNSIGNED zurückgibt (64-Bit) Wert
die die erste automatisch generierten Wert
erfolgreich für eine AUTO_INCREMENT Spalte eingefügt
als Ergebnis der zuletzt ausgeführten INSERT-Anweisung.

Es ist klar, dass es für auto_increment ist nur Felder aus.
Keines der Felder id_1 und id_2 ist auto_increment zugeordnet.
Aus dem Grund, obwohl Sie null als Eingabe für diese Felder beim Einfügen übergeben, wird kein Wert automatisch generiert und ihnen zugewiesen.

Ändern Sie Ihre Tabelle, um auto_increment auf eines dieser Felder id_x festzulegen, und beginnen Sie dann, Werte einzufügen. Eine Vorsichtsmaßnahme besteht darin, dass die einmalige Übergabe eines Werts an ein Feld auto_increment während des Einfügens dazu führt, dass last_insert_id einen zero oder den letzten automatisch generierten Wert zurückgibt, nicht jedoch die NEW.id. Wenn Sie ein null übergeben oder das Feld auto_increment während des Einfügens nicht auswählen, wird die Generierung eines NEUEN Werts für dieses Feld ausgelöst, und last_insert_id kann es auswählen und zurückgeben.

folgendes Beispiel zeigt obige Verhalten:

mysql> drop table if exists so_q27476005; 
Query OK, 0 rows affected, 1 warning (0.00 sec) 

mysql> create table so_q27476005(i int primary key); 
Query OK, 0 rows affected (0.33 sec) 

folgende Anweisung zeigt neben anwendbar auto_increment Wert für ein Feld.

mysql> select auto_increment 
    -> from information_schema.tables 
    -> where table_name='so_q27476005'; 
+----------------+ 
| auto_increment | 
+----------------+ 
|   NULL | 
+----------------+ 
1 row in set (0.00 sec) 

uns versuchen lassen einen null Wert in das Feld einfügen.

mysql> insert into so_q27476005 values(null); 
ERROR 1048 (23000): Column 'i' cannot be null 

obige Aussage schlug fehl, da Eingabe in ein not null primary key Feld war aber nicht für auto_increment zugeschrieben. Nur für auto_increment Felder können Sie null Eingaben übergeben.

Nun wollen wir das Verhalten von last_insert_id sehen:

mysql> insert into so_q27476005 values(1); 
Query OK, 1 row affected (0.04 sec) 

mysql> select last_insert_id(); 
+------------------+ 
| last_insert_id() | 
+------------------+ 
|    0 | 
+------------------+ 
1 row in set (0.00 sec) 

Da der Eingang explizit war und auch das Feld zugeschrieben wird, nicht für auto_increment,
Aufruf zur last_insert_id führte eine 0.Beachten Sie, dass dies auch ein anderer Wert sein kann, , wenn ein anderer insert Aufruf für ein anderes Feld auto_increment einer anderen Tabelle, in derselben Datenbankverbindungssitzung war.

Lassen Sie uns die Datensätze in der Tabelle sehen.

mysql> select * from so_q27476005; 
+---+ 
| i | 
+---+ 
| 1 | 
+---+ 
1 row in set (0.00 sec) 

Nun wollen wir uns iauto_increment auf das Gebiet gelten.

mysql> alter table so_q27476005 change column i i int auto_increment; 
Query OK, 1 row affected (0.66 sec) 
Records: 1 Duplicates: 0 Warnings: 0 

folgende Anweisung zeigt neben anwendbar auto_increment Wert für das Feld i.

mysql> select auto_increment 
    -> from information_schema.tables 
    -> where table_name='so_q27476005'; 
+----------------+ 
| auto_increment | 
+----------------+ 
|    2 | 
+----------------+ 
1 row in set (0.00 sec) 

können Sie überqueren prüfen, ob die last_insert_id immer noch das gleiche ist.

mysql> select last_insert_id(); 
+------------------+ 
| last_insert_id() | 
+------------------+ 
|    0 | 
+------------------+ 
1 row in set (0.00 sec) 

Lassen Sie uns i einen null Wert in das Feld ein.

mysql> insert into so_q27476005 values(null); 
Query OK, 1 row affected (0.03 sec) 

Es gelang eine null ein primary key Feld allerdings vorbei, weil das Feld für auto_increment zugeschrieben wird.
Lassen Sie uns sehen, welcher Wert generiert und eingefügt wurde.

mysql> select last_insert_id(); 
+------------------+ 
| last_insert_id() | 
+------------------+ 
|    2 | 
+------------------+ 
1 row in set (0.00 sec) 

Und der nächste anwendbar auto_increment Wert für das Feld i ist:

mysql> select auto_increment 
    -> from information_schema.tables 
    -> where table_name='so_q27476005'; 
+----------------+ 
| auto_increment | 
+----------------+ 
|    3 | 
+----------------+ 
1 row in set (0.00 sec) 

mysql> select * from so_q27476005; 
+---+ 
| i | 
+---+ 
| 1 | 
| 2 | 
+---+ 
2 rows in set (0.00 sec) 

Nun lassen Sie uns beobachten, wie last_insert_id Ergebnisse, wenn explizite Eingabe für das Feld gegeben ist. siehe

mysql> insert into so_q27476005 values(3); 
Query OK, 1 row affected (0.07 sec) 

mysql> select * from so_q27476005; 
+---+ 
| i | 
+---+ 
| 1 | 
| 2 | 
| 3 | 
+---+ 
3 rows in set (0.00 sec) 


mysql> select last_insert_id(); 
+------------------+ 
| last_insert_id() | 
+------------------+ 
|    2 | 
+------------------+ 
1 row in set (0.00 sec) 

können Sie, dass last_insert_id nicht den Wert erfassen aufgrund explizite Eingabe.
Aber Informationsschema registriert nächsten gültigen Wert. Jetzt

mysql> select auto_increment 
    -> from information_schema.tables 
    -> where table_name='so_q27476005'; 
+----------------+ 
| auto_increment | 
+----------------+ 
|    4 | 
+----------------+ 
1 row in set (0.08 sec) 

, lassen Sie uns beobachten, wie last_insert_id Ergebnisse, wenn die Eingabe für das Feld Auto ist/implizit.

mysql> insert into so_q27476005 values(null); 
Query OK, 1 row affected (0.10 sec) 

mysql> select last_insert_id(); 
+------------------+ 
| last_insert_id() | 
+------------------+ 
|    4 | 
+------------------+ 
1 row in set (0.00 sec) 

Hoffe, diese Details helfen Ihnen.

+0

Vielen Dank für die ausführliche Erklärung. Ich habe ein Problem damit, die 'id_2'-Spalte auf auto-increment zu setzen. Ich bekomme 'Fehlercode: 1075. Falsche Tabellendefinition; es kann nur eine automatische Spalte geben und sie muss als Schlüssel definiert werden. Meine Situation ist, dass ich den Wert von "id_1" kenne (der durch die Anwendung definiert wird), aber ich kenne den Wert von "id_2" nicht. Für jede eindeutige ID_1 muss ich also "id_2" Auto-Inkrement haben. Also die Spalte, die ich hätte Auto-Inkrement haben soll, ist "id_2". – Xeos

+0

Fehlermeldung ist ziemlich klar. Sie können nur ein * 'auto_increment' * -Feld pro Tabelle haben. Ich schlage vor, dass Sie Ihre Frage als neuen Beitrag neu einrahmen. erkläre, was du eigentlich willst. –

+0

Ich habe eine kurze Beschreibung des Systems hinzugefügt, das diese Tabelle zu der Frage verwendet. – Xeos