9

Es gab kürzlich eine Debatte auf der effizientesten Art und Weise, eine MS SQL-Datenbank mit LIKE und Platzhaltern zu durchsuchen. Wir vergleichen mit %abc%, %abc und abc%. Eine Person hat gesagt, dass Sie immer die Wildcard am Ende der Laufzeit haben sollten (abc%). Also, nach ihnen, wenn wir etwas finden wollten, das mit "abc" endete, wäre es am effizientesten, "reverse (column) LIKE reverse ('% abc') zu verwenden.SQL Wildcard-Suche - Effizienz?

Ich habe einen Test mit SQL Server 2008 (R2) bis jede der folgenden Aussagen vergleichen:

select * from CLMASTER where ADDRESS like '%STREET' 
select * from CLMASTER where ADDRESS like '%STREET%' 
select * from CLMASTER where ADDRESS like reverse('TEERTS%') 
select * from CLMASTER where reverse(ADDRESS) like reverse('%STREET') 

CLMASTER hält rund 500.000 Datensätze gibt es über 7.400 Adressen, die „Straße“, und über beenden 8.500 Adressen, die "Street" haben, aber nicht unbedingt am Ende. Jeder Testlauf dauerte 2 Sekunden und sie alle die gleiche Anzahl von Zeilen außer %STREET% zurückgegeben, die eine zusätzliche 900 oder so Ergebnisse gefunden, weil es Adressen, die eine Wohnung Nummer am Ende hatte.

Da der SQL Server-Test keinen Unterschied in der Ausführungszeit zeigen, habe ich in PHP bewegt, wo ich den folgenden Code verwendet wird, in jeder Anweisung Umschalten schnell mehrere Tests ausführen:

<?php 

    require_once("config.php"); 
    $connection = odbc_connect($connection_string, $U, $P); 

    for ($i = 0; $i < 500; $i++) { 
    $m_time = explode(" ",microtime()); 
    $m_time = $m_time[0] + $m_time[1]; 

    $starttime = $m_time; 

    $Message=odbc_exec($connection,"select * from CLMASTER where ADDRESS like '%STREET%'"); 
    $Message=odbc_result($Message,1); 

    $m_time = explode(" ",microtime()); 
    $m_time = $m_time[0] + $m_time[1]; 

    $endtime = $m_time; 

    $totaltime[] = ($endtime - $starttime); 

} 

odbc_close($connection); 

echo "<b>Test took and average of:</b> ".round(array_sum($totaltime)/count($totaltime),8)." seconds per run.<br>"; 
echo "<b>Test took a total of:</b> ".round(array_sum($totaltime),8)." seconds to run.<br>"; 

?> 

Die Ergebnisse der Dieser Test war ungefähr so ​​zweideutig wie die Ergebnisse beim Testen in SQL Server.

%STREET in 166.5823 Sekunden abgeschlossen (.3331 Durchschnitt pro Abfrage), und gemittelt 500 Ergebnisse in .0228 gefunden.

%STREET% in 149,4500 Sekunden abgeschlossen (Durchschnitt von .2989 pro Abfrage) und gemittelt 500 Ergebnisse in .0177 gefunden. (Schnellere Zeit pro Ergebnis, da in derselben Zeit mehr Ergebnisse gefunden werden als in den anderen.)

reverse(ADDRESS) like reverse('%STREET') in 134,0115 Sekunden abgeschlossen (Durchschnitt von .2680 pro Abfrage) und gemittelt 500 Ergebnisse in .0183 Sekunden gefunden.

reverse('TREETS%') in 167,6960 Sekunden abgeschlossen (0,3354 Durchschnitt pro Abfrage), und gemittelt 500 Ergebnisse in .0229 gefunden.

Wir erwarteten, dass dieser Test zeigen würde, dass %STREET% am langsamsten wäre, während es tatsächlich am schnellsten lief und die beste durchschnittliche Zeit hatte, um 500 Ergebnisse zu liefern. Während der vorgeschlagene reverse('%STREET') war am schnellsten, insgesamt zu laufen, war aber etwas langsamer in der Zeit, um 500 Ergebnisse zurückzugeben.

Zusätzlicher Spaß: Ein Mitarbeiter hat Profiler auf dem Server ausgeführt, während wir die Tests ausgeführt haben und festgestellt, dass die Verwendung des doppelten Platzhalters eine signifikante Erhöhung der CPU-Auslastung zur Folge hatte, während die anderen Tests innerhalb von 1-2% lagen.

Gibt es irgendwelche SQL Efficiency-Experten, die erklären können, warum der Platzhalter am Ende des Suchstrings besser geeignet ist als der Anfang, und vielleicht warum die Suche mit Platzhaltern am Anfang und Ende des Strings schneller war als die Wildcard nur am Anfang zu haben?

+0

'ABC' haben Did Sie löschen die Puffer und Cache vor jedem Test? –

+0

Ja, bevor jede Abfrage getestet wurde, starteten wir den Server neu, um sicherzustellen, dass es ein fairer Test war. – Jeremy1026

+1

Der reverse() - Ansatz wird einen Tabellenscan erzwingen, da jede Zeile umgekehrt werden muss. Normalerweise wird sie mit Präfix-Platzhalter + einer vorberechneten umgekehrten Spalte verwendet. –

Antwort

16

die Platzhalter am Ende der Schnur zu haben, wie 'abc%' würde wenn helfen, die Spalte indiziert wurden, da sie direkt mit den Aufzeichnungen zu suchen, wäre in der Lage, die mit 'abc' beginnen und alles andere ignorieren. Wenn der Platzhalter am Anfang steht, muss er jede Zeile unabhängig von der Indizierung betrachten.

Guter Artikel here mit mehr Erklärung.

+2

Was bedeutet, dass etwas wie 'reverse (col) wie' abc% 'zu tun ist eine schlechte Idee. –

+0

Ja, 'REVERSE' oder eine andere Berechnung, die die indizierte Spalte ändert, bedeutet, dass Sie die Sargability verlieren. – Bort

+0

Vielen Dank für die Antwort/Kommentare zur Verfügung gestellt – Jeremy1026

1

Von Microsoft ist es effizienter, den schließenden Platzhalter zu lassen, da es, falls vorhanden, einen Index verwenden kann, anstatt einen Scan durchzuführen. Überlegen Sie, wie funktionieren könnte die Suche, wenn Sie keine Ahnung, was ist, bevor es dann muss man alles scannen, aber wenn man nur das Ende der Suche sind, dann können Sie die Zeilen bestellen und sogar möglich (je nachdem, was Sie suchen) mache eine quasi-binäre Suche.

Einige Operatoren in Joins oder Prädikaten neigen dazu, ressourcenintensive Operationen zu erstellen. Der Operator LIKE mit einem in Platzhaltern eingeschlossenen Wert ("% a Wert%") verursacht fast immer einen Tabellenscan. Diese Art der Tabellensuche ist wegen des vorhergehenden Platzhalters eine sehr teure Operation. LIKE-Operatoren mit nur dem schließenden Platzhalter können einen Index verwenden, da der Index Teil einer B + -Baumstruktur ist und der Index durchlaufen wird, indem der Zeichenfolgenwert von links nach rechts abgeglichen wird.

So, das obige Zitat erklärt auch, warum es gab eine riesige Prozessor Spike, wenn zwei Wildcards ausgeführt wird. Es wird schneller nur durch Zufall abgeschlossen, weil es genug Pferdestärken gibt, um die Ineffizienz zu überdecken. Wenn Sie versuchen, die Leistung einer Abfrage zu ermitteln, sollten Sie die Ausführung der Abfrage und nicht die Ressourcen des Servers überprüfen, da diese irreführend sein können. Wenn ich einen Server habe, der genug Leistung hat, um einem Wetter zu dienen, und ich Abfragen auf Tabellen so klein wie 500.000 Zeilen führe, werden die Ergebnisse irreführend sein.

Weniger die Tatsache, dass Microsoft Ihre Antwort zitiert, wenn Performance-Analyse zu tun, sollten Sie den Tauchgang in dem Lernen, wie der Ausführungsplan zu lesen. Es ist eine Investition und sehr trocken, aber es wird sich auf lange Sicht lohnen.

Kurz aber wer wurde darauf hinweist, dass der hintere Platzhalter nur effizienter ist, ist richtig.

+0

@ Jeremy1026 - Ich habe meine Antwort mit ein bisschen mehr Klarheit in Bezug auf die Ergebnisse der Server-Performance-Nutzung aktualisiert. –

+0

Danke für die Antwort, die Sie zur Verfügung gestellt haben. – Jeremy1026

+0

@ Jeremy1026 - kein Problem. –

2

Platzhalter nur am Ende eines Like Zeichenkette einen Index verwenden.

Sie sollten sich mit FTS Contains befassen, wenn Sie die Geschwindigkeit von Wildcards an der Vorder- und Rückseite einer Zeichenkette verbessern möchten. Auch see this related SO post regarding Contains versus Like.

+0

Vielen Dank für die Antwort zur Verfügung gestellt, leider zu Contains wechseln ist keine praktikable Lösung für uns, da wir einen vollständigen Text Index einige (in die Hunderte) Tabellen benötigen, um es zu einer realisierbaren Lösung zu machen. Und wir suchen oft nach bestimmten Teilzeichenfolgen und anderen Dingen. – Jeremy1026

-2

In MS SQL, wenn Sie die Namen mit ‚ABC‘ diejenigen enden haben wollen, dann kann u die Abfrage haben wie unter

select * from student where student_name like'%[ABC]' 

(suppose Tabellenname student ist), so wird es diejenigen geben Namen, die mit 'A', 'B', 'C' enden.

2) wenn u wollen Namen haben, die mit 'ABC' beginnen Bedürftigkeits

select * from student where student_name like '[ABC]%' 

3), wenn u Namen haben wollen, die in der Mitte

select * from student where student_name like '%[ABC]%'