简体   繁体   中英

PL/SQL For one line how to get the number of cols with distinct value

I'm stuck on something simple. I have to work with this type of table :

CREATE TABLE FORM (
    ID_FORM INT, 
    ACT_1 VARCHAR2 (10),
    ACT_2 VARCHAR2 (10),
    ACT_3 VARCHAR2 (10),
    ACT_4 VARCHAR2 (10),
    DESC_1 VARCHAR2 (10),
    DESC_2 VARCHAR2 (10),
    DESC_3 VARCHAR2 (10),
    DESC_4 VARCHAR2 (10),
    ECH_1 INT,
    ECH_2 INT,
    ECH_3 INT,
    ECH_4 INT
);

INSERT INTO FORM VALUES ('1','A1','A2','A3',null,'D1','D2','D3',null,'2','12','6',null);
INSERT INTO FORM VALUES ('2','A1','A1','A3',null,'D1','D2','D1',null,'2','2','2',null);
INSERT INTO FORM VALUES ('3','A3','A3','A1',null,'D4','D4','D1',null,'2','2','12',null);

I want to create a function that return for one ID_FORM (PRIMARY KEY) the number of distinct values group by / concatanate cols like :

ACT_1|| DESC_1|| ECH_1 -> presta1
ACT_2|| DESC_2|| ECH_2 -> presta2
ACT_3|| DESC_3|| ECH_3 -> presta3
ACT_4|| DESC_4|| ECH_4 -> presta4

And i have to "COUNT DISTINCT" item

For example for :

select 
ACT_1|| DESC_1|| ECH_1 as presta1,
ACT_2|| DESC_2|| ECH_2 as presta2,
ACT_3|| DESC_3|| ECH_3 as presta3,
ACT_4|| DESC_4|| ECH_4 as presta4
from FORM;

PRESTA1 PRESTA2 PRESTA3 PRESTA4
A1D12   A2D212  A3D36    -   (Function have to return 3)
A1D12   A1D22   A3D12    -    (Function have to return 3)
A3D42   A3D42   A1D112   -    (function have to return 2)

Note that in reality the table is much larger and extends up to 8 "presta" (till ACT_8, DESC_8, ECH_8)

Someone can help me ?

You'd better normalize the table to make queries simple. If it is not an option you may unpivot it first in the query. Or proceed with lateral join

select ID_FORM, n
from FORM f
cross apply (
  select count(*) n 
  from (
     select f.act_1 || f.DESC_1 || f.ECH_1  presta from dual
     union
     select f.act_2 || f.DESC_2 || f.ECH_2 from dual
     union
     select f.act_3 || f.DESC_3 || f.ECH_3 from dual
     union
     select f.act_4 || f.DESC_4 || f.ECH_4 from dual
  )
  where presta is not null
)

You can unpivot your column groups to rows and do COUNT(DISTINCT ) on them. Fiddle

  select
   id_form,
   count(distinct act || desc_ || ech) as cnt
  from form
  unpivot (
    (act, desc_, ech) for presta_no in (
      (act_1, desc_1, ech_1) as '1',
      (act_2, desc_2, ech_2) as '2',
      (act_3, desc_3, ech_3) as '3',
      (act_4, desc_4, ech_4) as '4'
    )
  ) p
  group by id_form

I think the following query will actually answer your question:

WITH destruct AS
(
SELECT ID_FORM, 
       SUBSTR(COL_TYPE, 1, INSTR(COL_TYPE, '_')-1) AS COL_TYPE,
       SUBSTR(COL_TYPE, INSTR(COL_TYPE, '_')+1) AS COL_NUM,
       VAL
FROM (SELECT ID_FORM, ACT_1, ACT_2, ACT_3, ACT_4, DESC_1, DESC_2, DESC_3, DESC_4,
             TO_CHAR(ECH_1) AS ECH_1, TO_CHAR(ECH_2) AS ECH_2,
             TO_CHAR(ECH_3) AS ECH_3, TO_CHAR(ECH_4) AS ECH_4
      FROM FORM
      /*WHERE id_form = :1*/)
UNPIVOT INCLUDE NULLS (VAL FOR COL_TYPE IN (ACT_1, ACT_2, ACT_3, ACT_4,
                                            DESC_1, DESC_2, DESC_3, DESC_4,
                                            ECH_1, ECH_2, ECH_3, ECH_4))
)
SELECT da.id_form, da.val, dd.val, de.val
FROM destruct da
INNER JOIN destruct dd ON da.id_form = dd.id_form AND da.col_num = dd.col_num AND dd.col_type = 'DESC'
INNER JOIN destruct de ON da.id_form = de.id_form AND da.col_num = de.col_num AND de.col_type = 'ECH'
WHERE da.col_type = 'ACT';

This is actually a bit more complicated than it needs to be due to the ECH columns being of a different type. If you want it for just one ID_FORM you can uncomment the WHERE clause and use the bind variable from your function.

Edit: Adding a DBFiddle ( Link )

Precompute PRESTA columns (8 columns is not too much IMHO) and count distinct values in correlated subquery on some collection of that columns. (I used built-in sys.ku$_vcnt here, some consider it as antipattern, you can easily create your own nested table.)

with pr(PRESTA1, PRESTA2, PRESTA3, ...) as (
  select ACT_1|| DESC_1|| ECH_1, ... (your concatenating expression)
  from form
)
select pr.*
     , (select count(distinct column_value)
        from table(sys.ku$_vcnt(PRESTA1, PRESTA2, PRESTA3, ...))
       ) 
from pr

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM