views:

61

answers:

3

I have an application that needs to return search results from a SQL Server 2008 database. I would like to use a single stored procedure to return the results but I am finding that as I build the stored procedure it is full of many Else .. Else If statements with the query repeated over and over with slight variations depending on the users search criteria.

Is there a better way to go about this? I am trying to avoid writing dynamic SQL because I would like the benefits of an execution plan but I am thinking there must be a better way. Does anyone have any suggestions or perhaps examples of how best to design a stored procedure that has to deal with many search parameters, many of which may be NULL? Thank you.

A: 

I always run into this problem myself. Tend to use dynamic SQL, as long as you use the sp_executesql then the optimizer will try to use the same execution plan.

http://ayyanar.blogspot.com/2007/11/performance-difference-between-exec-and.html

CountZero
Thanks for the article. I guess I always have the option of using dynamic SQL from within the stored procedure.
webworm
The only option if number of criteria grows, table sizes grow and you care about performance.
wqw
+1  A: 

I've always done this by using default values and conditions; e.g.

CREATE PROCEDURE [dbo].[searchForElement]
(
    @Town     nvarchar(100) = '',
    @County   nvarchar(100) = '',
    @postcode nvarchar(100) = ''
)
AS
BEGIN
    SET NOCOUNT ON;

    SELECT <fields> 

    FROM table 
    WHERE 
        (@Town = ''             OR Town     LIKE '%'+@Town+'%')
        AND (@County = ''       OR County   LIKE '%'+@County+'%')
        AND (@postcode = ''     OR postcode LIKE '%'+@PostCode +'%')
END

Edit:

As @gbn correctly advises the above will result in an index scan which may be a problem for large tables. If this is a problem the solution is to below using ISNULL and the fact that adding NULL to anything results in NULL it will allow an index seek because the '%' is understood by the optimiser (tested on SQL2008). This may be less readable but it makes better use of the indexes.

CREATE PROCEDURE [dbo].[searchForElement]
(
    @Town     nvarchar(100) = NULL,
    @County   nvarchar(100) = NULL,
    @postcode nvarchar(100) = NULL
)
AS
BEGIN
    SET NOCOUNT ON;

    SELECT <fields> 

    FROM table 
    WHERE   Town     LIKE ISNULL('%'+@Town+'%', '%')
       AND  County   LIKE ISNULL('%'+@County+'%', '%')
       AND  Postcode LIKE ISNULL('%'+@PostCode +'%', '%')
END
Richard Harrison
I am afraid I am having a little trouble understanding the logic in the WHERE clause. If the @Town parameter is 'blank' doesn't the LIKE clause still look for a match using LIKE?
webworm
The reason for the @Town='' is to allow the optimiser to shortcircuit the entire clause because it will always be true and therefore shouldn't need an index scan. Certainly this is something I've always believed and have checked it against execution plans.This technique I've only used with tens of thousands of records and performance hasn't been a problem.
Richard Harrison
+1  A: 

Not really.

With SQL Server 2005 and above with statement level recompilation then there is less of a penalty with OR clauses, just maintenance complexity.

Using Richard Harrison's approach makes it worse because OR is not-sargable, runs slowly, most likely won't use indexes.

Dynamic SQL opens up SQL injection, quoting and caching issues.

This leaves sp_executesql as per CountZero's answer which still requires building up strings.

The solution may not be code based... do you really need to search on all fields at any one time? I'd try to split into simple and advanced searches, or work out what the most common are and try to cover these queries.

gbn
The app needs to search for employees. The number variables used to search for employees is large. Things from partials matches on first and last name to a date range in which the person was hired and current status with the company. Then I need to add logic to return results with different order clauses to support sorting in the UI. I am not sure how I would break that out into separate stored procs.
webworm
Sort in the UI would help, in some whizzy grid and only do your initial sort in SQL. But again, is every column searched and used equally? i should have said have a few ELSE clauses for the most common, and one slow horrible one for the rest.
gbn
@gbn I think you're wrong about my approach making it worse as I think that the constant expression causes the entire clause to be optimised away. I've checked the execution plans and I can find nothing untowards when comparing against a straight field=value select.However if I'm wrong I'd be happy to learn why.
Richard Harrison
@Richard Harrison: SQL Server is not guaranteed to shortcut (ie evaluate in the order you expect) unless you use CASE. The leading % invalidates index usage too. Your plans will all be table and index scans, no?
gbn
@gbn You're right - it does an index scan - so I've come up with a version that allows index seek (non clustered). It looks much better in the plans. Thanks.
Richard Harrison