2015-12-24 5 views
1

Я пишу запрос, чтобы суммировать данные в базе данных Postgres:граф столбцов присоединяемой таблицы

SELECT products.id, 
    products.NAME, 
    product_types.type_name AS product_type, 
    delivery_types.delivery, 
    products.required_selections, 
    Count(s.id)    AS selections_count, 
    Sum(CASE 
     WHEN ss.status = 'WARNING' THEN 1 
     ELSE 0 
     END)    AS warning_count 
FROM products 
    JOIN product_types 
    ON product_types.id = products.product_type_id 
    JOIN delivery_types 
    ON delivery_types.id = products.delivery_type_id 
    LEFT JOIN selections_products sp 
      ON products.id = sp.product_id 
    LEFT JOIN selections s 
      ON s.id = sp.selection_id 
    LEFT JOIN selection_statuses ss 
      ON ss.id = s.selection_status_id 
    LEFT JOIN listings l 
      ON (s.listing_id = l.id 
       AND l.local_date_time BETWEEN 
        To_timestamp('2014/12/01', 'YYYY/mm/DD' 
        ) AND 
        To_timestamp('2014/12/30', 'YYYY/mm/DD')) 
GROUP BY products.id, 
     product_types.type_name, 
     delivery_types.delivery 

В основном мы имеем продукт с выбором, эти выборы имеют списки и списки имеют local_date. Мне нужен список всех продуктов и количество списков, которые у них есть между двумя датами. Независимо от того, что я делаю, я получаю подсчет всех выборов (всего). Я чувствую, что я что-то пропускаю. Та же концепция относится к warning_count. Кроме того, я не совсем понимаю, почему Postgres требует от меня добавить group by.

Схема выглядит следующим образом (части вы заботитесь о любом случае):

products 
    name:string 
, product_type:fk 
, required_selections:integer 
, deliver_type:fk 

selections_products 
    product_id:fk 
, selection_id:fk 

selections 
    selection_status_id:fk 
, listing_id:fk 

selection_status 
    status:string 

listing 
local_date:datetime 
+0

Ваша версия Postgres? Откуда происходит 'f' в' count (f) '? Когда вы пишете «сколько списков», вы хотите дважды подсчитать списки, если два выбора одного и того же листинга содержат один и тот же продукт? –

ответ

1

так, как вы это вы LEFT JOIN на все выборы Вне зависимости от listings.local_date_time.

Существует место для интерпретации, нам нужно будет увидеть фактические определения таблиц со всеми ограничениями и типами данных, чтобы быть уверенным. Выйдя на конечности, моя догадка, что вы можете исправить запрос с использованием скобок в предложении FROM приоритизации присоединяется:

SELECT p.id 
    , p.name 
    , pt.type_name AS product_type 
    , dt.delivery 
    , p.required_selections 
    , count(s.id) AS selections_count 
    , sum(CASE WHEN ss.status = 'WARNING' THEN 1 ELSE 0 END) AS warning_count 
FROM products  p 
JOIN product_types pt ON pt.id = p.product_type_id 
JOIN delivery_types dt ON dt.id = p.delivery_type_id 
LEFT JOIN ( -- LEFT JOIN! 
      selections_products sp 
    JOIN selections s ON s.id = sp.selection_id -- INNER JOIN! 
    JOIN listings l ON l.id = s.listing_id  -- INNER JOIN! 
         AND l.local_date_time >= '2014-12-01' 
         AND l.local_date_time < '2014-12-31' 
    LEFT JOIN selection_statuses ss ON ss.id = s.selection_status_id 
    ) ON sp.product_id = p.id 
GROUP BY p.id, pt.type_name, dt.delivery; 

Таким образом, вы сначала устранить все выборы за пределами заданного периода времени с [INNER] JOINдо вы LEFT JOIN товарам, таким образом сохраняя все продукты в результате, в том числе те, которые не имеют отношения к выбору.

Похожие:

При выборе все или большинство продуктов, это может быть переписан, чтобы быть быстрее:

SELECT p.id 
    , p.name 
    , pt.type_name AS product_type 
    , dt.delivery 
    , p.required_selections 
    , COALESCE(s.selections_count, 0) AS selections_count 
    , COALESCE(s.warning_count, 0) AS warning_count 
FROM products  p 
JOIN product_types pt ON pt.id = p.product_type_id 
JOIN delivery_types dt ON dt.id = p.delivery_type_id 
LEFT JOIN (
    SELECT sp.product_id 
     , count(*) AS selections_count 
     , count(*) FILTER (WHERE ss.status = 'WARNING') AS warning_count 
    FROM selections_products sp 
    JOIN selections   s ON s.id = sp.selection_id 
    JOIN listings   l ON l.id = s.listing_id 
    LEFT JOIN selection_statuses ss ON ss.id = s.selection_status_id 
    WHERE l.local_date_time >= '2014-12-01' 
    AND l.local_date_time < '2014-12-31' 
    GROUP BY 1 
    ) s ON s.product_id = p.id; 

Это дешевле заполнить и подсчитать выбор и предупреждения за product_id, а затем , затем присоединиться к продуктам. (Если только вы не получить небольшой выбор продуктов, то это дешевле, чтобы уменьшить связанные строки первой.)

Связанный:


Кроме того, я не» я действительно понимаю, почему Postgres требует от меня добавить группу сюда.

С Postgres 9.1, колонка ПК в GROUP BY охватывает все столбцы же таблицы.То есть не обложки колонны другие таблицы, даже если они функционально зависимы. Вы должны указать их явно в GROUP BY, если вы не хотите их агрегировать.

Мой второй запрос избегает этой проблемы с самого начала путем объединения до соединения.


Помимо: шансы, это не делает то, что вы хотите: (! Не timestamptz)

l.local_date_time BETWEEN To_timestamp('2014/12/01', 'YYYY/mm/DD') 
         AND To_timestamp('2014/12/30', 'YYYY/mm/DD') 

Поскольку date_time кажется, типа timestamp, вы бы включать «2014- 12-30 00:00 ', но не включая остаток дня' 2014-12-30 '. И всегда лучше использовать формат ISO 8601 для дат и временных меток, что означает то же самое с каждые locale и datestyle. Следовательно:

WHERE l.local_date_time >= '2014-12-01' 
AND l.local_date_time < '2014-12-31' 

Это включает в себя все из '2014-12-30', и ничего больше. Не знаю, почему вы решили исключить «2014-12-31». Может быть, вы действительно хотите включить весь декабрь 2014 года?

WHERE l.local_date_time >= '2014-12-01' 
AND l.local_date_time < '2015-01-01' 
+0

отличный ответ - очень ценю, что вы нашли время, чтобы объяснить это так хорошо. принято. – user3186332

Смежные вопросы