通过连接表进行排序和计数的SELECT

时间:2018-10-17 21:30:42

标签: sql postgresql eloquent left-join aggregate

我在使用PostgreSQL SELECT时遇到了困难,起初看起来很简单。涉及的表:

CREATE TABLE events (id INT, customers_id INT);
CREATE TABLE jobs (
  events_id INT,
  "from"    TIMESTAMP,
  until     TIMESTAMP,
  users_id  INT);
  • 每个事件可以有多个工作
  • 事件的开始和结束由相应作业的最低"from"和最高until定义
  • 每个作业都可以分配给一个用户

我需要一个如下表:

events_id | customers_id | min(from) | max(until) | total_jobs | open_jobs
        1 |            1 |  .. 08:00 |   .. 11:00 |          4 |        1

到目前为止,我的选择:

SELECT e.id, e.customers_id, min(j.from) as min_from, max(j.until) as max_until, 
  count(j.id) as total_jobs
FROM events e
LEFT JOIN jobs j ON j.events_id = e.id
GROUP BY e.id, e.customers_id

这为我提供了前5列的结果,但是如何只为users_id = NULL的工作添加第二个计数呢?我以为我需要在作业表上再加上LEFT JOIN,但是以某种方式我无法使其正常工作。

我如何正确有效地实现这一目标?

3 个答案:

答案 0 :(得分:1)

假设open job的定义为until为空

WITH open_jobs_cte as (select events_id, customer_id, count(*) open_jobs FROM jobs WHERE until is null group by 1,2)
SELECT e.id, e.customers_id, min(j.from) as min_from, max(j.until) as max_until, count(j.id) as total_jobs, open_jobs
FROM events e
LEFT JOIN jobs j ON j.events_id = e.id
LEFT JOIN open_jobs_cte oj ON oj.events_id = e.id
GROUP BY e.id, e.customers_id

根据您的要求/设计,客户可以从cte中排除

答案 1 :(得分:1)

假设open_jobs是user_id为null的作业数?

Dic-swedish.xml

请注意SELECT e.id, e.customers_id, min(j.from) as min_from, max(j.until) as max_until, count(j.id) as total_jobs,sum(case user_id is null then 1 else 0) as open_jobs FROM events e LEFT JOIN jobs j ON j.events_id = e.id GROUP BY e.id, e.customers_id 。如果user_id为null,则您有一个空缺职位,因此您添加1,否则添加0。

sum(case user_id is null then 1 else 0)由于还需要按min_from进行排序,因此可以

EDIT:

答案 2 :(得分:1)

在获取全部或大多数事件时,通常在联接之前聚集n表 最快(也是最简单):

SELECT e.id, e.customers_id
     , j.min_from
     , j.max_until
     , j.total_jobs
     , j.open_jobs
FROM   events e
LEFT   JOIN (
   SELECT events_id    AS id -- alias only to ease join syntax
        , min("from")  AS min_from
        , max(until)   AS max_until
        , count(*)     AS total_jobs
        , count(*) FILTER (WHERE users_id IS NULL) AS open_jobs
-- equivalent for old versions:
--      , count(users_id IS NULL OR NULL)          AS open_jobs
   FROM   jobs
   GROUP  BY 1
   )  j USING (id);

这样,您根本不需要GROUP BY 1张表。

由于您正在考虑第二个LEFT JOIN:如果不首先进行汇总,则会遇到“代理交叉联接”的情况。

聚合FILTER子句需要Postgres 9.4或更高版本。

相关:

此外:除非遇到复杂的情况,否则不要像from那样使用reserved words作为列名...