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.