web-dev-qa-db-ja.com

PostgreSQLでは、タイプセーフなfirst()集約関数はありますか?

質問全体を書き直す

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
);

問題は、varchar(n)列がfirst()関数を通過するときに、単純なvarchar(サイズなし)に変換されることです。関数のクエリをRETURNS SETOF anyelementとして返そうとすると、次のエラーが発生します。

エラー:クエリの構造が関数の結果の型と一致しませんEstado de SQL:42804 Detalhe:返される型の文字の型が列2の予想される型の文字の型(40)と一致しません。Contexto:PL/pgSQL関数vsr_table_at_time(anyelement、timestamp without time zone )RETURN QUERYの31行目

同じWikiページに、上記の代わりとなる 関数のCバージョン へのリンクがあります。インストール方法はわかりませんが、このバージョンで問題を解決できるのでしょうか。

一方、上記の関数を変更して、まったく同じタイプの入力列を返す方法はありますか?

22
Alexandre Neto

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;

それは効果的です

SELECT DISTINCT ON(x,y) z
FROM foo;
-- ORDER BY z;

最初のzが必要です。 2つの重要な違いがあります。

  1. さらに集約することなく、他の列をまた選択できます。

    SELECT DISTINCT ON(x,y) z, k, r, t, v
    FROM foo;
    -- ORDER BY z, k, r, t, v;
    
  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;
    

忘れないでくださいORDER BY

また、私はそれを太字にしなかったが、今は

必要な行が最初に表示されるようにORDER BYを使用しない限り、各セットの「最初の行」は予測できないことに注意してください。例

常にORDER BYDISTINCT ONとともに使用します

順序セット集計関数の使用

多くの人がfirst_valueOrdered-Set Aggregate Functions を探していると思います。それをそこに捨てたかっただけです。関数が存在する場合、次のようになります。

SELECT a, b, first_value() WITHIN GROUP (ORDER BY z)    
FROM foo
GROUP BY a,b;

しかし、悲しいかなあなたはこれを行うことができます。

SELECT a, b, percentile_disc(0) WITHIN GROUP (ORDER BY z)   
FROM foo
GROUP BY a,b;
18
Evan Carroll

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

それがあなたの場合に役立つことを願っています。

6
Mabu Kloesen

あなたの質問に対する直接的な答えではありませんが、first_valueウィンドウ関数。それはこのように動作します:

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

);

次に、各cat(カテゴリ)の最初のアイテムが必要な場合は、次のようにクエリします。

SELECT
    cat,
    first_value(date) OVER (PARTITION BY cat ORDER BY date)
FROM
    test;

または:

SELECT
    cat,
    first_value(date) OVER w
FROM
    test
WINDOW w AS (PARTITION BY cat ORDER BY date);
5