Pivot satırları birden çok sütuna


21

Bir Oracle sunucusuna bağlı bir sunucuya sahip bir SQL Server örneğim var. Oracle sunucusunda PersonOptions, aşağıdaki verileri içeren bir tablo vardır :

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

Bu verileri döndürmem gerekiyor, böylece sonuçlar şöyle:

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

Baska öneri?

Yanıtlar:


20

Bu veri dönüşümünü gerçekleştirmenin birkaç yolu vardır. İşleve erişiminiz vardır, PIVOTo zaman bu en kolay olur, ancak o zaman bir toplama işlevi ve a kullanabilirsiniz CASE.

Toplu / Vaka sürümü:

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;

Demo ile SQL Fiddle'ı görün

Statik Pivot:

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

Demo ile SQL Fiddle'ı görün

Dinamik Sürüm:

Yukarıdaki iki sürüm, bilinen sayıda değere sahipseniz harika çalışır, ancak değerleriniz bilinmiyorsa, dinamik sql uygulamak isteyeceksiniz ve Oracle'da bir yordam kullanabilirsiniz:

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

Sonra sonuçları döndürürsün, kullanacaksın:

variable x refcursor
exec dynamic_pivot_po(:x)
print x

Sonuçlar tüm sürümlerle aynıdır:

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

Ancak Statik Pivot çözümü sadece üç seçenek olduğunu varsayar. Potansiyel olarak sınırsız sayıda seçeneğiniz varsa? ABCDEFGHIJK örneğin? Pivotu düzenli sql ile dinamik hale getirmenin bir yolu yok mu? Seçenekleri sütun başlıkları yapmak yerine, sütunlara koyabilir miyiz? Böylece şöyle görünürdü: | KİŞİSEL | Sütun2 | Sütun3 | Sütun4 | -------------------------------------------- | | 1 | A | B | null | | 2 | C | null | null | | 3 | boş | C | boş |
Matthew,

1
@Matthew, cevabın son bölümünde gösterdiğim gibi Dynamic Sql kullanmanız gerekecek.
Taryn

Hızlı cevabınız için teşekkür ederim! Aslında bunu yeni bir sütun oluşturarak ve oradaki tüm seçenekleri virgülle ayırarak yapıyorum. Col, aynı tablolardan seçen bir alt sorgudan üretilir where a.personId = a2.personId order by a2.personId for xml path(''). a2 alt sorgudaki tablodur. Sonra da excel içindeki verileri, sınırlayıcı olarak virgül içeren metinlere sütunlar kullanarak ayırırım. Bir prosedür yazmak zorunda kalmadan bunu düzenli sql'de yapmanın bir yolunu bulmayı umuyordum, ama belki de yolu yok. Şu anda koşmak zorundayım ama daha iyi açıklamak için bunun bir örneğini göndermeye çalışacağım.
Matthew,

9

Bu, SQL Server sözdizimindeki eşdeğeri olacaktır. Oracle dokümanlarını okuduğumda, NULLIF ve PIVOT , SQL Server akrabalarıyla aynı formata sahip görünüyor. Meydan okuma, Itzik'in gösterdiği gibi sorguyu dinamik yapmadığınız sürece statik olması gereken pivot listesi olacak, ancak bunun P / SQL'e çevrilip çevrilemeyeceği konusunda hiçbir fikrim yok.

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

Sorguyu el ile döndürmeyi tercih ediyorum, ancak siz de kullanabilirsiniz 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
Bunu biraz daha açıklamaktan çekinmeyin. Özet, diğerlerinin sağlayamayacağı neyi sağlar? Bu ne zaman bozulur? Bilinçli olmak için cevap verdiğini, bildiğin şeyler konusunda özel alan uzmanlığı olan biri için değil.
jcolebrand

2
@jcolebrand: Daha çok kişisel tercihle ilgili - Ben kendim PIVOTsözdiziminin kullandığım yaklaşıma kıyasla daha sarsıldığını düşünüyorum . Bununla birlikte, her ikisinin de aynı sonucu verdiğinin farkındayım ve diğer insanların ters düşünebileceğini kabul ediyorum.
a1ex07

1
İpucu: Düzenleme düğmesini kullanın ;-) ~ Bir kod-cevap yanıtından daha fazla teşvik etmek istiyoruz .
jcolebrand
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.