web-dev-qa-db-fra.com

Faire pivoter les lignes dans plusieurs colonnes

J'ai une instance SQL Server qui a un serveur lié à un serveur Oracle. Il existe une table sur le serveur Oracle appelée PersonOptions qui contient les données suivantes:

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

J'ai besoin de faire pivoter ces données pour que les résultats soient:

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

Aucune suggestion?

21
NotMe

Vous pouvez effectuer cette transformation de données de plusieurs manières. Vous avez accès à la fonction PIVOT alors ce sera la plus simple, mais sinon, vous pouvez utiliser une fonction d'agrégation et un CASE.

Version agrégée/cas:

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;

Voir SQL Fiddle with Demo

Pivot statique:

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

Voir SQL Fiddle with Demo

Version dynamique:

Les deux versions ci-dessus fonctionnent très bien si vous avez un nombre connu de valeurs, mais si vos valeurs sont inconnues, alors vous voudrez implémenter SQL dynamique et dans Oracle, vous pouvez utiliser une procédure:

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

Ensuite, vous retournez les résultats, vous utiliserez:

variable x refcursor
exec dynamic_pivot_po(:x)
print x

Les résultats sont les mêmes avec toutes les versions:

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

Ce serait l'équivalent dans la syntaxe SQL Server. D'après ma lecture de la documentation Oracle, NULLIF et PIVOT semblent avoir le même format que leur parent SQL Server. Le défi sera la liste croisée dynamique qui doit être statique à moins que vous ne rendiez la requête dynamique comme Itzik montre mais je n'ai aucune idée si cela peut être traduit en 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;
9
billinkc

Je préfère faire pivoter la requête manuellement, mais vous pouvez également utiliser 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
5
a1ex07