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