如何查询具有良好性能的多个COUNT(*)

时间:2013-03-21 18:21:14

标签: sql postgresql relational-database postgresql-9.2

我有这张桌子:

CREATE TABLE schedule (
schedule_id serial NOT NULL,
start_date date,
CONSTRAINT schedule_id PRIMARY KEY (schedule_element_id)
)

这张表:

CREATE TABLE schedule_user (
schedule_user_id serial NOT NULL,
schedule_id integer,
state int,
CONSTRAINT fk_schedule_id   FOREIGN KEY (schedule_id)
      REFERENCES schedule (schedule_id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)
schedule 
 -------------------------
|schedule_id |date        |
|------------+------------|
|1           |'2013-10-10'|
|2           |'2013-10-20'|
|3           |'2013-08-13'|
 -------------------------

schedule_user 
 -----------------------------------
|schedule_user_id|schedule_id |state|
|----------------+------------+-----|
|1               | 1          |0    |
|2               | 1          |1    |
|3               | 1          |2    |
|4               | 1          |0    |
|5               | 1          |1    |
|6               | 1          |1    |
|4               | 2          |0    |
|5               | 2          |1    |
|7               | 2          |0    |
|2               | 3          |1    |
 -----------------------------------

我想要一张这样的桌子:

characteristic
 ---------------------------------------
|schedule_id |state0|state1|state2|total|
|------------+------+------+------+-----|
|1           |2     |3     |1     |6    |
|2           |2     |1     |0     |3    |
|3           |1     |1     |0     |2    |
 ---------------------------------------

我使这个查询看起来和它的性能一样糟糕。

SELECT 
    schedule.schedule_id AS id, 
    (( SELECT count(*) AS count
         FROM schedule_user
         WHERE schedule_user.schedule_id = schedule.schedule_id
           AND state=0))::integer AS state0, 
    (( SELECT count(*) AS count
         FROM schedule_user
         WHERE schedule_user.schedule_id = schedule.schedule_id
           AND state=1))::integer AS state1,
    (( SELECT count(*) AS count
         FROM schedule_user
         WHERE schedule_user.schedule_id = schedule.schedule_id
           AND state=2))::integer AS state2,          
    (( SELECT count(*) AS count
         FROM schedule_user
         WHERE schedule_user.schedule_id = schedule.schedule_id))::integer
       AS total
  FROM schedule

有没有更好的方法来执行此类查询? 我应该创建“州”列的索引吗?如果是这样,它应该怎么样?

3 个答案:

答案 0 :(得分:4)

您想制作数据透视表。如果您事先知道所有可能的状态值,那么在SQL中创建一个的简单方法是使用sumcase语句。

select schedule_id,
       sum(case state when 0 then 1 else 0 end) as state0,
       sum(case state when 1 then 1 else 0 end) as state1,
       sum(case state when 2 then 1 else 0 end) as state2,
       count(*) as total
from schedule_user
group by schedule_id;

另一种方法是使用crosstab表函数。

这些都不会让你不知道状态值的集合(以及结果集中的列)。

答案 1 :(得分:3)

我会尝试

SELECT s.schedule_id,
       COUNT(CASE WHEN su.state = 0 THEN 1 END) AS state0,
       COUNT(CASE WHEN su.state = 1 THEN 1 END) AS state1,
       COUNT(CASE WHEN su.state = 2 THEN 1 END) AS state2,
       COUNT(su.state) AS total
  FROM schedule s
  LEFT
 OUTER
  JOIN schedule_user su
    ON su.schedule_id = s.schedule_id
 GROUP
    BY s.schedule_id
;

答案 2 :(得分:0)

标准方法是使用带有CASE的SUM()和带有GROUP BY的JOIN:

SELECT 
schedule.schedule_id AS id, 
SUM (case when state=0 then 1 else 0 end) AS state0, 
SUM (case when state=1 then 1 else 0 end) AS state1, 
SUM (case when state=2 then 1 else 0 end) AS state2, 
count(*) AS total
FROM schedule
LEFT JOIN schedule_user
  ON schedule_user.schedule_id = schedule.schedule_id
GROUP BY 1