tags:

views:

83

answers:

6

How can I return two columns that each use different WHERE critia? Obviously, this won't work:

SELECT Name, COUNT(Column1) AS Total, COUNT(Column1) AS YearToDate
FROM Table1
WHERE Occurred_Date BETWEEN '2010-06-01' AND '2010-06-30' --Total
WHERE Occurred_Date BETWEEN '2010-01-01' AND '2010-06-30' --YearToDate

This is the output I'm looking for:

Name  | Total | YTD  
-------------------
Item1 | 2     | 3
Item2 | 4     | 8
A: 

You can probably use the MySQL IF statement on the COUNT

Kerry
+3  A: 

If you specify a column name for the COUNT function, it doesn't count NULL values.

So, the simple way would be to use CASE statements to convert the values you don't want counted to NULL

SELECT
    Name,
    COUNT(CASE
            WHEN Occurred_Date >= '2010-01-01' AND Occurred_Date < '2011-01-01'
                THEN Occurred_Date
            ELSE NULL
            END) AS [YTD]
    COUNT(CASE
            WHEN Occurred_Date >= '2010-06-01' AND Occurred_Date < '2011-07-01'
                THEN Occurred_Date
            ELSE NULL
            END) AS [MTD]
FROM Table1
GROUP BY Name

I'm not 100% certain the query engine will let you use CASE within COUNT (I'm not even sure what DB platform you're using), but it gives you the idea. If this way doesn't work, you can write the query using a derived table that will give you the same result.

Toby
Oops, I didn't notice that your "Total" is the month-to-date count.I'm testing to see if CASE works with COUNT on SQL server. I'll post a more complete snippet when I'm done.
Toby
If you don't want to rely on the Null behavior, you could always just have the case statement return 1 or 0 and do a sum instead of a count.
JohnFx
@JohnFx: Yes, this works, too. Depending on the table definition, I sometimes write both and test for performance if I know I'll be dealing with large sets.
Toby
@Matt: I've verified that this works on SQL Server 2008. I've also updated the posted code so that it does MTD/YTD
Toby
@Toby: Works just as expected. Thanks!
Matt Hanson
Of course you'll want to add a where clause after FROM Table1. WHEN Occurred_Date >= '2010-01-01' AND Occurred_Date < '2011-01-01'. Otherwise you'll be reading lots of rows you don't use.
Stephanie Page
+1  A: 

Seems like a good situation for a UNION.

SELECT Name, COUNT(Column1) AS Total, COUNT(Column1) AS YearToDate
FROM Table1
WHERE Occurred_Date BETWEEN '2010-06-01' AND '2010-06-30' --Total
UNION 
SELECT Name, COUNT(Column1) AS Total, COUNT(Column1) AS YearToDate
FROM Table1
WHERE Occurred_Date BETWEEN '2010-01-01' AND '2010-06-30' --YearToDate

Basically you are doing the two queries separately and then combining them into one set.

Not sure what type of DB you are using but here are links for SQL Server and MySql.

Abe Miessler
Um, no. This results in 2 rows, well actually an error in most DBs. Need Group By Name... after that you'll get two rows. One row will have the correct value for Total and the wrong value for YeartoDate, the other row will be the reverse. Also never use UNION when you know you have two different result sets. UNION forces a sort, UNION ALL doesn't. Yes this is just two rows... it's just a bad habit. Use UNION ALL all the time and remove the ALL when you need the extra work done.
Stephanie Page
A: 

You can also use

SELECT m.count, ytd.count FROM 
   (SELECT COUNT( id ) count FROM table WHERE date BETWEEN BETWEEN '2010-06-01' AND '2010-06-30') m, 
   (SELECT COUNT( id ) count FROM table WHERE date BETWEEN BETWEEN '2010-01-01' AND '2010-06-30') ytd 
nico
A: 
SELECT COALESCE(CurrMonth.Name, YTD.Name) AS Name, CurrMonth.Total AS Total, YTD.Total AS YearToDate
FROM (
    SELECT Name, COUNT(Column1) AS Total
    FROM Table1 
    WHERE Occurred_Date BETWEEN '2010-06-01' AND '2010-06-30' --Total 
    GROUP BY Name
) AS CurrMonth
FULL OUTER JOIN
(
    SELECT Name, COUNT(Column1) AS Total
    FROM Table1 
    WHERE Occurred_Date BETWEEN '2010-01-01' AND '2010-06-30' --YearToDate 
    GROUP BY Name
) AS YTD
ON CurrMonth.Name = YTD.Name

The full outer join is not necessary, but just demonstrates how you might need to handle a case where one set is not a strict subset of the other. I would typically use the YTD subquery LEFT JOIN to the current month subquery.

Another strategy - using CASE:

SELECT Name
    ,COUNT(CASE WHEN Occurred_Date BETWEEN '2010-06-01' AND '2010-06-30' THEN Column1 ELSE NULL END) AS Total 
    ,COUNT(CASE WHEN Occurred_Date BETWEEN '2010-01-01' AND '2010-06-30' THEN Column1 ELSE NULL END) AS YearToDate
FROM Table1  
WHERE Occurred_Date BETWEEN '2010-06-01' AND '2010-06-30' -- (unnecessary)
    OR Occurred_Date BETWEEN '2010-01-01' AND '2010-06-30'
GROUP BY Name 
Cade Roux
A: 
SELECT Name
    ,SUM(
        CASE WHEN Occurred_Date BETWEEN '2010-06-01' AND '2010-06-30' 
        THEN 1 
        ELSE 0 
        END) AS Total 
    ,SUM(
        CASE WHEN Occurred_Date BETWEEN '2010-01-01' AND '2010-06-30' 
        THEN 1 
        ELSE 0 
        END) AS YearToDate
FROM Table1  
GROUP BY Name 

EDIT: This should work in SQL Server.

shahkalpesh