Reputation: 10720
I have made this query where I get a list of items by their statuses and get the quantities for each status. Here is the query:
SELECT DISTINCT (status) AS Status, COUNT(status) AS Quantity
FROM projects
GROUP BY status
ORDER BY Quantity DESC
And here are the results:
Status Quantity
'PRO', 238
'TER', 75
'SUS', 14
'REI', 3
Now for those that have a 'TER' status, I want to get those 'TER' statuses that were updated or modified this year and add them to the results (excluding those 'TER' from previous years). To do this, I have this column updated_at
(so to speak).
How can I filter those values?
For example, I have tried:
SELECT DISTINCT (status) AS Status, COUNT(status) AS Quantity,
IF(status='TER',SELECT * FROM projects WHERE year(updated_at)=YEAR(CURDATE()))
FROM projects
GROUP BY status
ORDER BY Quantity DESC
But it didn't work.
Any ideas?
Thanks for the ideas.
Posible solution:
I have figured out another solution by using UNION:
(SELECT DISTINCT (status) AS Status, COUNT(status) AS Quantity,
IF(status='TER',SELECT * FROM projects WHERE year(updated_at)=YEAR(CURDATE()))
FROM projects
GROUP BY status
ORDER BY Quantity DESC)
UNION
(
SELECT DISTINCT (status) AS Status, COUNT(status) AS Quantity,
IF(status='TER',SELECT * FROM projects WHERE year(updated_at)=YEAR(CURDATE()))
FROM projects
WHERE year(updated_at) = YEAR(curdate())
AND status IN ('TER')
GROUP BY status
ORDER BY Quantity DESC
)
So now I get those projects in 'TER' status and only those within the current year.
So the filter results for 'TER' are now (and should be less results):
Status Quantity
'PRO', 238
'SUS', 14
'REI', 3
'TER', 45
The problem now is that I need to reorder the results, I mean, 'TER' should be in second place... well, I have just found out here.
So my final query is:
SELECT * FROM (
(SELECT DISTINCT (status) AS Status, COUNT(status) AS Quantity,
IF(status='TER',SELECT * FROM projects WHERE year(updated_at)=YEAR(CURDATE()))
FROM projects
GROUP BY status
ORDER BY Quantity DESC)
UNION
(
SELECT DISTINCT (status) AS Status, COUNT(status) AS Quantity,
IF(status='TER',SELECT * FROM projects WHERE year(updated_at)=YEAR(CURDATE()))
FROM projects
WHERE year(updated_at) = YEAR(curdate())
AND status IN ('TER')
GROUP BY status
ORDER BY Quantity DESC
)
) a
GROUP BY status
ORDER BY Quantity DESC
And now I get the results I want and ordered desc:
Status Quantity
'PRO', 238
'TER', 45
'SUS', 14
'REI', 3
Or is there a better way out there?
Upvotes: 0
Views: 230
Reputation: 108370
Based on the edit to the question, which now better explains the expected result, I've updated this answer with a different query. I still recommend conditional aggregation.
SELECT p.status AS Status
, COUNT( IF( p.status = 'TER'
, IF( YEAR(p.updated_at) = YEAR(CURDATE())
, 1
, NULL
)
, 1
)
) AS Quantity
FROM projects p
GROUP BY p.status
ORDER BY Quantity DESC
The line breaks and spacing aren't required... I just do that to make it easier to decipher.
For a row that has status = 'TER'
, it is included in the "count" only if the condition on updated_at
is satisfied. For all other values of status
, the row is included in the "count" for that status.
The MySQL-specific expression:
IF( p.status = 'TER'
, IF( YEAR(p.updated_at) = YEAR(CURDATE())
, 1
, NULL
)
, 1
)
Could be replaced with a more ANSI standards compliant expression, for example:
CASE WHEN p.status = 'TER'
CASE WHEN YEAR(p.updated_at) = YEAR(CURDATE()
THEN 1
END
ELSE 1
END
ORIGINAL ANSWER
What's not clear is what result you want returned.
It looks like you want an additional column returned, and it looks like you might want that to be a "count" of a subset of the rows included in the "Quantity" column. We can do that.
But first, let's fix some issues with the original query.
The keyword DISTINCT
is not necessary, it's redundant with the GROUP BY
. And there's no need for parens around the reference to status
. That's not illegal to do that, but it doesn't do anything, so it's just confusing. It makes it look as if someone believes that DISTINCT
is a function. It's not. It's a keyword that applies to the entire SELECT
list.
SELECT p.status AS Status
, COUNT(p.status) AS Quantity
FROM projects p
GROUP BY p.status
ORDER BY Quantity DESC
It looks like you want conditional aggregation... if the updated_at
is a date or datetime in the current year, then include it in the count, otherwise don't.
SELECT p.status AS Status
, COUNT(p.status) AS Quantity
, SUM(IF(p.status = 'TER' AND YEAR(p.updated_at)=YEAR(CURDATE()),1,NULL)) AS curcnt
FROM projects p
GROUP BY p.status
ORDER BY Quantity DESC
The expression:
IF(p.status = 'TER' AND YEAR(p.updated_at)=YEAR(CURDATE()),1,NULL)
is MySQL shorthand. An equivalent result could be obtained with a more ANSI standards compliant expression:
CASE WHEN p.status = 'TER' AND YEAR(p.updated_at)=YEAR(CURDATE()) THEN 1 END
Depending on how you want a count of "zero" to be displayed on that row, and other rows, you might want to use 0
in place of NULL
, and/or use an expression that includes additional IF
, IFNULL
or NULLIF
functions.
FOLLOWUP
The desired resultset is not clear from the question.
The answer above is based on the assumption that the desired result is an additional column that contains a "count" of the rows that have status='TER'
and updated_at
within the current year.
The answer above demonstrates conditional aggregation, and does not make use of a "nested query".
A "nested query" could be used to obtain an equivalent result.
As an example of using a "nested query" as an inline view:
SELECT p.status AS Status
, COUNT(p.status) AS Quantity
, NULLIF(MAX(q.curcnt),0) AS curcnt
FROM projects p
LEFT
JOIN ( SELECT r.status
, COUNT(1) AS curcnt
FROM projects r
WHERE r.status = 'TER'
AND YEAR(r.updated_at) = YEAR(NOW())
GROUP BY r.status
) q
ON q.status = p.status
GROUP BY p.status
ORDER BY Quantity DESC
And as an example of a "nested query" as a subquery in the SELECT list:
SELECT p.status AS Status
, COUNT(p.status) AS Quantity
, NULLIF( SELECT COUNT(1)
FROM projects r
WHERE r.status = p.status
AND r.status = 'TER'
AND YEAR(r.updated_at) = YEAR(NOW())
,0) AS curcnt
FROM projects p
GROUP BY p.status
ORDER BY Quantity DESC
Upvotes: 2