简体   繁体   中英

Repetitive CASE WHEN in Aggregate SQL SELECT Statements

I am trying to to reduce cost and size of a query I use regularly, it looks far too repetitive to be the most efficient way of doing things.

A simplified representation can be seen below:

SELECT [Year],
       [Month],
       COUNT(CASE WHEN Type = 'Quotation' THEN clientID ELSE NULL END),
       COUNT(CASE WHEN Type = 'Purchase' THEN ClientID ELSE NULL END),
       SUM(CASE WHEN Type = 'Purchase' THEN Cost ELSE NULL END)
       ...           
FROM dbo.Example
GROUP BY [Year],[Month]

It is the CASE WHEN ... THEN ELSE NULL END 's that I think I must be able to trim down, in terms of size and cost. Any recommendations?

I am using SQL Server 2008 R2, Thanks.

How about making a view by type ? (Quotation, purchase, etc). In that way, you can optimize view by view and make a join query to get what you want.

I think that is the recommended way to 'rotate' a table.

Check this article from Microsoft -> https://support.microsoft.com/en-us/kb/175574

Probably the cost is in the 'Type' column, maybe you can index that -> How to create an index for a string column in sql?

Erm, what's wrong with:

SELECT [Year],
       [Month],
       [Type],
       COUNT(ClientID) "Count",
       SUM(Cost) "TotalCost",
       ...           
FROM dbo.Example
GROUP BY [Year],[Month],[Type]

The way you're doing it is de-normalizing it. That's why the performance is so poor. Sure, you're generating SUM(Cost) when Type = 'Quotation' and you might not need that, but addition is not typically a limiting factor for query performance.

Is there some reason you must have only one record for each [Year],[Month] ?

When you've got something that complex, with lots of conditional aggregates, in my experience it's faster and clearer if you build it in a stepwise way. That is, have a table variable with the required data structure, and add records to it with individual queries that avoid using CASE statements and (hopefully) do their record selection based on indexed columns.

So, if you had a table variable called @TypeTotals , you could do something like this (using your simplified example):

 INSERT INTO @TypeTotals
    SELECT [Year],
           [Month],
           [Type],
           COUNT(ClientID),
           Null,
           Null
    FROM   dbo.Example
    WHERE  [Type] = 'Quotation'
    GROUP BY
           [Year], [Month]

 INSERT INTO @TypeTotals
    SELECT [Year],
           [Month],
           [Type],
           Null,
           COUNT(ClientID),
           SUM(Cost)
    FROM   dbo.Example
    WHERE  [Type] = 'Purchase'
    GROUP BY
           [Year], [Month]

... etc ...

You could then summarize the contents of @TypeTotals .

The key to speed would be to avoid those CASE statements (as you already figured out) and to make what use you can of indexes.

And to experiment. Make use of the Profiler, if you can, because execution plans don't always represent the speeds you really will get.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM