2009-08-18 22 views
8

Ich habe eine Tabelle mit etwa 1 Million Datensätze (mit SQL Server 2008 Web). Ich habe eine Suchroutine, die versucht, auf Produktcode sowie Produktbeschreibung zu entsprechen. In manchen Fällen ist es jedoch sehr langsam. Im Folgenden finden Sie (cut-down) SQL-Anweisung:Warum ist diese SQL-Anweisung sehr langsam?

WITH AllProducts AS (
    SELECT  p.*, Row_Number() OVER (ORDER BY ProductId) AS RowNumber 
    FROM  Product AS p 
    WHERE p.IsEnabled=1 AND 
    (
     p.BaseSku = 'KPK-3020QWC-C' -- this on its own is fast 
     OR 
     CONTAINS(p.FreeTextStrings, '"KPK-3020QWC*"') -- and this on its own is fast, but not both 
    ) 
) SELECT * FROM AllProducts   
    WHERE RowNumber BETWEEN 1 AND 20; 

Beachten Sie, dass, wenn ich nur miteinander vergleichen [p.BaseSku = 'KPK-3020QWC-C'] oder [ENTHÄLT (p.FreeTextStrings ‚", KPK-3020QWC * "')] individuell (aber nicht beides) sein Moment. Und wenn ich sie miteinander vergleiche, dauert es ewig (mehrere Minuten) - und es gibt nur eine Zeile.

IsEnabled und BaseSku sind indiziert und FreeTextStrings ist FTS-indiziert.

Ich erinnere mich, dass dies funktionierend war.

Kann jemand etwas Licht darauf werfen und einige Lösungen vorschlagen?

Ausführungsplan-Datei finden Sie hier: http://wiki.webgear.co.nz/GetFile.aspx?File=Temp%5cSearch%20Test.sqlplan.zip

+0

Können Sie uns einen Ausführungsplan von Ihrem SQL Server zeigen? –

+0

Diese Probleme traten nach der Aktualisierung von SQL 2005 auf SQL 2008 auf. – Muxa

Antwort

10

or auf SQL Server notorisch langsam ist. Es ist ärgerlich, um es gelinde auszudrücken.

Versuchen Splitting es in zwei Abfragen mit einem union:

WITH AllProducts AS (
    select *, Row_Number() OVER (ORDER BY ProductId) AS RowNumber 
    from (
    SELECT  p.* 
    FROM  Product AS p 
    WHERE p.IsEnabled=1 AND 
     p.BaseSku = 'KPK-3020QWC-C' 
    UNION 
    SELECT  p.* 
    FROM  Product AS p 
    WHERE p.IsEnabled=1 AND 
     CONTAINS(p.FreeTextStrings, '"KPK-3020QWC*"') 
) 
) SELECT * FROM AllProducts   
    WHERE RowNumber BETWEEN 1 AND 20; 
+1

Genau gesagt verursacht das "oder" wahrscheinlich einen Tabellenscan in der Produkttabelle, selbst wenn es Indizes sowohl für BaseSku als auch für FreeTextStrings gibt ... Eine Union wird das ändern Index + Index scannen ... (vorausgesetzt, es gibt Indizes, die diese beiden Spalten abdecken) – KristoferA

+0

Ich habe das versucht und es hat tatsächlich große Verbesserungen gezeigt.Ich werde diese Technik mit der vollständigen SQL-Anweisung versuchen. – Muxa

+0

Ja, das hat es gelöst. Allerdings finde ich es merkwürdig, dass dieselbe Anweisung in SQL 2005 sehr schnell funktioniert hat und in SQL 2008 langsam arbeitet. Könnte dies im nächsten Service Pack behoben werden? – Muxa

1

Dies scheint gut zu funktionieren:

WITH AllProducts AS (
    SELECT  p.*, Row_Number() OVER (ORDER BY ProductId) AS RowNumber 
    FROM  Product AS p 
    WHERE p.IsEnabled=1 AND 
    (
     CONTAINS(p.BaseSku, 'KPK-3020QWC-C') /* instead of p.BaseSku = 'KPK-3020QWC-C' */ 
     OR 
     CONTAINS(p.FreeTextStrings, '"KPK-3020QWC*"') 
    ) 
) SELECT * FROM AllProducts   
    WHERE RowNumber BETWEEN 1 AND 20; 

(ich hatte schon BaseSku FTS-indexiert)

0

Make sure all necessary indexes are in place. I hatte das gleiche Problem mit einer or Klausel in einer meiner Abfragen und die Erstellung eines NONCLUSTERED INDEX mit INCLUDE Spalten die Leistung behoben .

Nach weiteren Tests war es die Spalten Teil des Index enthalten, die wirklich das Leistungsproblem fixiert. Hier ist, was ich tat, um das Problem zu bestimmen und wie man es beheben:

den Ausführungsplan verwenden, um Ihnen die fehlenden Indizes zu erstellen:

Ohne den Index die Abfrage wurde unter 2+ min, wenn es haben sollte lief in ein paar Millisekunden. So verglich ich Ausführungspläne der Abfrage mit und ohne die or Klausel in SSMS und es war nicht offensichtlich, was ich tun musste (hauptsächlich wegen meines Mangels an Verständnis der Ausführungspläne).

Wenn Sie jedoch über den Ausführungsplan in grünem Text schauen, kann SSMS Ihnen sagen, dass Sie einen Nonclustered-Index erstellen sollen. Hmm ... einen Versuch wert. Also habe ich den Index erstellt und das Problem gelöst! Sie können mit der rechten Maustaste auf die Abfrage "CREATE INDEX" klicken und "Fehlende Indexdetails ..." auswählen. Dies öffnet eine neue Registerkarte mit der vollständigen Abfrage für Sie ausgeführt werden. Gib ihm einen Namen.