rbntd rbntd - 17 days ago 6
SQL Question

How to group by month and year and also separate the entries?

I have a table

[MY_TABLE]
with the following datas : a date
[DOCUMENT_DATE]
and a status
[STATUS]
. I want to separate and count the 3 differents status : open when status < 8, lost when status = 8 or win when status > 8 while grouping them by month and year.

The final result would be something like that :
year, month, count(won), count(lost), count(open)
, giving effectively the count of each status for each month.

Some months don't have status at all (can be ignored) and some have only some status and not all of them (should write the month and year correctly)

I have a working query right now but it is really huge :

SELECT
CASE WHEN "open".year IS NOT NULL
THEN
"open".year
ELSE
(CASE WHEN "lost".year IS NOT NULL
THEN
"lost".year
ELSE
"won".year
END)
END AS "Année",
CASE WHEN "open".month IS NOT NULL
THEN
"open".month
ELSE
(CASE WHEN "lost".month IS NOT NULL
THEN
"lost".month
ELSE
"won".month
END)
END AS "Mois",
"open".count AS "Ouvertes",
"lost".count AS "Perdues",
"won".count AS "Gagnées"
FROM (SELECT
year([DOCUMENT_DATE]) AS "year",
MONTH([DOCUMENT_DATE]) AS "month",
COUNT(*) AS "count"
FROM [MY_TABLE]
WHERE [STATUS] < 8 AND [DOCUMENT_DATE] >= ?1 AND [DOCUMENT_DATE] <= ?2 AND ([SEGMENT] = ?3 OR ?3 IS NULL)
GROUP BY YEAR([DOCUMENT_DATE]), MONTH([DOCUMENT_DATE])) AS "open"
FULL JOIN (SELECT
year([DOCUMENT_DATE]) AS "year",
MONTH([DOCUMENT_DATE]) AS "month",
COUNT(*) AS "count"
FROM [MY_TABLE]
WHERE [STATUS] = 8 AND [DOCUMENT_DATE] >= ?1 AND [DOCUMENT_DATE] <= ?2 AND ([SEGMENT] = ?3 OR ?3 IS NULL)
GROUP BY YEAR([DOCUMENT_DATE]), MONTH([DOCUMENT_DATE])) AS "lost"
ON "open".month = "lost".month AND "open".year = "lost".year
FULL JOIN (SELECT
year([DOCUMENT_DATE]) AS "year",
MONTH([DOCUMENT_DATE]) AS "month",
COUNT(*) AS "count"
FROM [MY_TABLE]
WHERE [STATUS] > 8 AND [DOCUMENT_DATE] >= ?1 AND [DOCUMENT_DATE] <= ?2 AND ([SEGMENT] = ?3 OR ?3 IS NULL)
GROUP BY YEAR([DOCUMENT_DATE]), MONTH([DOCUMENT_DATE])) AS "won"
ON "open".month = "won".month AND "open".year = "won".year
ORDER BY CASE WHEN "open".year IS NOT NULL
THEN
"open".year
ELSE
(CASE WHEN "lost".year IS NOT NULL
THEN
"lost".year
ELSE
"won".year
END)
END,
CASE WHEN "open".month IS NOT NULL
THEN
"open".month
ELSE
(CASE WHEN "lost".month IS NOT NULL
THEN
"lost".month
ELSE
"won".month
END)
END


I'm fairly sure there is a much simpler and cleaner way to do that but I can't figure it out.

vkp vkp
Answer

I think this may be what you are looking for, based on the description.

SELECT  year([DOCUMENT_DATE])  AS "year",
        MONTH([DOCUMENT_DATE]) AS "month",
        COUNT(case when [STATUS] > 8 then 1 end) win_count,    
        COUNT(case when [STATUS] = 8 then 1 end) lost_count,
        COUNT(case when [STATUS] < 8 then 1 end) open_count
FROM [MY_TABLE]
GROUP BY year([DOCUMENT_DATE]),MONTH([DOCUMENT_DATE])
ORDER BY 1,2

Add WHERE [DOCUMENT_DATE] >= ?1 AND [DOCUMENT_DATE] <= ?2 AND ([SEGMENT] = ?3 OR ?3 IS NULL) if the condition is common across all the counts.

Comments