SELECT с сортировкой и подсчетом по объединенной таблице - PullRequest
0 голосов
/ 18 октября 2018

У меня трудные времена с 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 соответствующегоjobs
  • каждое задание может быть назначено пользователю

Мне нужна таблица, подобная следующей:

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 голосов
/ 18 октября 2018

Предполагается, что open_jobs - это число заданий с user_id, равным нулю?

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

Обратите внимание на sum(case user_id is null then 1 else 0).Если user_id равен null, то у вас есть открытая работа, поэтому вы добавляете 1, в противном случае вы добавляете 0.

EDIT: Так как вам также нужно отсортировать по min_from, вы можете

select *from
(
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
) as results 
order by min_from
0 голосов
/ 18 октября 2018

Получая все или большинство событий, как правило, быстрее (и проще) объединить 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 в качестве столбцаимена, если только вы не представляете сложности ...

0 голосов
/ 18 октября 2018

Предполагается, что 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

...