MySQL: Primary Key - automatische Rückgabe

Mik3e

Erfahrenes Mitglied
Hallo zusammen!

Ich arbeite klassisch mit MySQL & PHP, basierend auf PEAR.
Dabei bin ich auf folgendes "Problem" gestoßen:

Ich schreibe Datensätze in eine Tabelle, die mit einem künstlichen und automatischen Primary Key versehen ist (BIGINT[20] | AUTO INCREMENT).

Über ein simples Insert Statement wird in dieser Tabelle ein Wert hinzugefügt. Nun hätte ich gern, dass der Wert des Primary Key des neuen Datensatzes zurückgeliefert wird, da ich in der selben Methode (zweites SQL Statement) diesen Primary Key als Foreign Key in eine Assoziative Tabelle schreiben muss.

Ich weiß, es gibt natürlich die Möglichkeit, nach dem Insert mit neuem SELECT Statement und MAX() den höchsten Key auszulesen. Das ganze ist aber nicht sonderlich sicher, da die Datensätze zwischen Anlegen und Auslesen des Keys nicht gelockt werden. Theoretisch könnte also ein zweiter Prozess genau zwischen diese beiden Statements greifen, wodurch der ausgelesene Primary Key natürlich nicht mehr stimmt.

Gibt es bei MySQL oder PEAR irgendwie eine Möglichkeit, den Primärschlüssel automatisch und in einem Schritt zurückzugeben (eine Art RETURN Wert vom Insert Statement)?

PHP:
function CREATE_Rabattstufe($testwert)
	{
	$sql = ' INSERT INTO '
  	        . ' `tbl_test` '
	        . ' (`test_spalte`) '
	        . ' VALUES '
	        . ' (\''.$testwert.'\') ';
	$result=$this->_db->query($sql);

                -------------------------------------------------------------------------------------
                 HIER RÜCKGABE DES ANGELEGTEN PRIMÄRSCHLÜSSELS !
                In Pseudocode:
                $this->_db->GET_CREATED_PRIMARYKEY($result);   
                -------------------------------------------------------------------------------------

	if (mysql_errno()==0) {
		$errormsg='';
	} else {
		$errormsg=mysql_errno).'/'.mysql_error();
	}
}

Ich hoffe ich habe mein Anliegen verständlich erklärt und Ihr könnt mir weiterhelfen...
Möglicherweise gibts auch eine andere Workaround Lösung. Vom künstlichen Primärschlüssel komme ich nicht weg....

Danke & LG
Mike
 
Habe nun weiter recherchiert und habe festgestellt, dass man um ein Transaction Lock nicht herum kommt. Es gibt zwar Funktionen, die im Vorhinein ermitteln, welcher Primärschlüssel der nächste sein wird, diese sind aber natürlich auch unsicher.

Hier die Funktion als kleines "Tutorial" wie ich Sie nun für diese Thema verwende (nachdem ich denke, dass ich nicht der einzige mit diesem Problem bin):

PHP:
	function CREATE_Rabattstufe($katid, $steuersatz, $bezeichnung,$preis_ek,$preis_vk,$buchbar_von_datetime,$buchbar_bis_datetime,$sysuser_freischaltung)
	{
		// TABELLE RABATTSTUFFE LOCK
		$sql = ' LOCK TABLES `tbl_rabattstufe` WRITE';
		$this->_db->query($sql);
		if (mysql_errno()==0) {
			$errormsg='';
		} else {
			$errormsg='Interner Fehler aufgetreten:<br>'.mysql_errno().'/'.mysql_error();
		}
		// ENDE TABELLE RABATTSTUFFE LOCK
		
		// ANLEGEN DER NEUEN RABATTSTUFE
		$sql = ' INSERT INTO '
			 . ' `tbl_rabattstufe` '
			 . ' (`fk_kategorie_id`,`fk_steuersatz_id`,`rabattstufe_bezeichnung`,`rabattstufe_preis_ek_brutto`,`rabattstufe_preis_vk_brutto`,`rabattstufe_buchbar_von`,`rabattstufe_buchbar_bis`) '
			 . ' VALUES '
			 . ' (\''.$katid.'\',\''.$steuersatz.'\',\''.$bezeichnung.'\',\''.$preis_ek.'\',\''.$preis_vk.'\',\''.$buchbar_von_datetime.'\',\''.$buchbar_bis_datetime.'\') ';
		$this->_db->query($sql);
		if (mysql_errno()==0) {
			$errormsg='';
		} else {
			$errormsg='Interner Fehler aufgetreten:<br>'.mysql_errno().'/'.mysql_error();
		}
		
		// AUSLESEN DES PRIMÄRSCHLÜSSELS DER NEUEN RABATTSTUFE
		$sql = ' SELECT '
			. ' MAX(`pk_rabattstufe_id`) AS `pk_rabattstufe_id_created` '
			. ' FROM '
			. ' `tbl_rabattstufe` ';
		$result=$this->_db->query($sql);
		if (mysql_errno()==0) {
			$errormsg='';
		} else {
			$errormsg='Interner Fehler aufgetreten:<br>'.mysql_errno().'/'.mysql_error();
		}
		
		// NEUEN PRIMÄRSCHLÜSSEL IN VARIABLE HINTERLEGEN
		while($row = $result->fetchRow(DB_FETCHMODE_ASSOC)):
			$pk_rabattstufe_id_created=$row['pk_rabattstufe_id_created'];
		endwhile;
		
		// TABELLE RABATTSTUFFE UNLOCK
		$sql = ' UNLOCK TABLES';
		$this->_db->query($sql);
		if (mysql_errno()==0) {
			$errormsg='';
		} else {
			$errormsg='Interner Fehler aufgetreten:<br>'.mysql_errno().'/'.mysql_error();
		}
		// ENDE TABELLE RABATTSTUFFE UNLOCK
		
		// ANLEGEN DER WERTE IN DER ASSOZIATIVEN TABELLE
		foreach ($sysuser_freischaltung as $key => $value) {
			$sql = ' INSERT INTO '
			 . ' `tbl_sysuser_join_rabattstufe` '
			 . ' (`fk_sysuser_id`,`fk_rabattstufe_id`) '
			 . ' VALUES '
			 . ' (\''.$value.'\',\''.$pk_rabattstufe_id_created.'\') ';
			$this->_db->query($sql);
			if (mysql_errno()==0) {
				$errormsg='';
			} else {
				$errormsg='Interner Fehler aufgetreten:<br>'.mysql_errno().'/'.mysql_error();
			}
		}
		return $errormsg;
	}

LG
Mike
 
Es geht einfacher, mit SELECT LAST_INSERT_ID()
Die letzte ID, die erzeugt wurde, wird im Server für jede Verbindung separat gespeichert. Sie wird nicht durch andere Clients geändert.
Du benötigst also keine umständlichen Transaktionen.
Erst das INSERT INTO ..., dann SELECT LAST_INSERT_ID(), und dann das nächste INSERT INTO ... mit der ermittelten ID.
 
Stimmt.. war noch eine Testvariante.. hier die elegante Lösung:

PHP:
	function CREATE_Rabattstufe($katid, $steuersatz, $bezeichnung,$preis_ek,$preis_vk,$buchbar_von_datetime,$buchbar_bis_datetime,$sysuser_freischaltung)
	{
		
		// ANLEGEN DER NEUEN RABATTSTUFE
		$sql = ' INSERT INTO '
			 . ' `tbl_rabattstufe` '
			 . ' (`fk_kategorie_id`,`fk_steuersatz_id`,`rabattstufe_bezeichnung`,`rabattstufe_preis_ek_brutto`,`rabattstufe_preis_vk_brutto`,`rabattstufe_buchbar_von`,`rabattstufe_buchbar_bis`) '
			 . ' VALUES '
			 . ' (\''.$katid.'\',\''.$steuersatz.'\',\''.$bezeichnung.'\',\''.$preis_ek.'\',\''.$preis_vk.'\',\''.$buchbar_von_datetime.'\',\''.$buchbar_bis_datetime.'\') ';
		$this->_db->query($sql);
		if (mysql_errno()==0) {
			$errormsg='';
		} else {
			$errormsg='Interner Fehler aufgetreten:<br>'.mysql_errno().'/'.mysql_error();
		}
		
		// AUSLESEN DES PRIMÄRSCHLÜSSELS DER NEUEN RABATTSTUFE
		$sql = ' SELECT LAST_INSERT_ID() AS `pk_rabattstufe_id_created` ';
		$result=$this->_db->query($sql);
		if (mysql_errno()==0) {
			$errormsg='';
		} else {
			$errormsg='Interner Fehler aufgetreten:<br>'.mysql_errno().'/'.mysql_error();
		}
		
		// NEUEN PRIMÄRSCHLÜSSEL IN VARIABLE HINTERLEGEN
		while($row = $result->fetchRow(DB_FETCHMODE_ASSOC)):
			$pk_rabattstufe_id_created=$row['pk_rabattstufe_id_created'];
		endwhile;
		
		// ANLEGEN DER WERTE IN DER ASSOZIATIVEN TABELLE
		foreach ($sysuser_freischaltung as $key => $value) {
			$sql = ' INSERT INTO '
			 . ' `tbl_sysuser_join_rabattstufe` '
			 . ' (`fk_sysuser_id`,`fk_rabattstufe_id`) '
			 . ' VALUES '
			 . ' (\''.$value.'\',\''.$pk_rabattstufe_id_created.'\') ';
			$this->_db->query($sql);
			if (mysql_errno()==0) {
				$errormsg='';
			} else {
				$errormsg='Interner Fehler aufgetreten:<br>'.mysql_errno().'/'.mysql_error();
			}
		}
		return $errormsg;
	}
 
Danke, Du bist ein Lichtblick.
Du versucht selbst eine Lösung zu finden, nimmst Lösungshinweise an und entwickelst daraus sebstständig Quellcode, den Du für Nutzer der Suchfunktion auch noch veröffentlichst.
Und Du schreibst auch noch unter Verwendung der Shift-Taste mit klarem Betreff und Angabe des DBMS und der Programmiersprache.
Neben der ganzen Meckerei über User, die ihr Problem nicht formulieren können und alles vorgekaut bekommen wollen, muss an dieser Stelle mal Lob erlaubt sein: Mach weiter so!

Gruß hpvw

PS: Falls jemand jetzt noch anmerkt, dass es auch [phpf]mysql_insert_id[/phpf] in PHP gibt: Ja gibt es, aber die Funktion kann bei BIGINT falsche Ergebnisse liefern, daher ist die MySQL-SELECT-Variante hier angebracht.
 
Zuletzt bearbeitet:
Zurück