Oracle
 sql >> Base de données >  >> RDS >> Oracle

Comment lier les valeurs horizontales d'une table aux valeurs verticales d'une autre table dans la base de données Oracle

with a as (
    select a.*, row_number() over (partition by department order by attributeID) rn
      from attributes a),
  e as (
    select employeeId, department, attribute1, 1 rn from employees union all
    select employeeId, department, attribute2, 2 rn from employees union all
    select employeeId, department, attribute3, 3 rn from employees
  )
select e.employeeId, a.attributeid, e.department, a.attribute, a.meaning, 
       e.attribute1 as value 
  from e join a on a.department=e.department and a.rn=e.rn 
  order by e.employeeId, a.attributeid

Données de test et sortie :

create table employees (employeeID number(3), name varchar2(10), department varchar2(5), age number(3), attribute1 varchar2(10), attribute2 varchar2(10), attribute3 varchar2(10));
insert into employees values (1, 'john', 'IT', 22, 'attr1val1', 'attr2val2',  null);
insert into employees values (2, 'jane', 'HR', 32, 'attr1val3', 'attr2val4',  'attr3val5');
insert into employees values (3, 'joe',  'HR', 23, 'attr1val6', 'attr2val7',  'attr3val8');
insert into employees values (4, 'jack', 'IT', 45, 'attr1val9', 'attr2val10', null);

create table attributes (attributeID number(3), department varchar2(10), attribute varchar2(10), meaning varchar2(10));
insert into attributes values (1, 'IT', 'attribute1', 'laptoptype');
insert into attributes values (2, 'IT', 'attribute2', 'networkloc');
insert into attributes values (3, 'HR', 'attribute1', 'location');
insert into attributes values (4, 'HR', 'attribute2', 'position');
insert into attributes values (5, 'HR', 'attribute3', 'allocation');

EMPLOYEEID ATTRIBUTEID DEPARTMENT ATTRIBUTE  MEANING    VALUE
---------- ----------- ---------- ---------- ---------- ----------
         1           1 IT         attribute1 laptoptype attr1val1
         1           2 IT         attribute2 networkloc attr2val2
         2           3 HR         attribute1 location   attr1val3
         2           4 HR         attribute2 position   attr2val4
         2           5 HR         attribute3 allocation attr3val5
         3           3 HR         attribute1 location   attr1val6
         3           4 HR         attribute2 position   attr2val7
         3           5 HR         attribute3 allocation attr3val8
         4           1 IT         attribute1 laptoptype attr1val9
         4           2 IT         attribute2 networkloc attr2val10

Modifier :Explication

En réponse, j'ai utilisé with clause juste pour diviser la solution en étapes lisibles. Vous pouvez les déplacer dans from clause de la requête principale si elle est plus confortable pour vous. Quoi qu'il en soit :sous-requête a lit les données de la table attributes et ajoute un nombre pour les lignes, donc pour chaque département, ils sont toujours numérotés à partir de 1. J'ai utilisé numéro_ligne() pour ça. Sous-requête e les syndicats (tous) ont requis des attributs et les numérotent en conséquence. Les nombres générés dans les deux sous-requêtes sont ensuite utilisés dans la jointure principale :a.department=e.department and a.rn=e.rn .

Alternative 1 - si vous utilisez Oracle 11g, vous pouvez utiliser le unpivot . Voir ce qui est généré par la sous-requête et comment elle est jointe aux attributes tableau :

with e as (
    select employeeId, name, department, attribute, value from employees
      unpivot (value for attribute in ("ATTRIBUTE1", "ATTRIBUTE2", "ATTRIBUTE3"))  
  )
select e.employeeId, a.attributeid, e.department, a.attribute, 
       a.meaning, e.value 
  from e join attributes a on a.department=e.department 
                          and lower(a.attribute)=lower(e.attribute)
  order by e.employeeId, a.attributeid;

Alternative 2 - avec générateur de sous-requête hiérarchique (sous-requête r ), réalisé par connect by qui crée simplement des nombres à partir de 1, 2, 3 qui sont ensuite rejoints par employees et l'attribut approprié est attaché comme valeur dans case clause. Le repos est fait de la même manière que dans la réponse originale.

with a as (
    select a.*, row_number() over (partition by department order by attributeID) rn
      from attributes a),
  r as (select level rn from dual connect by level<=3),
  e as (
    select employeeId, department, rn,
           case when r.rn = 1 then attribute1
                when r.rn = 2 then attribute2
                when r.rn = 3 then attribute3
           end value
      from employees cross join r
  )
select e.employeeId, a.attributeid, e.department, a.attribute,
       a.meaning, e.value
  from e join a on a.department=e.department and a.rn=e.rn
  order by e.employeeId, a.attributeid

Les trois versions m'ont donné le même résultat. J'ai également testé la première option sur une table similaire avec 100k lignes et j'obtiens une sortie en quelques secondes (pour 5 attributs). Veuillez tester toutes les solutions et essayer de les comprendre. Si vous pouvez utiliser la version non pivotée, je préférerais ceci. Désolé pour les explications tardives et les erreurs de langue.