在 PostgreSQL 中,是否有类型安全的 first() 聚合函数?

Ale*_*eto 25 postgresql aggregate functions greatest-n-per-group

完整问题重写

我正在寻找 First() 聚合函数。

在这里,我发现了一些几乎有效的东西:

CREATE OR REPLACE FUNCTION public.first_agg ( anyelement, anyelement )
RETURNS anyelement LANGUAGE sql IMMUTABLE STRICT AS $$
        SELECT $1;
$$;

-- And then wrap an aggregate around it
CREATE AGGREGATE public.first (
        sfunc    = public.first_agg,
        basetype = anyelement,
        stype    = anyelement
);
Run Code Online (Sandbox Code Playgroud)

问题是当 varchar(n) 列通过 first() 函数时,它会被转换为简单的 varchar(没有大小)。尝试在函数中将查询返回为 RETURNS SETOF anyelement,我收到以下错误:

错误:查询的结构与函数结果类型 Estado de SQL:42804 不匹配:返回的类型字符变化与第 2 列中的预期类型字符变化(40) 不匹配。上下文:PL/pgSQL 函数 vsr_table_at_time(anyelement,timestamp without time zone ) 第 31 行在 RETURN QUERY

在同一个 wiki 页面中,有一个指向该函数C 版本的链接,该链接将替换上述内容。我不知道如何安装它,但我想知道这个版本是否可以解决我的问题。

同时,有没有办法可以更改上述函数,使其返回与输入列完全相同的类型?

Eva*_*oll 27

DISTINCT ON()

顺便说一句,这正是DISTINCT ON()(不要与 混淆DISTINCT

SELECT DISTINCT ON ( expression [, ...] ) 仅保留给定表达式求值为 equal 的每组行的第一行。该DISTINCT ON表达式是使用相同的规则解释ORDER BY(见上文)。请注意,每组的“第一行”是不可预测的,除非ORDER BY用于确保所需的行首先出现。例如

所以如果你要写,

SELECT myFirstAgg(z)
FROM foo
GROUP BY x,y;
Run Code Online (Sandbox Code Playgroud)

它有效

SELECT DISTINCT ON(x,y) z
FROM foo;
-- ORDER BY z;
Run Code Online (Sandbox Code Playgroud)

因为它需要第一个z. 有两个重要的区别,

  1. 您还可以选择其他列而无需进一步聚合。

    SELECT DISTINCT ON(x,y) z, k, r, t, v
    FROM foo;
    -- ORDER BY z, k, r, t, v;
    
    Run Code Online (Sandbox Code Playgroud)
  2. 因为没有GROUP BY不能使用(真实的)聚合。

    CREATE TABLE foo AS
    SELECT * FROM ( VALUES
      (1,2,3),
      (1,2,4),
      (1,2,5)
    ) AS t(x,y,z);
    
    SELECT DISTINCT ON (x,y) z, sum(z)
    FROM foo;
    
    -- fails, as you should expect.
    SELECT DISTINCT ON (x,y) z, sum(z)
    FROM foo;
    
    -- would not otherwise fail.
    SELECT myFirstAgg(z), sum(z)
    FROM foo
    GROUP BY x,y;
    
    Run Code Online (Sandbox Code Playgroud)

不要忘记 ORDER BY

另外,虽然我没有加粗,但我现在会

请注意,每个集合的“第一行”是不可预测的,除非使用 ORDER BY 来确保所需的行首先出现。例如

总是使用ORDER BYwithDISTINCT ON

使用有序集聚合函数

我想很多人都在寻找first_value, Ordered-Set Aggregate Functions。只是想把它扔出去。如果函数存在,它看起来像这样:

SELECT a, b, first_value() WITHIN GROUP (ORDER BY z)    
FROM foo
GROUP BY a,b;
Run Code Online (Sandbox Code Playgroud)

但是,唉,你可以做到这一点。

SELECT a, b, percentile_disc(0) WITHIN GROUP (ORDER BY z)   
FROM foo
GROUP BY a,b;
Run Code Online (Sandbox Code Playgroud)

  • 此答案的问题在于,它仅在您希望选择列表中的 ONE 聚合时才有效,问题并未暗示这一点。例如,如果您想从一个表中进行选择并找到多个有序的第一个值,则在这种情况下`DISTINCT ON` 将不起作用。它不是聚合函数,您实际上是在过滤数据,因此您只能执行一次。 (3认同)

Ghi*_*que 6

不是您问题的直接答案,但您应该尝试first_value窗口函数。它是这样工作的:

CREATE TABLE test (
    id SERIAL NOT NULL PRIMARY KEY,
    cat TEXT,
    value VARCHAR(2)
    date TIMESTAMP WITH TIME ZONE
Run Code Online (Sandbox Code Playgroud)

);

然后,如果您想要每个cat(类别)中的第一项,您将这样查询:

SELECT
    cat,
    first_value(date) OVER (PARTITION BY cat ORDER BY date)
FROM
    test;
Run Code Online (Sandbox Code Playgroud)

或者:

SELECT
    cat,
    first_value(date) OVER w
FROM
    test
WINDOW w AS (PARTITION BY cat ORDER BY date);
Run Code Online (Sandbox Code Playgroud)

  • 可以通过将不同的放入混合中来使上述内容起作用:`选择不同的 x,first_value(y) over (partition by x), first_value(z) over (partition by x) from ...`。可能效率低下,但足以让我继续进行原型设计。不过绝对值得重温! (2认同)

小智 6

是的,我通过使用 PostgreSQL 9.4+ 中的一些功能找到了一个简单的方法来处理你的案例

让我们看看这个例子:

select  (array_agg(val ORDER BY i))[1] as first_value_orderby_i,
    (array_agg(val ORDER BY i DESC))[1] as last_value_orderby_i,
    (array_agg(val))[1] as last_value_all,
    (array_agg(val))[array_length(array_agg(val),1)] as last_value_all
   FROM (
        SELECT i, random() as val
        FROM generate_series(1,100) s(i)
        ORDER BY random()
    ) tmp_tbl
Run Code Online (Sandbox Code Playgroud)

我希望它对你的情况有所帮助。