Home > Software design >  Oracle SQL - Create identifier for couple of values
Oracle SQL - Create identifier for couple of values

Time:10-30

I'm struggling with the following problem, I have the follwing data in a table:

Param ID Param Val Other Cols
1 15 XXX
1 15 XXX
1 16 XXX
1 16 XXX
2 21 XXX
2 21 XXX
2 22 XXX
2 22 XXX

I would like to select a new colum in order to create 4 sets of data to have all the possible combination between the values of parameter 1 and 2; so I would like to obtain something like this:

Set Param ID Param Val Other Cols
1 1 15 XXX
2 1 15 XXX
3 1 16 XXX
4 1 16 XXX
1 2 21 XXX
3 2 21 XXX
2 2 22 XXX
4 2 22 XXX

So for example for the Set 1 I will have the Couple of values 15 and 21, for the set 2 the values 15 and 22 etc etc.

I tried using different analytic functions, but I was not able to have what I need. Thanks in advance.

CodePudding user response:

Try to use analytic function like here:

WITH
    tbl AS
        (
            Select 1 "ID", 15 "VAL" From Dual Union All
            Select 1 "ID", 15 "VAL" From Dual Union All
            Select 1 "ID", 16 "VAL" From Dual Union All
            Select 1 "ID", 16 "VAL" From Dual Union All
            Select 2 "ID", 21 "VAL" From Dual Union All
            Select 2 "ID", 21 "VAL" From Dual Union All
            Select 2 "ID", 22 "VAL" From Dual Union All
            Select 2 "ID", 22 "VAL" From Dual 
        )
            
        
Select
    Sum(1) OVER(Partition By ID Order By ID, VAL ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) "SET_ID",
    ID "ID",
    VAL "VAL"
From
    tbl

The result should be:

SET_ID ID VAL
1 1 15
2 1 15
3 1 16
4 1 16
1 2 21
2 2 21
3 2 22
4 2 22

It is summing 1 from first to current row (for every row) of each ID (Partition By) and ordered by ID, VAL. More about basics of analytic functions here ( https://docs.oracle.com/cd/E11882_01/server.112/e41084/functions004.htm#SQLRF06174 )
You can select the sets (from this resulting set as my_set) like here:

Select 
    SET_ID "SET_ID",
    LISTAGG(To_Char(ID) || '/' || To_Char(VAL), ', ') WITHIN GROUP (Order By ID, VAL) "IDS"
From
    my_set 
Group By
    SET_ID
Order By
    SET_ID
SET_ID IDS
1 1/15, 2/21
2 1/15, 2/21
3 1/16, 2/22
4 1/16, 2/22

Regards...

ADDITION AFTER THE COMMENT
If you want the combinations then you will have to use the MODEL clause. To do that you should prepare the data (cte named grid) a bit so you could do the addressing to the particular data and manage all the combinations you want. It looks like here:

    grid AS
        ( Select  Distinct
                  Sum(1) OVER(Partition By ID Order By ID ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) "IDS_TOTAL_ORDER",
                  Sum(1) OVER(Partition By VAL Order By VAL ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) "VALS_TOTAL_ORDER",
                  ID "ID",  
                  VAL "VAL"
            From
                  tbl   
            Order By ID 
        )
Select     SET_ID, ID, VAL
From (   Select   0 "SET_ID", IDS_TOTAL_ORDER, VALS_TOTAL_ORDER, ID "ID", VAL "VAL"
         From     grid  
     )
    MODEL
        Dimension By (ID, IDS_TOTAL_ORDER, VALS_TOTAL_ORDER)
        Measures(SET_ID, VAL)
        RULES
            (
                SET_ID[1, ANY, ANY] = CV(IDS_TOTAL_ORDER),
                SET_ID[2, 1, 1] = CV(IDS_TOTAL_ORDER),
                SET_ID[2, 2, 2] = CV(IDS_TOTAL_ORDER)   1,
                SET_ID[2, 3, 1] = CV(IDS_TOTAL_ORDER) - 1,
                SET_ID[2, 4, 2] = CV(IDS_TOTAL_ORDER)
            )
Order By  ID, IDS_TOTAL_ORDER

This way you can get any combination. Here is your result:

SET_ID ID VAL
1 1 15
2 1 15
3 1 16
4 1 16
1 2 21
3 2 21
2 2 22
4 2 22

More about MODEL clause: https://www.oracle.com/webfolder/technetwork/tutorials/obe/db/10g/r2/prod/bidw/sqlmodel/sqlmodel_otn.htm

Sets now looks like here:

SET_ID IDS_VALS
1 1/15, 2/21
2 1/15, 2/22
3 1/16, 2/21
4 1/16, 2/22

Regards...

CodePudding user response:

Despite a good hint on the MODEL clause, I guess I will go for a solution combining cross join and unpivot, maybe it is not the best, but it fit my needs.

WITH tbl AS
(
       Select 1 "ID", 15 "VAL" From Dual Union All
       Select 1 "ID", 16 "VAL" From Dual Union All
       Select 2 "ID", 21 "VAL" From Dual Union All
       Select 2 "ID", 22 "VAL" From Dual )
SELECT *
FROM   (
              SELECT ROWNUM AS SET_ID,
                     id1,
                     id4
              FROM   (
                            SELECT
                                   CASE a.id
                                          WHEN 1 THEN a.val
                                          ELSE 0
                                   END AS id1,
                                   CASE a.id
                                          WHEN 2 THEN a.val
                                          ELSE 0
                                   END AS id2,
                                   CASE b.id
                                          WHEN 1 THEN b.val
                                          ELSE 0
                                   END AS id3,
                                   CASE b.id
                                          WHEN 2 THEN b.val
                                          ELSE 0
                                   END AS id4
                            FROM   tbl a,
                                   tbl b)
              WHERE  id2 = 0
              AND    id3 = 0) UNPIVOT (VAL FOR ID IN (id1 AS '1',
                                                      id4 AS '2'))

that results in :

SET_ID ID VAL
1 1 15
1 2 21
2 1 15
2 2 22
3 1 16
3 2 21
4 1 16
4 2 22

tried with all the combination of source data, and it seems work :)

  • Related