这个问题没有任何押韵或理由,除了我对如何做到这一点感到好奇。
平台:虽然我希望使用SQL-Standard解决方案,但我主要关注 PostgreSQL 8.4 + 。 (我知道9.0+有一些数组排序功能。)
SELECT id, group, dt
FROM foo
ORDER BY id;
id | group | dt -------+-------+----------- 1 | foo | 2012-01-01 1 | bar | 2012-01-03 1 | baz | 2012-01-02 2 | foo | 2012-01-01 3 | bar | 2012-01-01 4 | bar | 2012-01-01 4 | baz | 2012-01-01
我知道以下查询是错误的,但结果类似于我所追求的;一种绑定两个字段的方法(group
的排序也应该排序dt
):
SELECT id, sort_array(array_agg(group)), array_agg(dt)
FROM foo
GROUP BY id;
id | group | dt -------+----------------+------------------------------------ 1 | {bar,baz,foo} | {2012-01-03,2012-01-02,2012-01-01} 2 | {foo} | {2012-01-01} 3 | {bar} | {2012-01-01} 4 | {bar,baz} | {2012-01-01,2012-01-01}
是否有一种简单的方法可以将字段用于排序,而不使用子查询?也许构建一个数组数组然后不需要?
答案 0 :(得分:4)
我将您的列名group
更改为grp
,因为group
是Postgres中的reserved word和每个SQL标准,不应该用作标识符。< / p>
我理解你的问题:
获取以相同排序顺序排序的两个数组,以便相同的元素位置对应于两个数组中的同一行。
使用子查询或 CTE 并在汇总之前对行进行排序。
SELECT id, array_agg(grp) AS grp, array_agg(dt) AS dt
FROM (
SELECT *
FROM tbl
ORDER BY id, grp, dt
) x
GROUP BY id;
更快比使用个人ORDER BY
clauses in the aggregate function array_agg()
更像@Mosty demonstrates(自PostgreSQL 9.0以来一直存在)。莫斯蒂也以不同的方式解释你的问题,并使用适当的工具进行解释。
ORDER BY
是否安全?汇总函数
array_agg
,json_agg
,[...]以及 类似的用户定义的聚合函数,产生有意义的 不同的结果值取决于输入值的顺序。 默认情况下,此排序未指定,但可以通过控制 在聚合调用中编写ORDER BY
子句,如图所示 第4.2.7节。或者,从a提供输入值 排序的子查询通常会起作用。例如:SELECT xmlagg(x) FROM (SELECT x FROM test ORDER BY y DESC) AS tab;
请注意,如果外部查询级别包含,则此方法可能会失败 附加处理,例如连接,因为这可能会导致 子计数的输出在计算聚合之前要重新排序。
所以是的,在示例中它是安全的。
如果你真的需要一个没有子查询的解决方案,你可以:
SELECT id
, array_agg(grp ORDER BY grp)
, array_agg(dt ORDER BY grp, dt)
FROM tbl
GROUP BY id;
请注意ORDER BY grp, dt
。除了断开关系之外,我按dt
排序并使排序顺序明确无误。但grp
不需要。
SELECT DISTINCT ON (id)
id
, array_agg(grp) OVER w AS grp
, array_agg(dt) OVER w AS dt
FROM tbl
WINDOW w AS (PARTITION BY id ORDER BY grp, dt
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
ORDER BY id;
请注意DISTINCT ON (id)
而不仅仅是DISTINCT
,它会产生相同的结果但速度提高一个数量级,因为我们不需要额外的排序。
我进行了一些测试,这几乎和其他两个解决方案一样快。正如预期的那样,子查询版本仍然是最快的。使用EXPLAIN ANALYZE
进行测试,亲眼看看。
答案 1 :(得分:1)
我知道解决这个问题的唯一方法是使用recursive CTE。以下是查询的外观:
--We first need to create the order hierarchy to recurse properly
SELECT *, ROW_NUMBER() OVER(PARTITION BY id ORDER BY group) AS rownum
INTO TEMP TableToRecurse
FROM foo
WITH RECURSIVE FinalOutput (id, group, dt, rownum) AS
(
--Anchor row of recursion
SELECT id, group, dt, rownum
FROM TableToRecurse
WHERE rownum = 1
UNION ALL
--Recursion piece
SELECT tr.id, FinalOutput.group || ', ' || tr.group,
FinalOutput.dt || ', ' || tr.dt, tr.rownum
FROM TableToRecurse AS tr
JOIN FinalOutput
ON FinalOutput.id = tr.id AND FinalOutput.rownum = tr.rownum +1
)
--Final output only showing the last row (Max)
--Which should have everything concatenated together
SELECT FinalOutput.id, FinalOutput.group, FinalOutput.dt
FROM FinalOutput
JOIN
(
SELECT MAX(rownum) AS MaxRowNum, id
FROM FinalOutput
GROUP BY id
) AS MaxForEach
ON FinalOutput.id = MaxForEach.id
AND FinalOutput.rownum = MaxForEach.MaxRowNum
答案 2 :(得分:1)
试试这个:
select id,
array_agg(agroup order by agroup) as AGroup,
array_agg(dt order by dt desc) as dt
from t
group by id
这应该适用于PostgreSQL 9.1 +