PostgreSQL에는 형식 안전 first () 집계 함수가 있습니까?


21

전체 질문 다시 작성

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로 함수에서 쿼리를 반환하려고하면 다음 오류가 발생합니다.

오류 : 쿼리 구조가 함수 결과 유형과 일치하지 않습니다. Estado de SQL : 42804 Detalhe : 리턴 된 유형 문자 변경이 열 2의 예상 유형 문자 varying (40)과 일치하지 않습니다. ) RETURN QUERY의 31 행

동일한 위키 페이지에는 위의 내용을 대체 할 함수C 버전에 대한 링크가 있습니다. 설치 방법을 모르지만이 버전으로 문제를 해결할 수 있는지 궁금합니다.

한편 위의 함수를 변경하여 정확히 동일한 유형의 입력 열을 반환하는 방법이 있습니까?

답변:


17

DISTINCT ON()

부수적으로, 이것은 정확히 무엇입니까 DISTINCT ON()(와 혼동하지 마십시오 DISTINCT)

SELECT DISTINCT ON ( expression [, ...] ) 주어진 표현식이 동일하다고 평가되는 각 행 세트의 첫 번째 행만 유지합니다 . 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. 두 가지 중요한 차이점이 있습니다.

  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 BY함께 사용DISTINCT ON

순서 집합 집계 함수 사용

나는 많은 사람들이 찾고있는 상상 first_value, 정렬 된 세트 집계 함수 . 그냥 버리고 싶었어 함수가 존재하면 다음과 같습니다.

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;

1
이 답변의 문제점은 선택 목록에서 하나의 집계를 원할 때만 작동한다는 것입니다. 이는 질문에 암시되지 않습니다. 예를 들어 하나의 테이블에서 선택하고 여러 개의 정렬 된 첫 번째 값을 찾으려면 DISTINCT ON이 경우 작동하지 않습니다. 집계 함수가 아니며 실제로 데이터를 필터링하므로 한 번만 수행 할 수 있습니다.
DB140141

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

귀하의 경우에 도움이되기를 바랍니다.


이 솔루션의 문제점은 DOMAIN데이터 유형 또는 기타 작은 예외에서는 작동하지 않는다는 것입니다 . 또한 전체 데이터 세트의 배열을 구축하는 것이 훨씬 더 복잡하고 시간 소모적입니다. 간단한 해결책은 사용자 지정 집계를 만드는 것이지만 지금까지는 이상적인 해결책을 찾지 못했습니다. 창 함수도 집계를 사용할 수있는 것과 같은 방식으로 사용할 수 없기 때문에 나쁩니다 (FILTER 문 또는 CROSS JOIN LATERAL 사용)
AlexanderMP

5

귀하의 질문에 대한 직접적인 대답은 아니지만 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);

죄송합니다. 이것이 유스 케이스에 적용되지 않는다고 생각합니다. First_value는 집계 함수가 아니며, 특정 순서 (예 : 날짜)에 따라 첫 번째로 평가되는 특정 공통 값 (예 : cat)으로 모든 레코드를 표시합니다. 내 필요는 다릅니다. 동일한 선택에서 첫 번째 null이 아닌 값을 선택하여 여러 열을 집계해야합니다. 즉, GROUP BY의 각 값 조합에 대해 단일 레코드를 출력해야합니다.
Alexandre Neto

2
위의 내용은 믹스로 구분하여 작동하도록 만들 수 있습니다 select distinct x, first_value(y) over (partition by x), first_value(z) over (partition by x) from .... 아마도 비효율적이지만 프로토 타이핑을 시작하기에 충분할 것입니다. 그래도 다시 방문해야 할 것이 있습니다!
Max Murphy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.