【问题标题】:Ordering distinct column values by (first value of) other column in aggregate function按聚合函数中其他列的(第一个值)排序不同的列值
【发布时间】:2014-09-30 13:42:20
【问题描述】:

我正在尝试根据另一列的值对一些不同的聚合文本的输出顺序进行排序,例如:

string_agg(DISTINCT sometext, ' ' ORDER BY numval)

但是,这会导致错误:

错误:在具有 DISTINCT 的聚合中,ORDER BY 表达式必须出现在参数列表中

我确实理解为什么会这样,因为如果两个重复值的 numval 不同,而另一个重复值介于两者之间,则排序将是“不明确的”。

理想情况下,我想按首次出现/最低排序值对它们进行排序,但在我的数据中,定义不明确的情况实际上很少见(它主要是我想用 @ 删除的顺序重复值987654324@),我最终并不特别关心他们的排序,并且会喜欢 MySQL 的 GROUP_CONCAT(DISTINCT sometext ORDER BY numval SEPARATOR ' ') 这样的东西,尽管它很草率,但它仍然可以正常工作。

我希望一些 Postgres 的扭曲是必要的,但我真的不知道最有效/最简洁的方法是什么。

【问题讨论】:

标签: sql postgresql sql-order-by distinct aggregate-functions


【解决方案1】:

DISTINCT ON 为基础

SELECT string_agg(sometext, ' ' ORDER BY numval) AS no_dupe
FROM  (
    SELECT DISTINCT ON (1,2) <whatever>, sometext, numval
    FROM   tbl
    ORDER  BY 1,2,3
    ) sub;

这是@Gordon's query 的更简单等价物。
从你的描述来看,我会建议@Clodoaldo's simpler variant

uniq() 表示整数

对于integer 值而不是text,附加模块intarray 为您提供正好

uniq(int[])     int[]   remove adjacent duplicates

每个数据库安装一次:

CREATE EXTENSION intarray;

那么查询很简单:

SELECT uniq(array_agg(some_int ORDER BY <whatever>, numval)) AS no_dupe
FROM   tbl;

Result 是一个数组,如果需要字符串,请将其包装在 array_to_string() 中。 相关:

事实上,创建一个自定义聚合函数来对text做同样的事情并不难......

任何数据类型的自定义聚合函数

仅在与前一个元素不同时将下一个元素添加到数组中的功能。 (NULL 值已被删除!):

CREATE OR REPLACE FUNCTION f_array_append_uniq (anyarray, anyelement)
  RETURNS anyarray
  LANGUAGE sql STRICT IMMUTABLE AS
'SELECT CASE WHEN $1[array_upper($1, 1)] = $2 THEN $1 ELSE $1 || $2 END';

使用polymorphic types 使其适用于任何 标量数据类型。 自定义聚合函数:

CREATE AGGREGATE array_agg_uniq(anyelement) (
   SFUNC = f_array_append_uniq
 , STYPE = anyarray
 , INITCOND = '{}'
);

呼叫:

SELECT array_to_string(
          array_agg_uniq(sometext ORDER BY <whatever>, numval)
        , ' ') AS no_dupe
FROM   tbl;

请注意,聚合本质上是PARALLEL UNSAFE(默认),即使转换函数可以标记为PARALLEL SAFE

相关答案:

【讨论】:

  • 我无法让它与 Postgres 13 一起工作 (PostgreSQL 13.0 (Debian 13.0-1.pgdg100+1));它只是为所有输入返回一个空数组。例如:select b, array_agg_uniq(a+c order by a+c) from generate_series(1, 10) a, generate_series(2, 5) b, generate_series(2, 5) c group by b; 虽然我实际上打算将它用于字符串。我认为问题在于array_upper('{}', 1) 为空,它通过$1[idx] 传播。 SELECT CASE WHEN array_length($1, 1) &lt;&gt; 0 THEN CASE WHEN $1[array_upper($1, 1)] &lt;&gt; $2 THEN $1 || $2 ELSE $1 END ELSE ARRAY[$2] END 有效,但似乎不太好?
  • 认为 我可以使用它:CREATE OR REPLACE FUNCTION f_array_append_uniq (anyarray, anyelement) RETURNS anyarray AS $func$ SELECT CASE WHEN $1[array_upper($1, 1)] = $2 THEN $1 ELSE $1 || $2 END $func$ LANGUAGE sql STRICT IMMUTABLE;(交换条件子句,并添加 strict 以忽略空输入。
  • @Shabble:是的,您的两个更改都很好。感谢您的报告!
【解决方案2】:

如果这是较大表达式的一部分,则在子查询中执行select distinct 可能不方便。在这种情况下,您可以利用 string_agg() 忽略 NULL 输入值这一事实并执行以下操作:

select string_agg( (case when seqnum = 1 then sometext end) order by numval)
from (select sometext, row_number() over (partition by <whatever>, sometext order by numval) as seqnum
      from t
     ) t
group by <whatever>

子查询添加一列但不需要聚合数据。

【讨论】:

  • 这会起作用,除了partition by 需要在sometext 之前包含原始分组列(即&lt;whatever&gt;)才能按预期工作;否则sometext 的每个实例,但第一个实例会在聚合文本中丢失。
【解决方案3】:

我最终做的是完全避免使用DISTINCT,而是选择使用正则表达式替换来删除顺序重复的条目(这是我的主要目标),如下所示:

regexp_replace(string_agg(sometext, ' ' ORDER BY numval), 
               '(\y\w+\y)(?:\s+\1)+', '\1', 'g')

如果外部排序导致它们之间出现另一个条目,这不会删除重复,但这对我有用,可能更好。它可能比其他选项慢一些,但我发现它的速度足以满足我的目的。

【讨论】:

  • 事实证明,早于 9.3.5 到 9.2 的 PostgreSQL 版本至少在正则表达式上存在内存泄漏,包括反向引用(例如我的),并且可以吞噬大量内存在会话的整个生命周期内。这有recently been fixed
【解决方案4】:

通过预先聚合消除了做不同的需要

select string_agg(sometext, ' ' order by numval)
from (
    select sometext, min(numval) as numval
    from t
    group by sometext
) s

@Gordon's answer 带来了一个好点。也就是说,如果还有其他需要的列。在这种情况下,建议使用distinct on

select x, string_agg(sometext, ' ' order by numval)
from (
    select distinct on (sometext) *
    from t
    order by sometext, numval
) s
group by x

【讨论】:

  • 如果sometextnumval 在未分组的行中不是唯一的,恐怕这并不能真正达到人们所期望的效果,对我来说就是这种情况。
  • @Dologan 对我来说没有意义。你能发布样本数据和期望的结果吗?
猜你喜欢
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-27
  • 2017-10-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多