Zeilen in mehrere Spalten schwenken


21

Ich habe eine SQL Server-Instanz, die einen Verbindungsserver mit einem Oracle-Server hat. Auf dem Oracle-Server befindet sich eine Tabelle mit PersonOptionsden folgenden Daten:

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

Ich muss diese Daten pendeln, damit die Ergebnisse sind:

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

Irgendwelche Vorschläge?

Antworten:


20

Es gibt verschiedene Möglichkeiten, wie Sie diese Datenumwandlung durchführen können. Sie haben Zugriff auf die PIVOTFunktion, dann ist dies die einfachste. Wenn nicht, können Sie eine Aggregatfunktion und eine verwenden CASE.

Aggregat / Fallversion:

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;

Siehe SQL Fiddle with Demo

Statischer Drehpunkt:

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

Siehe SQL Fiddle with Demo

Dynamische Version:

Die beiden obigen Versionen funktionieren hervorragend, wenn Sie eine bekannte Anzahl von Werten haben, aber wenn Ihre Werte unbekannt sind, möchten Sie dynamisches SQL implementieren und in Oracle können Sie eine Prozedur verwenden:

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

Dann geben Sie die Ergebnisse zurück und verwenden:

variable x refcursor
exec dynamic_pivot_po(:x)
print x

Die Ergebnisse sind bei allen Versionen gleich:

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

Bei der statischen Pivot-Lösung werden jedoch nur drei Optionen vorausgesetzt. Was ist, wenn Sie eine potenziell unbegrenzte Anzahl von Optionen haben? ABCDEFGHIJK zum Beispiel? Gibt es keine Möglichkeit, den Pivot mit normalem SQL zu dynamisieren? Können wir die Optionen, anstatt sie als Spaltenüberschriften zu definieren, einfach in die Spalten einfügen? Es würde also so aussehen: | PERSONID | Column2 | Spalte3 | Column4 | ------------------------------------------ | 1 | A | B | null | | 2 | C | null | null | | 3 | null | C | null |
Matthew

1
@Matthew, du müsstest Dynamic Sql verwenden, wie ich im letzten Teil der Antwort demonstriere.
Taryn

Danke für die schnelle Antwort! Ich mache das tatsächlich, indem ich eine neue Spalte erstelle und alle Optionen dort durch Kommas getrennt einfüge. Die Spalte wird aus einer Unterabfrage generiert, die aus denselben Tabellen ausgewählt wird where a.personId = a2.personId order by a2.personId for xml path(''). a2 ist die Tabelle in der Unterabfrage. Dann trenne ich die Daten in Excel mithilfe von Text in Spalten mit Komma als Trennzeichen. Ich hatte gehofft, einen Weg zu finden, dies in regulärem SQL zu tun, ohne eine Prozedur schreiben zu müssen, aber vielleicht gibt es keinen Weg. Müssen im Moment laufen, aber ich werde versuchen, ein Beispiel dafür zu posten, um es besser zu erklären.
Matthew

9

Dies entspricht der SQL Server-Syntax. Aufgrund meiner Lektüre der Oracle-Dokumente scheinen NULLIF und PIVOT dasselbe Format zu haben wie ihre SQL Server-Verwandten. Die Herausforderung wird die Pivot-Liste sein, die statisch sein muss, es sei denn, Sie machen die Abfrage dynamisch, wie Itzik demonstriert, aber ich habe keine Ahnung, ob das in P / SQL übersetzt werden kann

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

Ich bevorzuge die manuelle Pivot-Abfrage, aber Sie können auch verwenden 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
Fühlen Sie sich frei, dies ein wenig mehr zu erklären. Was bietet der Pivot, was die anderen möglicherweise nicht? Und wann bricht das zusammen? Denken Sie daran, dass Sie sich für die Nachwelt einsetzen und nicht für jemanden, der über spezielle Fachkenntnisse in den Dingen verfügt, die Sie ebenfalls kennen.
jcolebrand

2
@jcolebrand: Es geht eher um persönliche Vorlieben - ich selbst denke, dass die PIVOTSyntax im Vergleich zu meinem Ansatz komplizierter ist. Ich bin mir jedoch bewusst, dass beide das gleiche Ergebnis liefern, und ich bin mir einig, dass andere Leute möglicherweise gegenteilig denken.
a1ex07,

1
Tipp: Benutze den Editier-Button ;-) ~ Wir möchten mehr als nur eine Code-Antwort-Antwort ermutigen .
jcolebrand
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.