Postgre SQL Sequenz Problematik
Siramon
- datenbank
0 André Laugks0 Siramon
Hallo Forum,
Folgende Problematik:
Ich habe zwei Tabellen in einer Postgre-Datenbank:
tabelle1
+------+--------+
| id1 | desc |
+------+--------+
id1: bint, not null, primary, default: nextval('tabelle1_id_seq')
tabelle2
+------+--------+---------+
| id2 | id1 | name |
+------+--------+---------+
(Das ist nur eine Beispielstruktur! Bitte nicht über deren Sinn diskutieren *g*)
"Id1" hat eine Sequenz namens ("tabelle1_id_seq" --> default values!)
******* dump.sql ******
-- tabelle 1 erstellen
CREATE TABLE "public"."tabelle1" (
"id1" BIGINT NOT NULL,
"desc" CHAR(50),
PRIMARY KEY("id1")
) WITH OIDS;
-- sequenz erstellen
CREATE SEQUENCE "tabelle1_id_seq";
-- default wert für primary setzten
ALTER TABLE "public"."tabelle1" ALTER COLUMN "id1" SET DEFAULT nextval('tabelle1_id_seq');
*******
Ziel ist: Ich möchte eine neue Zeile in die Tabelle1 schreiben und brauche die neue Id für die Eingabe in Tabelle2 (foreign key).
(Tabelle2 ist nur zur Veranschaulichung und wird für die Testumgebung nicht benötigt)
Ouso, ich habe folgenden Test-Code in php:
****** test.php ******
<?
$conn = pg_connect("host=xxx.xxx.xxx.xxx port=5432 dbname=db1 user=user1 password=*****");
// zuerst die nächste id in der sequenz auslesen
$sql = "select nextval('tabelle1_id_seq') as nextId from tabelle1;";
$rs = pg_exec($conn, $sql);
$rn = pg_numrows($rs);
echo $rn; // geben wir das mal aus
if ($rn > 0) $nextIdArr = pg_fetch_array($rs,0);
print_r($nextIdArr); // auch dieses brauchen wir zu debug-zwecken
pg_freeresult($rs);
$nextId = 0;
$nextId = $nextIdArr[nextid];
if ($nextId == 0) {
$nextId = 1; // ganz lustig und unsauber. [1]
}
$sql = "insert into tabelle1 (id1, desc)";
$sql .= " values ($nextId,";
$sql .= " 'test')";
echo $sql; // auch wieder zu debug-zwecken
pg_exec($conn, $sql)
?>
******
Also, das ganze mal ausgeben und es passiert folgendes:
----------------------
output --> "0insert into tabelle1 (id1, "desc") values (1, 'test')"
Hit-Reload
output --> "duplicated key" --> siehe [1]
Hit-Reload
output --> "1Array ( [0] => 2 [nextid] => 2 ) insert into tabelle1 (id1, "desc") values (2, 'test')"
Hit-Reload
output --> "2Array ( [0] => 3 [nextid] => 3 ) insert into tabelle1 (id1, "desc") values (3, 'test')"
Hit-Reload
output --> "3Array ( [0] => 5 [nextid] => 5 ) insert into tabelle1 (id1, "desc") values (5, 'test')"
Hit-Reload
output --> "4Array ( [0] => 8 [nextid] => 8 ) insert into tabelle1 (id1, "desc") values (8, 'test')"
Hit-Reload
output --> "5Array ( [0] => 12 [nextid] => 12 ) insert into tabelle1 (id1, "desc") values (12, 'test')"
------------------------
Erklärung:
Bei der ersten Ausgabe muss ich den Primary Key faken [1], weil mir "select nextval('tabelle1_id_seq') as nextId from tabelle1;" nichts zurück gibt. Hier würde ich eigentlich "1" erwarten.
Beim Reload gibt es natürlich einen Fehler weil mir das "nextval"-Statement wieder "1" zurückgibt (es wurde anscheinend erst jetzt initialisiert).
Danach klappt es mit den "inserts" und jetzt taucht das nächste Problem auf:
Das "nextval"-Statment liefert mir je öfters aufgerufen, desto mehr "Recordsets" zurück. ("Primary-Key"-Pakete *g*)
Dementsprechend wird die verwendete ID folgendermassen hochgezählt:
k(n) = k(n-1) + (k(n-1) - k(n-2) + 1)
n >= 5
1, 2, 3, 5, 8, 12, 17 ...
Irgendwie unerklärlich und irgendwie doof :-(
Kann mir jemand bei diesem Problem auf die Sprünge helfen?
Auch [1] ist recht komisch, meiner Meinung nach müsste beim ersten "select nextval"-Statement schon eine "1" zurückgeliefert werden und nicht "nichts".
Vielen Dank schon mal!
Grüsse
Siramon,
ja der Penner aus Nr. 14
Hallo!
Ziel ist: Ich möchte eine neue Zeile in die Tabelle1 schreiben und brauche die neue Id für die Eingabe in Tabelle2 (foreign key).
(Tabelle2 ist nur zur Veranschaulichung und wird für die Testumgebung nicht benötigt)
Du benötigst also ein LAST_INSERT_ID?
Ein LAST_INSERT_ID kenn PostgreSQL nicht. PostgreSQL kennt aber ein "LAST_INSERT_OID", also man bekommt die zu letzt generierte Objekt-ID einer Sitzung, also wie LAST_INSERT_ID unter MySQL.
Jeder Datensatz wird in PostgreSQL mit einer OID versehen. Die Spalte "oid" legt PostgreSQL automatisch mit an. Ist aber nur mit einem explizitem Aufruf (SELECT oid, spalte FROM tabelle;) sichtbar.
// tabelle
$last_insert_oid = pg_last_oid($connect_id);
Nun liegt z.B. auf die Spalte "id" eine Sequence.
Mit
SELECT id FROM tabelle WHERE oid=$last_insert_oid;
bekommst Du nun die zuletzt eingefügte ID.
MfG, André Laugks
Quatsch
// tabelle
$last_insert_oid = pg_last_oid($connect_id);
$resource_result = pg_query($resource_connection, "SELECT ...");
$last_insert_oid = pg_last_oid($resource_result);
MfG, André Laugks
Hallo Forum,
Erstmal danke André für deine Hilfe.
Mit den Oids wird es leider nicht eindeutig :-(
Siehe die Kommentare auf http://ch.php.net/manual/en/function.pg-last-oid.php
Dort ist auch mein Ansatz mit der "Nextval" - Methode beschrieben.
Dank auch an Daniela für ihre Tipps im Chat!
Nochmals das ganze: ;-)
Folgende Problematik:
Ich habe zwei Tabellen in einer Postgre-Datenbank:tabelle1
+------+--------+
| id1 | desc |
+------+--------+id1: bint, not null, primary, default: nextval('tabelle1_id_seq')
tabelle2
+------+--------+---------+
| id2 | id1 | name |
+------+--------+---------+(Das ist nur eine Beispielstruktur! Bitte nicht über deren Sinn diskutieren *g*)
"Id1" hat eine Sequenz namens ("tabelle1_id_seq" --> default values!)
******* dump.sql ******
-- tabelle 1 erstellen
CREATE TABLE "public"."tabelle1" (
"id1" BIGSERIAL NOT NULL,
-- mit einer id vom typ serial oder bigserial werden sequenz und defaultvalue automatisch erstellt.
"desc" CHAR(50),
PRIMARY KEY("id1")
) WITH OIDS;-- sequenz erstellen
-- CREATE SEQUENCE "tabelle1_id_seq";
-- habs jetzt mit "bigserial" gelöst
-- default wert für primary setzten
-- ALTER TABLE "public"."tabelle1" ALTER COLUMN "id1" SET DEFAULT nextval('tabelle1_id_seq');
-- wird auch nicht mehr benötigt (wegen "bigserial")
*******
Ziel ist: Ich möchte eine neue Zeile in die Tabelle1 schreiben und brauche die neue Id für die Eingabe in Tabelle2 (foreign key).
(Tabelle2 ist nur zur Veranschaulichung und wird für die Testumgebung nicht benötigt)
Mit folgendem PHP Code funktioniert das ganze plötzlich. Schön, ich weiss nur nicht so genau, was ich zuvor falsch gemacht habe...
Für die Akten:
********** schnipp *************
$query = $_GET["seq"];
// zuerst die nächste id in der sequenz auslesen
for ($i = 0; $i < 1000; $i++) {
$sql = "select nextval('tabelle1_id1_seq') as nextid;";
$rs = pg_exec($conn, $sql);
$rn = pg_numrows($rs);
if ($rn > 0) {
$nextIdArr = pg_fetch_array($rs,0);
//if ($nextIdArr[nextid] > 1) {
$nextId = $nextIdArr[nextid];
//}
}
$sql = "insert into tabelle1 (id1, name)";
$sql .= " values ($nextId,";
$sql .= " 'test $i $query')";
pg_exec($conn, $sql);
pg_freeresult($rs);
echo "$i<br>";
}
************ schnapp ************
Das ganze funktioniert tadellos plötzlich *g*
Zum testen die Seite mit verschiedenen "?seq=xxx" aufrufen und dann in der Datenbank nachkucken.
Supi, dupi - alle glücklich *g*
Grüsse
Siramon,
ja der Penner aus Nr. 14
Hallo!
Erstmal danke André für deine Hilfe.
Mit den Oids wird es leider nicht eindeutig :-(
Bei mir schon!
MfG, André Laugks