PostgreSQL。选择与聚合函数中的值相关的列

Posted

技术标签:

【中文标题】PostgreSQL。选择与聚合函数中的值相关的列【英文标题】:PostgreSQL. Select a column that correlates with value in the aggregate function 【发布时间】: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 个项目,则取“id”最小的行。 按“数量”(ASC) 对结果进行排序。 取 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)”))?然后进行排序和挑选行...

【问题讨论】:

【参考方案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 中服务很好。见:

Select first row in each GROUP BY group? PostgreSQL DISTINCT ON with different ORDER BY

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

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 可能会在尝试删除重复项时搞砸 - 徒劳无功。

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

...获取独特的类别:

Optimize GROUP BY query to retrieve latest row per user

.. 用于获取随机行:

Best way to select random rows PostgreSQL

【讨论】:

以上是关于PostgreSQL。选择与聚合函数中的值相关的列的主要内容,如果未能解决你的问题,请参考以下文章

选择列表中的列无效,因为该列没有包含在聚合函数或 GROUP BY 子句中

如何使用 postgresql 按多列中表示的值进行聚合

SQL:选择列表中的列无效,因为它不包含在聚合函数或 GROUP BY 子句中[关闭]

选择列表中的列“X”无效,因为它既不包含在聚合函数中,也不包含在 GROUP BY 子句中

mysql 聚合函数相关问题

SQL Server报错:选择列表中的列无效,因为该列没有包含在聚合函数或 GROUP BY 子句中