Агрегировать один и тот же столбец несколькими различными способами - PullRequest
2 голосов
/ 07 апреля 2020

Я пытаюсь получить массив категорий, связанных с каждым продуктом, а затем также получить родительскую категорию верхнего уровня каждого продукта в другом столбце, который по моим логикам c находит те же значения для массива категорий, но только выбрав where parent_id is NULL, который должен откатывать только одно значение и 1 запись на идентификатор.

Я действительно не знаю лучшего способа структурировать этот запрос. То, что у меня есть, работает, но оно также показывает значения NULL в столбце родительской категории для категорий, у которых есть родительский идентификатор, и создает вторую запись для каждого продукта, потому что я вынужден поместить его в группу. В принципе, я думаю, что я делаю это не правильным или наиболее эффективным способом.

Желаемый результат:

+----+----------------+------------------+------------------------------------------------+------------------+
| id | name           | category_ids     | category_names                                 | parent_category  |
+----+----------------+------------------+------------------------------------------------+------------------+
| 1  | Product Name 1 | {111,222,333}    | {Electronics, computers, computer accessories} | Electronics      |
+----+----------------+------------------+------------------------------------------------+------------------+

Мой текущий запрос (который не идеален):

select p.id, 
p.name, 
array_agg(category_id) as category_ids,
regexp_replace(array_agg(c.name)::text,'"|''','','gi') as category_names,
c1.name as parent_category
from products p
join product_categorizations pc  on pc.product_id = p.id
join categories c  on pc.category_id = c.id
full outer join (
   select name, id from categories
   where parent_id is null and name is not null
   ) c1 on c.id = c1.id
group by 1,2,5;
+----+----------------+------------------+-----------------------------------+------------------+
| id | name           | category_ids     | category_names                    | parent_category  |
+----+----------------+------------------+-----------------------------------+------------------+
| 1  | Product Name 1 | {111}            | {Electronics}                     | Electronics      |
+----+----------------+------------------+-----------------------------------+------------------+
| 1  | Product Name 1 | {222,333}        | {computers, computer accessories} | NULL             |
+----+----------------+------------------+-----------------------------------+------------------+

1 Ответ

0 голосов
/ 08 апреля 2020

Замените FULL JOIN агрегированным FILTER предложением:

SELECT p.id
     , p.name
     , array_agg(pc.category_id) AS category_ids
     , string_agg(c.name, ', ')  AS category_names  -- regexp_replace .. ?
<b>     , min(c.name) FILTER (WHERE c.parent_id IS NULL) AS parent_category</b>
FROM   products                p
JOIN   product_categorizations pc ON pc.product_id = p.id
JOIN   categories              c  ON pc.category_id = c.id
GROUP  BY p.id;

См .:

(Зачем вы добавляете AND name IS NOT NULL? В любом случае, min() все равно игнорирует NULL значений.)

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

SELECT p.name, pc.*
FROM   products p
JOIN  (
   SELECT pc.product_id AS id
        , array_agg(pc.category_id) AS category_ids
        , string_agg(c.name, ', ')  AS category_names
        , min(c.name) FILTER (WHERE c.parent_id IS NULL) AS parent_category
   FROM   product_categorizations pc
   JOIN   categories              c  ON pc.category_id = c.id
   GROUP  BY 1
   ) pc  USING (id);

Дело в том, что product объединяет только после агрегирующих строк.

В сторону: " имя "не очень полезно имя столбца. Связанный:

...