Optimierung DB-Abfrage, Abbruch nach erstem falschem WHERE?

Fragen zu allen Themen rund ums Programmieren außerhalb von phpBB können hier gestellt werden - auch zu anderen Programmiersprachen oder Software wie Webservern und Editoren.
Xwitz
Mitglied
Beiträge: 1104
Registriert: 21.06.2005 21:41

Optimierung DB-Abfrage, Abbruch nach erstem falschem WHERE?

Beitrag von Xwitz »

Hallo,

ich wollte fragen, ob bei einer mySQL-DB-Abfrage nach der ersten falschen AND-verknüpften Bedingung in WHERE (in der Reihenfolge, wie sie in der Abfrage steht) abgebrochen wird oder die anderen auch noch durchlaufen werden. Auf den einschlägigen Seiten habe ich nichts darüber gefunden (und ich kann mich auch nicht erinnern, mal auf etwas in der Richtung gestoßen zu sein).

Falls nicht klar ist was ich meine:
Ich schreibe gerade ein script, bei dem es nötig ist, Einträge mit Wortgruppen auf exakte Übereinstimmung zu untersuchen (es ist etwas noch anders, aber es verdeutlicht die Lage). Diese Suche dürfte reichlich Arbeit machen und so habe ich mir überlegt, in einer weiteren Spalte die Zeichnzahl vom Text zu speichern und die Übereinstimmung zu prüfen um die Suche nach dem Text nur da durchzuführen.

Code: Alles auswählen

$sql = 'SELECT text FROM db_text WHERE zeichenzahl = '.$zeichenzahl.' AND text = '.$text;
Die Frage ist nun, ob bei fehlender Übereinstimmung bei den Zahlen wirklich nicht mehr nach einer Übereinstimmung im Text gesucht wird.
Benutzeravatar
bgx
Mitglied
Beiträge: 228
Registriert: 30.11.2005 22:32
Kontaktdaten:

Beitrag von bgx »

Na ich würde sagen durch das "AND" in der Abfrage wird genau der gewünschte Effekt erziehlt!

Teste es doch mal mit ein paar Einträgen in mysqladmin direkt! :wink:
Xwitz
Mitglied
Beiträge: 1104
Registriert: 21.06.2005 21:41

Beitrag von Xwitz »

Ich habe noch keine DB und keine Daten. Und weil ich darin nicht routiniert bin, wollte ich nicht eine DB mit Daten in einer Struktur erzeugen, die mir nachher nichts nützt.

Ich werde es mal mit der Datenbank von phpbb versuchen (falls mir einfällt mit welcher Tabelle und Spalten). Aber falls sich jemand der Antwort sicher ist ... :grin: Ich sitze so wie so auch noch an anderen Problemen.

PS: Gibt es wirklich keinen php-Befehl der den Teilstring vor einem Suchmuster/Suchstring zurückgibt oder finde ich den nur nicht?
BB-BF-BM
Mitglied
Beiträge: 2179
Registriert: 28.10.2005 16:38
Wohnort: Essen

Beitrag von BB-BF-BM »

Xwitz hat geschrieben:PS: Gibt es wirklich keinen php-Befehl der den Teilstring vor einem Suchmuster/Suchstring zurückgibt oder finde ich den nur nicht?
eine Kombination von substr und strpos?

Code: Alles auswählen

$haystack = 'efgdcbabacabcfgehabcdebd';
$needle = 'abc';

echo substr($haystack, 0, strpos($haystack, $needle));

// gewünschte Ausgabe:
// efgdcbabac
ich kann nicht dafür garantieren, dass das funktioniert; hab es noch nicht getestet. Ich weiß auch nicht, ob du so etwas meinst.

EDIT:
Ansonsten preg_match('#$(.*?)'.$needle.'#', $haystack, $matches), oder?
möglicherweise noch mit preg_quote($needle)...
PhilippK
Vorstand
Vorstand
Beiträge: 14662
Registriert: 13.08.2002 14:10
Wohnort: Stuttgart
Kontaktdaten:

Beitrag von PhilippK »

Bei SQL-Abfragen gilt: möglichst viel Abfragen auf den SQL-Server verlagern - weil der hat in der Regel einen Query Optimizer, der die ganze Abfrage möglichst effizient abarbeitet.
Wie die Abfrage am Schluss am besten aufgebaut ist, hängt ganz von der jeweiligen Situation ab. Wenn nach einem Feld viel sortiert/gefiltert werden soll, so macht sich hier meist ein Index sehr positiv bemerkbar. Vorberechnete Felder (also so was wie die Zeichenzahl) machen dann Sinn, wenn die Berechnung komplexer ist und viele Felder ausgewertet werden müssen (dabei zieht der Query Optimizer natürlich solche Kriterien vor, die schnell zu überprüfen sind). Wenn du nach einem Text suchst, würde ich einen Index drauf setzen - die Prüfung der Zeichenzahl ist da ja nicht besonders hilfreich (wenn 'Text' = 'Text', dann hat 'Text' auch gleich viel Zeichen wie 'Text')

Gruß, Philipp
Kein Support per PN!
Der Sozialstaat ist [...] eine zivilisatorische Errungenschaft, auf die wir stolz sein können. Aber der Sozialstaat heutiger Prägung hat sich übernommen. Das ist bitter, aber wahr. (Horst Köhler)
Meine Mods
Xwitz
Mitglied
Beiträge: 1104
Registriert: 21.06.2005 21:41

Beitrag von Xwitz »

@ PhilippK, ich werde zwar jeder Wortgruppe eine ID fürs Handling verpassen, aber ich muß trotzdem alle Wortgruppen nach Übereinstimmung mit dem Suchtext durchsuchen (es können auch welche doppelt vorkommen, also kein Limit). Ich dachte, es würde in der Regel schneller gehen, nur zwei Zahlen miteinander zu Vergleichen und nur bei Übereinstimmung auch den Text zu prüfen.

Dann werde ich es doch mal testen müssen.

@ BB-BF-BM, wie ich mir Ersatz zusammenbaue ist mir schon klar.
Xwitz
Mitglied
Beiträge: 1104
Registriert: 21.06.2005 21:41

Beitrag von Xwitz »

Ergebnis:

Die Reihenfolge der AND-verknüpften Bedingungen macht keinen Unterschied in der Ausführungszeit.

Das bedeutet entweder, daß durch Optimierung beide Abfragen identisch sind oder kein Abbruch erfolgt. Letzteres bezweifle ich aber. Unklar ist aber, ob der "Optimierer" wirklich das Beste macht (da habe ich genug Erfahrung aus anderen Sprachen um das nicht für unwahrscheinlich zu halten).

Die Abfrage nur nach Übereinstimmung des Textes ist grob 10% schneller. Ich vermute, der Abbruch des Vergleichs erfolg nach dem ersten unterschiedlichen Zeichen. Durchschnittlich ist ein Abbruch nach relativ wenigen Zeichen sehr wahrscheinlich und damit nicht groß langsamer als der Vergleich der Zahlen. Der Mehraufwand für die Ermittlung der Zeichenanzahl überwiegt.

Ich werde es noch mal mit gezielter passenden Suchtexten (und damit häufiger späteren Abbrüchen) und mit Auslagerung der Ermittlung der Zeichenzahl versuchen um deren Zeitbedarf rauszufiltern.

PS: Was komisch ist, die Durchlaufzeit mit vertauschten Bedingungen ist innerhalb eines gemeinsamen Aufrufes bis auf die Tausendstel Sekunde gleich.

mal ein paar Ergebnisse (1000 Durchgänge, 100 mit je 10 verschiedenen Suchtexten):
Xwitz (Zahl AND Text): 0.731485843658
Xwitz (Text AND Zahl): 0.731485843658
Philipp: 0.661705970764

Xwitz (Zahl AND Text): 0.751853942871
Xwitz (Text AND Zahl): 0.751853942871
Philipp: 0.662776947021

Xwitz (Zahl AND Text): 0.750592947006
Xwitz (Text AND Zahl): 0.750592947006
Philipp: 0.662474870682

Xwitz (Zahl AND Text): 0.749037981033
Xwitz (Text AND Zahl): 0.749037981033
Philipp: 0.667639017105

Xwitz (Zahl AND Text): 0.73312997818
Xwitz (Text AND Zahl): 0.73312997818
Philipp: 0.664833784103

Xwitz (Zahl AND Text): 0.754051923752
Xwitz (Text AND Zahl): 0.754051923752
Philipp: 0.669788122177
PhilippK
Vorstand
Vorstand
Beiträge: 14662
Registriert: 13.08.2002 14:10
Wohnort: Stuttgart
Kontaktdaten:

Beitrag von PhilippK »

Das die Reihenfolge keinen Unterschied macht, war zu erwarten. Wobei das mit den Millisekunden schon etwas komisch ist - evtl. war beim Testverfahren was nicht ganz in Ordnung.
Hast du 'nen Index auf das Feld mit der Zahl gesetzt? Das könnte noch deutliche Unterschiede machen.

Die Optimizer sind eigentlich recht gut - zumindest bei so einfachen Aufgaben. Der Trick ist eigentlich recht einfach: der Optimizer versucht zu ermitteln, wie viel Zeit ein Teil einer Abfrage in Anspruch nimmt. Wenn du die Aufgabe bekommst, aus dem Telefonbuch alle Meiers in der A-Straße zu suchen, deren Quersumme der Telefonnummer gerade ist, wirst du ja sicherlich auch nicht anfangen, erst die Quersummen aller Telefonnummern zu ermitteln bevor du dann den Rest aussortierst ;-)
So wird auch der Optimizer vermeiden, in die eigentlichen Tabellendaten reinzugehen. Also nimmt erst mal alle Teile raus, die er nicht aus der Tabelle sondern allein aus dem Index beantworten kann. Dabei lassen sich selbst zwischen den einzelnen Indexabfragen Unterschiede vorhersagen. Vielleicht mal lesen: http://en.wikipedia.org/wiki/Query_optimizer

Etwas konkreter auf MySQL: http://dev.mysql.com/doc/refman/5.1/en/ ... ation.html
http://dev.mysql.com/doc/refman/5.1/en/ ... tions.html

Evtl. auch mal den EXPLAIN-Befehl von MySQL anschauen: http://dev.mysql.com/doc/refman/5.1/en/explain.html

Da sind wir dann aber schon sehr tief in der Materie drin.

Gruß, Philipp
Kein Support per PN!
Der Sozialstaat ist [...] eine zivilisatorische Errungenschaft, auf die wir stolz sein können. Aber der Sozialstaat heutiger Prägung hat sich übernommen. Das ist bitter, aber wahr. (Horst Köhler)
Meine Mods
Xwitz
Mitglied
Beiträge: 1104
Registriert: 21.06.2005 21:41

Beitrag von Xwitz »

Nein, ich habe keinen Index auf der Zahl gehabt, obwohl ich die in einer Spalte gespeichert habe und sie nicht erst zur Abfrage ermittle (schließlich wollte ich die Abfrage optimieren) aber ich werde es mit dem Index auf der Spalte mal probieren.

Zum Telefonbuch, ja würde ich (nachdem ich eine Spalte mit der Information gerade '0' oder ungerade '1' angelegt habe). Wenn nicht sicher ausgeschlossen werden kann, ob nach einem bestimmten Zeichen kein Name mehr kommt, muß ich den ganzen String bis zum Ende durchsuchen (bis zur ersten Zahl könnte klappen aber wie bringe ich das effektiv der Abfrage bei?), das wäre bei mir noch nicht mal nötig (der erste falsche Buchstabe reicht). Wie lange dauert es im Gegensatz dazu, eine 1 oder 0 zu vergleichen und somit ca. die Hälfte aller Texte auszuschließen? Sagen wir mal weniger.

Danke für die Links, ich fürchte nur, mein Englisch reicht nicht um alles entscheidende hinter den Links zu verstehen aber wie man logische Verknüpfungen kürzt hatte ich in der Schule, in der Lehre, in der FOS und noch mal im Studium (und da war ich besser als in Englisch :) ).

Das Testscript wollte ich eigentlich so wie so posten, falls es was daran auszusetzen gibt.
Hier die Datenschleuder:

Code: Alles auswählen

<?php

//Benötigte Dateien und Variablen von phpBB 
define('IN_PHPBB', true); 
$phpbb_root_path = './'; 
include($phpbb_root_path . 'extension.inc'); 
include($phpbb_root_path . 'common.'.$phpEx);

mysql_query("CREATE TABLE test_daten (
text TEXT,
zeichenzahl INT
);
");

$otext =''; //Testtext min. 250 Wörter
$otext = preg_replace( "/[^a-z ÖÜÄöüäß]/im", "", $otext );
$otext = preg_replace( "/ {2,}/m", " ", $otext );

for ($i = 1; $i <= 100; $i++){
for ($ii = 2; $ii <= 5; $ii++){
$text = explode(' ', $otext, $ii);
$otext = array_pop($text);
$text = implode(' ', $text);
//echo $text;
$zeichenzahl = strlen($text);
//echo $zeichenzahl;
$treffer_sql = "INSERT INTO test_daten (text,zeichenzahl) VALUES ('$text', '$zeichenzahl')";
mysql_query($treffer_sql);
}
}

?>
Ich habe es mit ein paar Tausend Wörter gefüttert.

Und hier der Tester:

Code: Alles auswählen

<?php

function zeit($tn, $ta, $zeit)
{
$ta_array = explode(' ', $ta);
$tn_array = explode(' ', $tn);
$ta = $ta_array[1] - $zeit + $ta_array[0];
$tn = $tn_array[1] - $zeit + $tn_array[0];
$t = $tn - $ta;

return $t;
}

$zeit = time();

//Benötigte Dateien und Variablen von phpBB 
define('IN_PHPBB', true); 
$phpbb_root_path = './'; 
include($phpbb_root_path . 'extension.inc'); 
include($phpbb_root_path . 'common.'.$phpEx);

$vtext = array('', '', '', '', '', '', '', '', '', '' ); //Mischung aus 10 Wortgruppen die verschieden gut vorkommen oder verschieden knapp nicht vorkommen
 
$t1 = microtime();
for ($i = 1; $i <= 100; $i++){
for ($ii = 0; $ii <= 9; $ii++){
$zeichenzahl = count($vtext[$ii]);
$text = $vtext[$ii];
$treffer_sql = "SELECT text FROM test_daten WHERE zeichenzahl = '$zeichenzahl' AND text = '$text'";
$treffer_result = mysql_query($treffer_sql);
while($treffer_row = mysql_fetch_array($treffer_result)) 
     {
     $treffer_text = $treffer_row[0]; //um zu sehen, ob überhaupt was trifft
     }
}
}
$t2 = microtime();

$t3 = microtime();
for ($i = 1; $i <= 100; $i++){
for ($ii = 0; $ii <= 9; $ii++){
$zeichenzahl = count($vtext[$ii]);
$text = $vtext[$ii];
$treffer_sql = "SELECT text FROM test_daten WHERE text = '$text' AND zeichenzahl = '$zeichenzahl'";
$treffer_result = mysql_query($treffer_sql);
while($treffer_row = mysql_fetch_array($treffer_result)) 
     {
     echo $treffer_row[0].'<br>';
     }
}
}
$t4 = microtime();

$t5 = microtime();
for ($i = 1; $i <= 100; $i++){
for ($ii = 0; $ii <= 9; $ii++){
$text = $vtext[$ii];
$treffer_sql = "SELECT text FROM test_daten WHERE text = '$text'";
$treffer_result = mysql_query($treffer_sql);
while($treffer_row = mysql_fetch_array($treffer_result)) 
     {
     echo $treffer_row[0].'<br>';
     }
}
}
$t6 = microtime();

$t_xwitz1 = zeit($t2, $t1, $zeit);
$t_xwitz2 = zeit($t4, $t3, $zeit);
$t_philipp = zeit($t6, $t5, $zeit);

echo  "Scriptzeit:<br>Xwitz (Zahl AND Text): $t_xwitz1<br>Xwitz (Text AND Zahl): $t_xwitz1<br>Philipp: $t_philipp"

?>
Edit: $zeit wird an Funktion zeit() übergeben

PS: Ich habe an manchen Stellen Optimizer schon um das 5-fache geschlagen (nur nicht in php oder sql) und wundere mich auch nicht, den der Optimizer kann nicht wissen was ich unter Umständen weiß und sagen kann ich es ihm in der Regel auch nicht. Ich verdiene z.Z. mein Geld mit prozessortaktgenauer Echtzeitprogrammierung (kein PC :wink: ) um so unbehaglicher sind mir Systeme bei denen ich nicht weiß was sie wie und wann machen. Ich bin aber kein Informatiker und gehe auch nicht mit dem Vorsatz an meine Hobbyprogrammiererei, ein aufs letzte durchoptimiertes script "abzuliefern".

PPS: Ich habe den Test lokal ausgeführt.
Zuletzt geändert von Xwitz am 05.01.2007 18:40, insgesamt 1-mal geändert.
PhilippK
Vorstand
Vorstand
Beiträge: 14662
Registriert: 13.08.2002 14:10
Wohnort: Stuttgart
Kontaktdaten:

Beitrag von PhilippK »

In der Funktion zeit() ist $zeit undefiniert :roll:

Gruß, Philipp
Kein Support per PN!
Der Sozialstaat ist [...] eine zivilisatorische Errungenschaft, auf die wir stolz sein können. Aber der Sozialstaat heutiger Prägung hat sich übernommen. Das ist bitter, aber wahr. (Horst Köhler)
Meine Mods
Antworten

Zurück zu „Coding & Technik“