【问题标题】:PostgreSQL. Select a column that correlates with value in the aggregate functionPostgreSQL。选择与聚合函数中的值相关的列
【发布时间】:2021-04-03 06:47:14
【问题描述】:

这是 'items' 表,包含超过 10 行:

+-----+-----------+-----------+----------+
| id  | item_name | category  | quantity |
+=====+===========+===========+==========+
| 3   | item33    | category1 | 5        |
+-----+-----------+-----------+----------+
| 2   | item52    | category5 | 1        |
+-----+-----------+-----------+----------+
| 1   | item46    | category1 | 3        |
+-----+-----------+-----------+----------+
| 4   | item11    | category3 | 2        |
+-----+-----------+-----------+----------+
| ... | ...       | ...       | ...      |
+-----+-----------+-----------+----------+

“items”列中的值是唯一的,“category”列中的值不是唯一的。

任务是:

  1. 删除重复的类别:如果一个类别包含超过 1 个项目,则取“id”最小的行。
  2. 按“数量”(ASC) 对结果进行排序。
  3. 取 10 行:前 5 行,其余结果数据输出中随机 5 行。

所以,排序表(在#2 子任务之后)应该是这样的:

+-----+-----------+-----------+----------+
| id  | item_name | category  | quantity |
+=====+===========+===========+==========+
| 2   | item52    | category5 | 1        |
+-----+-----------+-----------+----------+
| 4   | item11    | category3 | 2        |
+-----+-----------+-----------+----------+
| 1   | item46    | category1 | 3        |
+-----+-----------+-----------+----------+
| ... | ...       | ...       | ...      |
+-----+-----------+-----------+----------+

我知道如何排除类别的重复项:

SELECT min(id) as id, category
FROM items
GROUP BY category

但我不知道如何按数量订购。 如果我尝试将 'quantity' 添加到 'select' 行,然后进行 'ORDER BY quantity',我会收到错误:"column "quantity" 必须出现在 GROUP BY 子句中或用于聚合函数中".

如果有办法将此“数量”列添加到数据输出(该列中的值应与生成的“id”值相关(即“min(id)”))?然后进行排序和挑选行...

【问题讨论】:

    标签: sql postgresql random greatest-n-per-group


    【解决方案1】:

    你需要使用解析函数如下:

    Select * from
    (Select t.*,
           Row_number() over (order by quantity) as rn_q
     from
    (Select t.*,
           Row_number() over (partition by category order by id) as rn
      From your_table) t
    Where rn = 1) t
    Order by case when rn_q <= 5 then quantity else 6 end;
    

    【讨论】:

      【解决方案2】:

      考虑将您的聚合查询加入到包括quantity 在内的所有列的单元级数据中:

      SELECT i.id, i.item_name, i.category, i.quantity
      FROM items i
      INNER JOIN 
        (SELECT category, min(id) AS min_id
         FROM items
         GROUP BY category) agg
       ON i.id = agg.min_id
       AND i.category = agg.category
      ORDER BY i.quantity
      

      对于前 5 和随机 5 拆分,将联合与 CTE 集成以保存结果集:

      WITH sub AS (
        SELECT i.id, i.item_name, i.category, i.quantity
        FROM items i
        INNER JOIN 
          (SELECT category, min(id) AS min_id
           FROM items
           GROUP BY category) agg
         ON i.id = agg.min_id
         AND i.category = agg.category
      )
      
      -- TOP 5 ROWS
      SELECT id, item_name, category, quantity
      FROM sub
      ORDER BY i.quantity
      LIMIT 5
      
      UNION
      
      -- RANDOM ROWS OF NON-TOP 5
      SELECT id, item_name, category, quantity
      FROM 
        (SELECT id, item_name, category, quantity
         FROM sub
         ORDER BY i.quantity
         OFFSET 5) below5
      ORDER BY random()
      LIMIT 5
      

      【讨论】:

      • 这个解决方案最适合我,因为我对 Postgres 知之甚少,我至少可以理解这段代码 :D 感谢大家的帮助,无论如何。我真的很感激。
      • 很高兴听到并乐于提供帮助!此解决方案也适用于其他 RDBMS,并且不限于 Postgres 方言方法。快乐的 SQLing!
      【解决方案3】:

      基本上,DISTINCT ON 在 Postgres 中服务很好。见:

      简单(正确!)解决方案:

      WITH dist_cat AS (
         SELECT t, row_number() OVER (ORDER BY quantity, id) AS rn   -- added id as tiebreaker
         FROM  (
            SELECT DISTINCT ON (category) *
            FROM   tbl
            ORDER  BY category, id
            ) t  -- distinct categories
         ORDER  BY ORDER BY quantity, id  -- match sort for row_number()
         )
      SELECT (t).*
      FROM   dist_cat
      WHERE  rn <= 5
      
      UNION ALL   -- not just UNION
      (  -- parentheses required
      SELECT (t).*
      FROM   dist_cat
      WHERE  rn > 5
      ORDER  BY random()
      LIMIT  5
      );
      

      添加了id 作为排序的决胜局,因为按quantity 排序几乎没有确定性。将任何适合您要求的独特表达放在那里。或者,如果您对可能随每次调用而改变的任意结果感到满意,则可以跳过它。

      行类型t是为了方便,所以我们不用把所有的列名都拼出来,还是把结果中附加的rn去掉,没有被请求。

      我选择在 CTE 中对行进行排序并附加行号 rn 以避免额外的排序操作。

      另外 5 个随机行是真正随机挑选的,而不是随意挑选的。

      使用UNION ALL,而不仅仅是UNION。因为它对于我们正在做的事情正确,而且也更便宜。还要保留 CTE 的排序顺序; UNION 可能会在尝试删除重复项时搞砸 - 徒劳无功。

      对于大表,根据数据分布,可能有(很多)更快的技术...

      ...获取独特的类别:

      .. 用于获取随机行:

      【讨论】:

        猜你喜欢
        • 2011-06-12
        • 2011-04-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-10-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多