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 中服务很好。见:
简单(正确!)解决方案:
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 子句中
SQL:选择列表中的列无效,因为它不包含在聚合函数或 GROUP BY 子句中[关闭]