Erfolg bei Abfrage - Logikproblem

Zero2000

Erfahrenes Mitglied
Hallo,

aus einer MySQL-DB lese ich eine ganze Reihe Datensätze aus, bei denen ich eine Stk.-Anzahl ändern kann und dann wieder zurück in die DB schreibe. Dazu habe ich die Stk.-Anzahl in einem Textfeld drin. Demzufolge ist die alte Anzahl gleich die neue Anzahl wenn nichts geändert wird.

Nun habe ich das Problem, dass zwar die Felder geändert werden die ich ändere, aber wenn der letzte Datensatz nicht geändert wird ist mysql_num_rows() ja 0 und somit ändert er mir zwar die Bestände gibt mir aber bei meiner IF-Abfrage logischerweise aus das nichts geändert wurde.

Nachfolgend mal der Code damit man sich das besser vorstellen kann.

PHP:
if(isset($_POST['tl_bestand']))
{
$nbestand = $_POST['nbestand'];
$dsteid = $_POST['dsteid'];

$arranzahl = count($nbestand); // Anzahl der Datensätze aus der Suche

for($i=0;$i<$arranzahl;$i++)
{
mysql_query("UPDATE tl_textilien SET anzahl = ".$nbestand[$i]." WHERE id = ".$dsteid[$i]."")or die("FEHLER:<br><br>".mysql_error());
}

$num = mysql_affected_rows();

if ($num>0)
{
  echo '<div class="container" id="nachricht"><div class="alert alert-success" role="alert">';
  echo 'Der Bestand wurde geändert.';
  echo '</div></div>';
  }
else
{
  echo '<div class="container" id="nachricht"><div class="alert alert-danger" role="alert">';
  echo '<strong>Es ist ein Fehler aufgetreten, der Bestand konnte nicht geändert werden!</strong>';
  echo '</div></div>';
  }
}
 
Hi @Zero2000 !
  1. Spreche ich hier wieder gern @sheel aus der Seele, dass du noch die alten (bald nicht mehr verfügbaren) MySQL-Befehle verwendest und du lieber die neueren MySQLi oder PDO befehle nehmen sollst.
  2. Solltest du niemals die $_POST und $_GET Inhalte, direkt mit dem SQL-Code ausführen, sondern diese vorher überprüfen und entschärfen oder noch besser mit Prepared Statements dabei arbeiten.
  3. Empfehle ich dir, sende in einem Teil der Session oder per Hidden-Formularfeld, die unveränderten Werte, dass du diese ohne Datenabfrage bereits überprüfen kannst, ob sie sich geändert haben.
  4. Empfehle ich dir, das du überprüfst, ob der Wert aus dem Formular ein gültiger Wert für das Datenbankfeld ist. Dies kannst du ebenfalls gut, mit dem Prepared Statements abdecken.
Zu deinem Fehler, habe ich jetzt noch nichts genaues gefunden, aber es scheint auch nicht der komplette betreffende Code zu sein, weil über dem Code schreibst du was von mysql_num_rows() und dieser Befehl, befindet sich nicht in dem Code.
 
Zuletzt bearbeitet:
Die Nummer der tatsächlich aktualisierten Datensätze (also mit echter Änderung) kannst du ermitteln, indem du in der Schleife mitzählst.

Code:
$num = 0;
for (...) {
    // ...
    $num += mysql_affected_rows();
}

Das könnte aber dennoch natürlich $num = 0 ergeben, wenn sich gar kein Datensatz geändert hat.

Das wäre erst mal kein Fehler. Dann hättest du eben in der Ausgabe so was wie: „Operation erfolgreich. Keine Datensätze verändert, da alle Daten identisch.“

---

Am Rande:

mysql_affected_rows liefert nur leider auch 0, wenn das WHERE im UPDATE keinen Datensatz auswählt, weil etwa kein Datensatz mit der passenden ID existiert. Das wäre möglicherweise ein Fehler. Nur möglicherweise, weil der Datensatz in der Zwischenzeit regulär gelöscht worden sein könnte.

Wenn du mit der Info dennoch irgendwie arbeiten möchtest (weiß nicht, ob das Sinn ergibt, wenn man das durchdenkt), kommst du wahrscheinlich nicht umhin, vor jedem UPDATE per SELECT zu checken, ob der Datensatz mit der passenden ID existiert. Das müsste dann vermutlich noch in einer Transaktion passieren, wenn es ganz genau sein soll.

(Alternativ wäre statt SELECT noch Gebastel mit http://php.net/manual/en/function.mysql-info.php denkbar, aber das würde ich nicht als robust ansehen.)

Das garantiert aber im Grunde auch nicht, dass die IDs „richtig“ sind, weil falsche IDs, die durch einen Programmierfehler entstanden sind, auf andere existierende Datensätze zeigen könnten. Um das möglichst auszuschließen, müsstest du wohl einen entsprechend komplexen Unit-Test dafür entwickeln.
 
Zuletzt bearbeitet:
(OT und nicht wirklich auf irgendwas bezogen und auch eher allgemeiner Natur.)

Wobei es schon ein wenig darauf ankommt, was jetzt „andere Sachen“ genau meint. Man kann sich bei so DB-Zeugs schon sehr bizarre Bugs fabrizieren, die man ohne recht ordentliche Test-Infrastruktur nicht leicht entdeckt, weil sie selten bis nie zu gesicherten Fehlerzuständen führen.

Ein (simplifiziertes) Beispiel wäre so was:

PHP:
$id = (int) $_POST['id'];
$id--; // Irgendein Code, der -- wie auch immer -- den
       // Wert unbeabsichtigt verändert

$query = "UPDATE table SET field = 'whatever' WHERE id = $id";

Das ist natürlich sehr konstruiert, aber für das Datenbanksystem ist die Query absolut korrekt, auch wenn $id offenkundig den falschen Wert hat. Da wird keiner der üblichen Mechanismen einen Fehler melden, weil es sich eben um einen Highlevel-Logikfehler auf Applikationsebene handelt. (Sorry, ich weiß gerade nicht, ob es dafür einen passenderen Begriff gibt.)

Auch bei Nicht-DB-Code ist das nicht großartig anders, aber aus einem Grund, den ich nicht genau benennen kann, wird mir so was bei DB-Code immer sehr bewusst. Vielleicht deshalb, weil es so verhältnismäßig schwierig/umständlich ist, Unit-Tests dafür zu schreiben.


Oder eben auch Race-Conditions/„Time of check to time of use“-Geschichten:

PHP:
$result = runQuery("SELECT id FROM table WHERE id = $id LIMIT 1");

if ($result !== null) {
    runQuery("UPDATE table SET field = 'whatever' WHERE id = $id");
}

Da die beiden Queries keine atomare Einheit (Transaktion[1] oder dergleichen) bilden, ist beim zweiten Aufruf von runQuery in keiner Weise gesichert, dass der Datensatz tatsächlich noch existiert.

So was muss nicht zu Problemen führen, aber es ist optimistisch programmiert. Wird schon schiefgehen. Ich weiß zum Beispiel ehrlich gesagt nicht, wann ich in einem PHP-Forum in irgendwelchem Code zuletzt eine Transaktion gesehen habe.



1: http://de.wikipedia.org/wiki/ACID, http://de.wikipedia.org/w/index.php...nbank)&oldid=131353235#M.C3.B6gliche_Probleme
 
Zuletzt bearbeitet:
Zurück