CASE in WHERE clause

  • I am converting embedded SQL (in Java code) to a stored procedure.

    They are building SQL WHERE clause dynamically in JAVA.

    What is the best way to implement this java logic in SQL?

    sqlStmt = "SELECT * FROM

    Imports..tdsix_balance B

    LEFT JOIN Imports..tdsix_account A ON B.ACID = A.ACID

    LEFT JOIN (SELECT DISTINCT ACID, MAX(AsOf) AS ASOF FROM Imports..tdsix_account GROUP BY ACID) Z

    ON A.ACID = Z.ACID"

    " +

    "where ";

    if (account.equals("")){

    sqlStmt = sqlStmt +

    "(B.ACID like '5F%' or B.ACID like '5G%' or B.ACID like '5H%' or B.ACID like '5T%'" +

    " or B.ACID like '5J%' or B.ACID like '01%' or B.ACID like '03%')" +

    "and B.asof =? " +

    "and (A.asof = Z.ASOF or A.asof is null) " +

    "order by B.acid";

    }

    else {

    sqlStmt = sqlStmt +

    "B.ACID like '" + account + "%' " +

    "and B.asof =? " +

    "and (A.asof = Z.ASOF or A.asof is null) " ;

    To me it looks like I have to use CASE in WHERE clause but I don't know how to write all those "LIKE ... OR LIKE ..."

    Or maybe I should forget about CASE and just use IF:

    IF @account IS NULL

    BEGIN

    SELECT * FROM

    Imports..tdsix_balance B

    LEFT JOIN Imports..tdsix_account A ON B.ACID = A.ACID

    LEFT JOIN (SELECT DISTINCT ACID, MAX(AsOf) AS ASOF FROM Imports..tdsix_account GROUP BY ACID) Z

    ON A.ACID = Z.ACID

    WHERE

    LEFT(B.ACID,2) IN ('5F','5G','5H','5T','5J','01','03')

    AND(A.AsOf = Z.ASOF or A.AsOf IS NULL)

    ORDER BY

    B.ACID

    END

    ELSE

    BEGIN

    SELECT * FROM

    Imports..tdsix_balance B

    LEFT JOIN Imports..tdsix_account A ON B.ACID = A.ACID

    LEFT JOIN (SELECT DISTINCT ACID, MAX(AsOf) AS ASOF FROM Imports..tdsix_account GROUP BY ACID) Z

    ON A.ACID = Z.ACID

    WHERE

    B.ACID LIKE @account+'%'

    AND (A.AsOf = Z.ASOF OR A.AsOf IS NULL)

    ORDER BY

    B.ACID

    END

  • Personally, if the where clause is that complex I like the idea of just using an IF statement like in your second example if for no other reason that it is more readable and 2 years from now it will be easier for you or someone else to figure out what it's doing.

    On the other hand, I don't use CASE statements in the WHERE clause at all anymore. I use something like this:

    AND ((@Status = 1 AND table.Status = 1)

    OR

    (@Status = 0))

    AND ((@ProductColor = 'yellow' AND table.Color = 'yellow')

    OR

    (@ProductColor = 'red' AND table.Color = 'red')

    OR

    (@ProductColor IN ('blue','green','')))

  • This is a type of catch-all query. Gail's blog has a great way of handling this type of thing.

    http://sqlinthewild.co.za/index.php/2009/03/19/catch-all-queries/[/url]

    _______________________________________________________________

    Need help? Help us help you.

    Read the article at http://www.sqlservercentral.com/articles/Best+Practices/61537/ for best practices on asking questions.

    Need to split a string? Try Jeff Modens splitter http://www.sqlservercentral.com/articles/Tally+Table/72993/.

    Cross Tabs and Pivots, Part 1 – Converting Rows to Columns - http://www.sqlservercentral.com/articles/T-SQL/63681/
    Cross Tabs and Pivots, Part 2 - Dynamic Cross Tabs - http://www.sqlservercentral.com/articles/Crosstab/65048/
    Understanding and Using APPLY (Part 1) - http://www.sqlservercentral.com/articles/APPLY/69953/
    Understanding and Using APPLY (Part 2) - http://www.sqlservercentral.com/articles/APPLY/69954/

  • Thanks all for your response.

    Gail's blog recommends using dynamic SQL

    but our boss doesn't like it at all.

    I have to learn how to handle it either with CASE

    or IF. My problem with IF was when there are

    a lot of conditions IF becomes messy so I try to find out

    if I can write logic in WHERE clause more accurate, easy to understand.

    I'm talking about something like this:

    Pseudo Java:

    IF @account = '' OR LEN(@account) = 0 OR @account IS NULL

    whereStmt = "WHERE Id1 BETWEEN 'AA' AND 'KK' AND Id2 IS NULL"

    ELSEIF @account <> '' AND LEN(@account) > 0 AND @account IS NOT NULL

    whereStmt = "WHERE Id1 BETWEEN 'MM' AND 'ZZ' AND Id2 IS NOT NULL"

    ELSEIF @account > 100

    whereStmt = "WHERE ......." and so on

  • RVO (3/13/2013)


    Thanks all for your response.

    Gail's blog recommends using dynamic SQL

    but our boss doesn't like it at all.

    I have to learn how to handle it either with CASE

    or IF. My problem with IF was when there are

    a lot of conditions IF becomes messy so I try to find out

    if I can write logic in WHERE clause more accurate, easy to understand.

    I'm talking about something like this:

    Pseudo code

    IF @account = '' OR LEN(@account) = 0 OR @account IS NULL

    WHERE Id1 BETWEEN 'AA' AND 'KK' AND Id2 IS NULL

    ELSEIF @account <> '' AND LEN(@account) > 0 AND @account IS NOT NULL

    WHERE Id1 BETWEEN 'MM' AND 'ZZ' AND Id2 IS NOT NULL

    ELSEIF @account > 100

    WHERE ....... and so on

    IF statements control the flow of processing. You can't use them like that in a where clause.

    _______________________________________________________________

    Need help? Help us help you.

    Read the article at http://www.sqlservercentral.com/articles/Best+Practices/61537/ for best practices on asking questions.

    Need to split a string? Try Jeff Modens splitter http://www.sqlservercentral.com/articles/Tally+Table/72993/.

    Cross Tabs and Pivots, Part 1 – Converting Rows to Columns - http://www.sqlservercentral.com/articles/T-SQL/63681/
    Cross Tabs and Pivots, Part 2 - Dynamic Cross Tabs - http://www.sqlservercentral.com/articles/Crosstab/65048/
    Understanding and Using APPLY (Part 1) - http://www.sqlservercentral.com/articles/APPLY/69953/
    Understanding and Using APPLY (Part 2) - http://www.sqlservercentral.com/articles/APPLY/69954/

  • You will have to suffer with performance issues because of the inability to properly use execution plans with this type of thing unless you can use the dynamic sql approach that Gail suggests.

    Something like this.

    WHERE

    (

    (@account = '' OR LEN(@account) = 0 OR @account IS NULL)

    AND

    (

    (Id1 BETWEEN 'AA' AND 'KK' AND Id2 IS NULL)

    OR

    (Id1 BETWEEN 'MM' AND 'ZZ' AND Id2 IS NOT NULL)

    )

    )

    OR

    (

    @account > 100

    )

    The big issue here is that it difficult to decipher the logic. Not too bad when you write it but in 6 months when you have to debug it or the next person comes along it is painful.

    Surprising your boss is ok with pass through sql and not dynamic parameterized sql. Many people don't like dynamic sql because they don't understand how to properly protect yourself using parameters.

    _______________________________________________________________

    Need help? Help us help you.

    Read the article at http://www.sqlservercentral.com/articles/Best+Practices/61537/ for best practices on asking questions.

    Need to split a string? Try Jeff Modens splitter http://www.sqlservercentral.com/articles/Tally+Table/72993/.

    Cross Tabs and Pivots, Part 1 – Converting Rows to Columns - http://www.sqlservercentral.com/articles/T-SQL/63681/
    Cross Tabs and Pivots, Part 2 - Dynamic Cross Tabs - http://www.sqlservercentral.com/articles/Crosstab/65048/
    Understanding and Using APPLY (Part 1) - http://www.sqlservercentral.com/articles/APPLY/69953/
    Understanding and Using APPLY (Part 2) - http://www.sqlservercentral.com/articles/APPLY/69954/

  • Gail's blog recommends using dynamic SQL

    but our boss doesn't like it at all.

    Is your boss SQL Server professional or just an idiot with a lot of ambitions?

    For what you're trying to achieve, dynamic SQL is the best option!

    So just tell your boss, if he wants good code, you better to follow the right direction, not the one you boss like or dislike. Sounds like kindergarten to me!

    _____________________________________________
    "The only true wisdom is in knowing you know nothing"
    "O skol'ko nam otkrytiy chudnyh prevnosit microsofta duh!":-D
    (So many miracle inventions provided by MS to us...)

    How to post your question to get the best and quick help[/url]

  • My boss is a professional. He was a developer recently.

    I saw some of his stored procedures - good code.

    I think he has a point. A few years ago at one large bank

    production support team had big problems debugging complex long stored procedures

    with lots of dynamic SQL. Production Support Team Lead just begged us to avoid using too much dynamic SQL.

    Some people argue that for dynamic SQL, SQL Server engine cannot build optimized execution plan.

    Not sure who's right who's wrong.

  • In this case I would have both the Dynamic SQL stored proc and non dynamic stored proc.

    Compare both the execution plans. Also checking statistics (SET STATISTICS IO ON) and clearing before executing each stored proc.

    It seems your manager really wants assurance and was burnt in the past. I would add some a @debug paramter to the dynamic stored proc. If @debug=1 then print the statement and do not execute. This way it makes it easier for a dev to see what is going on. I use this all the time if I am doing dynamic SQL.

    If you see huge gains by using dynamic sql and allow for easy debugging your boss might be happy. It is worth a try. I think you will be surprised at the results.

  • Forgot to include cache

    Also checking statistics (SET STATISTICS IO ON) and clearing cache [/b]before executing each stored proc.

    --Do not run in PROD!!!

    DBCC FREEPROCCACHE

    DBBC DROPCLEANBUFFERS

  • RVO (3/13/2013)


    My boss is a professional. He was a developer recently.

    I saw some of his stored procedures - good code.

    I think he has a point. A few years ago at one large bank

    production support team had big problems debugging complex long stored procedures

    with lots of dynamic SQL. Production Support Team Lead just begged us to avoid using too much dynamic SQL.

    Some people argue that for dynamic SQL, SQL Server engine cannot build optimized execution plan.

    Not sure who's right who's wrong.

    I'm sure. Your boss and Production Support Team are wrong on the following:

    "for dynamic SQL, SQL Server engine cannot build optimized execution plan"

    Now, you can write crap code in any form, dynamic or non-dynamic.

    For the issue you are solving, properly written dynamic SQL will outperform any other solution. If you try, you will find it yourself! For dynamic-SQL optimizer will find and cach the best possible plans for each of SQL variation which will be built. If you use IF or CASE WHEN based solution, optimizer will not be able to come up with effective plan for any of the cases. Why? Gail Shaw explained it very well in her blog. If you have a doubt - just check it yourself.

    Please remember, that there were many people around who argued that the Earth is flat, as if it would be round, we would fall down from it. http://en.wikipedia.org/wiki/Flat_Earth_Society

    😉

    _____________________________________________
    "The only true wisdom is in knowing you know nothing"
    "O skol'ko nam otkrytiy chudnyh prevnosit microsofta duh!":-D
    (So many miracle inventions provided by MS to us...)

    How to post your question to get the best and quick help[/url]

  • RVO (3/13/2013)


    Thanks all for your response.

    Gail's blog recommends using dynamic SQL

    but our boss doesn't like it at all.

    I have to learn how to handle it either with CASE

    or IF. My problem with IF was when there are

    a lot of conditions IF becomes messy so I try to find out

    if I can write logic in WHERE clause more accurate, easy to understand.

    I'm talking about something like this:

    Pseudo Java:

    IF @account = '' OR LEN(@account) = 0 OR @account IS NULL

    whereStmt = "WHERE Id1 BETWEEN 'AA' AND 'KK' AND Id2 IS NULL"

    ELSEIF @account <> '' AND LEN(@account) > 0 AND @account IS NOT NULL

    whereStmt = "WHERE Id1 BETWEEN 'MM' AND 'ZZ' AND Id2 IS NOT NULL"

    ELSEIF @account > 100

    whereStmt = "WHERE ......." and so on

    You will either use dynamic sql to do this work or you will have to code a bajillion permutations of actual tsql that is hit with a massive sequence of IF statements testing the parameters or you will suffer HORRIBLY BAD PERFORMANCE AND CONCURRENCY. There is absolutely no other option. From my quick review I will say that dynamic sql is by far the best solution here. Tell your boss that I have gotten FIVE ORDERS OF MAGNITUDE performance improvement from scenarios like this by using dynamic sql. And concurrency goes WAY up when you stop doing table scans and hash joins that will result from the IS NULL OR construct. Guard against SQL Injection and you are good to go.

    Best,
    Kevin G. Boles
    SQL Server Consultant
    SQL MVP 2007-2012
    TheSQLGuru on googles mail service

  • ;-)Thanks a lot for your response.

    I always feel a very friendly supportive environment here.

    I will definetly compare performance of regular vs dynamic SQL procedures.

    But later.

    My goal now is to come up with a "pure TSQL" mechanism

    that handles this dynamic WHERE clause.

    I see it all over the place in the Java code I am converting.

    So it's like a pattern that I need find out how to handle nicely.

    I feel like if I give up and do it with dynamic SQL - I surrender

    and don't improve my TSQL skills .. 🙂

    Again, a pattern is (java code)

    sqlStmt = "select * from IndustryMap";

    sqlStmt += " WHERE 1=1 " + whereSQLStmt;

    if(cusip != null && cusip.length()>0){

    whereSQLStmt += " and id_cusip = '" + cusip + "' ";}

    if(ticker != null && ticker.length()>0){

    whereSQLStmt += " and ticker = '" + ticker + "' ";}

    if(exchangeCode != null && exchangeCode.length()>0){

    whereSQLStmt += " and exch_Code = '" + exchangeCode + "' ";

    They dynamically build WHERE clause

    if input parameter is not null.

    There are hundreds of these cases in Java.

  • I tried this

    select * from IndustryMap

    WHERE 1 =1

    AND id_cusip = COALESCE(@cusip,id_cusip)

    AND ticker = COALESCE(@ticker,ticker)

    AND exch_code = COALESCE(@exchangeCode,exch_code)

    and was ready to scream Hurray but realized that

    when any of the columns have a null value

    this won't work. Because "AND col = NULL" is not valid.

    Keep searching.... 🙂

  • RVO (3/14/2013)


    I tried this

    select * from IndustryMap

    WHERE 1 =1

    AND id_cusip = COALESCE(@cusip,id_cusip)

    AND ticker = COALESCE(@ticker,ticker)

    AND exch_code = COALESCE(@exchangeCode,exch_code)

    and was ready to scream Hurray but realized that

    when any of the columns have a null value

    this won't work. Because "AND col = NULL" is not valid.

    Keep searching.... 🙂

    That will get you HORRIBLE plans too for several reasons.

    Best,
    Kevin G. Boles
    SQL Server Consultant
    SQL MVP 2007-2012
    TheSQLGuru on googles mail service

Viewing 15 posts - 1 through 15 (of 16 total)

You must be logged in to reply to this topic. Login to reply