【问题标题】:GROUP BY error: column "f.path" must appear in the GROUP BY clause or be used in an aggregate functionGROUP BY 错误:列“f.path”必须出现在 GROUP BY 子句中或在聚合函数中使用
【发布时间】:2020-08-05 13:06:50
【问题描述】:

在 Postgres 12 中,我试图在 recipes 表上执行 SELECT,只带来一个图像(文件)。但是,在没有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,我回到最初的问题,根据图像(文件)的数量接收带有重复项目的列表。

【问题讨论】:

  • 将第一行更改为SELECT r.id, max(r.title), max(c.name) AS chef_name, max(f.path) 可以解决直接错误,但我怀疑问题出在其他地方。请添加几行示例数据和预期的结果。
  • 那么如果一个菜谱有多个文件,那么你想看哪一个?

标签: sql postgresql group-by


【解决方案1】:

如果您希望每个食谱有一行,请使用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;

【讨论】:

    【解决方案2】:

    如果您只想为每个配方创建一个文件,您可以加入一个只选择一个的派生表:

    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
    

    【讨论】:

      【解决方案3】:

      我对SQL Server比较熟悉,但是貌似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;
      

      【讨论】:

        【解决方案4】:

        尽早消除不需要的行。在这种情况下,应用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;
        

        见:

        【讨论】:

          猜你喜欢
          • 2014-12-26
          • 1970-01-01
          • 2018-05-16
          • 2014-01-23
          • 2020-06-11
          • 2020-11-04
          • 2016-09-16
          • 2015-06-22
          相关资源
          最近更新 更多