views:

140

answers:

4

I have a simple query that relies on two full-text indexed tables, but it runs extremely slow when I have the CONTAINS combined with any additional OR search. As seen in the execution plan, the two full text searches crush the performance. If I query with just 1 of the CONTAINS, or neither, the query is sub-second, but the moment you add OR into the mix the query becomes ill-fated.

The two tables are nothing special, they're not overly wide (42 cols in one, 21 in the other; maybe 10 cols are FT indexed in each) or even contain very many records (36k recs in the biggest of the two).

I was able to solve the performance by splitting the two CONTAINS searches into their own SELECT queries and then UNION the three together. Is this UNION workaround my only hope?

Thanks.

SELECT     a.CollectionID
FROM       collections    a
INNER JOIN determinations b ON a.CollectionID = b.CollectionID 
WHERE      a.CollrTeam_Text LIKE '%fa%'
           OR CONTAINS(a.*, '"*fa*"')
           OR CONTAINS(b.*, '"*fa*"')

Execution Plan (guess I need more reputation before I can post the image): http://yfrog.com/7dslowcontainsj

+2  A: 

I was going to suggest to UNION each as their own query, but as I read your question I saw that you have found that. I can't think of a better way, so if it helps use it. The UNION method is a common approach to a poor performing query that has several OR conditions where each performs well on its own.

KM
+1 I would also suggest UNION. As a note, I would say that UNION is the *right* solution, not the workaround.
Remus Rusanu
Thanks, the reassurance that the UNION approach is very helpful; plus it doesn't hurt that the query runs well now to boot.
scolja
And if the two union statements will be mutually exclusive every time, then use UNION ALL for additional performance imporvements.
HLGEM
+1  A: 

I would probably use the UNION. If you are really against it, you might try something like:

SELECT a.CollectionID
FROM collections a
  LEFT OUTER JOIN (SELECT CollectionID FROM collections WHERE CONTAINS(*, '"*fa*"')) c
    ON c.CollectionID = a.CollectionID
  LEFT OUTER JOIN (SELECT CollectionID FROM determinations WHERE CONTAINS(*, '"*fa*"')) d
    ON d.CollectionID = a.CollectionID
WHERE a.CollrTeam_Text LIKE '%fa%'
   OR c.CollectionID IS NOT NULL
   OR d.CollectionID IS NOT NULL
Rob Boek
I had to add DISTINCT, but this actually results in a slight performance improvement over the UNION method. Cost comparison is 46% to 54% with about 2/3rd the number of logical reads. Intriguing!
scolja
+3  A: 

I'd be curious to see if a LEFT JOIN to an equivalent CONTAINSTABLE would perform any better. Something like:

SELECT     a.CollectionID
FROM       collections    a
INNER JOIN determinations b ON a.CollectionID = b.CollectionID 
LEFT JOIN CONTAINSTABLE(a, *, '"*fa*"') ct1 on a.CollectionID = ct1.[Key]
LEFT JOIN CONTAINSTABLE(b, *, '"*fa*"') ct2 on b.CollectionID = ct2.[Key]
WHERE      a.CollrTeam_Text LIKE '%fa%'
           OR ct1.[Key] IS NOT NULL
           OR ct2.[Key] IS NOT NULL
Joe Stefanelli
Incredible, even better performance. Again DISTINCT had to be added and CONTAINSTABLE needed the actual table names, but with some minor tweaks it ran perfect. Cost comparison between the 3 methods: 29%, 32%, and 38% (CONTAINSTABLE, LEFT OUTER JOIN, then UNION). Logical reads now 1/5 of the UNION query. Well done.
scolja
A: 

We've experience the exact same problem and at the time, put it down to our query being badly formed - that SQL 2005 had let us get away with it, but 2008 wouldn't.

In the end, we split the query into 2 SELECTs that were called using an IF. Glad someone else has had the same problem and that it's a known issue. We were seeing queries on a table with ~150,000 rows + full-text going from < 1 second (2005) to 30+ seconds (2008).

Lazlow
Exactly! This query was originally working fine on a SQL 2000 machine, but once we moved to SQL 2008 this appeared. Welcome to our new club.
scolja