행을 여러 열로 피벗


21

Oracle 서버에 연결된 서버가있는 SQL Server 인스턴스가 있습니다. Oracle 서버 PersonOptions에는 다음과 같은 데이터가 포함 된 테이블 이 있습니다.

╔══════════╦══════════╗
║ PersonID ║ OptionID ║
╠══════════╬══════════╣
║        1 ║ A        ║
║        1 ║ B        ║
║        2 ║ C        ║
║        3 ║ B        ║
║        4 ║ A        ║
║        4 ║ C        ║
╚══════════╩══════════╝

그 데이터를 피벗해야 결과가 다음과 같습니다.

╔══════════╦═════════╦══════════╦══════════╗
║ PersonID ║ OptionA ║ Option B ║ Option C ║
╠══════════╬═════════╬══════════╬══════════╣
║        1 ║       1 ║        1 ║          ║
║        2 ║         ║          ║        1 ║
║        3 ║         ║        1 ║          ║
║        4 ║       1 ║          ║        1 ║
╚══════════╩═════════╩══════════╩══════════╝

어떤 제안?

답변:


20

이 데이터 변환을 수행 할 수있는 몇 가지 방법이 있습니다. PIVOT함수에 액세스 하면 가장 쉽습니다. 그렇지 않은 경우 집계 함수 및을 사용할 수 있습니다 CASE.

집계 / 케이스 버전 :

select personid,
  max(case when optionid = 'A' then 1 else 0 end) OptionA,
  max(case when optionid = 'B' then 1 else 0 end) OptionB,
  max(case when optionid = 'C' then 1 else 0 end) OptionC
from PersonOptions
group by personid
order by personid;

데모가 포함 된 SQL Fiddle 참조

정적 피벗 :

select *
from
(
  select personid, optionid
  from PersonOptions
) src
pivot
(
  count(optionid)
  for optionid in ('A' as OptionA, 'B' OptionB, 'C' OptionC)
) piv
order by personid

데모가 포함 된 SQL Fiddle 참조

동적 버전 :

위의 두 버전은 알려진 수의 값이있는 경우 효과적이지만 값을 알 수없는 경우 동적 SQL을 구현하고 Oracle에서 프로 시저를 사용할 수 있습니다.

CREATE OR REPLACE procedure dynamic_pivot_po(p_cursor in out sys_refcursor)
as
    sql_query varchar2(1000) := 'select personid ';

    begin
        for x in (select distinct OptionID from PersonOptions order by 1)
        loop
            sql_query := sql_query ||
                ' , min(case when OptionID = '''||x.OptionID||''' then 1 else null end) as Option_'||x.OptionID;

                dbms_output.put_line(sql_query);
        end loop;

        sql_query := sql_query || ' from PersonOptions group by personid order by personid';
        dbms_output.put_line(sql_query);

        open p_cursor for sql_query;
    end;
/

그런 다음 결과를 반환하면 다음을 사용합니다.

variable x refcursor
exec dynamic_pivot_po(:x)
print x

결과는 모든 버전에서 동일합니다.

| PERSONID | OPTIONA | OPTIONB | OPTIONC |
------------------------------------------
|        1 |       1 |       1 |       0 |
|        2 |       0 |       0 |       1 |
|        3 |       0 |       1 |       0 |
|        4 |       1 |       0 |       1 |

그러나 정적 피벗 솔루션에서는 세 가지 옵션 만 있다고 가정합니다. 잠재적으로 무제한의 옵션이있는 경우 어떻게합니까? 예를 들어 ABCDEFGHIJK? 일반 SQL로 피벗을 동적으로 만드는 방법이 없습니까? 옵션을 열 머리글로 만드는 대신 열에 넣을 수 있습니까? 따라서 다음과 같습니다. | PERSONID | 열 2 | 열 3 | 열 4 | ------------------------------------------ | 1 | A | B | 널 | | 2 | C | 널 | 널 | | 3 | null | C | null |
Matthew

1
@Matthew 답변의 마지막 부분에서 보여 주듯이 Dynamic Sql을 사용해야합니다.
Taryn

빠른 응답에 감사합니다! 실제로 새 열을 만들고 거기에있는 모든 옵션을 쉼표로 구분하여 채워 넣습니다. col은 동일한 테이블에서 선택하는 부속 쿼리에서 생성 where a.personId = a2.personId order by a2.personId for xml path('')합니다. a2는 하위 쿼리의 테이블입니다. 그런 다음 텍스트를 열로 쉼표를 구분 기호로 사용하여 Excel에서 데이터를 분리합니다. 절차를 작성하지 않고도 일반 SQL 에서이 작업을 수행 할 수있는 방법을 찾고 있었지만 방법이 없을 수도 있습니다. 지금 당장 실행해야하지만 더 잘 설명하기 위해 그 예를 게시하려고합니다.
Matthew

9

이것은 SQL Server 구문과 동일합니다. Oracle 문서를 읽은 결과, NULLIFPIVOT 은 SQL Server 유형과 동일한 형식으로 나타납니다. Itzik이 보여 주듯이 쿼리를 동적으로 만들지 않으면 정적이어야하는 피벗 목록이 문제 이지만 P / SQL로 변환 할 수 있는지 모르겠습니다.

WITH PersonOptions(PersonID, OptionId) AS
(
    SELECT 1, 'A'
    UNION ALL SELECT 1, 'B'
    UNION ALL SELECT 2, 'C'
    UNION ALL SELECT 3, 'B'
    UNION ALL SELECT 4, 'A'
    UNION ALL SELECT 4, 'C'
)
SELECT
    P.PersonId
,   NULLIF(P.A, 0) AS OptionA
,   NULLIF(P.B, 0) AS OptionB
,   NULLIF(P.C, 0) AS OptionC
FROM
    PersonOptions  PO
    PIVOT 
    (
        COUNT(PO.OptionId)
        FOR OPtionId IN (A, B, C)
    )  P;

5

쿼리를 수동으로 피벗하는 것을 선호하지만 사용할 수도 있습니다 PIVOT.

SELECT PersonID,
MAX(CASE WHEN OptionId ='A' THEN 1 END) AS OptionA,
MAX(CASE WHEN OptionId ='B' THEN 1 END) AS OptionB, 
MAX(CASE WHEN OptionId ='C' THEN 1 END) AS OptionC
FROM PersonOptions
GROUP BY PersonID

1
이걸 조금만 더 설명해주세요. 피봇은 다른 사람들이 할 수 없도록 무엇을 제공합니까? 그리고 언제 고장입니까? 알고있는 바로 그 분야에서 특정 도메인 전문 지식을 가진 사람이 아니라 후손에 대한 답변임을 기억하십시오.
jcolebrand

2
@ jcolebrand : 개인적인 취향에 관한 것입니다 PIVOT. 내가 사용하는 접근법에 비해 구문이 더 복잡 하다고 생각합니다 . 그러나 나는 둘 다 동일한 결과를 제공한다는 것을 알고 있으며 다른 사람들이 반대 생각을 할 수 있음에 동의합니다.
a1ex07

1
힌트 : 편집 버튼을 사용하십시오 ;-) ~ 우리 는 코드-응답 이상의 것을 권장 합니다.
jcolebrand
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.