Datenbank-Werte mit übergebenen Form-Werten vergleichen
borisbaer
- php
Ich sitze seit Stunden an einem Problem, das ich einfach nicht lösen kann: Ich möchte, dass beim Schreiben in die DB überprüft wird, ob eine Reihe mit einer bestimmten ID bereits vorhanden ist und wenn ja, soll diese aktualisiert werden, ansonsten soll eine neue Reihe erstellt werden.
Das Formular übergibt mir die ID des Inputs sowie dessen Wert (von 0 bis 2). Bei drei Inputs sieht das so aus:
Array
(
[3] => 0
[7] => 1
[5] => 2
)
Wichtig ist, dass die IDs in keiner chronologischen Abfolge auftreten müssen. In die Datenbank sollen nur Reihen geschrieben werden, die nicht den Wert 0 haben. In obigem Beispiel wären das die [7] und die [5]. Wenn ich dann die DB-Tabelle auslese, erscheint richtigerweise Folgendes:
$dbTable = Array
(
[0] => Array
(
[id] => 7
[value] => 1
)
[1] => Array
(
[id] => 5
[value] => 1
)
)
Bei der nächsten Formular-Übergabe soll das Skript schauen, ob in der DB eine entsprechende ID schon vorhanden ist. Wenn ja, soll der Wert aktualisiert werden, wenn nicht, soll eine neue Reihe erstellt werden. Hingekriegt habe ich nur, dass die richtigen IDs bei Bedarf aktualisiert werden, aber beim Erstellen neuer Reihen schießt mir ein doppelter foreach-Loop quer, denn es werden gleich mehrere Reihen erstellt. Hier der Code-Auszug:
foreach ( $_POST['input'] as $id => $value ) {
foreach ( $dbTable as $key => $row ) {
if ( $row['id'] === ( int ) $id ) {
$ReleasesModel -> update();
}
}
}
Ich bitte um Hilfe! 😞
Vorausgesetzt, Deine „ID“ ist zwingend unique:
https://dev.mysql.com/doc/refman/8.0/en/insert-on-duplicate.html
Die ID wird es mehrmals in der Tabelle geben. Es ist quasi die Objekt-ID und sie wird für die jeweiligen Benutzer-ID manipuliert. Ich habe das der Einfachheit halber aber bei meinem ursprünglichen Beitrag nicht mit einbezogen.
Moin,
Die ID wird es mehrmals in der Tabelle geben. Es ist quasi die Objekt-ID und sie wird für die jeweiligen Benutzer-ID manipuliert. Ich habe das der Einfachheit halber aber bei meinem ursprünglichen Beitrag nicht mit einbezogen.
Aber die Kombination aus Benutzer-ID und Objekt-ID ist eindeutig, oder? Das klappt dann trotzdem, im INSERT-Query müssen halt alle drei Felder genannt werden.
Gruß
Tobias
Moin,
Die ID wird es mehrmals in der Tabelle geben. Es ist quasi die Objekt-ID und sie wird für die jeweiligen Benutzer-ID manipuliert. Ich habe das der Einfachheit halber aber bei meinem ursprünglichen Beitrag nicht mit einbezogen.
Aber die Kombination aus Benutzer-ID und Objekt-ID ist eindeutig, oder? Das klappt dann trotzdem, im INSERT-Query müssen halt alle drei Felder genannt werden.
Jepp. Und der genannte „UNIQUE Constraint“ muss sich dann über die Spalten erstecken, die in jeglicher Kombination maximal einmal auftauchen sollen.
@@borisbaer
ALTER TABLE `tab` (
UNIQUE KEY ( `UserID`, `ObjectID` )
)
Die Benennung eines Feldes mit „ID“, welches keine echte ID ist, ist höchst unglücklich. Das wird zu Fehlern führen, weil es im sachlichen Zusammenhang einfach mal keine ID ist, aber jeder wegen des Bezeichners denkt, es sei eine solche - und also sei jede einzigartig…
„Ich lösche mal fix einen Datensatz:“
DELETE FROM `tab` WHERE ID=121;
346967 Zeilen gelöscht.
„Oh.“
Jepp. Und der genannte „UNIQUE Constraint“ muss sich dann über die Spalten erstecken, die in jeglicher Kombination maximal einmal auftauchen sollen.
Gut zu wissen, danke!
ALTER TABLE `tab` ( UNIQUE KEY ( `UserID`, `ObjectID` ) )
Dieser Befehl hat leider nicht funktioniert, dafür aber dieser hier …
ALTER TABLE `table` ADD UNIQUE ( `UserID`, `ObjectID` )
Die Benennung eines Feldes mit „ID“, welches keine echte ID ist, ist höchst unglücklich. Das wird zu Fehlern führen, weil es im sachlichen Zusammenhang einfach mal keine ID ist, aber jeder wegen des Bezeichners denkt, es sei eine solche - und also sei jede einzigartig…
Danke für den Hinweis, dann werde ich die Benennung überdenken!
„Ich lösche mal fix einen Datensatz:“
DELETE FROM `tab` WHERE ID=121; 346967 Zeilen gelöscht.
„Oh.“
🤯
Moin,
Ich sitze seit Stunden an einem Problem, das ich einfach nicht lösen kann: Ich möchte, dass beim Schreiben in die DB überprüft wird, ob eine Reihe mit einer bestimmten ID bereits vorhanden ist und wenn ja, soll diese aktualisiert werden, ansonsten soll eine neue Reihe erstellt werden.
Das klingt – vorausgesetzt du verwendest MySQL/MariaDB[1] und auf den IDs liegt ein Unique-Index o.ä. – nach einem Fall für INSERT … ON DUPLICATE KEY, etwa so:
INSERT INTO tabelle (id, value) VALUES (:id, :value) as new
ON DUPLICATE KEY UPDATE value = new.value;
Dein doppeltes foreach brauchst du dann natürlich nicht und :id/:value sind Platzhalter für prepared Statements. Zu klären wäre natürlich noch was passieren soll wenn ein Wert 0 ist (dann müsste der Datensatz ggf. gelöscht werden).
Gruß
Tobias
das ist kein SQL-Standard deswegen nur MySQL/MariaDB - andere DBMS haben aber wohl ähnliche Lösungen dafür, Postgresql z.B. INSERT … ON CONFLICT ↩︎
Das klingt – vorausgesetzt du verwendest MySQL/MariaDB[^1] und auf den IDs liegt ein Unique-Index o.ä. – nach einem Fall für INSERT … ON DUPLICATE KEY, etwa so:
INSERT INTO tabelle (id, value) VALUES (:id, :value) as new ON DUPLICATE KEY UPDATE value = new.value;
Ja, MariaDB wird verwendet.
Vielen Dank, das funktioniert so tatsächlich. Aber das as new
und dann value = new.value
will er irgendwie nicht schlucken. Liegt vielleicht daran, dass ich mit bindValue arbeite, aber wenn ich ganz normal value = :value am Ende als Syntax habe, geht der Befehl.
Dein doppeltes foreach brauchst du dann natürlich nicht und :id/:value sind Platzhalter für prepared Statements. Zu klären wäre natürlich noch was passieren soll wenn ein Wert 0 ist (dann müsste der Datensatz ggf. gelöscht werden).
Mich würde dennoch interessieren, ob und wie man das auch mit PHP-Schleifen bewerkstelligen könnte. Aber ich konnte mir einfach keine abstrakte Logik dazu ausdenken, die funktioniert.
Gruß
Boris
Moin,
Vielen Dank, das funktioniert so tatsächlich. Aber das
as new
und dannvalue = new.value
will er irgendwie nicht schlucken. Liegt vielleicht daran, dass ich mit bindValue arbeite, aber wenn ich ganz normal value = :value am Ende als Syntax habe, geht der Befehl.
Nein, das liegt daran dass du unter MariaDB noch mit VALUES(value)
arbeiten musst, siehe Handbuch – bei MySQL ist der Syntax seit Version 8.0.20 als veraltet eingestuft.
Mich würde dennoch interessieren, ob und wie man das auch mit PHP-Schleifen bewerkstelligen könnte. Aber ich konnte mir einfach keine abstrakte Logik dazu ausdenken, die funktioniert.
Du bräuchtest vor dem inneren foreach noch eine Variable (z.B. $gefunden = false;
) welche du innerhalb des if-Blocks auf true
setzt. Wenn $gefunden
dann nach der inneren foreach-Schleife immer noch false
ist, gibt es den Datensatz noch nicht und du müsstest ein INSERT ausführen.
Gruß
Tobias
Hallo Tobias,
der Syntax
autsch, gute Besserung. Es heißt im Deutschen die Syntax.
Wahrscheinlich angelehnt an die Satzlehre.
Einen schönen Tag noch
Martin
Nein, das liegt daran dass du unter MariaDB noch mit
VALUES(value)
arbeiten musst, siehe Handbuch – bei MySQL ist der Syntax seit Version 8.0.20 als veraltet eingestuft.
Ja, mein Erklärungsversuch mit bindValue ergibt keinen Sinn.
Du bräuchtest vor dem inneren foreach noch eine Variable (z.B.
$gefunden = false;
) welche du innerhalb des if-Blocks auftrue
setzt. Wenn$gefunden
dann nach der inneren foreach-Schleife immer nochfalse
ist, gibt es den Datensatz noch nicht und du müsstest ein INSERT ausführen.
Boah, na klar. Mann, vielen Dank, Tobias. Die Lösung ist so einfach und ich habe da sonst was für Verrenkungen angestellt. Das merke ich mir auf jeden Fall fürs nächste Mal. 🙏
So sieht das nun aus:
$isNew = true;
foreach ( $releases as $row )
if ( $row['id'] === $id )
$isNew = false;
if ( $isNew === true && $value > 0 )
$Releases -> create();
else $Releases -> update();
header( 'Location:' . $_SERVER['HTTP_REFERER'] );
Muss nur noch was rein, damit die Reihe wieder gelöscht wird, wenn der aktualisierte Wert 0 entspricht. Aber das geht ja wieder leicht.
Grüße
Boris