Update one table from another table with duplicate keys - sql

I am trying to merge data from one table into another.
Table 1 (Tab1)
ID col2 col3 col_to_update
1 s1 a1 null
2 s1 a2 null
3 s1 a2 null
4 s2 a1 null
5 s3 a1 null
6 s4 a1 null
Table 2 (Tab2)
ID col2 col3 col4
10 s1 a1 v1
11 s1 a1 v2
12 s1 a2 v3
13 s2 a1 v4
14 s3 a1 v5
15 s4 a1 v6
16 s4 a1 v7
I am trying to map column col4 from table Tab2 into column col_to_update in table Tab1 based on matching Tab1.col2 = Tab2.col2 and Tabl.col3 = Tab2.col3 to get below expected output:
Expected Output
ID col2 col3 col4
1 s1 a1 v1
2 s1 a2 v3
3 s1 a2 v3
4 s2 a1 v4
5 s3 a1 v5
6 s4 a1 v6
I tried unsuccessfully with below query:
MERGE INTO Tab1 x1
USING
(
SELECT t1.id as t1id, t2.id as t2id, t2.col2 t2col2, t2.col3 t2col3, t2.col4 from Tab2 t2
JOIN Tab1 t1 ON t2.col2 = t1.col2 AND t2.col3 = t1.col3
) x2
ON (x1.id = x2.t1id)
WHEN MATCHED THEN UPDATE SET
x1.col_to_update = x2.col4;
Is there a way to get the expected output.

You simply want to update tab1:
update tab1
set col_to_update =
(
select min(tab2.col4) -- or whichever value you want to use
from tab2
where tab2.col2 = tab1.col2
and tab2.col3 = tab1.col3
);

Related

select a value from different table based on conditions in sql

I'm trying to select a value from a different table based on current table values and condition
Table 1:
C1
C2
C3
1
2
3
1
4
5
1
6
6
2
3
3
Table 2:
D1
D2
D3
D4
1
2
3
Value1
1
4
5
Value2
1
6
8
Value3
2
3
4
Value4
2
Value5
And Im trying to get the below expected result table a single line sql
Results:
C1
C2
C3
D4
1
2
3
Value1
1
4
5
Value2
1
6
6
2
3
3
Value5
The condition is to pick D4 value only
(C1=D1 and C2=D2 and C3=D3) matches then D4 or when C1=D1 matches then D4 else null for all
I tried inner join and also case statement but no success
Here is the fiddle i had created
This looks like a left join:
select t1.*, t2.d4
from table1 t1 left join
table2 t2
on t1.C1 = t2.D1 and
(t1.C2 = t2.D2 or t2.D2 is null) and
(t1.C3 = t2.D3 or t2.D3 is null);
Note: It is a little hard to tell from the explanation if you need for both D2 and D3 to be NULL. If so:
select t1.*, t2.d4
from table1 t1 left join
table2 t2
on t1.C1 = t2.D1 and
(t1.C2 = t2.D2 and t1.C3 = t2.D3 or
t2.D2 is null and t2.D3 is null
);
Here is the fiddle.

Getting exception while executing the query to compare two values

While executing a query to compare the rates of two columns and display if one is grater than other I am getting ORA-01427 exception
Here is the data set. This is mocked up data . Data size is huge in databse
Table1
col1 col2 col3 col4 col5 col6
c1 c1test 85 85 I 5
c2 c2test 85 85 I 3
c3 c3test 85 85 E 6
c4 c4test G1 G1 E 7
c5 c5test G1 G1 E 5
c6 c6test G1 G1 E 8
c7 c7test G1 G1 I 3
c8 c8test G1 G1 G 7
Table2
col1 col2 col3 col4 col5
85 85 D I 3
85 85 D E 5
G1 G1 D E 5
G1 G1 D I 3
G1 G1 D G 5
G1 G1 E I 2
G1 G1 E E 2
85 85 E I 3
Expected result
We need to compare the value of col5 of table2 with the col6 of table1 to find out the values greater in col6 of table1 and display the records. Comparison needs to be done only for col3 equals D values.
col1 col2 col3 col4 col5 col6
c1 c1test 81 81 I 5
c3 c3test 81 81 E 6
c4 c4test G1 G1 E 7
c6 c6test G1 G1 E 8
c8 c8test G1 G1 G 7
I am using the below query
Select * from table1 where
col6 > (select col5 from
table2 where col3='D'
and col1=table1.col3
and col2=table1.col4
and col4=table1.col5
This throws an ora-01427 exception. Can you pls hel to get the expected output.
If you are looking for value grater than any value of table2.col5, try this
SELECT t1.*
FROM table1 t1
WHERE t1.col6 > ANY (SELECT t2.col5
FROM table2 t2
WHERE t2.col3 = 'D'
AND t2.col1 = t1.col3
AND t2.col2 = t1.col4
AND t2.col4 = t1.col5
);
If you need table1.col6 should be greater than all values found in table2.col5 use ALL instead of ANY
Your subquery is returning multiple rows. You can use min() or max() to get around this. I'm not sure which logic you really want:
Select t1.*
from table1 t1
where t1.col6 > (select max(t2.col5)
from table2 t2
where t2.col3 = 'D' and
t2.col1 = t1..col3 and
t2.col2 = t1.col4 and
t2.col4 = t1.col5
);

compare rows between two ids with in the same table and insert the missing row for that id

i have a table which has structure mentioned below
let's say table name -> tab1
id c1 c2 c3 c4
1 a b 01-02-18 c row1
1 o b 01-02-18 c row2
1 a b 04-05-16 c row3
1 n g 01-02-18 d row4
2 a b 01-02-18 c row5
So i want to insert id 1 rows to id 2. As data for row1 and row5 is same for column c1,c2,c3,c4 so i want to skip row1 to be inserted for id 2 .
Table should look like this
id c1 c2 c3 c4
1 a b 01-02-18 c row1
1 o b 01-02-18 c row2
1 a b 04-05-16 c row3
1 n g 01-02-18 d row4
2 a b 01-02-18 c row5
2 o b 01-02-18 c row6
2 a b 04-05-16 c row7
2 n g 01-02-18 d row8
i have written this query but doesn't give me the expected result
for selecting the unique record based on column :
select Count(*) FROM tab1 A
WHERE Not EXISTS
(select * from tab1 B where A.c1 = B.c1 AND A.c2 = B.c2 AND A.c3= B.c3
AND A.c4 = B.c4
and B.id=2 )and A.id = 1;
for inserting the records
insert into rsk_mdl_sec_map_ts
select '2', c1, c2, c3, c4
FROM tab1 A
WHERE Not EXISTS
(select * from tab1 B where A.c1 = B.c1 AND A.c2 = B.c2 AND A.c3= B.c3
AND A.c4 = B.c4
and B.id=2 )and A.id = 1;
can anyone help what is wrong in this or suggest me some other approach to achieve the same . Thanks
First build a query that pick rows which should be insered and omits those that already exist as id=2 :
SELECT *
FROM tab1 t1
WHERE id = 1
AND NOT EXISTS (
SELECT 'anything' FROM tab1 t2
WHERE t1.c1=t2.c1
AND t1.c2=t2.c2
AND t1.c3=t2.c3
AND t1.c4=t2.c4
AND id = 2
)
| ID | C1 | C2 | C3 | C4 |
|----|----|----|-----------------------|----|
| 1 | o | b | 2018-01-02 00:00:00.0 | c |
| 1 | a | b | 2016-04-05 00:00:00.0 | c |
| 1 | n | g | 2018-01-02 00:00:00.0 | d |
Demo: http://sqlfiddle.com/#!4/d66fc/4
Next, use INSERT ... SELECT .... command, just put INSERT above the SELECT command, and use 2 constant as ID, and DISTINCT clause to remove possible duplicates:
INSERT into tab1( id, c1, c2, c3, c4 )
SELECT DISTINCT 2, c1, c2, c3, c4
FROM tab1 t1
WHERE id = 1
AND NOT EXISTS (
SELECT 'anything' FROM tab1 t2
WHERE t1.c1=t2.c1
AND t1.c2=t2.c2
AND t1.c3=t2.c3
AND t1.c4=t2.c4
AND id = 2
)
Using your test data, we can do the following cross join - which will give us each ID combined with all rows that have id 1:
select
T2.id
, T1.c1
, T1.c2
, T1.c3
, T1.c4
from (
select *
from tab1
where id = 1
) T1, ( select unique id from tab1 ) T2
;
-- result
ID C1 C2 C3 C4
1 a b 01-FEB-18 c
1 o b 01-FEB-18 c
1 a b 04-MAY-18 c
1 n g 01-FEB-18 d
2 a b 01-FEB-18 c
2 o b 01-FEB-18 c
2 a b 04-MAY-18 c
2 n g 01-FEB-18 d
Then, use the MINUS operator and insert all resulting rows.
insert into tab1 ( id, c1, c2, c3, c4 )
(
select
T2.id
, T1.c1
, T1.c2
, T1.c3
, T1.c4
from (
select *
from tab1
where id = 1
) T1, ( select unique id from tab1 ) T2
)
minus -- do not insert existing rows
select * from tab1
;
3 rows inserted.
Tab1 now contains:
select * from tab1 ;
ID C1 C2 C3 C4
1 a b 01-FEB-18 c
1 o b 01-FEB-18 c
1 a b 04-MAY-18 c
1 n g 01-FEB-18 d
2 a b 01-FEB-18 c
2 a b 04-MAY-18 c
2 n g 01-FEB-18 d
2 o b 01-FEB-18 c
See dbfiddle for more details.
The simplest solution is to use MERGE:
merge into tab1
using ( select 2 as id, c1, c2, c3, c4
from tab1
where id = 1 -- optional
) q
on (q.id = tab1.id
and q.c1 = tab1.c1
and q.c2 = tab1.c2
and q.c3 = tab1.c3
and q.c4 = tab1.c4)
when not matched then
insert values (q.id, q.c1, q.c2, q.c3, q.c4)
;
This solution will work provided tab1(id, c1, c2, c3, c4) defines a set of unique rows.
Here is a demo on SQL Fiddle.
simply create a temporary table TMP_TABLE to do full join
CREATE TABLE TMP_TABLE AS SELECT *
FROM
(SELECT DISTINCT id FROM tab1 ) a,
(SELECT DISTINCT c1, c2, c3 FROM tab1 ) b

Filter data based on result set of group and count

I have the following table
Col1 Col2 Col3
A1 B1 C1
A1 B1 C2
A1 B2 C1
A1 B2 C2
A1 B2 C3
A2 B1 C1
A2 B1 C2
A2 B2 C1
A2 B2 C2
From this table I want all the unique records from Col1 where for the combination of col1 and col2 there's a different count for the same value in Col1. The only possible answer is A1 in the table above.
The following query gives me the count of each col1 and col2.
select col1, col2, count(*) from table
group by col1, col2;
Col1 Col2 Count
A1 B1 2
A1 B2 3
A2 B1 2
A2 B2 2
From the above query I can see that A1 has two records with a different count. How do I return A1 in a single query?
You can use another level of aggregation:
select col1
from (select col1, col2, count(*) as cnt
from table
group by col1, col2
) t
group by col1
having min(cnt) <> max(cnt);

SQL Query - Indirect joining of two tables

I have two tables like the following
Table1
COL1 COL2 COL3
A 10 ABC
A 11 ABC
A 1 DEF
A 2 DEF
B 10 ABC
B 11 ABC
B 1 DEF
C 3 DEF
C 12 ABC
C 21 GHI
Table2
COL1 GHI ABC DEF
A1 21 10 1
A2 21 12 1
A3 21 10 1
A4 23 10 1
A5 25 11 3
A6 21 14 3
A7 25 11 1
A8 23 10 1
A9 29 10 2
A10 21 12 3
I have created another temporary table that returns all the distinct values from tbl1.col1
The values of col3 in tbl1 are columns in tbl2, which are populated by some values.
What I need is for each of these distinct values of table1.column1, (A, B, C) in this case, return a combination of table2.column1 and table1.column1 such that
the ABC value of table2.column1 matches any of the ABC value of the "group" from table1,
AND the DEF value of table2.column1 matches any of the DEF value of the "group" from table1,
AND IF THE GROUP CONTAINS GHI VALUES, the GHI value of table2.column1 matches any of the GHI value of the "group" from table1
So, I would need something like the following
Output Table
Table2.COL1 Table1.Col1
A1 A
A3 A
A4 A
A7 A
A8 A
A9 A
A1 B
A3 B
A4 B
A7 B
A8 B
A10 C
I tried something like this, but Im not sure if this is the right way of approaching
select table2.col1, temp_distinct_table.column1
from table2, temp_distinct_table
where table2.def IN (SELECT col2
FROM table1
WHERE table1.col1 = temp_distinct_table.col1
AND table1.col3 = 'DEF')
AND table2.abc IN (SELECT col2
FROM table1
WHERE table1.col1 = temp_distinct_table.col1
AND table1.col3 = 'ABC')
AND (
table2.ghi IN (SELECT col2
FROM table1
WHERE table1.col1 = temp_distinct_table.col1
AND table1.col3 = 'GHI')
OR NOT EXISTS (SELECT col2
FROM table1
WHERE table1.col1 = temp_distinct_table.col1
AND table1.col3 = 'GHI')
)
where temp_distinct_table contains of all the distinct values from table1.col1
Could someone guide me on the matter?
Another approach, counting how many matches there are for each t1.col/t2.col combination after joining all the possible matches:
select distinct t2_col1, t1_col1
from (
select t2.col1 as t2_col1, t1.col1 as t1_col1, t1.ghi_count as t1_ghi_count,
count(case when t1.col3 = 'ABC' then 1 end)
over (partition by t1.col1, t2.col1) as abc_matches,
count(case when t1.col3 = 'DEF' then 1 end)
over (partition by t1.col1, t2.col1) as def_matches,
count(case when t1.col3 = 'GHI' then 1 end)
over (partition by t1.col1, t2.col1) as ghi_matches
from (
select t1.*,
count(case when t1.col3 = 'GHI' then 1 end)
over (partition by t1.col1) as ghi_count
from table1 t1
) t1
join table2 t2
on (t1.col3 = 'ABC' and t2.abc = t1.col2)
or (t1.col3 = 'DEF' and t2.def = t1.col2)
or (t1.col3 = 'GHI' and t2.ghi = t1.col2)
)
where abc_matches > 0
and def_matches > 0
and (t1_ghi_count = 0 or ghi_matches > 0)
order by t1_col1, t2_col1;
Which with your sample data gets:
T2_COL T1_COL
------ ------
A1 A
A3 A
A4 A
A7 A
A8 A
A9 A
A1 B
A3 B
A4 B
A7 B
A8 B
A10 C
Not sure if the efficiency of that will be significantly different to MTO's cross join with your real data.
This becomes quite simple when you use collections (and you only need to do one table scan for each table):
Oracle Setup:
CREATE TYPE intlist AS TABLE OF INT;
/
Query:
SELECT t2.col1 AS t2_col1,
t1.col1 AS t1_col1
FROM (
SELECT col1,
CAST( COLLECT( CASE col3 WHEN 'ABC' THEN col2 END ) AS INTLIST ) AS abc,
CAST( COLLECT( CASE col3 WHEN 'DEF' THEN col2 END ) AS INTLIST ) AS def,
CAST( COLLECT( CASE col3 WHEN 'GHI' THEN col2 END ) AS INTLIST ) AS ghi
FROM table1
GROUP BY col1
) t1
INNER JOIN table2 t2
ON ( t2.abc MEMBER OF t1.abc
AND t2.def MEMBER OF t1.def
AND ( t2.ghi MEMBER OF t1.ghi OR t1.ghi IS EMPTY ) );
Output:
t2_col1 t1_col1
------- -------
A1 A
A3 A
A4 A
A7 A
A8 A
A9 A
A1 B
A3 B
A4 B
A7 B
A8 B
A10 C
Update
An alternative query without using collections (it is going to be more efficient than your query but probably less efficient than collections):
SELECT t2.col1,
t1.col1
FROM table1 t1
CROSS JOIN
table2 t2
GROUP BY t1.col1, t2.col1
HAVING COUNT( CASE WHEN t1.col2 = t2.abc AND t1.col3 = 'ABC' THEN 1 END ) > 0
AND COUNT( CASE WHEN t1.col2 = t2.def AND t1.col3 = 'DEF' THEN 1 END ) > 0
AND ( COUNT( CASE WHEN t1.col2 = t2.ghi AND t1.col3 = 'GHI' THEN 1 END ) > 0
OR COUNT( CASE t1.col3 WHEN 'GHI' THEN 1 END ) = 0 )
ORDER BY t1.col1, t2.col1;
Update 2:
Changed from CROSS JOIN to INNER JOIN:
SELECT t2.col1 AS t2_col1,
t1.col1 AS t1_col1
FROM (
SELECT t1.*,
COUNT( CASE col3 WHEN 'GHI' THEN 1 END )
OVER ( PARTITION BY col1 ) AS has_ghi
FROM table1 t1
) t1
INNER JOIN table2 t2
ON ( t1.col3 = 'ABC' AND t2.abc = t1.col2 )
OR ( t1.col3 = 'DEF' AND t2.def = t1.col2 )
OR ( t1.col3 = 'GHI' AND t2.ghi = t1.col2 )
GROUP BY t1.col1, t2.col1, t1.has_ghi
HAVING COUNT( CASE t1.col3 WHEN 'ABC' THEN 1 END ) > 0
AND COUNT( CASE t1.col3 WHEN 'DEF' THEN 1 END ) > 0
AND ( COUNT( CASE t1.col3 WHEN 'GHI' THEN 1 END ) > 0 OR has_ghi = 0 )
ORDER BY t1.col1, t2.col1;