PostgreSQL 크로스 탭 쿼리


196

PostgreSQL에서 크로스 탭 쿼리를 만드는 방법을 아는 사람이 있습니까?
예를 들어 다음 표가 있습니다.

Section    Status    Count
A          Active    1
A          Inactive  2
B          Active    4
B          Inactive  5

쿼리에서 다음 크로스 탭을 반환하고 싶습니다.

Section    Active    Inactive
A          1         2
B          4         5

이게 가능해?


1
나는 약간 다른 구조를 가지고 있었고이 예제를 이해하기가 다소 어렵다는 것을 알았 으므로이 stackoverflow.com/q/49051959/808723 에 대한 내 생각을 문서화했습니다 . 아마 누군가에게 도움이 될 것입니다.
GameScripting

답변:


317

기능을 제공하는 데이터베이스 당 한 번 추가 모듈을tablefunc 설치하십시오 . Postgres 9.1부터 다음을 사용할 수 있습니다 .crosstab()CREATE EXTENSION

CREATE EXTENSION IF NOT EXISTS tablefunc;

테스트 케이스 개선

CREATE TABLE tbl (
   section   text
 , status    text
 , ct        integer  -- "count" is a reserved word in standard SQL
);

INSERT INTO tbl VALUES 
  ('A', 'Active', 1), ('A', 'Inactive', 2)
, ('B', 'Active', 4), ('B', 'Inactive', 5)
                    , ('C', 'Inactive', 7);  -- ('C', 'Active') is missing

간단한 양식-누락 된 속성에 적합하지 않음

crosstab(text)함께 1 개 입력 파라미터 :

SELECT *
FROM   crosstab(
   'SELECT section, status, ct
    FROM   tbl
    ORDER  BY 1,2'  -- needs to be "ORDER BY 1,2" here
   ) AS ct ("Section" text, "Active" int, "Inactive" int);

보고:

섹션 | 활성 | 비활성
--------- + -------- + ----------
 A | 1 | 2
 B | 4 | 5
 C |      7 | -!!
  • 캐스팅 및 이름을 바꿀 필요가 없습니다.
  • 에 대한 잘못된 결과에 유의하십시오 . 첫 번째 열에 C7이 채워집니다. 때로는이 동작이 바람직하지만이 사용 사례에는 적합하지 않습니다.
  • 간단한 입력 양식은 제공된 입력 쿼리에서 row_name , category , value의 정확히 3 개의 열로 제한됩니다 . 아래의 2- 파라미터 대안에서와 같이 추가 열을 위한 공간이 없습니다 .

안전한 형태

crosstab(text, text)함께 2 개 입력 파라미터 :

SELECT *
FROM   crosstab(
   'SELECT section, status, ct
    FROM   tbl
    ORDER  BY 1,2'  -- could also just be "ORDER BY 1" here

  , $$VALUES ('Active'::text), ('Inactive')$$
   ) AS ct ("Section" text, "Active" int, "Inactive" int);

보고:

섹션 | 활성 | 비활성
--------- + -------- + ----------
 A | 1 | 2
 B | 4 | 5
 C | |        7   ~ !!
  • 에 대한 올바른 결과를 참고하십시오 C.

  • 번째 매개 변수 는 끝에 열 정의의 순서와 일치하는 속성 당 하나의 을 리턴하는 모든 조회 일 수 있습니다 . 종종 다음과 같이 기본 테이블에서 고유 속성을 쿼리하려고 할 것입니다.

    'SELECT DISTINCT attribute FROM tbl ORDER BY 1'

    그것은 매뉴얼에 있습니다.

    어쨌든 열 정의 목록의 모든 열을 철자해야하므로 (사전 정의 된 변형 제외 ) 일반적으로 다음과 같은 표현식 으로 짧은 목록을 제공하는 것이 더 효율적입니다 .crosstabN()VALUES

    $$VALUES ('Active'::text), ('Inactive')$$)

    또는 (설명서에 없음) :

    $$SELECT unnest('{Active,Inactive}'::text[])$$  -- short syntax for long lists
  • 나는 인용을 쉽게하기 위해 달러 인용 을 사용했습니다 .

  • 값 열의 텍스트 표현이 대상 유형에 유효한 입력 인 경우-를 사용 하여 다른 데이터 유형의 열을 출력 할 수도 있습니다 crosstab(text, text). 당신이있을 수 있습니다이 방법은 다른 종류의 출력의 속성 text, date, numeric각각의 속성 등. 이 장의crosstab(text, text) 끝에는 코드 예제 가 있습니다 .

db <> 바이올린 여기

고급 예


\crosstabview psql에서

Postgres 9.6 은이 메타 명령을 기본 대화식 터미널 psql에 추가했습니다 . 첫 번째 crosstab()매개 변수 로 사용할 쿼리를 실행하여 \crosstabview즉시 또는 다음 단계로 전달할 수 있습니다. 처럼:

db=> SELECT section, status, ct FROM tbl \crosstabview

위와 비슷한 결과이지만 독점적으로 클라이언트 측표현 기능 입니다. 입력 행은 약간 다르게 처리되므로 ORDER BY필요하지 않습니다. \crosstabview매뉴얼에 대한 자세한 내용 . 해당 페이지 하단에 더 많은 코드 예제가 있습니다.

Daniel Vérité (psql 기능의 저자)의 dba.SE 관련 답변 :



이전에 허용 대답은 구식이다.

  • 함수의 변형 crosstab(text, integer)이 오래되었습니다. 두 번째 integer매개 변수는 무시됩니다. 나는 현재 매뉴얼을 인용한다 :

    crosstab(text sql, int N) ...

    사용되지 않는 버전입니다 crosstab(text). N값 열의 개수는 항상 호출 쿼리에 의해 결정되므로 매개 변수 가 무시됩니다.

  • 불필요한 캐스팅 및 이름 바꾸기.

  • 행에 모든 속성이없는 경우 실패합니다. 누락 된 속성을 올바르게 처리하려면 위의 두 개의 입력 매개 변수가있는 안전한 변형을 참조하십시오.

  • ORDER BY의 한 매개 변수 형식으로 필요 crosstab()합니다. 매뉴얼 :

    실제로 SQL 쿼리는 항상 ORDER BY 1,2입력 행이 올바르게 정렬되도록 지정 해야합니다.


3
+1, 훌륭한 글쓰기, 감사의 말In practice the SQL query should always specify ORDER BY 1,2 to ensure that the input rows are properly ordered
ChristopheD

$$ VALUES .. $$를 사용하는 데 문제가 있습니다. 대신 'VALUES (' '<attr>' ':: <type>), ..'를 사용했습니다.
Marco Fantasia

크로스 탭 쿼리에서 매개 변수 바인딩을 지정할 수 있습니까? 이 오류가 발생했습니다 => 매개 변수 $ 2의 데이터 유형을 결정할 수 없습니다
Ashish

1
크로스 탭 쿼리에서 열의 기본값을 설정할 수 있습니까?
Ashish

2
@Ashish : 새로운 질문을 시작하십시오. 의견은 장소가 아닙니다. 컨텍스트를 위해 항상이 링크에 연결할 수 있습니다.
Erwin Brandstetter

30

데이터베이스 당 한 번 설치 해야하는 추가 모듈 tablefunccrosstab()기능을 사용할 수 있습니다 . PostgreSQL 9.1부터 다음을 사용할 수 있습니다 .CREATE EXTENSION

CREATE EXTENSION tablefunc;

귀하의 경우 다음과 같이 보일 것이라고 믿습니다.

CREATE TABLE t (Section CHAR(1), Status VARCHAR(10), Count integer);

INSERT INTO t VALUES ('A', 'Active',   1);
INSERT INTO t VALUES ('A', 'Inactive', 2);
INSERT INTO t VALUES ('B', 'Active',   4);
INSERT INTO t VALUES ('B', 'Inactive', 5);

SELECT row_name AS Section,
       category_1::integer AS Active,
       category_2::integer AS Inactive
FROM crosstab('select section::text, status, count::text from t',2)
            AS ct (row_name text, category_1 text, category_2 text);

크로스 탭 쿼리에서 매개 변수를 사용하는 경우 올바르게 이스케이프해야합니다. 예 : (위에서) 활성화 된 것만 원한다고 말합니다 : SELECT ... FROM crosstab ( 'select section :: text, status, count :: text from t where status =' 'active' '', 2) AS. .. (큰 따옴표에 유의하십시오). 사용자가 매개 변수를 런타임에 전달하는 경우 (예 : 함수 매개 변수) 다음과 같이 말할 수 있습니다. SELECT ... FROM crosstab ( 'select section :: text, status, count :: text from t 여기서 status =' ' '|| par_active ||' '' ', 2) AS ... (여기 인용 부호 3 개!). BIRT에서 이것은 또한? 자리 표시 자.
Wim Verhavert

26
SELECT section,
       SUM(CASE status WHEN 'Active' THEN count ELSE 0 END) AS active, --here you pivot each status value as a separate column explicitly
       SUM(CASE status WHEN 'Inactive' THEN count ELSE 0 END) AS inactive --here you pivot each status  value as a separate column explicitly

FROM t
GROUP BY section

1
tablefunc 모듈의 크로스 탭 함수 가이 답변에 추가 된 내용을 설명 할 수 있습니까?이 두 가지 작업은 현재 작업 중이며 내 마음은 이해하기 쉽습니다.
존 파월

4
@ JohnBarça : 이와 같은 간단한 사례는 CASE 문으로 쉽게 해결할 수 있습니다. 그러나 이것은 정수보다 더 많은 속성 및 / 또는 다른 데이터 유형으로 인해 다루기가 매우 어려워집니다. 제쳐두고으로 :이 양식은 집계 함수를 사용하여 sum(), 그것을 사용하는 것이 더하지 않을 것이다 min()또는 max()ELSE작동하는 text도. 그러나 이는 corosstab()속성과 "첫 번째"값만 사용하는 와는 미묘한 차이가 있습니다 . 하나만있을 수있는 한 중요하지 않습니다. 마지막으로 성능도 관련이 있습니다. crosstab()C로 작성되어 작업에 최적화되었습니다.
Erwin Brandstetter

postgresql의 경우 작동하지 않습니다. 오류가 발생했습니다ERROR: 42803: aggregate function calls may not be nested
Audrey

1
@ 오드리 당신은 같은 SQL을 실행하지 않습니까?

2
설명과 코드 블록 추가
Daniel L. VanDenBosch

10

JSON 집계 솔루션 :

CREATE TEMP TABLE t (
  section   text
, status    text
, ct        integer  -- don't use "count" as column name.
);

INSERT INTO t VALUES 
  ('A', 'Active', 1), ('A', 'Inactive', 2)
, ('B', 'Active', 4), ('B', 'Inactive', 5)
                   , ('C', 'Inactive', 7); 


SELECT section,
       (obj ->> 'Active')::int AS active,
       (obj ->> 'Inactive')::int AS inactive
FROM (SELECT section, json_object_agg(status,ct) AS obj
      FROM t
      GROUP BY section
     )X

감사합니다. 관련 문제를 해결하는 데 도움이되었습니다.
JeffCharter

1

여기에서 테스트 할 수 없기 때문에 완료되지 않았지만 올바른 방향으로 벗어날 수 있습니다. 비슷한 쿼리를 만드는 내가 사용하는 것을 번역하고 있습니다.

select mt.section, mt1.count as Active, mt2.count as Inactive
from mytable mt
left join (select section, count from mytable where status='Active')mt1
on mt.section = mt1.section
left join (select section, count from mytable where status='Inactive')mt2
on mt.section = mt2.section
group by mt.section,
         mt1.count,
         mt2.count
order by mt.section asc;

내가 일하고있는 코드는 다음과 같습니다.

select m.typeID, m1.highBid, m2.lowAsk, m1.highBid - m2.lowAsk as diff, 100*(m1.highBid - m2.lowAsk)/m2.lowAsk as diffPercent
from mktTrades m
   left join (select typeID,MAX(price) as highBid from mktTrades where bid=1 group by typeID)m1
   on m.typeID = m1.typeID
   left join (select typeID,MIN(price) as lowAsk  from mktTrades where bid=0 group by typeID)m2
   on m1.typeID = m2.typeID
group by m.typeID, 
         m1.highBid, 
         m2.lowAsk
order by diffPercent desc;

이는 typeID, 최고 가격 입찰 및 요청 된 최저 가격 및이 둘의 차이를 반환합니다.


1
from 절이 누락되었습니다. 그렇지 않으면 이것이 맞습니다. 설명 계획은 시스템에서 크게 다릅니다. 크로스 탭 기능의 비용은 22.5이고 LEFT JOIN 방식의 비용은 91.38입니다. 또한 약 2 배의 물리적 읽기를 생성하고 해시 조인을 수행하므로 다른 조인 유형에 비해 상당히 비쌉니다.
예레미아 페 쉬카

예레미야에게 감사합니다. 다른 답변을 상향 조정했지만 귀하의 의견은 가치가 있으므로 삭제하지는 않습니다.
LanceH

-1

Crosstab기능은 tablefunc확장명에서 사용할 수 있습니다 . 데이터베이스에 대해이 확장을 한 번 작성해야합니다.

확장 만들기 tablefunc ;

크로스 탭을 사용하여 아래 코드를 사용하여 피벗 테이블을 만들 수 있습니다.

create table test_Crosstab( section text,
<br/>status text,
<br/>count numeric)

<br/>insert into test_Crosstab values ( 'A','Active',1)
                <br/>,( 'A','Inactive',2)
                <br/>,( 'B','Active',4)
                <br/>,( 'B','Inactive',5)

select * from crosstab(
<br/>'select section
    <br/>,status
    <br/>,count
    <br/>from test_crosstab'
    <br/>)as ctab ("Section" text,"Active" numeric,"Inactive" numeric)

1
이 답변은 기존 답변보다 아무것도 추가하지 않습니다.
Erwin Brandstetter
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.