Pivot baris menjadi beberapa kolom

21

Saya memiliki contoh SQL Server yang memiliki server yang terhubung ke server Oracle. Ada tabel di server Oracle yang disebut PersonOptionsyang berisi data berikut:

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

Saya perlu menggerakkan data itu sehingga hasilnya adalah:

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

Ada saran?

Bukan saya
sumber

Jawaban:

20

Ada beberapa cara untuk melakukan transformasi data ini. Anda memiliki akses ke PIVOTfungsi maka itu akan menjadi yang termudah, tetapi jika tidak maka Anda dapat menggunakan fungsi agregat dan a CASE.

Versi agregat / Kasus:

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;

Lihat SQL Fiddle dengan Demo

Pivot Statis:

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

Lihat SQL Fiddle dengan Demo

Versi Dinamis:

Dua versi di atas berfungsi dengan baik jika Anda memiliki jumlah nilai yang diketahui, tetapi jika nilai Anda tidak diketahui, maka Anda ingin mengimplementasikan sql dinamis dan di Oracle Anda dapat menggunakan prosedur:

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;
/

Kemudian Anda mengembalikan hasilnya, Anda akan menggunakan:

variable x refcursor
exec dynamic_pivot_po(:x)
print x

Hasilnya sama dengan semua versi:

| PERSONID | OPTIONA | OPTIONB | OPTIONC |
------------------------------------------
|        1 |       1 |       1 |       0 |
|        2 |       0 |       0 |       1 |
|        3 |       0 |       1 |       0 |
|        4 |       1 |       0 |       1 |
Taryn
sumber
Namun solusi Static Pivot mengasumsikan hanya ada tiga opsi. Bagaimana jika Anda memiliki jumlah opsi yang berpotensi tidak terbatas? ABCDEFGHIJK misalnya? Apakah tidak ada cara untuk membuat pivot dinamis dengan sql reguler? Alih-alih membuat opsi header kolom, bisakah kita menempatkannya di kolom? Jadi akan terlihat seperti ini: | ORANG | Kolom2 | Kolom3 | Kolom4 | ------------------------------------------ | 1 | A | B | null | | 2 | C | null | null | | 3 | null | C | null |
Matius
1
@ Matius Anda harus menggunakan Dynamic Sql seperti yang saya tunjukkan di bagian terakhir dari jawaban.
Taryn
Terima kasih atas tanggapan cepatnya! Saya benar-benar melakukan ini dengan membuat kolom baru, dan memasukkan semua opsi di sana dipisahkan dengan koma. Kol dihasilkan dari pemilihan subquery dari tabel yang sama where a.personId = a2.personId order by a2.personId for xml path(''). a2 adalah tabel dalam subquery. Kemudian saya memisahkan data dalam excel menggunakan teks ke kolom dengan koma sebagai pembatas. Saya berharap menemukan cara untuk melakukan ini dalam sql biasa tanpa harus menulis prosedur, tetapi mungkin tidak ada cara. Harus menjalankan saat ini tetapi saya akan mencoba memposting contoh itu untuk lebih menjelaskan.
Matius
9

Ini akan menjadi setara dalam sintaks SQL Server. Berdasarkan bacaan saya tentang dokumen Oracle, NULLIF dan PIVOT tampaknya memiliki format yang sama dengan kerabat SQL Server mereka. Tantangannya adalah daftar pivot yang harus statis kecuali jika Anda membuat kueri dinamis seperti yang ditunjukkan oleh Itzik, tetapi saya tidak tahu apakah itu dapat diterjemahkan ke 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;
billinkc
sumber
5

Saya lebih memilih untuk memutar permintaan secara manual, tetapi Anda juga dapat menggunakannya 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
a1ex07
sumber
1
Jangan ragu untuk menjelaskan yang ini sedikit lagi. Apa yang disediakan pivot yang mungkin tidak dimiliki orang lain? Dan kapan itu rusak? Ingat Anda menjawab untuk anak cucu, bukan untuk seseorang dengan keahlian domain spesifik dalam hal-hal yang Anda ketahui juga.
jcolebrand
2
@ jcolebrand: Ini lebih tentang preferensi pribadi - Saya sendiri berpikir bahwa PIVOTsintaks lebih berbelit-belit dibandingkan dengan pendekatan yang saya gunakan. Namun, saya sadar keduanya memberikan hasil yang sama, dan saya setuju orang lain mungkin berpikir sebaliknya.
a1ex07
1
Petunjuk: Gunakan tombol edit ;-) ~ Kami ingin mendorong lebih dari sekadar respons kode jawaban.
jcolebrand