How to improve that query?

  • Is there any other way to write the below query:

    SELECT

    SUM(CASE WHEN Type = 4 AND STATUS IN (1, 2, 3) THEN Value ELSE 0 END)

    ,SUM(CASE WHEN Type = 3 AND STATUS IN (1, 2) THEN Value ELSE 0 END)

    ,SUM(CASE WHEN Type = 4 AND STATUS IN (1, 2) THEN Value - Discount ELSE 0 END)

    FROM Orders

    WHERE STATUS IN (1, 2, 3)

  • That's probably the best way to write it, I'd just add and additional condition for Type IN(3, 4).

    Why do you want to change it?

    Luis C.
    General Disclaimer:
    Are you seriously taking the advice and code from someone from the internet without testing it? Do you at least understand it? Or can it easily kill your server?

    How to post data/code on a forum to get the best help: Option 1 / Option 2
  • Lets say someone asked me if there are any ways that this query can be improved by writing it differently and considering some indexing strategies, purely theoretical question. I couldnt think of anything so i thought I'll ask here, because it's Friday afternoon i'm sitting in the garden with a beer but this is still bothering me.

  • Yep. Just add the schema to the table and add the other column to the WHERE:

    ...

    FROM dbo.Orders

    WHERE STATUS IN (1, 2, 3) AND Type IN (3, 4)

    SQL DBA,SQL Server MVP(07, 08, 09) A socialist is someone who will give you the shirt off *someone else's* back.

  • For indexing, you could use something like this:

    CREATE NONCLUSTERED INDEX IX_Orders_Cover1

    ON dbo.Orders(Type, Status)

    INCLUDE (Value, Discount)

    The Type and Status columns can change order depending on their selectivity.

    Other options to write the query will be expensive, such as a multiple pivot or three queries using a cross join or an update that will scan the table several times. Basically, the cross tabs approach that you posted is probably the best method around, it just needed the extra filter in the WHERE clause.

    Luis C.
    General Disclaimer:
    Are you seriously taking the advice and code from someone from the internet without testing it? Do you at least understand it? Or can it easily kill your server?

    How to post data/code on a forum to get the best help: Option 1 / Option 2
  • Hmm... You must be right... I must be overcomplicating it in my head, i shouldnt be thinking about work anymore.

    Thanks for quick replies, at leasti know i'm not going crazy and my initial thinking was correct. Have a good weekend guys.

  • Kutang Pan (7/10/2015)


    Is there any other way to write the below query:

    SELECT

    SUM(CASE WHEN Type = 4 AND STATUS IN (1, 2, 3) THEN Value ELSE 0 END)

    ,SUM(CASE WHEN Type = 3 AND STATUS IN (1, 2) THEN Value ELSE 0 END)

    ,SUM(CASE WHEN Type = 4 AND STATUS IN (1, 2) THEN Value - Discount ELSE 0 END)

    FROM Orders

    WHERE STATUS IN (1, 2, 3)

    First thought is an index (Type) filtered on STATUS to satisfy the where clause, include the Value to make it a covering index.

    Puzzles me though that the last condition will never be hit as it is a subset of the first one, typo perhaps?

    😎

  • Eirikur Eiriksson (7/10/2015)


    Puzzles me though that the last condition will never be hit as it is a subset of the first one, typo perhaps?

    😎

    It's more an optic illusion, as those are separate CASEs.

    Luis C.
    General Disclaimer:
    Are you seriously taking the advice and code from someone from the internet without testing it? Do you at least understand it? Or can it easily kill your server?

    How to post data/code on a forum to get the best help: Option 1 / Option 2
  • Luis Cazares (7/10/2015)


    Eirikur Eiriksson (7/10/2015)


    Puzzles me though that the last condition will never be hit as it is a subset of the first one, typo perhaps?

    😎

    It's more an optic illusion, as those are separate CASEs.

    Me being dyslexic:rolleyes:

    😎

  • Luis Cazares (7/10/2015)


    That's probably the best way to write it, I'd just add and additional condition for Type IN(3, 4).

    Why do you want to change it?

    In support of this, here is a sample where you can get a bit of a boost - maybe. This is based on a 1 million row test table with random test data that I generated. Using the additional filter Luis specified, the performance of the query remains the same (as the OP posted) as without, but the query I changed it to gets a healthy boost to where it outperforms the OPs original (in other words as it was, it was performing rather well).

    DECLARE @string VARCHAR(60) = '1,2,3';

    CREATE TABLE #ordstatus (OStatus INT INDEX IX_OrdStatus CLUSTERED);

    INSERT INTO #ordstatus

    SELECT i.Item AS OStatus

    FROM DBA.dbo.DelimitedSplit8K(@string, ',') i;

    SELECT SUM(CASEWHEN Type = 4

    AND STATUS IN ( 1, 2, 3 ) THEN Value

    ELSE 0

    END)

    , SUM(CASEWHEN Type = 3

    AND STATUS IN ( 1, 2 ) THEN Value

    ELSE 0

    END)

    , SUM(CASEWHEN Type = 4

    AND STATUS IN ( 1, 2 ) THEN Value - Discount

    ELSE 0

    END)

    FROM Orders o

    INNER JOIN #ordstatus os

    ON o.status = os.OStatus

    WHERE Type IN (3, 4)

    /*with this predicate, this query now outperforms the original by 10% */

    ;

    DROP TABLE #ordstatus;

    GO

    SELECT SUM(CASEWHEN Type = 4

    AND STATUS IN ( 1, 2, 3 ) THEN Value

    ELSE 0

    END)

    , SUM(CASEWHEN Type = 3

    AND STATUS IN ( 1, 2 ) THEN Value

    ELSE 0

    END)

    , SUM(CASEWHEN Type = 4

    AND STATUS IN ( 1, 2 ) THEN Value - Discount

    ELSE 0

    END)

    FROM Orders

    WHERE STATUS IN ( 1, 2, 3 )

    AND Type IN(3, 4)

    ;

    FWIW - I also tested with CE 70 and 120 and got the same results as far as performance.

    Jason...AKA CirqueDeSQLeil
    _______________________________________________
    I have given a name to my pain...MCM SQL Server, MVP
    SQL RNNR
    Posting Performance Based Questions - Gail Shaw[/url]
    Learn Extended Events

  • Kutang Pan (7/10/2015)


    Is there any other way to write the below query:

    SELECT

    SUM(CASE WHEN Type = 4 AND STATUS IN (1, 2, 3) THEN Value ELSE 0 END)

    ,SUM(CASE WHEN Type = 3 AND STATUS IN (1, 2) THEN Value ELSE 0 END)

    ,SUM(CASE WHEN Type = 4 AND STATUS IN (1, 2) THEN Value - Discount ELSE 0 END)

    FROM Orders

    WHERE STATUS IN (1, 2, 3)

    Unless you care when other Types occur and since you only want 2 types I might would add

    AND TYPE IN (3, 4)

    Also I am trying to figure out what the intent is for the second value because you can have

    1,0,1

    0,1,0

    But never

    1,1,1 or 0,1,1 or 1,1,0

    Otherwise indexing the Status column and maybe do a composite index with Status, Type if might improve performance on read. It will cause a hit on inserting, updating and deleting data so weigh that fact.

  • Antares686 (7/13/2015)


    Kutang Pan (7/10/2015)


    Is there any other way to write the below query:

    SELECT

    SUM(CASE WHEN Type = 4 AND STATUS IN (1, 2, 3) THEN Value ELSE 0 END)

    ,SUM(CASE WHEN Type = 3 AND STATUS IN (1, 2) THEN Value ELSE 0 END)

    ,SUM(CASE WHEN Type = 4 AND STATUS IN (1, 2) THEN Value - Discount ELSE 0 END)

    FROM Orders

    WHERE STATUS IN (1, 2, 3)

    Unless you care when other Types occur and since you only want 2 types I might would add

    AND TYPE IN (3, 4)

    Also I am trying to figure out what the intent is for the second value because you can have

    1,0,1

    0,1,0

    But never

    1,1,1 or 0,1,1 or 1,1,0

    Otherwise indexing the Status column and maybe do a composite index with Status, Type if might improve performance on read. It will cause a hit on inserting, updating and deleting data so weigh that fact.

    +1

    I was also thinking that adding a filter on Type would alter the original query to exclude zeroes for rows where the Type is neither 3 nor 4.

  • Chris Wooding (7/14/2015)


    Antares686 (7/13/2015)


    Kutang Pan (7/10/2015)


    Is there any other way to write the below query:

    SELECT

    SUM(CASE WHEN Type = 4 AND STATUS IN (1, 2, 3) THEN Value ELSE 0 END)

    ,SUM(CASE WHEN Type = 3 AND STATUS IN (1, 2) THEN Value ELSE 0 END)

    ,SUM(CASE WHEN Type = 4 AND STATUS IN (1, 2) THEN Value - Discount ELSE 0 END)

    FROM Orders

    WHERE STATUS IN (1, 2, 3)

    Unless you care when other Types occur and since you only want 2 types I might would add

    AND TYPE IN (3, 4)

    Also I am trying to figure out what the intent is for the second value because you can have

    1,0,1

    0,1,0

    But never

    1,1,1 or 0,1,1 or 1,1,0

    Otherwise indexing the Status column and maybe do a composite index with Status, Type if might improve performance on read. It will cause a hit on inserting, updating and deleting data so weigh that fact.

    +1

    I was also thinking that adding a filter on Type would alter the original query to exclude zeroes for rows where the Type is neither 3 nor 4.

    Why would you think only with ones and zeros? It's possible to get a row as 1,1,1 or 1,1,0. A row as 0,1,1 wouldn't be logic as it would indicate negative discounts (a.k.a. charges).

    Basically, this query will return either one row or no rows depending if there's data in the table. If zeros are needed, there are ways to add them without scanning the whole table.

    Luis C.
    General Disclaimer:
    Are you seriously taking the advice and code from someone from the internet without testing it? Do you at least understand it? Or can it easily kill your server?

    How to post data/code on a forum to get the best help: Option 1 / Option 2
  • Luis Cazares (7/15/2015)


    Chris Wooding (7/14/2015)


    Antares686 (7/13/2015)


    Kutang Pan (7/10/2015)


    Is there any other way to write the below query:

    SELECT

    SUM(CASE WHEN Type = 4 AND STATUS IN (1, 2, 3) THEN Value ELSE 0 END)

    ,SUM(CASE WHEN Type = 3 AND STATUS IN (1, 2) THEN Value ELSE 0 END)

    ,SUM(CASE WHEN Type = 4 AND STATUS IN (1, 2) THEN Value - Discount ELSE 0 END)

    FROM Orders

    WHERE STATUS IN (1, 2, 3)

    +1

    I was also thinking that adding a filter on Type would alter the original query to exclude zeroes for rows where the Type is neither 3 nor 4.

    Why would you think only with ones and zeros? It's possible to get a row as 1,1,1 or 1,1,0. A row as 0,1,1 wouldn't be logic as it would indicate negative discounts (a.k.a. charges).

    Basically, this query will return either one row or no rows depending if there's data in the table. If zeros are needed, there are ways to add them without scanning the whole table.

    Recheck the logic, the Type cannot be both 4 and 3, which means 1,1,1 , 1,1,0 and 0,1,1 cannot occur. Which is why I bring it up to see if this is intended or if the goal may be something else. Just like saying use where Type in (3,4) if other values really don't need to be reported in the query. The issue is understanding the final goal of the output and what may be negatively happening overall.

  • Antares686 (7/16/2015)


    Recheck the logic, the Type cannot be both 4 and 3, which means 1,1,1 , 1,1,0 and 0,1,1 cannot occur. Which is why I bring it up to see if this is intended or if the goal may be something else. Just like saying use where Type in (3,4) if other values really don't need to be reported in the query. The issue is understanding the final goal of the output and what may be negatively happening overall.

    I just made up some sample data including the columns mentioned in the query and an additional to group by so I can show you the different examples which would generate the results that you say can't happen. I guess the problem is that you're missing the part that these are separate CASE statements in different aggregate functions, so no need to think row-by-row.

    CREATE TABLE Orders(

    OrderIdint IDENTITY,

    OrderDatedate,

    TYPEint,

    STATUSint,

    VALUEint,

    Discountint

    )

    INSERT INTO Orders(OrderDate, TYPE, STATUS, VALUE, Discount) VALUES

    ('20150701', 3, 1, 1, 0),

    ('20150701', 4, 1, 1, 0),

    ('20150702', 3, 1, 1, 0),

    ('20150702', 4, 1, 1, 1),

    ('20150703', 3, 1, 1, 0),

    ('20150703', 4, 1, 0, -1),

    ('20150704', 1, 1, 1, 0),

    ('20150704', 2, 1, 5, 1),

    ('20150705', 3, 1, 51, 0),

    ('20150705', 4, 1, 61, 1),

    ('20150705', 3, 1, 19, 0),

    ('20150705', 4, 1, 11, 5);

    SELECT OrderDate

    ,SUM(CASE WHEN Type = 4 AND STATUS IN (1, 2, 3) THEN Value ELSE 0 END)

    ,SUM(CASE WHEN Type = 3 AND STATUS IN (1, 2) THEN Value ELSE 0 END)

    ,SUM(CASE WHEN Type = 4 AND STATUS IN (1, 2) THEN Value - Discount ELSE 0 END)

    FROM Orders

    WHERE STATUS IN (1, 2, 3)

    GROUP BY OrderDate;

    Luis C.
    General Disclaimer:
    Are you seriously taking the advice and code from someone from the internet without testing it? Do you at least understand it? Or can it easily kill your server?

    How to post data/code on a forum to get the best help: Option 1 / Option 2

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

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