我有一个名为“工作”的表。对于特定用户,作业可以是活动的,存档的,逾期的,未决的或关闭的。现在每个页面请求都生成5 COUNT个查询,并尝试优化,我试图将它减少到单个查询。这是我到目前为止,但它几乎比5个单独的查询更快。请注意,我已经简化了每个子查询的条件,使其更容易理解,但是完整的查询行为相同。
有没有办法得到这5个计数在同一个查询中,而不使用低效的子查询?
SELECT (SELECT count(*) FROM "jobs" WHERE jobs.creator_id = 5 AND jobs.status_id NOT IN (8,3,11) /* 8,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
这继续有2个子查询,但我认为你得到了这个想法。
是否有更简单的方式来收集这些数据,因为基本上与作业表中相同的数据子集有5个不同的COUNT?
数据子集是“creator_id = 5”,之后每个计数基本上只有1-2个附加条件。请注意,现在我们正在使用Postgres,但可能会在不久的将来迁移到MysqL。所以如果你可以提供ANSI兼容的解决方案,我会很高兴:)
解决方法
这是典型的解决方案。使用case语句来分解不同的条件。如果一个记录满足,它会得到一个1的另一个0.然后对值进行一个SUM
SELECT SUM(active_count) active_count,SUM(overdue_count) overdue_count SUM(due_today_count) due_today_count FROM ( SELECT CASE WHEN jobs.status_id NOT IN (8,11) THEN 1 ELSE 0 END active_count,CASE WHEN jobs.due_date < '2011-06-14' AND jobs.status_id NOT IN(8,3) THEN 1 ELSE 0 END overdue_count,CASE WHEN jobs.due_date BETWEEN '2011-06-14' AND '2011-06-15 06:00:00.000000' THEN 1 ELSE 0 END due_today_count FROM "jobs" WHERE jobs.creator_id = 5 ) t
UPDATE
如所指出的,当0个记录返回为t时,这将导致所有值中的Null的单个结果。你有三个选择
1)Add A Having子句,使您没有返回No记录,而不是所有NULLS的结果
HAVING SUM(active_count) is not null
2)如果你希望所有的零返回,你可以添加合并到所有的总和
例如
SELECT COALESCE(SUM(active_count)) active_count,COALESCE(SUM(overdue_count)) overdue_count COALESCE(SUM(due_today_count)) due_today_count
3)利用sbarro所示的COUNT(NULL)= 0的事实。您应该注意,非空值可能是不必为1的任何东西
例如
SELECT COUNT(CASE WHEN jobs.status_id NOT IN (8,11) THEN 'Manticores Rock' ELSE NULL END) as [active_count]