Joining 2 tables for value comparision - oracle

I have 2 tables which contain data as follows:
Table1-
Col1. Col2
A1. B1
A1. B2
Table2-
Col1. Col2
A1. C1
A1. C2
Now when I am joining these 2 tables to compare col2, sometimes I get the output like A1-B1-C1;A1-B2-C2 and some time I get it as A1-B1-C2;A1-B2-C1.
But I don't want it to come in the second form ever.can anyone suggest any query which will help me achieve this.
Thanks in advance

You can do it with ROW_NUMBER() window function:
select t1.col1, t1.col2, t2.col2
from (
select t.*, row_number() over (partition by col1 order by col2) rn
from table1 t
) t1 inner join (
select t.*, row_number() over (partition by col1 order by col2) rn
from table2 t
) t2 on t2.col1 = t1.col1 and t2.rn = t1.rn
See the demo.
Results:
> COL1 | COL2 | COL2
> :--- | :--- | :---
> A1 | B1 | C1
> A1 | B2 | C2

The most obvious join is on col1 column which returns 4 rows:
SQL> with
2 t1 (col1, col2) as
3 (select 'A1', 'B1' from dual union all
4 select 'A1', 'B2' from dual
5 ),
6 t2 (col1, col2) as
7 (select 'A1', 'C1' from dual union all
8 select 'A1', 'C2' from dual
9 )
10 select a.col1, a.col2 a_col2, b.col2 b_col2
11 from t1 a join t2 b on a.col1 = b.col1
12 order by a.col1, a.col2, b.col2;
COL1 A_COL2 B_COL2
------- ------- -------
A1 B1 C1
A1 B1 C2
A1 B2 C1
A1 B2 C2
SQL>
These are all combinations you named, so - how do you "sometimes" get only two of them, and "sometimes" another two? What's wrong with another two results (as you said you don't want them to show at all)?

Yet another option is to use the RANK windows function as follows:
SELECT T1C1, T1C2, T2C2 FROM
(SELECT T1.COL1 T1C1, T1.COL2 T1C2, T2.COL1 T2C1, T2.COL2 T2C2,
RANK() OVER (PARTITION BY T1.COL1 ORDER BY T1.COL2) AS T1RN,
RANK() OVER (PARTITION BY T1.COL1 ORDER BY T2.COL2) AS T2RN
FROM TABLE1 T1 JOIN TABLE2 T2 ON T1.COL1 = T2.COL1)
WHERE T1RN = T2RN;

Related

split data in a single column into multiple columns in oracle

my_table :
Name
Value
item_1
AB
item_2
2
item_3
B1
item_1
CD
item_1
EF
item_2
3
item_3
B2
item_4
ZZ
required output:
item_1
item_2
item_3
item_4
AB
2
B1
ZZ
CD
3
B2
NULL
EF
NULL
NULL
NULL
SQL query :
with item_1 as (select value from my_table where name = 'item_1'),
item_2 as (select value from my_table where name = 'item_2'),
item_3 as (select value from my_table where name = 'item_3'),
item_4 as (select value from my_table where name = 'item_4')
select item_1.value, item_2.value,item_3.value, item_4.value from item_1 cross join item_2 cross join item_3 cross join item_4;
If I am using pivot along with MAX aggregate function, the query will display only max values of the corresponding items instead of displaying all the values.
Is there any way to split a single column into multiple columns(using where condition as mentioned in the above query) without cross join.
Use ROW_NUMBER and then PIVOT:
SELECT item_1,
item_2,
item_3,
item_4
FROM (
SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY name ORDER BY ROWNUM) AS rn
FROM table_name t
)
PIVOT (
MAX(value) FOR name IN (
'item_1' AS item_1,
'item_2' AS item_2,
'item_3' AS item_3,
'item_4' AS item_4
)
)
Which, for the sample data:
CREATE TABLE table_name (Name, Value) AS
SELECT 'item_1', 'AB' FROM DUAL UNION ALL
SELECT 'item_2', '2' FROM DUAL UNION ALL
SELECT 'item_3', 'B1' FROM DUAL UNION ALL
SELECT 'item_1', 'CD' FROM DUAL UNION ALL
SELECT 'item_1', 'EF' FROM DUAL UNION ALL
SELECT 'item_2', '3' FROM DUAL UNION ALL
SELECT 'item_3', 'B2' FROM DUAL UNION ALL
SELECT 'item_4', 'ZZ' FROM DUAL;
Outputs:
ITEM_1
ITEM_2
ITEM_3
ITEM_4
AB
2
B1
ZZ
CD
3
B2
null
EF
null
null
null
db<>fiddle here
How about this?
DF column is calculated by row_number analytic function which partitions by each name (and sorts by value). It is ignored from the final column list, but its role is crucial in the GROUP BY clause.
SQL> with test (name, value) as
2 (select 'item_1', 'AB' from dual union all
3 select 'item_2', '2' from dual union all
4 select 'item_3', 'B1' from dual union all
5 select 'item_1', 'CD' from dual union all
6 select 'item_1', 'EF' from dual union all
7 select 'item_2', '3' from dual union all
8 select 'item_3', 'B2' from dual union all
9 select 'item_4', 'ZZ' from dual
10 ),
11 temp as
12 (select name, value,
13 row_number() over (partition by name order by value) df
14 from test
15 )
16 select
17 max(case when name = 'item_1' then value end) item_1,
18 max(case when name = 'item_2' then value end) item_2,
19 max(case when name = 'item_3' then value end) item_3,
20 max(case when name = 'item_4' then value end) item_4
21 from temp
22 group by df;
ITEM_1 ITEM_2 ITEM_3 ITEM_4
------ ------ ------ ------
AB 2 B1 ZZ
CD 3 B2
EF
SQL>

row number based on unique column value in oralce?

I do need to implement a oracle sql to have row number defined as below:
row_num, column1, column2, cloumn3
1, ABC, 123, a1
1, ABC, 125, a2
2, ABD, 123, a3
2, ABD, 124, a4
2, ABD, 125, a5
3, ABE, 123, a1
Here I defined row number based on unique value of column1.
Can any one help me to write oracle sql to define row number in this way?
Thanks
Venkat
You need the DENSE_RANK() analytic function:
WITH your_table AS (SELECT 'ABC' col1, 123 col2, 'a1' col3 FROM dual UNION ALL
SELECT 'ABC' col1, 125 col2, 'a2' col3 FROM dual UNION ALL
SELECT 'ABD' col1, 123 col2, 'a3' col3 FROM dual UNION ALL
SELECT 'ABD' col1, 124 col2, 'a4' col3 FROM dual UNION ALL
SELECT 'ABD' col1, 125 col2, 'a5' col3 FROM dual UNION ALL
SELECT 'ABE' col1, 123 col2, 'a1' col3 FROM dual)
-- end of subquery mimicking your_table with data in; See SQL below:
SELECT dense_rank() OVER (ORDER BY col1) row_num,
col1,
col2,
col3
FROM your_table
ORDER BY col1, col2, col3;
ROW_NUM COL1 COL2 COL3
---------- ---- ---------- ----
1 ABC 123 a1
1 ABC 125 a2
2 ABD 123 a3
2 ABD 124 a4
2 ABD 125 a5
3 ABE 123 a1
DENSE_RANK() is similar to RANK() in that it will assign tied rows the same rank (that is, rows that have the same values in the columns being ordered - in your case, col1), but unlike RANK(), DENSE_RANK() won't skip rank numbers.
You can use this as well, but I forgot about dense rank. So Boneist's answer is better.
select rn.row_num , t.* from
(select column1,row_number() over (order by column1) as row_num from
(select distinct column1 column1 from tbl23) )rn
inner join tbl23 t
on rn.column1=t.column1

joining based on columns priority

I want to join 2 tables based on columns priority
ex. Suppose Table1 has six columns(Col1,Col2,Col3,Col4,Col5,Col6)
If i want to join Table 1 with table2 (Col1,Col2,Col3,Col4,Col5,Col7), it should
otherwise
Select Table2.col7
where
first check col1 , col2 and col3 if match found no need go check more
second check col1 , col2 if match found no need go check more
third check col1 if match found no need go check more
last ignore all col1 , col2 and col3
AND Table1.Col4=Table2.Col4
AND Table1.Col5=Table2.Col5
I may not be clear with my words, if any concern please shout
You cannot tell SQL to try to join on a certain condition first and in case it finds no match to go on searching. What you can do is join all allowed combinations (matches on col4 and col5 in your case) and then rank your matches (such that a match on col1 and col2 and col3 is considered best etc.). Then only keep the best matches:
select col7
from
(
select
t1.*,
t2.*,
row_number() over
(
partition by t1.col4, t1.col5
order by case
when t2.col1 = t1.col1 and t2.col2 = t1.col2 and t2.col3 = t1.col3 then 1
when t2.col1 = t1.col1 and t2.col2 = t1.col2 then 2
when t2.col1 = t1.col1 then 3
else 4
) as rn
from table1 t1
join table2 t2 on t2.col4 = t1.col4 and t2.col5 = t1.col5
)
where rn = 1;
Select t2.col7
from Table1 t1 inner join Table2 t2
on
case
when t1.col1 = t2.col1 then 1
when t1.col2 = t2.col2 then 1
when t1.col3 = t2.col3 then 1
when t1.Col4=t2.Col4
and t1.Col5=t2.Col5 then 1
else 0 end = 1
;

SQL query to get grouped sums and concatenated lists, preferably without using functions

I have a table like
colA colB colC
A 10 1
A 20 2
A 30 3
B 10 1
B 20 2
I want an output like this
ColA colB colC
A 60 1,2,3
B 30 1,2
Can someone tell me how to do it with and without using functions, and in PL/SQL?
Here is sql you needed.
Note : it will only run on oracle 11g R2 onwards.
with tab as(
select 'A' col1,10 col2,1 col3 from dual union all
select 'A' col1,20 col2,2 col3 from dual union all
select 'A' col1,30 col2,3 col3 from dual union all
select 'B' col1,10 col2,1 col3 from dual union all
select 'B' col1,20 col2,2 col3 from dual )
select col1, sum(col2),listagg(col3,',') WITHIN GROUP (ORDER BY col3) AS col3_list
from tab group by col1
Assume your table name is tab
with tab1(col1, col2) as (
select colA, sum(colB)
from tab group by colA
),
tab2(col1, col2) as (
select colA, LISTAGG(TO_CHAR(colC),',')
WITHIN GROUP (ORDER BY colC)
from tab group by colA
)
select a.col1, a.col2, b.col2
from tab1 a, tab2 b
where a.col1 = b.col1
OutPut:
COL1 COL2 COL2
A 60 1,2,3
B 30 1,2
For more information about LISTAGG link
This link explains about multiple with clause
Or simply;
select colA, sum(colB) colB,LISTAGG(TO_CHAR(colC),',')
WITHIN GROUP (ORDER BY colC)
from tab group by colA
Probably this may the easiest solution syntactically :)
SELECT COLA,
SUM(COLB),
WMSYS.WM_CONCAT(COLC)
FROM
(SELECT 'A' cola,10 AS colb,1 AS colc FROM dual
UNION
SELECT 'A' cola,20 AS colb,2 AS colc FROM dual
UNION
SELECT 'A' cola,30 AS colb,3 AS colc FROM dual
UNION
SELECT 'B' cola,10 AS colb,1 AS colc FROM dual
UNION
SELECT 'B' cola,20 AS colb,2 AS colc FROM dual
)
GROUP BY COLA ;
OUTPUT
COLA SUM(COLB) WMSYS.WM_CONCAT(COLC)
A 60 1,2,3
B 30 1,2

SQL Server 2012: Update table with inner join after sorted

I am using SQL Server 2012. I have a table called table1 like below:
Id col1 col2 col3 Name
1 a b abc null
2 b c mno null
And I have another table table2, like below:
Id col1 col2 col3 Name
1 % % abc Name1
2 a % abc Name2
3 % b abc Name3
4 a b abc Name4
I have to update Name column in Table1 From Name column in Table2 based on columns: col1, col2 and col3.
The Id = 1 in the table1 finds all 4 matches in the table because I am using like operator in col1 and col2 to compare(why I am using like is if it didn't find exact match it should accept % as a match).
Now my problem is if exact match is there for the columns col1, col2 and col3 in the table, it should consider that only not the rows with '%' value. For example, for the Id=1 in the table1, the result should be from id=4 in the table2.
I tried with following query:
UPDATE table1
SET name = t2.Name
FROM (SELECT TOP 1
t1.id, t2.name
FROM table1 t1
INNER JOIN table2 t2
ON t1.col3 = t2.col3 AND t1.col1 LIKE t2.col1
AND t1.col2 LIKE t2.col2
ORDER BY t2.col1, t2.col2) AS t3
WHERE id = t3.id;
But I am not getting result which I expected. And also, there are 8,000,000 records are there in table1 so it should not affect performance.
Please help to fix this issue.
At a first try, I suggest this:
UPDATE table1
SET name = t2.Name
FROM (SELECT TOP(1) *
FROM (SELECT
t1.id, t2.name, t2.col1, t2.col2, 2 As ord
FROM table1 t1
INNER JOIN table2 t2
ON t1.col3 = t2.col3 AND t1.col1 LIKE t2.col1
AND t1.col2 LIKE t2.col2
UNION ALL
SELECT
t1.id, t2.name, t2.col1, t2.col2, 1 As ord
FROM table1 t1
INNER JOIN table2 t2
ON t1.col3 = t2.col3 AND t1.col1 = t2.col1
AND t1.col2 = t2.col2
) DT
ORDER BY ord, col1, col2) AS t3
WHERE id = t3.id;

Resources