I have a table named 'jobs'. For a particular user a job can be active, archived, overdue, pending, or closed. Right now every page request is generating 5 COUNT queries and in an attempt at optimization I'm trying to reduce this to a single query. This is what I have so far but it is barely faster than the 5 individual queries. Note that I've simplified the conditions for each subquery to make it easier to understand, the full query acts the same however.
Is there a way to get these 5 counts in the same query without using the inefficient subqueries?
SELECT
(SELECT count(*)
FROM "jobs"
WHERE
jobs.creator_id = 5 AND
jobs.status_id NOT IN (8,3,11) /* 8,3,11 being 'inactive' related statuses */
) AS active_count,
(SELECT count(*)
FROM "jobs"
WHERE
jobs.creator_id = 5 AND
jobs.due_date < '2011-06-14' AND
jobs.status_id NOT IN(8,11,5,3) /* Grabs the overdue active jobs
('5' means completed successfully) */
) AS overdue_count,
(SELECT count(*)
FROM "jobs"
WHERE
jobs.creator_id = 5 AND
jobs.due_date BETWEEN '2011-06-14' AND '2011-06-15 06:00:00.000000'
) AS due_today_count
This goes on for 2 more subqueries but I think you get the idea.
Is there an easier way to collect this data since it's basically 5 different COUNT's off of the same subset of data from the jobs table?
The subset of data is 'creator_id = 5', after that each count is basically just 1-2 additional conditions. Note that right now we're using Postgres but may be moving to MySQL in the near future. So if you can provide an ANSI-compatible solution I'd be gratetful :)
Brief
SQL Server 2012 introduced the
IIF
logical function. Using SQL Server 2012 or greater you can now use this new function instead of aCASE
expression. TheIIF
function also works with Azure SQL Database (but at the moment it does not work with Azure SQL Data Warehouse or Parallel Data Warehouse). It's shorthand for theCASE
expression.I find myself using the
IIF
function rather than theCASE
expression when there is only one case. This alleviates the pain of having to writeCASE WHEN condition THEN x ELSE y END
and instead writing it asIIF(condition, x, y)
. If multiple conditions may be met (multipleWHEN
s), you should instead consider using the regularCASE
expression rather than nestedIIF
functions.Code
Implementation of the
IIF
function in SQL would resemble the following (using the same logic presented by @rsbarro in his answer):This is the typical solution. Use a case statement to break out the different conditions. If a record meets it gets a 1 else a 0. Then do a
SUM
on the valuesUPDATE As noted when 0 records are returned as t this result in as single result of Nulls in all the values. You have three options
1) Add A Having clause so that you have No records returned rather than result of all NULLS
2) If you want all zeros returned than you could add coalesce to all your sums
For example
3) Take advantage of the fact that
COUNT(NULL) = 0
as sbarro's demonstrated. You should note that the not-null value could be anything it doesn't have to be a 1for example
I would use this approach, use COUNT in combination with CASE WHEN.