1
votes

Effectuer le comptage du nombre d'occurrences dans SQL

Est-il courant et pratique pour SQL d'effectuer une telle manipulation de données, la capture des résultats uniquement dans les colonnes satisfaisant les conditions et d'effectuer le comptage du nombre d'occurrences? Comment écrire du code SQL pour générer la sortie souhaitée (si possible).

Le nom n'est présenté que lorsque les conditions (Cond1 à Cond5) sont oui.

Entrée souhaitée

ID Counting Name
1  1        Result1
1  2        Result2
2  1        Result3
2  2        Result4
2  3        Result5

Résultat souhaité

ID Cond1 Cond2 Cond3 Cond4 Cond5 Name1   Name2   Name3   Name4    Name5
1  No    Yes   No    No    Yes   (null)  Result1 n/a     (null)   Result2
2  Yes   No    Yes   No    Yes   Result3 n/a     Result4 (null)   Result5


0 commentaires

5 Réponses :


3
votes

Cela peut être fait avec union all et row_number():

select id,  row_number() over(partition by id order by seq) couting, name
from (
    select id, name1 name, 1 seq from mytable where cond1 = 'Yes'
    union all select id, name2, 2 from mytable where cond2 = 'Yes'
    union all select id, name3, 3 from mytable where cond3 = 'Yes'
    union all select id, name4, 4 from mytable where cond4 = 'Yes'
    union all select id, name5, 5 from mytable where cond5 = 'Yes'
) x
order by id, rn


2 commentaires

Nice & clean (par opposition à ma requête monstre ).


@Littlefoot Oui, c'est une requête courte mais il faudra un scan de table / index pour chaque SELECT dans la requête interne, donc cela peut être inefficace. db <> violon



0
votes

Vous pouvez utiliser CONNECT BY LEVEL pour obtenir le résultat souhaité comme suit:

SELECT
    ID,
    ROW_NUMBER() OVER(PARTITION BY ID ORDER BY LVL) AS "Counting",
    NAME_   AS "Name"
FROM
    (SELECT
            T.ID,
            DECODE(LVL, 1, COND1, 2, COND2, 3, COND3, 4, COND4, 5, COND5) AS COND,
            DECODE(LVL, 1, NAME1, 2, NAME2, 3, NAME3, 4, NAME4, 5, NAME5) AS NAME_,
            LVL   AS LVL
        FROM
            YOUR_TABLE T join 
       (Select level as lvl from dual CONNECT BY  LEVEL <= 5) on (1=1)
     )
WHERE COND = 'Yes';

Cheers !!


1 commentaires

Hm, je l'ai essayé - renvoie 39 lignes, ce qui ne ressemble pas au résultat souhaité .



0
votes

Une autre option:

SQL> with
  2  test (id, cond1, cond2, cond3, cond4, cond5, name1, name2, name3, name4, name5) as
  3    -- your sample data
  4    (select 1, 'no' , 'yes', 'no' , 'no', 'yes', null     , 'result1', 'n/a'    , null, 'result2' from dual union all
  5     select 2, 'yes', 'no' , 'yes', 'no', 'yes', 'result3', 'n/a'    , 'result4', null, 'result5' from dual
  6    ),
  7  temp as
  8    -- values whose COND column is 'yes'
  9    (select id,
 10       decode(cond1, 'yes', name1) n1,
 11       decode(cond2, 'yes', name2) n2,
 12       decode(cond3, 'yes', name3) n3,
 13       decode(cond4, 'yes', name4) n4,
 14       decode(cond5, 'yes', name5) n5
 15     from test
 16    ),
 17  up as
 18    -- unpivot data
 19    (select *
 20     from temp
 21     unpivot (c_name for pc in (n1, n2, n3, n4, n5))
 22    )
 23    -- final result
 24  select id,
 25         row_number() over (partition by id order by c_name) counting,
 26         c_name as name
 27  from up
 28  order by id;

        ID   COUNTING NAME
---------- ---------- -------
         1          1 result1
         1          2 result2
         2          1 result3
         2          2 result4
         2          3 result5

SQL>


0 commentaires

0
votes

Voici une autre option utilisant UNPIVOT.

ID COUNTING NAMES
1  1        Result1
1  2        Result2
2  1        Result3
2  2        Result4
2  3        Result5

Résultat:

with ns as (
select id,
       n,
       names 
  from mytab
unpivot(names for n in (name1 as 'n1', 
                        name2 as 'n2', 
                        name3 as 'n3',
                        name4 as 'n4',
                        name5 as 'n5'))),
cs as (
select id,
       n,
       condns 
  from mytab
unpivot(condns for n in (cond1 as 'n1', 
                         cond2 as 'n2', 
                         cond3 as 'n3',
                         cond4 as 'n4', 
                         cond5 as 'n5')))
select ns.id, 
       row_number() over(partition by ns.id order by ns.n) counting, 
       ns.names 
  from ns inner join cs
    on ns.id = cs.id 
   and ns.n = cs.n 
   and cs.condns = 'Yes'
 order by 1,2;

Solution basée sur UNPIVOT.

ID COND1 COND2 COND3 COND4 COND5 NAME1   NAME2   NAME3   NAME4    NAME5
1  No    Yes   No    No    Yes   (null)  Result1 n/a     (null)   Result2
2  Yes   No    Yes   No    Yes   Result3 n/a     Result4 (null)   Result5

Sortie :

create table mytab(id number, 
                   cond1 varchar2(3), 
                   cond2 varchar2(3), 
                   cond3 varchar2(3), 
                   cond4 varchar2(3), 
                   cond5 varchar2(3), 
                   Name1 varchar2(7),
                   Name2 varchar2(7), 
                   Name3 varchar2(7), 
                   Name4 varchar2(7), 
                   Name5 varchar2(7));

insert into mytab values(1,'No','Yes','No','No','Yes',null,'Result1','n/a',null,'Result2');

insert into mytab values(2,'Yes','No','Yes','No','Yes','Result3','n/a','Result4',null,'Result5');

commit;

select * from mytab;


0 commentaires

3
votes

Vous pouvez utiliser UNPIVOT avec des paires de colonnes, puis filtrer sur les lignes Yes et utiliser la fonction analytique ROW_NUMBER pour obtenir l'index incrémentiel du résultat:

Requête :

ID | COUNT | NAME   
-: | ----: | :------
 1 |     1 | Result1
 1 |     2 | Result2
 2 |     1 | Result3
 2 |     2 | Result4
 2 |     3 | Result5

Données de test :

CREATE TABLE table_name (
  ID    NUMBER(10,0) PRIMARY KEY,
  Cond1 VARCHAR2(3) CHECK ( Cond1 IN ( 'Yes', 'No' ) ),
  Cond2 VARCHAR2(3) CHECK ( Cond2 IN ( 'Yes', 'No' ) ),
  Cond3 VARCHAR2(3) CHECK ( Cond3 IN ( 'Yes', 'No' ) ),
  Cond4 VARCHAR2(3) CHECK ( Cond4 IN ( 'Yes', 'No' ) ),
  Cond5 VARCHAR2(3) CHECK ( Cond5 IN ( 'Yes', 'No' ) ),
  Name1 VARCHAR2(10),
  Name2 VARCHAR2(10),
  Name3 VARCHAR2(10),
  Name4 VARCHAR2(10),
  Name5 VARCHAR2(10),
  CHECK ( ( Cond1 = 'Yes' AND Name1 IS NOT NULL ) OR ( Cond1 = 'No' AND ( Name1 IS NULL OR Name1 = 'n/a' ) ) ),
  CHECK ( ( Cond2 = 'Yes' AND Name2 IS NOT NULL ) OR ( Cond2 = 'No' AND ( Name2 IS NULL OR Name2 = 'n/a' ) ) ),
  CHECK ( ( Cond3 = 'Yes' AND Name3 IS NOT NULL ) OR ( Cond3 = 'No' AND ( Name3 IS NULL OR Name3 = 'n/a' ) ) ),
  CHECK ( ( Cond4 = 'Yes' AND Name4 IS NOT NULL ) OR ( Cond4 = 'No' AND ( Name4 IS NULL OR Name4 = 'n/a' ) ) ),
  CHECK ( ( Cond5 = 'Yes' AND Name5 IS NOT NULL ) OR ( Cond5 = 'No' AND ( Name5 IS NULL OR Name5 = 'n/a' ) ) )
);

INSERT INTO table_name ( ID, Cond1, Cond2, Cond3, Cond4, Cond5, Name1, Name2, Name3, Name4, Name5 )
SELECT 1, 'No',  'Yes', 'No',  'No', 'Yes', null,      'Result1', 'n/a',     null, 'Result2' FROM DUAL UNION ALL
SELECT 2, 'Yes', 'No',  'Yes', 'No', 'Yes', 'Result3', 'n/a',     'Result4', null, 'Result5' FROM DUAL;


4 commentaires

Question 1: PARTITION BY id ORDER BY valeur, doit-il être trié par "nom" (au lieu de tri par "valeur"?) Question 2: Puis-je avoir plusieurs UNPIVOT dans une seule requête SQL, en supposant que j'ai Cond_GROUP2 et Name_GROUP2 dans le même table? Merci.


@TonyChan Cela dépend de la façon dont vous souhaitez classer vos COUNT valeurs. ORDER BY value donnera un décompte incrémentiel dans l'ordre dans lequel ils apparaissent dans les colonnes; alors que ORDER BY name donnera un décompte incrémentiel dans l'ordre des chaînes dans la colonne name . Pour vos données d'exemple, la sortie sera identique à Result1 / Result2 est dans le même ordre que vous compariez les valeurs par ordre alphabétique ou la position dans les colonnes mais si vous deviez permuter les dans le tableau alors les deux requêtes donneraient une sortie différente. Je ne peux pas vous dire ce qui est valide car ce sont vos données et votre sortie.


@TonyChan Vous pouvez avoir plusieurs clauses UNPIVOT dans une seule instruction SQL; cependant, je ne suis pas ce que vous essayez de réaliser et cela dépasse le cadre de cette question. Si vous souhaitez étendre la question, essayez de trouver une solution vous-même à partir de la documentation et si vous ne pouvez pas la gérer, posez une nouvelle question avec les problèmes que vous rencontrez.


Merci. Pour la question 1, la valeur ORDER BY génère un résultat correct. ORDER BY nom perturbe la commande. Pour la question 2, j'ai résolu en utilisant vos requêtes plusieurs fois et en les rejoignant par la clé primaire.