PostgreSQL: строки COUNT () с использованием двухуровневых объединений с активным ONLY_FULL_GROUP_BY - PullRequest
1 голос
/ 12 октября 2019

Проблема

У меня проблемы с написанием функционального запроса, подсчитывающего строки результатов с двухуровневым объединением.

Таблицы базы данных

У меня есть следующее (упрощенно) таблицы (среда, в которой разрабатывается Drupal 8, используя службу базы данных):

Таблица nodes:

id - числовой идентификатор
title - varchar

Примеры записей:

id   title
1    My first article
2    My second article
3    My third article

Таблица comments:

cid - числовой идентификатор
entity_type - varchar, прокомментированная сущность
entity_id - числовой идентификатор, содержащий ссылку
status - int, 0 для неопубликованной, 1 для опубликованной
comment - текст

Пример записи:

cid   entity_type   entity_id   status   comment
1     node          1           1        foo
2     node          1           1        bar
3     comment       1           1        baz
4     node          1           0        spam/foul language/whatever
5     node          2           1        yeeeha

Описание структуры данных

«Узлы» можно комментировать. Комментарии затем сохраняются в «комментариях». Для каждого комментария есть выделенная строка, содержащая идентификатор комментария, прокомментированный тип объекта (может быть «узел» и «комментарий») и идентификатор прокомментированного объекта. И комментарии также могут быть прокомментированы - эти «ответы» также сохраняются в таблице «комментариев», таким образом, эти записи содержат «комментарий» в качестве entity_id и идентификатор комментария, на который был дан ответ.

Теперь я хотел бы получить следующий результат с помощью одного запроса:

id    title                comments
1     My first article     3
2     My second article    1
3     My third article     0

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * [10] * * * * * * * * * * *. Таким образом, если узел дважды комментируется напрямую, и один из этих комментариев также комментируется, счетчик comments должен указывать 3. (Примечание: atm "отвечает" на комментарии нельзя ответить, поэтому здесь есть только трехуровневая среда (node <<code>comment <<code>comment)).

Используемая база данных:

Используемая база данных - PostgreSQL 9.6 с ONLY_FULL_GROUP_BY active.

То, что я пытался

Я уже потратил несколько часов, пытаясь запросить данные с помощью запросов, которыепочти все они выглядят примерно так (используя интерфейс базы данных службы Drupal select):

SELECT n.id, n.title, COUNT(c.cid)+COUNTr.cid) AS comments
FROM nodes n
LEFT JOIN comments c
ON c.type = "node" AND n.id = c.entity_id AND c.status = 1
LEFT JOIN comments r
ON r.type = "comment" AND c.id = r.entity_id AND r.status = 1
GROUP BY n.id, n.title, c.entity_id, r.entity_id

Но я не могу понять, как правильно написать запрос. Моя основная идея состоит в том, чтобы выбрать узлы базовой таблицы, присоединиться к первой стадии комментариев слева и снова присоединиться к ответам на первое объединение. Но, похоже, у моей базы данных есть другие идеи по поводу моих запросов ... ¯ \ _ (ツ) _ / ¯

Я действительно надеюсь, что есть кто-то, кто сможет вернуть меня в нужное русло. Любая помощь с благодарностью! Спасибо, что нашли время, чтобы прочитать все это.

Ответы [ 2 ]

3 голосов
/ 12 октября 2019

Прежде всего, давайте поймем, что вы написали и пропустили - В запросе вы пропустили (после подсчета в первой строке.

Во-вторых, вы делали группу с дополнительным «r.entity_id», которыйне требуется, и он разделяет результат.

Третий порядок по результату по идентификатору узла в порядке возрастания.

Пожалуйста, используйте приведенный ниже запрос и отметьте правильный ответ, если он вам помогает.

SELECT n.id, n.title, COUNT(c.cid)+COUNT(r.cid) AS comments
FROM nodes n
 left JOIN comments c
ON c.entity_type = 'node' AND n.id = c.entity_id AND c.status = 1
 left JOIN comments r
ON r.entity_type = 'comment' AND c.cid = r.entity_id AND r.status = 1
GROUP BY n.id, n.title, c.entity_id 
order by n.id asc
1 голос
/ 12 октября 2019

Ваша логика довольно хороша. Вам просто нужно посчитать различные значения на первом уровне и правильно обработать NULL значения. Кроме того, вы хотите сгруппировать по столбцам, приходящимся на nodes, а не на comments.

select 
    n.id,
    n.title,
    coalesce(count(distinct c1.cid), 0) + coalesce(count(c2.cid), 0) "comments"
from nodes n
left join comments c1 
    on  c1.entity_id = n.id  
    and c1.entity_type = 'node'
    and c1.status = 1
left join comments c2 
    on c2.entity_id = c1.cid  
    and c2.entity_type = 'comment'
    and c2.status = 1
group by n.id, n.title

Это демо на БД Fiddle с вашими примерами данныхвозвращает:

| id  | title             | comments |
| --- | ----------------- | -------- |
| 1   | My first article  | 3        |
| 2   | My second article | 1        |
| 3   | My third article  | 0        |
...