Alexander (HH): Noch ein Nachtrag, Datentypen aus dem Request

Beitrag lesen

Moin Moin!

Woher kommt eigentlich die panische Angst vor oder Abneigung gegen Prepared Statements?

Ist es wirklich panische Angst und Abneigung oder einfach nur Unwissen? Ich denke, es wird einfach zu wenig gelehrt und die Tutorials beschränken sich auf den herkömmlichen Weg, weil deren Autoren den selbst kennen.

Also Cargo Cult.

Nichts könnte schneller oder sauberer sein als Prepared Statements. Ich muß mich nicht um das Quoting von Parametern kümmern, ich kann die Statements beliebig oft wiederverwenden (sofern die DB-API das mitmacht), und die DB kann sich großflächig sparen, Strings aus SQL-Statements herauszuparsen.

Einspruch! Sauberer sicher, aber schneller keinesfalls. Bei der herkömmlichen Methode hast du einen Funktionsaufruf plus n Maskierungen, sowie noch einen in der Fetch-Schleife. Bei den P.S. braucht es prepare(), bind_param(), bind_result(), execute() sowie das Fetchen. Das sind schonmal 5 statt 3 Funktionen, die man benötigt.

Das sagt nichts über Geschwindigkeit, sondern nur über Schreibaufwand. Und der wird mit ein paar Abenden in einem 10-Finger-Kurs schnell harmlos.

Wenn Du über Geschwindigkeit reden willst, fang mit Benchmarks an, statt Funktionsaufrufe zu zählen.

Erschwerend kommt hinzu, dass es nur vorgesehen ist, einzelne Variablen zu binden. Arrays gehen nur recht umständlich anzuflanschen.

Exakt das Problem hast Du auch, wenn Du Werte direkt ins SQL reinschreibst.

In der Hinsicht wurde bei PDO ein deutlich besseres Design implementiert.

Richtig, gut von DBI abgeschrieben.

Da kann man auch erst beim Execute die benötigten Parameter hinzufügen und ist auch nicht gezwungen bei der Verwendung von Prepared Statements das Ergebnis an Variablen zu binden.

Muß ich beim DBI auch nicht:

  
use DBI;  
  
my $dbh=DBI->connect("dbi:$treiber:$datenbank",$user,$password,{ RaiseError => 1, AutoCommit => 1 });  
  
# Hash Ref, Ergebnisspalten-Namen sind Keys  
my $sth=$dbh->prepare_cached('select laber,fasel from huhu where foo=? or bar=?');  
$sth->execute($fooWert,$barWert);  
while (my $hr=$sth->fetchrow_hashref('NAME_lc')) {  
  print "laber=",$hr->{'laber'}," fasel=",$hr->{'fasel'},"\n";  
}  
$sth->finish();  
  
# Array Ref, Ergebnisspalten in der Reihenfolge aus dem Select  
$sth=$dbh->prepare_cached('select laber,fasel from huhu where foo=? or bar=?');  
$sth->execute($fooWert,$barWert);  
while (my $ar=$sth->fetchrow_arrayref()) {  
  print "laber=",$ar->[0]," fasel=",$ar->[1],"\n";  
}  
$sth->finish();  

Ich *KANN* natürlich auch binden, das ist der schnellste Weg, wenn ich um Mikrosekunden feilschen muß. Spart auch Schreiberei, wenn man mit den Ergebnissen aus der DB sehr viel arbeiten muß.

  
# bind_col  
$sth=$dbh->prepare_cached('select laber,fasel from huhu where foo=? or bar=?');  
$sth->execute($fooWert,$barWert);  
my ($laber,$fasel);  
$sth->bind_columns(\$laber,\$fasel);  
while ($sth->fetch()) {  
  print "laber=$laber fasel=$fasel\n";  
}  
$sth->finish();  

Auch die Parameter für die Platzhalter kann man binden, per bind_param().

Wer möchte, kann auch per bind_param_array() Arrays als Parameter binden und entsprechend execute_array() aufrufen.

bind_param_in_out() ist auch möglich, wenn die DB die Parameter aktualisieren soll.

Zudem wird der Vorteil des bereits geparsten Statements im PHP-Webumfeld nicht besonders häufig genutzt, weil jede Script-Instanz erneut präparieren muss.

Das ist extrem ungeschickt. Wenn ich ein Perl-Script per mod_perl an den Webserver anbinde (analog zu mod_php), dann reicht eine einzige Zeile in httpd.conf bzw. startup.pl, um die DB-Connection und damit auch die Prepared Statements *immer* greifbar zu haben. Am Script selbst muß ich nichts ändern.

Nutze ich FastCGI, dann läuft mein Perl-Interpreter ohnehin ständig und hält die Connection und die Statements im Speicher (wenn ich das will).

Damit ergibt sich auch immer ein zusätzlicher Roundtrip zum DBMS gegenüber der herkömmlichen Variante.

Woher soll der kommen?

Wenn Du für jeden Request die Script-Instanz von Null neu startest, hast Du natürlich recht, aber dann läuft PHP im CGI-Modus und der Aufwand für das Prepare dürfte gegenüber dem Start des PHP-Interpreters ziemlich zu vernachlässigen sein.

Ich kann sehr gut verstehen, wenn jemand dieses umständliche Handling der Prepared Statements in PHPs mysqli-Extension nicht mag.

Tja, schlechtes API-Design. Schon angefangen damit, dass ich viel Code anfassen muß, wenn ich mit einer anderen DB arbeiten will. Bei DBI ist das exakt eine Stelle, dort wo ich den DB-Treiber im connect()-Aufruf festlege. Oft steht der Treiber aber nicht einmal fest im Code, sondern kommt aus einer Konfigurationsdatei. Dann ist der Wechsel auf eine DB eine reine Konfigurationsgeschichte -- vorausgesetzt, der geneigte Coder hat nicht angefangen, abgedrehte DB-Spezialitäten in den Code einzubauen.

Alexander

--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".