Ошибка GROUP BY: столбец "f.path" должен присутствовать в предложении GROUP BY или использоваться в агрегатной функции. - PullRequest
0 голосов
/ 05 августа 2020

В Postgres 12, я пытаюсь выполнить SELECT в таблице recipes, получив только одно изображение (файлы). Однако при выполнении запроса без GROUP BY я получаю дубликаты рецептов по количеству их изображений (файлов). При попытке использовать GROUP BY я получаю следующую ошибку:

столбец «f.path» должен появиться в предложении GROUP BY или использоваться в агрегатной функции

Выполняемый мной запрос:

SELECT r.id, r.title, c.name AS chef_name, f.path
FROM recipes AS r
LEFT JOIN chefs AS c ON (r.chef_id = c.id)
LEFT JOIN recipe_files AS rf ON (rf.recipe_id = r.id)
LEFT JOIN files AS f ON (rf.file_id = f.id)
GROUP BY r.id, c.id
ORDER BY r.title ASC

Если я добавлю f.path к GROUP BY, я вернусь к исходной проблеме получения списка с повторяющимися элементами в соответствии с количеством изображений ( файлы).

Ответы [ 4 ]

1 голос
/ 05 августа 2020

Если вам нужна одна строка для каждого рецепта, используйте distinct on:

SELECT DISTINCT ON (r.title, r.id) r.id, r.title, c.name AS chef_name, f.path
FROM recipes r LEFT JOIN chefs AS c ON (r.chef_id = c.id)
     recipe_files AS rf
     ON rf.recipe_id = r.id
     files f
     ON rf.file_id = f.id
ORDER BY r.title, r.id;
0 голосов
/ 06 августа 2020

Удалите ненужные строки как можно раньше. В этом случае примените DISTINCT ON перед присоединением к files:

SELECT r.id, r.title, c.name AS chef_name, f.path
FROM   recipes    r
LEFT   JOIN chefs c ON r.chef_id = c.id
LEFT   JOIN (
   SELECT DISTINCT ON (recipe_id)
          recipe_id, file_id
   FROM   recipe_files
   -- without ORDER BY it's truly arbitrary
   ) rf ON rf.recipe_id = r.id
LEFT   JOIN files f ON rf.file_id = f.id
ORDER  BY r.title;

О DISTINCT ON:

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

Для многих файлов на рецепт другие методы (намного) быстрее:

Получить только несколько рецептов, но другие методы (намного) более эффективны :. Например:

SELECT r.id, r.title, c.name AS chef_name, f.path
FROM   recipes    r
LEFT   JOIN chefs c ON r.chef_id = c.id
LEFT   JOIN LATERAL (
   SELECT recipe_id, file_id
   FROM   recipe_files
   WHERE  recipe_id = r.id
   ORDER  BY recipe_id, file_id
   LIMIT  1
   ) rf ON true
LEFT   JOIN files f ON rf.file_id = f.id
WHERE  r.title = 'foo'                     -- some selective filter
ORDER  BY r.title;

См.:

0 голосов
/ 05 августа 2020

Мне больше знаком SQL Сервер, но похоже, что Postgres имеет аналогичную функцию. Попробуйте использовать ROW_NUMBER (). Ссылку можно найти здесь: https://www.postgresqltutorial.com/postgresql-row_number/

Это будет примерно так. Вам нужно будет изменить его, чтобы заказать, как вы хотите.

SELECT 
    r.id
    , r.title
    , c.name AS chef_name
    , f.path
FROM (
    SELECT 
        r.id
        , r.title
        , c.name AS chef_name
        , f.path
        , ROW_NUMBER() OVER(PARTITION BY r.id, r.title, c.name AS chef_name, f.path ORDER BY r.title)
    FROM recipes AS r
        LEFT JOIN chefs AS c ON (r.chef_id = c.id)
        LEFT JOIN recipe_files AS rf ON (rf.recipe_id = r.id)
        LEFT JOIN files AS f ON (rf.file_id = f.id)
) 
WHERE row_number = 1;
0 голосов
/ 05 августа 2020

Если вам нужен только один файл для каждого рецепта, вы можете присоединиться к производной таблице, которая выбирает только один:

SELECT r.id, r.title, c.name AS chef_name, f1.path
FROM recipes AS r
  LEFT JOIN chefs AS c ON r.chef_id = c.id
  LEFT JOIN (
    SELECT DISTINCT ON (rf.recipe_id) rf.recipe_id, f.path
    FROM recipe_files AS rf 
      JOIN files AS f ON rf.file_id = f.id
    ORDER BY rf.recipe_id, f.id -- picks an arbitrary file
  ) f1 ON f1.recipe_id = r.id
ORDER BY r.title ASC
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...