Q:

Linq. Help me tune this!

I have a linq query that is causing some timeout issues. Basically, I have a query that is returning the top 100 results from a table that has approximately 500,000 records.
Here is the query:
using (var dc = CreateContext())
        {
            var accounts = string.IsNullOrEmpty(searchText)
                            ? dc.Genealogy_Accounts
                                .Where(a => a.Genealogy_AccountClass.Searchable)
                                .OrderByDescending(a => a.ID)
                                .Take(100)
                            : dc.Genealogy_Accounts
                                .Where(a => (a.Code.StartsWith(searchText)
                                            || a.Name.StartsWith(searchText))
                                            && a.Genealogy_AccountClass.Searchable)
                                .OrderBy(a => a.Code)
                                .Take(100);
            return accounts.Select(a => 
        }
    }

Oddly enough it is the first linq query that is causing the timeout. I thought that by doing a 'Take' we wouldn't need to scan all 500k of records. However, that must be what is happening. I'm guessing that the join to find what is 'searchable' is causing the issue. I'm not able to denormalize the tables... so I'm wondering if there is a way to rewrite the linq query to get it to return quicker... or if I should just write this query as a Stored Procedure (and if so, what might it look like). Thanks.

A:

Well to start with, I'd find out what query is being generated (in LINQ to SQL you'd set the Log on the data context) and then profile it in SQL Server Management Studio. Play with it there until you've found something that is fast enough (either by changing the query or adding indexes) and if you've had to change the query, work out how to represent that in LINQ.
I suspect the problem is that you're combining OrderBy and Take - which means it potentially needs to find out all the results in order to work out which the top 100 would look like. Is Code indexed? If not, try indexing that - it may help by allowing the server to consider records in the order in which they'd be returned, so it can stop after it's found 100 records. You should look at indexes for the other columns too.

A:

The Take(100) translates to "Select Top 100" etc.  This would help if your problem was an otherwise huge result set, where there are a lot of columns returned.  I bet though that your problem is a table scan resulting from the query.  In this case, .Take(100) might not help much at all.  
So, the likely culprit is the same as if you were doing SQL using ADO.NET:  How are your Indxes?  Are the fields being searched fields for which you don't have good indexes?  This would cause a drastic decrease in performance compared to queries that do utilize good indexes.  Add an index that includes Code and Name and see what happens.  Not using an index for Code is guaranteed to hose you, because of the Order By.  Also, what field links Genealogy_Accounts and Genealogy_AccountClass?  A lack of index on either table could hose things.  (I would guess an index including Searchable is unlikely to help.) 
Use SQL Profiler to see the actual query being run (though you can do this in VS too), and to see how bad it really is on the server.
The problem might be LINQ doing something stupid generating the query, but this is probably not the case.  We're finding LINQ-to-SQL often makes better queries than we do.  Even if it looks goofy, it's usually very efficient.  You can put the SQL in Query Analyzer, and check out the query plan.  Then rewrite the SQL to be more human-simple and see if it improve things -- I bet it won't.  I think you'll still see a table scan, indicating something is wrong with your index.

