2013-05-08 12 views
9

ich einen freien Textkatalog auf einer einfachen Tabelle auf SQL Server 2008R2 haben:Freitextsuche mit einem anderen Zustand zu verbinden, ist langsam

CREATE FULLTEXT CATALOG customer_catalog; 
CREATE FULLTEXT INDEX ON customer 
( 
    name1 
) 
    KEY INDEX customer_pk 
    ON customer_catalog; 
ALTER FULLTEXT INDEX ON customer START UPDATE POPULATION; 

Wenn ich die folgenden drei Abfragen, um die ersten beiden Rück führen fast sofort, während die letzte dauert ~ 14 Sekunden auf einem Tisch mit 100.000 Datensätze:

SELECT 
     customer_id 
    FROM 
     customer 
    WHERE 
     CONTAINS(customer.*, 'nomatch'); 

SELECT 
     customer_id 
    FROM 
     customer 
    WHERE 
     customer.customer_id = 0; 

SELECT 
     customer_id 
    FROM 
     customer 
    WHERE 
     CONTAINS(customer.*, 'nomatch') 
      OR customer.customer_id = 0; 

Hier sind die queryplans:

enter image description here

Warum ist die dritte Abfrage so viel langsamer? Kann ich etwas tun, um es zu verbessern, oder muss ich die Abfrage aufteilen?

+0

Durch das wiederholte Umschreiben von CONTAINS-Abfragen in CONTAINSTABLE wird diese Art von Problem behoben. [ZB wie in dieser Antwort] (http://stackoverflow.com/questions/2906812/adding-more-or-searches-with-contains-brings-query-to-crawl/2907331#2907331) –

+1

Abhängig von Ihrem 2008R2 SP Version, Ihr Problem könnte mit dem folgenden MS Connect Problem zusammenhängen: http://connect.microsoft.com/SQLServer/feedback/details/520653/full-text-performance-with-mixed-queries – MicSim

+0

@MicSim: Wenn Sie das machen in eine Antwort werde ich es akzeptieren. Während die anderen Antworten nette Workarounds geliefert haben, sehen deine aus wie die echte Antwort. Vielen Dank! –

Antwort

2

Abhängig von Ihrer MS SQL 2008 R2-Servicepack-Version kann Ihr Problem mit folgendem Microsoft Connect-Problem zusammenhängen: Full-text performance with "mixed queries"

Wie der MS Connect-Eintrag besagt, sollte das Problem nach der Installation des neuesten kumulativen Update-Paket für SQL Server 2008 R2 entfernt werden.

3

Es ist schwer zu sagen, warum, aber es scheint, dass SQL Server einen ineffizienten Abfrageplan auswählt. Hier sind einige Vorschläge:

Aktualisieren Sie die Statistiken auf den Tisch:

UPDATE STATISTICS dbo.customer 

Sobald die Statistiken auf dem neuesten Stand sind, können Sie Ihre Fragen noch einmal versuchen und sehen, ob es Verbesserungen.

Etwas anderes ist, dass für die kombinierte OR-Anweisung SQL Server einen Index-Scan statt einer Suche verwendet. Sie könnten den FORCESEEK Hinweis versuchen und sehen, ob das einen Unterschied macht:

SELECT customer_id 
FROM customer WITH (FORCESEEK) 
WHERE CONTAINS(customer.*, 'nomatch') 
OR customer.customer_id = 0; 

Eine andere Möglichkeit, wie Sie erwähnt haben, ist es, die Aussagen zu spalten. Die folgenden UNION führt ebenso wie die ersten beiden Aussagen kombiniert:

SELECT customer_id FROM customer 
WHERE CONTAINS(customer.*, 'nomatch') 

UNION 

SELECT customer_id FROM customer 
WHERE customer.customer_id = 0 

aktualisieren - geändert obige Abfrage zu UNION statt UNION ALL.

Wie @PondLife in den Kommentaren angegeben, wollte ich eine UNION in der obigen Abfrage statt . Nachdem ich darüber nachgedacht habe, versuchte ich es auch mit UNION ALL und es schien schneller zu sein. Dies setzt voraus, Sie nicht über die doppelte IDs egal:

SELECT customer_id FROM customer 
WHERE CONTAINS(customer.*, 'nomatch') 

UNION ALL 

SELECT customer_id FROM customer 
WHERE customer.customer_id = 0 
+0

Ich glaube, du meinst "UNION", nicht "UNION ALL" (in diesem speziellen Fall), sonst wird eine Zeile, die "nomatch" * und * enthält, ID 0 zweimal im Ergebnissatz statt einmal vorkommen. – Pondlife

+0

@Pondlife - Sie sind richtig, ich werde aktualisieren. Vielen Dank. –

3

Der „OR“ logicial Bedingung macht oft Abfragen laufen sehr langsam:/ Oft ist die beste Option UNION (ALL) zu verwenden.

In Ihrem Fall bin ich ganz neugierig über die Nutzung Sie

machen von
SELECT 
    customer_id 
FROM 
    customer 
WHERE 
    customer.customer_id = 0; 

würde es nur in einer Liste führen (vielleicht leer) von Nullen. Ist es zu zählen (!) Wie viele Kunden eine ID = 0 haben? Ist es zu prüfen, ob ein Kunde eine ID von 0 hat?

Wenn es nicht die Nullen zu zählen, sondern wissen, ob sie irgendwelche sind, dann sollten diese Abfrage effizient sein:

SELECT 
    customer_id 
FROM 
    customer 
WHERE 
    CONTAINS(customer.*, 'nomatch') 
    AND customer.customer_id <> 0 
UNION ALL 
SELECT TOP(1) 
    0 
FROM 
    customer 
WHERE 
    customer.customer_id = 0 

sonst die effiziente Abfrage ist diese:

SELECT 
    customer_id 
FROM 
    customer 
WHERE 
    CONTAINS(customer.*, 'nomatch') 
    AND customer.customer_id <> 0 
UNION ALL 
SELECT 
    0 
FROM 
    customer 
WHERE 
    customer.customer_id = 0 

(I hat die TOP-Klausel entfernt)

+0

Die 'customer_id = 0' war nur ein einfaches Beispiel. Die eigentliche Abfrage ist ein Join zweier Tabellen mit CONTAINS (table1. *) ODER CONTAINS (table2. *). Aber ich erkannte, dass ich das Problem nur mit einer Abfrage der ID-Spalte reproduzieren konnte, also dachte ich, das wäre ein einfacheres Beispiel. –

+0

Die Bedingung spielt keine Rolle, verwenden Sie meine zweite Abfrage. Beginnen Sie mit dem Teil, der den Volltextindex verwendet, und filtern Sie ihn, um den zweiten Teil auszuschließen (in diesem Beispiel mit customer.customer_id <> 0), und erstellen Sie dann im zweiten Teil eine Vereinigungsmenge. – Serge

Verwandte Themen