通过聚合函数中其他列的(第一个值)排序不同的列值

时间:2014-08-07 10:42:03

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

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

string_agg(DISTINCT sometext, ' ' ORDER BY numval)

但是,这会导致错误:

  

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

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

理想情况下,我想按照第一次出现/最低顺序排序它们,但是在我的数据中,定义不明确的情况实际上是非常罕见的(它主要是依次重复的值,我想用{去掉... {1}})我最终并不特别关心他们的排序,并且会对像MySQL GROUP_CONCAT(DISTINCT sometext ORDER BY numval SEPARATOR ' ')这样的东西感到高兴,尽管它很糟糕,但它仍能正常工作。

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

4 个答案:

答案 0 :(得分:4)

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

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

事实上,创建一个自定义聚合函数来对text执行相同操作并不困难...

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

仅当数组与前一个元素不同时才向数组添加下一个元素的函数。 (NULL值已删除!):

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 || $2 ELSE $1 END
$func$ LANGUAGE sql IMMUTABLE;

使用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;

相关答案:

答案 1 :(得分:2)

消除了通过预聚合

进行区分的需要
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

答案 2 :(得分:1)

如果这是较大表达式的一部分,则在子查询中执行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>

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

答案 3 :(得分:1)

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

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

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