行を複数の列にピボットする


21

Oracleサーバーへのリンクサーバーを持つSQL Serverインスタンスがあります。PersonOptions次のデータを含む、呼び出されたOracleサーバー上のテーブルがあります。

╔══════════╦══════════╗
║ 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 with Demoをご覧ください

静的ピボット:

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 with Demoをご覧ください

動的バージョン:

上記の2つのバージョンは、既知の値の数を持っている場合はうまく機能しますが、値が不明な場合は、動的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 |

ただし、静的ピボットソリューションでは、オプションが3つしかないことを前提としています。潜在的に無制限の数のオプションがある場合はどうなりますか?たとえば、ABCDEFGHIJK?通常のSQLでピボットを動的にする方法はありませんか?オプションを列ヘッダーにする代わりに、列に配置するだけでいいですか?したがって、次のようになります。PERSONID | Column2 | Column3 | Column4 | ------------------------------------------ | 1 | A | B | null | | 2 | C | null | null | | 3 | null | C | null |
マシュー

1
@Matthewは、答えの最後の部分で示すように、動的SQLを使用する必要があります。
タリン

早速のご返事ありがとうございます!実際にこれを行うには、新しい列を作成し、そこにあるすべてのオプションをコンマで区切って詰めます。colは、同じテーブルから選択するサブクエリから生成されwhere a.personId = a2.personId order by a2.personId for xml path('')ます。a2は、サブクエリのテーブルです。次に、テキストを列にカンマを区切り文字として使用して、Excelでデータを分離します。私は手続きを書かずにこれを通常のSQLで行う方法を見つけたいと思っていましたが、おそらく方法はありません。現時点で実行する必要がありますが、より良い説明のためにその例を投稿しようとします。
マシュー

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.