Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > de.comp.lang.php > #3551

Re: Problem mit PDO

From Thomas 'PointedEars' Lahn <PointedEars@web.de>
Newsgroups de.comp.lang.php
Subject Re: Problem mit PDO
Date 2015-11-15 14:23 +0100
Organization PointedEars Software (PES)
Message-ID <15739846.o4CBAyVrKO@PointedEars.de> (permalink)
References <n283h2$e0d$1@news.albasani.net> <56479A45.3090901@arnowelzel.de> <n288g5$n4i$1@news.albasani.net> <n28d72$dge$1@dont-email.me> <n29j1k$705$1@news.albasani.net>

Show all headers | View raw


Peter Müller wrote:

> CREATE TABLE wiealt2 (
> `id` int(11) NOT NULL,
> `jahr` smallint(5) unsigned DEFAULT NULL,
> `baldwin` int(10) unsigned DEFAULT NULL)
> ENGINE=MyISAM DEFAULT CHARSET=utf8;
> 
> INSERT INTO `wiealt2` (`id`, `jahr`, `baldwin`) VALUES
> (1, 1700, NULL),
> (10, 1810, 11),
> (11, 1811, 12),
> (12, 1812, 13),
> (13, 1813, 14),
> (14, 1814, 15),
> (15, 1815, 16),
> (16, 1816, 17),
> (17, 1817, 18),
> (18, 1818, 19),
> (19, 1819, NULL),
> (99, 1899, NULL);
> 
> $firma = 'baldwin';
> $nummer = '15';
> 
> $query1 = "SELECT max(jahr) as maxjahr FROM wiealt2 WHERE :firma <=
> :nummer";
> $stmt = $dbmerz -> prepare($query1);
> $stmt -> bindValue(':firma', $firma, PDO::PARAM_STR);
> $stmt -> bindValue(':nummer', $nummer, PDO::PARAM_INT);
> $stmt -> execute();
> $result1 = $stmt->fetchAll(PDO::FETCH_OBJ);
> if($result1)
> {
> $min=$result1[0]->maxjahr;
> var_dump($result1);
> }
> $query2 = "SELECT min(jahr) as minjahr FROM wiealt2 WHERE :firma >
> :nummer"; $stmt = $dbmerz -> prepare($query2);
> $stmt -> bindValue(':firma', $firma, PDO::PARAM_STR);
> $stmt -> bindValue(':nummer', $nummer, PDO::PARAM_INT);
> $stmt -> execute();
> $result2 = $stmt->fetchAll(PDO::FETCH_OBJ);
> if($result2)
> {
> $max=$result2[0]->minjahr;
> var_dump($result2);
> }
> print "<p>Nummer = $nummer; Firma = $firma; Min = $min, Max = $max.</p>";
> 
> Ausgabe: array(1) { [0]=> object(stdClass)#86 (1) { ["maxjahr"]=> NULL }
> } array(1) { [0]=> object(stdClass)#87 (1) { ["minjahr"]=> string(4)
> "1700" } }
> 
> Nummer = 15; Firma = baldwin; Min = , Max = 1700.
> 
> SQL-Ausgabe ist aber dagegen:
> mysql> SELECT max(jahr) as maxjahr FROM wiealt2 WHERE baldwin <= 15;
> +---------+
> | maxjahr |
> +---------+
> |    1814 |
> +---------+
> 1 row in set (0.01 sec)
> mysql> SELECT min(jahr) as minjahr FROM wiealt2 WHERE baldwin > 15;
> +---------+
> | minjahr |
> +---------+
> |    1815 |
> +---------+
> 1 row in set (0.00 sec)
> 
> Der PHP-Code liefert also NULL, 1700, SQL liefert 1814,1815 auf dieselbe
> Abfrage. Mir scheint, die WHERE-Abfrage wird hier verworfen. Wieso?

Es sind _nicht_ dieselben Abfragen und die WHERE-_Bedingung_ der _SELECT_-
Abfrage wird höchstens teilweise verworfen.  Denn:

In Prepared Statements (PSs) mindestens mit mysqli und PDO_MySQL können 
Namen keine ersetzten Werte sein (kann BTW in einer Frage in der ZCE-Prüfung 
vorkommen, siehe Trainingsmaterial).

Denn Deine PSs

  SELECT max(jahr) as maxjahr FROM wiealt2 WHERE :firma <= :nummer

bzw.

  SELECT min(jahr) as minjahr FROM wiealt2 WHERE :firma > :nummer

sind mit den verwendeten Wertbindungen äquivalent zu

  SELECT MAX(`jahr`) AS `maxjahr` FROM `wiealt2` WHERE 'baldwin' <= 15;

bzw.

  SELECT MIN(`jahr`) AS `minjahr` FROM `wiealt2` WHERE 'baldwin' > 15;

Es wird also nicht mit dem Wert der Feldes in der Spalte `baldwin` des 
jeweiligen Datensatzes, sondern mit dem fixen String-Wert 'baldwin' 
verglichen.  Da das nicht sinnvoll ist (der zweite Operand ist eine Zahl), 
wird der String gekürzt.  Debugging-Beispiel mit Deiner Tabelle (bei mir 
`tmp`:

,----
| $ mysql -u root -p                                                                                                                                   
| Enter password:                                                                                                                                      
| Welcome to the MySQL monitor.  Commands end with ; or \g.
| Your MySQL connection id is 106
| Server version: 5.6.25-3 (Debian)
| 
| Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights 
| reserved.
| 
| Oracle is a registered trademark of Oracle Corporation and/or its
| affiliates. Other names may be trademarks of their respective
| owners.
| 
| Type 'help;' or '\h' for help. Type '\c' to clear the current input 
| statement.
| 
| mysql> use tmp;
| Reading table information for completion of table and column names
| You can turn off this feature to get a quicker startup with -A
| 
| Database changed
| mysql> SELECT * FROM `tmp`;
| +----+------+---------+
| | id | jahr | baldwin |
| +----+------+---------+
| |  1 | 1700 |    NULL |
| | 10 | 1810 |      11 |
| | 11 | 1811 |      12 |
| | 12 | 1812 |      13 |
| | 13 | 1813 |      14 |
| | 14 | 1814 |      15 |
| | 15 | 1815 |      16 |
| | 16 | 1816 |      17 |
| | 17 | 1817 |      18 |
| | 18 | 1818 |      19 |
| | 19 | 1819 |    NULL |
| | 99 | 1899 |    NULL |
| +----+------+---------+
| 12 rows in set (0.00 sec)
| 
| mysql> SELECT * FROM `tmp` WHERE 'baldwin' <= 15;
| +----+------+---------+
| | id | jahr | baldwin |
| +----+------+---------+
| |  1 | 1700 |    NULL |
| | 10 | 1810 |      11 |
| | 11 | 1811 |      12 |
| | 12 | 1812 |      13 |
| | 13 | 1813 |      14 |
| | 14 | 1814 |      15 |
| | 15 | 1815 |      16 |
| | 16 | 1816 |      17 |
| | 17 | 1817 |      18 |
| | 18 | 1818 |      19 |
| | 19 | 1819 |    NULL |
| | 99 | 1899 |    NULL |
| +----+------+---------+
| 12 rows in set, 1 warning (0.00 sec)
| 
| mysql> SHOW WARNINGS;
| +---------+------+---------------------------------------------+
| | Level   | Code | Message                                     |
| +---------+------+---------------------------------------------+
| | Warning | 1292 | Truncated incorrect DOUBLE value: 'baldwin' |
| +---------+------+---------------------------------------------+
| 1 row in set (0.00 sec)
| 
| mysql> SELECT MAX(`jahr`) FROM `tmp` WHERE 'baldwin' <= 15;
| +-------------+
| | MAX(`jahr`) |
| +-------------+
| |        1899 |
| +-------------+
| 1 row in set, 1 warning (0.00 sec)
| 
| mysql> SELECT MIN(`jahr`) FROM `tmp` WHERE 'baldwin' > 15;
| +-------------+
| | MIN(`jahr`) |
| +-------------+
| |        NULL |
| +-------------+
| 1 row in set, 1 warning (0.00 sec)
`----

[Dieselben Ergebnisse erhalte ich auch über PHP.  Weshalb bei Dir angeblich 
für das Ergebnis der ersten Abfrage NULL und für das der zweiten 1700 
geliefert wird statt 1899 und NULL, ist mir noch nicht klar.  Klar ist aber, 
dass die Abfragen so nicht funktionieren *können*.]

Du musst stattdessen den Spaltennamen mit String-Konkatenation oder 
sprintf() in die Abfrage einbauen und solltest ihn dann geeignet (MySQL: 
mindestens mit `…`) escapen, so wie Du es anfangs getan hast.  (Da man das 
immer wieder braucht, schreibt man sich dafür entweder eine Datenbank-
Klasse, so wie ich es für mein PHPX-Framework [1] für die ECMAScript Support 
Matrix [2] getan habe, oder verwendet zum Beispiel die Datenbank-Klassen des 
Zend Framework 2 [3].)

Für eine einmalige Abfrage kannst Du übrigens PDOStatement::execute() ein 
assoziatives Array aus Werten übergeben.  Wertbindung ist nur 
sinnvoll/nötig, wenn die ansonsten gleiche Abfrage mehrfach mit 
unterschiedlichen Werten ausgeführt wird.  Und dann ist es nur sinnvoll, das 
jeweilige PDOStatement *einmal* zu erstellen und nur die Wertbindung zu 
ändern oder Parameter zu binden (PDOStatement::bindParam()) und nur die 
Werte der gebundenen Variablen zu ändern.

Bezüglich Codestil rate ich Dir:

- Keine Zeilen über die 80. Spalte hinaus (für Usenet: nicht über die 76. 
  Spalte hinaus)

- Nur Operatoren werden immer von Whitespace umschlossen.  “->” ist i.e.S.
  in PHP *kein* Operator¹:

  <http://php.net/manual/en/language.operators.php>

- “if” ist kein Funktionsbezeichner, sondern das Schlüsselwort einer 
  Steueranweisung; nach solchen steht immer ein Leerzeichen:

    if ($result1) …

- Bei Anweisungsblöcken wird nicht die erste geschweifte Klammer eingerückt,
  sondern nur der Inhalt des Blocks.

- Ganz allgemein: Whitespace wird konsistent verwendet.  Also zum Beispiel 
  nicht

    $dbmerz -> prepare($query2)

  und dann wieder

    $stmt->fetchAll(PDO::FETCH_OBJ)

  oder mal

    $stmt = $dbmerz -> prepare($query2);

  und dann wieder

    $max=$result2[0]->minjahr;

  Mit einer PHP-Entwicklungsumgebung wie zum Beispiel Eclipse PDT oder
  Zend Studio wird das einfacher.

- Alle Schlüsselwörter und Bezeichner eingebauter Funktionen in
  SQL-Anweisungen werden durchgängig gross geschrieben; andere Namen
  werden durchgängig so geschrieben, wie sie erstellt wurden
  (Empfehlung: Kleinschreibung mit Unterstrich) und gequotet (“MIN(`jahr`).

- Namen von Tabellen sollten Entitäten beschreiben und optimal auf Englisch
  sein, also nicht “wiealt2”, sondern “company_age”.  Bezüglich Englisch
  gilt dasselbe für Namen von Datenbanken, Spalten und Stored Procedures.

Ein leicht lesbarer Quelltext trägt viel dazu bei, Fehler im Quelltext zu 
finden und auszumerzen.  Das gilt sowohl für den Fall, dass Du Fehler in 
Deinem Quelltext finden musst als auch, dass andere Deinen Quelltext 
verstehen sollen.

Allerdings macht Deine Datenbank allgemein einen falsch strukturierten 
Eindruck: “baldwin” ist anscheinend der Name einer Firma (Du vergleichst mit 
einem Wert mit dem Platzhalter “:firma”); eine Spalte namens “baldwin” 
sollte es deshalb gar nicht geben, sondern 'baldwin' bzw. die ID von 
“baldwin” sollte ein *Wert* in einem *Feld* einer Spalte sein.  Das könnte 
man weiter in <news:de.comp.datenbanken.mysql> diskutieren.

[1] <http://PointedEars.de/wsvn/PHPX/trunk/Db/>
[2] <http://PointedEars.de/es-matrix/>
[3] 
<http://framework.zend.com/apidoc/2.4/classes/Zend.Db.Adapter.Platform.Mysql.html>

_________
¹ Ja, ich kenne T_OBJECT_OPERATOR und 
  <http://php.net/manual/en/language.oop5.properties.php>
-- 
PointedEars
Zend Certified PHP Engineer
Twitter: @PointedEars2
Please do not cc me. / Bitte keine Kopien per E-Mail.

Back to de.comp.lang.php | Previous | NextPrevious in thread | Next in thread | Find similar


Thread

Problem mit PDO Peter Müller <peter.mueller@c-major.de> - 2015-11-14 20:54 +0100
  Re: Problem mit PDO Arno Welzel <usenet@arnowelzel.de> - 2015-11-14 21:32 +0100
    Re: Problem mit PDO Peter Müller <peter.mueller@c-major.de> - 2015-11-14 22:19 +0100
      Re: Problem mit PDO Markus Grob <snoopy@ilnet.ch> - 2015-11-14 23:41 +0100
        Re: Problem mit PDO Peter Müller <peter.mueller@c-major.de> - 2015-11-15 10:25 +0100
          Re: Problem mit PDO Thomas Mlynarczyk <thomas@mlynarczyk-webdesign.de> - 2015-11-15 12:42 +0100
          Re: Problem mit PDO Thomas Mlynarczyk <thomas@mlynarczyk-webdesign.de> - 2015-11-15 13:03 +0100
            Re: Problem mit PDO Thomas 'PointedEars' Lahn <PointedEars@web.de> - 2015-11-15 14:26 +0100
              Re: Problem mit PDO Thomas Mlynarczyk <thomas@mlynarczyk-webdesign.de> - 2015-11-15 16:32 +0100
            Re: Problem mit PDO Peter Müller <peter.mueller@c-major.de> - 2015-11-15 14:37 +0100
              Re: Problem mit PDO Thomas 'PointedEars' Lahn <PointedEars@web.de> - 2015-11-15 15:30 +0100
                Re: Problem mit PDO Peter Müller <peter.mueller@c-major.de> - 2015-11-15 19:26 +0100
                Re: Problem mit PDO Thomas 'PointedEars' Lahn <PointedEars@web.de> - 2015-11-15 21:10 +0100
          Re: Problem mit PDO Thomas 'PointedEars' Lahn <PointedEars@web.de> - 2015-11-15 14:23 +0100
            Re: Problem mit PDO Peter Müller <peter.mueller@c-major.de> - 2015-11-15 14:39 +0100
      Re: Problem mit PDO Arno Welzel <usenet@arnowelzel.de> - 2015-11-16 08:43 +0100
        Re: Problem mit PDO Peter Müller <peter.mueller@c-major.de> - 2015-11-16 21:23 +0100
    Re: Problem mit PDO Thomas Mlynarczyk <thomas@mlynarczyk-webdesign.de> - 2015-11-15 13:09 +0100
      Re: Problem mit PDO Markus Grob <snoopy@ilnet.ch> - 2015-11-17 09:48 +0100
        Re: Problem mit PDO Thomas 'PointedEars' Lahn <PointedEars@web.de> - 2015-11-17 20:13 +0100
          Re: Problem mit PDO Stefan+Usenet@Froehlich.Priv.at (Stefan Froehlich) - 2015-11-17 22:09 +0000
          Re: Problem mit PDO Markus Grob <snoopy@ilnet.ch> - 2015-11-20 21:59 +0100

csiph-web