PLSQL - Compare two comma separated and remove duplicate - sql

I have comma separated values in two column and i wants to remove "common" values available in both column. Below is the sample data
col1 col2
1234, 5623, 1236,1567 5623, 9089,1567,2890,1234
145,126,1236,1478 1748,8956,1234,1478
Required Data
COL1 COL2
1236 9089,2890
145,126,1236 1748,8956,1234
Thanks

I've slightly modified your table and added the ID column which uniquely identifies a row, so it looks like this:
SQL> select * from test;
ID COL1 COL2
---------- ------------------------------ ------------------------------
1 1234, 5623, 1236,1567 5623, 9089,1567,2890,1234
2 145,126,1236,1478 1748,8956,1234,1478
This approach
splits all columns into rows (t_one is for col1, t_two is for col2)
inter CTE uses the INTERSECT set operator in order to find common values which should be removed from the result
t1_new and t2_new aggregate new column values (using the LISTAGG function)
the final SELECT returns the final result
Here it goes:
SQL> with
2 t_one as
3 (select id,
4 trim(regexp_substr(col1, '[^,]+', 1, column_value)) c1
5 from test,
6 table(cast(multiset(select level from dual
7 connect by level <= regexp_count(col1, ',') + 1
8 ) as sys.odcinumberlist))
9 ),
10 t_two as
11 (select id,
12 trim(regexp_substr(col2, '[^,]+', 1, column_value)) c2
13 from test,
14 table(cast(multiset(select level from dual
15 connect by level <= regexp_count(col2, ',') + 1
16 ) as sys.odcinumberlist))
17 ),
18 inter as
19 (select t1.id, t1.c1 cx from t_one t1
20 intersect
21 select t2.id, t2.c2 cx from t_two t2
22 ),
23 t1_new as
24 (select a.id, listagg(a.c1, ',') within group (order by a.c1) c1_new
25 from (select t1.id, t1.c1 from t_one t1
26 minus
27 select i.id, i.cx from inter i
28 ) a
29 group by a.id
30 ),
31 t2_new as
32 (select b.id, listagg(b.c2, ',') within group (order by b.c2) c2_new
33 from (select t2.id, t2.c2 from t_two t2
34 minus
35 select i.id, i.cx from inter i
36 ) b
37 group by b.id
38 )
39 select a.id, a.c1_new, b.c2_new
40 from t1_new a join t2_new b on a.id = b.id;
ID C1_NEW C2_NEW
---------- -------------------- --------------------
1 1236 2890,9089
2 1236,126,145 1234,1748,8956
SQL>

Related

Oracle - How use string data in (in operator)

In tbl_1 I have:
id
1
2
3
4
5
6
7
8
9
in tbl_2:
id value
1 1,2,3
2 5
Select * from tbl_1 where id in (Select value from tbl_2 where id = 2); --is OK
Select * from tbl_1 where id in (Select value from tbl_2 where id = 1);
--Need this resault: 3 rows: 1, 2 and 3
Fix your data model! You should not be storing numbers as strings. You should have properly declared foreign key relationships. Strings should not be used to store multiple values.
Sometimes, we are stuck with other people's really, really, really bad decisions. You can do what you want with `like:
Select t1.*
from tbl_1 t1
where exists (select 1
from tbl_2 t2
where t2.id = 1 and
',' || t2.value || ',' like '%,' || t1.id ',%'
);
However, your effort should be going into fixing the data, rather than trying to deal with it. The correct data would be a junction/association table with one row per id and value for table 2:
id value
1 1
1 2
1 3
2 5
Yet another option:
SQL> with
2 -- sample data
3 tbl_1 (id) as
4 (select 1 from dual union all
5 select 2 from dual union all
6 select 3 from dual union all
7 select 4 from dual union all
8 select 5 from dual union all
9 select 6 from dual union all
10 select 7 from dual union all
11 select 8 from dual union all
12 select 9 from dual
13 ),
14 tbl_2 (id, value) as
15 (select 1, '1,2,3' from dual union all
16 select 2, '5,6,7' from dual
17 )
18 -- query which returns what you want
19 select a.id
20 from tbl_1 a join
21 (select regexp_substr(b.value, '[^,]+', 1, column_value) id
22 from tbl_2 b cross join
23 table(cast(multiset(select level from dual
24 connect by level <= regexp_count(b.value, ',') + 1
25 ) as sys.odcinumberlist))
26 where b.id = 1
27 ) c on c.id = a.id;
ID
----------
1
2
3
SQL>
One option uses string functions:
select t1.*
from t1
inner join t2 on ',' || t2.value || ',' like '%,' || t1.id || ',%'
where t2.id = 1

SQL interview questions

Table-1
Col1 col2
11 A
26 B
31 C
43 D
Table-2
Col1 col2
16 E
46 F
39 G
42 H
And need output is
Col1 col2 Col1 col2
11 A 16 E
26 B 46 F
31 C 39 G
43 D 42 H
Here's one option:
SQL> with
2 -- sample data
3 a (col1, col2) as
4 (select 11, 'A' from dual union all
5 select 26, 'B' from dual union all
6 select 31, 'C' from dual union all
7 select 43, 'D' from dual
8 ),
9 b (col1, col2) as
10 (select 16, 'E' from dual union all
11 select 46, 'F' from dual union all
12 select 39, 'G' from dual union all
13 select 42, 'H' from dual
14 ),
15 -- find something to join rows on - for example, row number. Your example shows that
16 -- values are sorted by COL2
17 a2 as
18 (select col1, col2, row_number() over (order by col2) rn from a),
19 b2 as
20 (select col1, col2, row_number() over (order by col2) rn from b)
21 -- join a2 and b2 on RN
22 select a2.col1, a2.col2, b2.col1, b2.col2
23 from a2 join b2 on a2.rn = b2.rn
24 order by a2.col1;
COL1 COL2 COL1 COL2
----- ---- ----- ----
11 A 16 E
26 B 46 F
31 C 39 G
43 D 42 H
SQL>
select t1.col1, t1.col2, t2.col1, t2.col2 from table1 as t1
left join table2 as t2 on 1=1
Try this
select * from table1 t1 join table2 t2 on ascii(t1.col2)+4 = ascii(t2.col2);
Note: this only works with the specific input provided, but why not? It just goes to show that providing minimal input and expected output is not sufficient. You have to explain what rules the input should follow and what rules the processing should follow.
Littlefoot answers a different question than I do, but even he silently makes some assumptions. For example, he decided not to show any output if there are unmatched rows on one side or the other.
The correct real-world answer to this interview question would be to list the questions one would have to ask before being able to supply an appropriate answer.

Procedure for adding one column from one table to another table and its data

I have two tables table1 and table2.
Table 1 has three columns
id name age
----------------
1 ram 27
2 rafi 30
Table 2-
no place
--------------
101 agra
102 delhi
103 chennai
104 hyd
In this situation I want to create a procedure to get the no column of table2 will be added to id column of table1 and the remaining data should be copied same and and if the count of table2 is more then the data should be repeated as shown below
id name age
-----------------
1 ram 27
2 rafi 30
101 ram 27
102 rafi 30
103 ram 27
104 rafi 30
Please help
Assuming table1.id1 is a monotonically increasing series starting at 1 this simple trick will work:
insert into table1
select sq2.no#
, t1.name
, t1.age
from table1 t1
inner join (
select t2.no#
, mod(rownum, sq.cnt)+1 as mod#
from table2 t2
cross join (select count(*) as cnt from table1) sq
) sq2 on sq2.mod# = t1.id
/
There is a demo on db<>fiddle.
If table1.id1 contains gaps, or does not start from 1, you will need to replace table1 in the FROM clause above with another subquery ...
(select t1.*
, row_number() over (order by t1.id) as mod#
from table1 t1 ) sq1
... and inner join that to the existing subquery on mod#.
You can achieve it using row_number analytical function with join using MOD function as following:
SQL> WITH TABLE_1(ID, NAME, AGE)
2 AS (SELECT 1, 'ram', 27 FROM DUAL UNION ALL
3 SELECT 2, 'rafi', 30 FROM DUAL),
4 TABLE_2(NO, PLACE)
5 AS (SELECT 101, 'agra' FROM DUAL UNION ALL
6 SELECT 102, 'delhi' FROM DUAL UNION ALL
7 SELECT 103, 'chennai' FROM DUAL UNION ALL
8 SELECT 104, 'hyd' FROM DUAL)
9 -- YOUR QUERY STARTS FROM HERE
10 SELECT ID, NAME, AGE FROM TABLE_1
11 UNION ALL
12 SELECT T2.NO, T1.NAME, T1.AGE
13 FROM
14 (SELECT T.*,
15 ROW_NUMBER() OVER( ORDER BY ID DESC) AS R,
16 COUNT(1) OVER() C
17 FROM TABLE_1 T ) T1
18 --
19 JOIN (SELECT T.*,
20 ROW_NUMBER() OVER( ORDER BY NO ) AS R,
21 COUNT(1) OVER() C
22 FROM TABLE_2 T ) T2
23 ON ( MOD(T2.R, T1.C) = T1.R - 1 )
24 ORDER BY ID;
ID NAME AGE
---------- ---- ----------
1 ram 27
2 rafi 30
101 ram 27
102 rafi 30
103 ram 27
104 rafi 30
6 rows selected.
SQL>
Cheers!!

Sql Query for Unique and Duplicates in oracle sql?

I need to display unique records in one column and duplicates in another column in Oracle?
COL1 COL2
1 10
1 10
2 20
3 30
3 30
unique in one set duplicate in one set
col1 col2 col1 col2
2 20 1 10
1 10
3 30
3 30
You can use the group by for both cases with the having clause:
Unique records
select *
from table as t
inner join (
select col1, col2, count(*) as times
from table
group by col1, col2
having count(*) = 1) as t2 ON t.col1 = t2.col2 and t.col2 = t2.col2
Duplicate records:
select *
from table as t
inner join (
select col1, col2, count(*) as times
from table
group by col1, col2
having count(*) > 1) as t2 ON t.col1 = t2.col1 and t.col2 = t2.col2
Would something like this do? See comments within code.
SQL> with
2 test (col1, col2) as
3 -- sample data
4 (select 1, 10 from dual union all
5 select 1, 10 from dual union all
6 select 2, 20 from dual union all
7 select 3, 30 from dual union all
8 select 3, 30 from dual
9 ),
10 uni as
11 -- unique values
12 (select col1, col2
13 from test
14 group by col1, col2
15 having count(*) = 1
16 ),
17 dup as
18 -- duplicate values
19 (select col1, col2
20 from test
21 group by col1, col2
22 having count(*) > 1
23 )
24 -- the final result
25 select u.col1 ucol1,
26 u.col2 ucol2,
27 d.col1 dcol1,
28 d.col2 dcol2
29 from uni u full outer join dup d on u.col1 = d.col1;
UCOL1 UCOL2 DCOL1 DCOL2
---------- ---------- ---------- ----------
1 10
3 30
2 20
SQL>
You can identify the duplicate values using window functions, and then filter each query. Then to get unique records:
select col1, col2
from (select t.*, count(*) over (partition by col1) as cnt
from t
) t
where cnt = 1;
To get duplicates:
select col1, col2
from (select t.*, count(*) over (partition by col1) as cnt
from t
) t
where cnt > 1;

Join two tables with a column with multiple entries for the other table

I have the following problem.
I want to join two tables.
The first table has entries like the following:
T1
PK Info
1 one
2 two
3 three
The second table is build like this:
T2
PK FKT1
1 1,3
2 1,2,3
3 2
My Result should show the following
PK2 FKT1 InfoT1
1 1,3 One,Three
2 1,2,3 One,two,Three
3 2 Two
I just cant get an idea how to solve this.
Is this possible only using sql selects or is a function needed?
kind regards
It's not that difficult, but - as you were told, you'd rather NOT do that.
SQL> with
2 t1 (pk, info) as
3 (select 1, 'one' from dual union
4 select 2, 'two' from dual union
5 select 3, 'three' from dual
6 ),
7 t2 (pk, fkt1) as
8 (select 1, '1,3' from dual union
9 select 2, '1,2,3' from dual union
10 select 3, '2' from dual
11 ),
12 t2rows as
13 (select pk, regexp_substr(fkt1, '[^,]+', 1, column_value) fkt1, column_value rn
14 from t2,
15 table(cast(multiset(select level from dual
16 connect by level <= regexp_count(fkt1, ',') + 1
17 ) as sys.odcinumberlist))
18 )
19 select t2r.pk,
20 listagg(t2r.fkt1, ',') within group (order by t2r.rn) fkt1,
21 listagg(t1.info, ',') within group (order by t2r.rn) infot1
22 from t2rows t2r join t1 on t2r.fkt1 = t1.pk
23 group by t2r.pk
24 order by t2r.pk;
PK FKT1 INFOT1
---------- -------------------- --------------------
1 1,3 one,three
2 1,2,3 one,two,three
3 2 two
SQL>