Oracle pl/sql permutation and combination - sql

I am not sure what is the correct search term for this, please let me know if there's already an answer for this.
eg:
I have these data
A
B
C
D
E
What is the best way to calculate the addition of each possible combination? Such as:
A
A+B
A+C
A+D
A+E
A+B+C
A+B+D
A+B+E
A+C+D
A+C+E
A+C+D+E
A+B+C+D
A+B+C+E
A+B+C+D+E
B
B+C
B+D
B+E
B+C+D
B+C+E
B+C+D+E
C
C+D
C+E
...
The list goes on.......
Is there any way to achieve this?
The 5 data are not fixed. I might have 10.. 20 or 50 or 1000 :(
Thank you.

In SQL, you can almost do this with this set of left joins:
select (t1.col + coalesce(t2.col, 0) + coalesce(t3.col, 0) +
coalesce(t4.col, 0) + coalesce(t5.col, 0)
) as sumcombo
from t t1 left join
t t2
on t1.col < t2.col left join
t t3
on t2.col < t3.col left join
t t4
on t3.col < t4.col left join
t t5
on t4.col < t5.col;
It doesn't quite work, because you can never get just "A" for instance. Instead:
with t as (
select col
from table
union all
select NULL
from dual
)
select (t1.col + coalesce(t2.col, 0) + coalesce(t3.col, 0) +
coalesce(t4.col, 0) + coalesce(t5.col, 0)
) as sumcombo
from table t1 left join
t t2
on t1.col < t2.col or t2.col is null left join
t t3
on t2.col < t3.col or t3.col is null left join
t t4
on t3.col < t4.col or t4.col is null left join
t t5
on t4.col < t5.col or t5.col is null;

This can be solved by a hierarchical query. First of all, build a further child column col2 for connection:
-- your test data set
with testdata as
(select 'A' as col from dual
union
select 'B' from dual
union
select 'C' from dual
union
select 'D' from dual
union
select 'E' from dual),
-- create child column
testdata2 as
(select t.col as col1, t.col as col2 from testdata t)
select level, sys_connect_by_path(col1, '/') path
from testdata2 t
connect by prior col1 < col2
order by level, sys_connect_by_path(col1, '/');
result:
1 /A
1 /B
1 /C
1 /D
1 /E
2 /A/B
2 /A/C
2 /A/D
2 /A/E
2 /B/C
2 /B/D
2 /B/E
2 /C/D
2 /C/E
2 /D/E
3 /A/B/C
3 /A/B/D
3 /A/B/E
3 /A/C/D
3 /A/C/E
3 /A/D/E
3 /B/C/D
3 /B/C/E
3 /B/D/E
3 /C/D/E
4 /A/B/C/D
4 /A/B/C/E
4 /A/B/D/E
4 /A/C/D/E
4 /B/C/D/E
5 /A/B/C/D/E

Related

SQL QUERY FOR PAIRS

I have the following values in my table
A B
1 2
2 3
4 5
2 1
5 6
7 6
6 5
what a sql query in order to find the results which have a pair so this is the output is
1 2
2 1
5 6
6 5
Seems like there're already great solutions:
SELECT t.A, t.B FROM table AS t1 INNER JOIN table AS t2 ON t1.A = t2.B AND t1.B = t2.A
WITH CTE(A,B) AS
(
SELECT 1, 2 UNION ALL
SELECT 2, 3 UNION ALL
SELECT 4, 5 UNION ALL
SELECT 2 ,1 UNION ALL
SELECT 5, 6 UNION ALL
SELECT 7, 6 UNION ALL
SELECT 6 ,5
)
SELECT C.A,C.B
FROM CTE AS C
JOIN CTE AS C2 ON C.A=C2.B AND C.B=C2.A
This is another way to do it. With Self join
SELECT t1.*
FROM table1 t1
table1 t2 ON t1.a = t2.b AND t2.a = t1.b
Doing an Inner join will help you in this case:
SELECT t.col1, t.col2
FROM Tablename t
INNER JOIN Tablename t2
ON t.col1 = t2.col2 AND t.col2 = t2.col1
This will give you the required result:
Sample Output :
Innings
Totalplayer
2
1
1
2
6
5
5
6
View on DB Fiddle

Combining and checking table value on SQL (ORACLE)

Table 1
no name col1
1 a a_1
2 b b_1
Table 2
id name parent
a_1 zz c_1
b_1 yy d_1
c_1 aa null
d_1 bb e_1
e_1 dd1 null
what i want to show is showing the all list name. for example table 1 name a has col1 name a_1 it will show the name on table 2, and then check the parent in the table 2 and show it and keep checking until it found null. the example is like below.. im sorry for my bad explanation
t1_name t2_name t2_name t2_name
a zz aa
b yy bb dd1
or shows like below
t1_name t2_name
a aa/zz
b dd1/bb/yy
what I've done is this query
select t1.name,t2.name as folder from table1 as t1 inner join table2 as t2 on t1.col1=t2.id
and I don't know how to check again in query... I am using oracle version 12.2.0.1.0 in SQL developer any help?
You want to get the rows from the first table and then recursively fetch all the rows from the second table until you reach a null parent, so you do:
with cte(NAME,
PARENT,
CURRENTPATH) as
(select t1.NAME,
t2.PARENT,
t2.NAME as CURRENTPATH
from TABLE1 t1
join TABLE2 t2 on t1.COL1 = t2.ID
union all
select t1.NAME,
t2.PARENT,
t1.CURRENTPATH || '/' || t2.NAME as CURRENTPATH
from cte t1
join TABLE2 t2 on t2.ID = t1.PARENT)
select NAME,
CURRENTPATH
from cte
where PARENT is null;
You can use the hierarchical query as following:
SQL> -- Your data
SQL> with table1(no,name,col1) as
2 (SELECT 1, 'a','a_1' FROM DUAL UNION ALL
3 SELECT 2, 'b','b_1' FROM DUAL
4 ),
5 table2 (id, name, parent) as
6 (select 'a_1', 'zz', 'c_1' from dual union all
7 select 'b_1', 'yy', 'd_1' from dual union all
8 select 'c_1', 'aa', null from dual union all
9 select 'd_1', 'bb', 'e_1' from dual union all
10 select 'e_1', 'dd1', null from dual)
11 -- Your query starts from here
12 SELECT
13 T1.NAME AS T1_NAME,
14 T2.NAMES AS T2_NAMES
15 FROM TABLE1 T1
16 JOIN (
17 SELECT
18 T2.ID,
19 SYS_CONNECT_BY_PATH(T2.NAME, '/') AS NAMES,
20 ROW_NUMBER() OVER(PARTITION BY ID ORDER BY LEVEL DESC) AS L
21 FROM TABLE2 T2
22 CONNECT BY T2.PARENT = PRIOR T2.ID
23 ) T2 ON T1.COL1 = T2.ID
24 WHERE L = 1;
T1_NAME T2_NAMES
------- ---------------
a /aa/zz
b /dd1/bb/yy
SQL>
Cheers!!
Which Oracle version are you using?

Sql query to print values starting from column A till column B

New to SQL so looking for help
I'm trying to write a query which would print values starting from column A till the column B excluding the value present in column 'ANS' of second table.
Like here are the two tables X and Y
Table1
A FROM TO
a 6 9
b 3 6
c 0 3
d 2 3
Table2
A ANS
a 7
b 5
c 1
And I want the output as
A ANS
a 6
a 8
a 9
b 3
b 4
b 6
c 0
c 2
c 3
d 2
d 3
I've tried to write something like this but it doesn't work
WITH y(n) AS
(SELECT 1 AS n
FROM dual
UNION ALL
SELECT n + 1 AS n
FROM y, table1 T
WHERE n <= T.TO AND n>= T.FROM )
SELECT * FROM y;
Which prints 5000+ rows (that's why I am not attaching output)
Thanks in advance
After you get all the numbers between from and to with a recursive cte, left join on the generated table and get only those numbers which don't exist in table2 using not exists.
--Get the maximum value of `to` column and generate all numbers between 0 and that value
WITH maxto(maxt) as (SELECT MAX(TO) FROM TABLE1)
,y(n) AS
(SELECT 0 AS n FROM dual
UNION ALL
SELECT n + 1 AS n FROM y WHERE n < (SELECT maxt FROM maxto))
SELECT * FROM
(SELECT t1.a, y.n
FROM y
LEFT JOIN table1 t1 on y.n between t1.from and t1.to
WHERE t1.a IS NOT NULL) x
WHERE NOT EXISTS (SELECT 1 FROM table2 WHERE x.a = a and x.n = ans)
ORDER BY 1,2
Sample demo
WITH y(n) AS
(SELECT level - 1 FROM dual connect by level <= select max(TO- FROM) +2 from table1)
SELECT t1.a, t1.from + y.n FROM table1 t1
JOIN y on 1 = 1
left JOIN table2 on y.n + t1.FROM = t2.ANS and t2.a = t1.a
where y.n < t1.TO-t1.FROM
and t2.ANS is null;
You can use a "hierarchical query" and a MINUS operation and avoid joins altogether. MINUS is easy to understand if you are somewhat familiar with set theory. Generating numbers using hierarchical queries is somewhat unnatural (and may only be available in Oracle, I don't know any other db products), but it is used very often and it works very fast.
I changed the column names to from_n and to_n; I don't remember if "from" and/or "to" are reserved words in Oracle, but why take the risk.
with
table1 ( a, from_n, to_n ) as (
select 'a', 6, 9 from dual union all
select 'b', 3, 6 from dual union all
select 'c', 0, 3 from dual union all
select 'd', 2, 3 from dual
),
table2 ( a, ans ) as (
select 'a', 7 from dual union all
select 'b', 5 from dual union all
select 'c', 1 from dual
)
-- everything above this point is for testing only and can be removed
-- solution (SQL query) begins below
select a, from_n + level - 1 as ans
from table1
connect by level <= 1 + to_n - from_n
and prior a = a
and prior sys_guid() is not null
minus
select a, ans
from table2
;
Output:
A ANS
- ----------
a 6
a 8
a 9
b 3
b 4
b 6
c 0
c 2
c 3
d 2
d 3
11 rows selected

Oracle sql - display if record exists else display parent

I want to categorize data into two types based on:
If a value exists in T2, display "A", else display "B" as "Type". Is there a way to implement this in case when or decode?
T1 is the parent of T2.
T1
1
2
3
4
5
T2
1
1
3
3
3
4
Ideally my output would be
Type
A
B
A
A
B
edit: I want to add that A and B are text values I want to display based on my above condition, this is not coming from the db. Also, T2 will not have a corresponding record at all for 2 & 5. so I cannot really check for null.
Try this:
SELECT T1.Col,
CASE WHEN T2.Col IS NOT NULL THEN 'A' ELSE 'B' END AS Type
FROM T1
LEFT JOIN (
SELECT DISTINCT Col FROM T2
) AS T2 ON T1.Col = T2.Col
Oracle Setup:
CREATE TABLE T1 ( column_name ) AS
SELECT LEVEL FROM DUAL CONNECT BY LEVEL < 6;
CREATE TABLE t2 ( column_name ) AS
SELECT 1 FROM DUAL UNION ALL
SELECT 1 FROM DUAL UNION ALL
SELECT 3 FROM DUAL UNION ALL
SELECT 3 FROM DUAL UNION ALL
SELECT 4 FROM DUAL;
Query:
SELECT NVL2( T2.column_name, 'A', 'B' ) AS Type
FROM T1
LEFT OUTER JOIN
( SELECT DISTINCT column_name FROM T2 ) T2
ON T1.column_name = T2.column_name
ORDER BY T1.column_name;
Output:
TYPE
----
A
B
A
A
B

UNION ALL Sql query - how to bind three tables

I have the following tables:
T1
ID PRIORITY
1 1
2 1
3 2
4 4
T2
ID SERVICE
1 PSTN
1 ADSL
3 ADSL
T3
ID DEVICE
1 BSC1
3 BSC7
4 BSC7
I want as output
ID PRIORITY SERVICE/DEVICE
1 1 PSTN
1 1 ADSL
1 1 BSC1
2 1
3 2 ADSL
3 2 BSC7
How to bind those tables using UNION ALL? Also I must put WHERE clause for T1 WHERE PRIORITY!=4
Total number in output table for one id should be the summary of T2+T3 (FOR ID=1 2+1=3) but for ID=2 it also SHOULD exist in table output with blank second column.
Thank you
If you are okay using just a UNION and not UNION ALL this should give you what you want
SELECT t1.Id, t1.Priority, COALESCE(t2.Service, '') AS [Service/Device]
FROM t1
LEFT JOIN t2 ON t1.Id = t2.Id
WHERE t1.Priority != 4
UNION
SELECT t1.Id, t1.Priority, COALESCE(t3.Device, '') AS [Service/Device]
FROM t1
LEFT JOIN t3 ON t1.Id = t3.Id
WHERE t1.Priority != 4
SQL Fiddle example
select T1.id , T1.PRIORITY ,T2.SERVICE as service/Device from t1
left outer join T2 on T2.id=T1.id where T1.PRIORITY!=4
union all
select T1.id , T1.PRIORITY ,T3.DEVICE as service/Device from t1
left outer join T3 on T3.id=T1.id where PRIORITY!=4