복붙노트

[SQL] GROUP BY와 집계 순차적 인 숫자 값

SQL

GROUP BY와 집계 순차적 인 숫자 값

PostgreSQL의 9.0를 사용.

기업, 직업 및 연도 :의 내가 필드를 포함하는 테이블이 있다고 가정 해 봅시다. 나는 독특한 기업과 직업을 포함하는 결과를 반환하고 싶지만, 집계 숫자 순서에 따라 세 (배열로 괜찮습니다) :

예 테이블 :

+-----------------------------+
| company | profession | year |
+---------+------------+------+
| Google  | Programmer | 2000 |
| Google  | Sales      | 2000 |
| Google  | Sales      | 2001 |
| Google  | Sales      | 2002 |
| Google  | Sales      | 2004 |
| Mozilla | Sales      | 2002 |
+-----------------------------+

나는하는 다음과 같은 출력 행을하고자하는 쿼리에 관심이 있어요 :

+-----------------------------------------+
| company | profession | year             |
+---------+------------+------------------+
| Google  | Programmer | [2000]           |
| Google  | Sales      | [2000,2001,2002] |
| Google  | Sales      | [2004]           |
| Mozilla | Sales      | [2002]           |
+-----------------------------------------+

필수 기능은 년 연속이 함께 그룹화되어야한다는 것입니다.

해결법

  1. ==============================

    1.이미 PostgreSQL을에 윈도우 함수의 다른 종류를 사용하는 방법을 학습을위한 좋은 재료로 댓글에서 말했다 a_horse_with_no_name의 대답 @, 모두 정확한 솔루션으로하고, 같은 훨씬 가치가있다.

    이미 PostgreSQL을에 윈도우 함수의 다른 종류를 사용하는 방법을 학습을위한 좋은 재료로 댓글에서 말했다 a_horse_with_no_name의 대답 @, 모두 정확한 솔루션으로하고, 같은 훨씬 가치가있다.

    그럼에도 불구하고 나는 그 대답에 취한 접근 방식은 조금이 같은 문제에 대해 너무 많은 노력의 것을 도움말 느낌이 없습니다. 기본적으로, 당신이 필요하면 배열에 년 집계에 가기 전에 그룹화를위한 추가 기준입니다. 이미 지금은 다른 시퀀스에 속하는 년을 구별 할 뭔가가 필요, 회사와 직업을 가지고있다.

    즉 상술 한 대답 위의 제공과 그것이 내가 간단한 방법으로 수행 할 수 있습니다 생각 정확히 것입니다. 방법은 다음과 같습니다

    WITH MarkedForGrouping AS (
      SELECT
        company,
        profession,
        year,
        year - ROW_NUMBER() OVER (
          PARTITION BY company, profession
          ORDER BY year
        ) AS seqID
      FROM atable
    )
    SELECT
      company,
      profession,
      array_agg(year) AS years
    FROM MarkedForGrouping
    GROUP BY
      company,
      profession,
      seqID
    
  2. ==============================

    2.비 연속적인 값을 확인하는 것은 항상 조금 까다 롭습니다하고 (적어도 내가 더 나은 솔루션을 가지고 올 수 없습니다) 여러 가지 중첩 된 하위 쿼리를 포함한다.

    비 연속적인 값을 확인하는 것은 항상 조금 까다 롭습니다하고 (적어도 내가 더 나은 솔루션을 가지고 올 수 없습니다) 여러 가지 중첩 된 하위 쿼리를 포함한다.

    첫 번째 단계는 년 비 연속적인 값을 식별하는 것입니다 :

    select company, 
           profession,
           year,
           case 
              when row_number() over (partition by company, profession order by year) = 1 or 
                   year - lag(year,1,year) over (partition by company, profession order by year) > 1 then 1
              else 0
           end as group_cnt
    from qualification
    

    이것은 다음과 같은 결과를 반환합니다 :

     company | profession | year | group_cnt
    ---------+------------+------+-----------
     Google  | Programmer | 2000 |         1
     Google  | Sales      | 2000 |         1
     Google  | Sales      | 2001 |         0
     Google  | Sales      | 2002 |         0
     Google  | Sales      | 2004 |         1
     Mozilla | Sales      | 2002 |         1
    

    이제 group_cnt 값으로 우리는 년 연속이있는 각 그룹에 대해 "그룹 ID"를 만들 수 있습니다 :

    select company,
       profession,
       year,
       sum(group_cnt) over (order by company, profession, year) as group_nr
    from ( 
    select company, 
           profession,
           year,
           case 
              when row_number() over (partition by company, profession order by year) = 1 or 
                   year - lag(year,1,year) over (partition by company, profession order by year) > 1 then 1
              else 0
           end as group_cnt
    from qualification
    ) t1
    

    이것은 다음과 같은 결과를 반환합니다 :

     company | profession | year | group_nr
    ---------+------------+------+----------
     Google  | Programmer | 2000 |        1
     Google  | Sales      | 2000 |        2
     Google  | Sales      | 2001 |        2
     Google  | Sales      | 2002 |        2
     Google  | Sales      | 2004 |        3
     Mozilla | Sales      | 2002 |        4
    (6 rows)
    

    각 "그룹"을 볼 수 있듯이 자신의 group_nr있어이 우리는 마침내 또 다른 파생 테이블을 추가하여 이상 통합 할 수 있습니다 :

    select company,
           profession,
           array_agg(year) as years
    from (
      select company,
           profession,
           year,
           sum(group_cnt) over (order by company, profession, year) as group_nr
      from ( 
        select company, 
               profession,
               year,
               case 
                  when row_number() over (partition by company, profession order by year) = 1 or 
                       year - lag(year,1,year) over (partition by company, profession order by year) > 1 then 1
                  else 0
               end as group_cnt
        from qualification
      ) t1
    ) t2
    group by company, profession, group_nr
    order by company, profession, group_nr
    

    이것은 다음과 같은 결과를 반환합니다 :

     company | profession |      years
    ---------+------------+------------------
     Google  | Programmer | {2000}
     Google  | Sales      | {2000,2001,2002}
     Google  | Sales      | {2004}
     Mozilla | Sales      | {2002}
    (4 rows)
    

    어느 내가 잘못 아니에요 경우에 당신이 원하던 것입니다.

  3. ==============================

    3.문제는 집계 / 윈도우 기능을 가진 일반 SQL 오히려 다루기 힘든이다. 루프 동안 일반 SQL과 집합 기반 솔루션보다 일반적으로 느린 테이블 (A FOR 루프의 암시 적 커서)를 통해 하나의 순차 검색과 관련이 특별한 경우에 실질적으로 더 빠르게 처리 될 수 있습니다 plpgsql과 절차 솔루션 :

    문제는 집계 / 윈도우 기능을 가진 일반 SQL 오히려 다루기 힘든이다. 루프 동안 일반 SQL과 집합 기반 솔루션보다 일반적으로 느린 테이블 (A FOR 루프의 암시 적 커서)를 통해 하나의 순차 검색과 관련이 특별한 경우에 실질적으로 더 빠르게 처리 될 수 있습니다 plpgsql과 절차 솔루션 :

    테스트 테이블 :

    CREATE TEMP TABLE tbl (company text, profession text, year int);
    INSERT INTO tbl VALUES
     ('Google',  'Programmer', 2000)
    ,('Google',  'Sales',      2000)
    ,('Google',  'Sales',      2001)
    ,('Google',  'Sales',      2002)
    ,('Google',  'Sales',      2004)
    ,('Mozilla', 'Sales',      2002);
    

    함수:

    CREATE OR REPLACE FUNCTION f_periods()
      RETURNS TABLE (company text, profession text, years int[]) AS
    $func$
    DECLARE
       r  tbl; -- use table type as row variable
       r0 tbl;
    BEGIN
    
    FOR r IN
       SELECT * FROM tbl t ORDER BY t.company, t.profession, t.year
    LOOP
       IF ( r.company,  r.profession,  r.year)
       <> (r0.company, r0.profession, r0.year + 1) THEN -- not true for first row
    
          RETURN QUERY
          SELECT r0.company, r0.profession, years; -- output row
    
          years := ARRAY[r.year];     -- start new array
       ELSE
          years := years || r.year;   -- add to array - year can be NULL, too
       END IF;
    
       r0 := r;                       -- remember last row
    END LOOP;
    
    RETURN QUERY                      -- output last iteration
    SELECT r0.company, r0.profession, years;
    
    END
    $func$ LANGUAGE plpgsql;
    

    요구:

    SELECT * FROM f_periods();
    

    요청 된 결과를 생성합니다.

  4. from https://stackoverflow.com/questions/8014577/group-by-and-aggregate-sequential-numeric-values by cc-by-sa and MIT license