Update Syntax with joins - sql

I am trying to update a column only with the matched condition.
update table1 set col=Match
where id in(select id from
table1,table2 where table1.id=table2.id);
It says sql command not properly ended.

Here's an example which shows what to do (at least, that's what I understood, based on the question and comments you posted).
Test case first:
SQL> create table table1 (id number, criteria varchar2(10));
Table created.
SQL> create table table2 (id number);
Table created.
SQL> insert into table1 (id)
2 select 1 from dual union all
3 select 2 from dual;
2 rows created.
SQL> insert into table2 (id)
2 select 1 from dual;
1 row created.
As you can see, both tables share ID = 1 so we'd expect its table1.criteria to be modified.
Your query:
SQL> update table1 set
2 criteria = 'M1'
3 where id in (select a.id
4 from table1 a join table2 b on a.id = b.id
5 );
1 row updated.
SQL> -- Result
SQL> select * From table1;
ID CRITERIA
---------- ----------
1 M1
2
Alternatively:
SQL> update table1 a set
2 a.criteria = 'M2'
3 where exists (select null
4 from table2 b
5 where b.id = a.id
6 );
1 row updated.
SQL> select * From table1;
ID CRITERIA
---------- ----------
1 M2
2
See if it helps.

Related

Delete data from table1 based on data in table 2 with composite primary key

I have two tables in Oracle db. Table1 and table2. Both have composite primary key. Structure of the tables are same but not data.
I need to delete rows from table1 which are not in table2.
There are various options; you've already seen one in Impaler's post.
SQL> create table a (id1, id2, name) as
2 select 1, 10, 'Little' from dual union all
3 select 2, 20, 'Foot' from dual union all --> these two should be deleted
4 select 3, 30, 'Xyz' from dual; --> as (id1, id2) don't exist in table B
Table created.
SQL> create table b (id1, id2, name) as
2 select 1, 10, 'Mickey' from dual union all
3 select 4, 40, 'Mouse' from dual;
Table created.
NOT IN (the same as Impaler's, obviously):
SQL> delete from a
2 where (id1, id2) not in (select id1, id2 from b);
2 rows deleted.
SQL> select * from a;
ID1 ID2 NAME
---------- ---------- ------
1 10 Little
SQL> rollback;
Rollback complete.
NOT EXISTS:
SQL> delete from a
2 where not exists (select null from b
3 where b.id1 = a.id1
4 and b.id2 = a.id2
5 );
2 rows deleted.
SQL> select * from a;
ID1 ID2 NAME
---------- ---------- ------
1 10 Little
SQL> rollback;
Rollback complete.
IN, with the MINUS set operator:
SQL> delete from a
2 where (id1, id2) in (select id1, id2 from a
3 minus
4 select id1, id2 from b
5 );
2 rows deleted.
SQL> select * from a;
ID1 ID2 NAME
---------- ---------- ------
1 10 Little
SQL> rollback;
Rollback complete.
SQL>
You can do:
delete from t
where (a, b) not in (
select a, b from u
)
See running example at db<>fiddle.

How to use subquery to drop rows from Tab1 which are in Tab2 in Oracle SQL?

I have tables in Oracle SQL like below:
Tab1
ID
-----
1
2
3
Tab2
ID
-----
3
4
5
And I need to take values from Tab1 which are not in Tab2. I made query like below:
select ID
from Tab1
where ID not in (select ID from Tab2)
Above query does not work, how can I change it to achieve result as I need:
ID
---
1
2
I can add that I prefere to use subquery in this problem, how can I do that in Oracle SQL ?
With the MINUS set operator:
SQL> with
2 tab1 (id) as
3 (select 1 from dual union all
4 select 2 from dual union all
5 select 3 from dual
6 ),
7 tab2 (id) as
8 (select 3 from dual union all
9 select 4 from dual union all
10 select 5 from dual
11 )
12 select id from tab1
13 minus
14 select id from tab2;
ID
----------
1
2
SQL>
BTW, query you used (with a subquery) returns correct result; did you mean to say that you prefer NOT to use a subquery?
<snip>
12 select id from tab1
13 where id not in (select id from tab2);
ID
----------
1
2
I tried this code and it worked fine :
select ID
from Table1
where ID not in (select ID from Table2)
You cant DROP rows from a table, but you can DELETE them.
So correcting you title to
How to use subquery to DELETE rows from Tab1 which are in Tab2 in Oracle SQL?
do so:
delete from tab1
where id in (select id from tab2);
1 row deleted.
select * from tab1;
ID
----------
1
2
Do not forget to commit to make the change permanent.

MERGE with multiple UPDATE statements

Can we achieve below scenario using single MERGE statement:
source table - table1
destination table- table2
when table1.id in table2.id
then update table1 SET phone_number=123456
when table1.id not in table2.id
then update table1 SET phone_number=555555
Note:- i am able to achieve the result using below query .
MERGE INTO table1 tbl1
USING table2 tbl2
ON (tbl1.id = tbl2.id)
WHEN MATCHED THEN
UPDATE SET tbl1.phone_number=123456;
update table1 set phone_number = 555555 where id not in (select id from table2);
Is there any way to achieve it by using only MERGE Statement ?
When no rows are matched then you can not use UPDATE (in WHEN NOT MATCHED). as there are no rows matched then which data should be updated?
Normal merge statement must have following structure:
MERGE <hint> INTO <table_name>
USING <table_view_or_query>
ON (<condition>)
WHEN MATCHED THEN <update_clause>
DELETE <where_clause>
WHEN NOT MATCHED THEN <insert_clause>
[LOG ERRORS <log_errors_clause> <reject limit <integer | unlimited>];
When there is no match then there are no rows found by oracle into your target table which matches with source table using ON condition then how can it update the record? which record it will update?
WHEN NOT MATCHED is illustrated as following in oracle documentation:
You can handle your scenario using MERGE as follows:
-- Oracle data creation
SQL> CREATE TABLE table1 ( id number, phone_number number );
Table created.
SQL> INSERT INTO table1
2 SELECT 1, 111 from dual UNION ALL
3 SELECT 2, 222 from dual UNION ALL
4 SELECT 3, 333 from dual UNION ALL
5 SELECT 4, 444 from dual;
4 rows created.
SQL> drop table table2;
Table dropped.
SQL> CREATE TABLE table2 ( id number );
Table created.
SQL> INSERT INTO table2
2 SELECT 1 from dual UNION ALL
3 SELECT 2 from dual;
2 rows created.
-- Your merge statement
SQL> MERGE INTO TABLE1 TBL1
2 USING (
3 SELECT T1.ID, T2.ID AS T2ID
4 FROM TABLE1 T1
5 LEFT JOIN TABLE2 T2 ON T1.ID = T2.ID
6 )
7 TBL2 ON ( TBL1.ID = TBL2.ID )
8 WHEN MATCHED THEN
9 UPDATE SET TBL1.PHONE_NUMBER = NVL2(TBL2.T2ID, 123456, 555555);
4 rows merged.
-- Result
SQL> SELECT * FROM TABLE1;
ID PHONE_NUMBER
---------- ------------
1 123456
2 123456
3 555555
4 555555
SQL>
Cheers!!
Just use an UPDATE statement:
Oracle Setup:
CREATE TABLE table1 ( id, phone_number ) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 2 FROM DUAL;
CREATE TABLE table2 ( id ) AS
SELECT 1 FROM DUAL UNION ALL
SELECT 3 FROM DUAL;
Update:
UPDATE table1 t1
SET phone_number = COALESCE(
(
SELECT 123456
FROM table2 t2
WHERE t1.id = t2.id
),
555555
)
Output:
SELECT *
FROM table1
ID | PHONE_NUMBER
-: | -----------:
1 | 123456
2 | 555555
db<>fiddle here
The syntax you are looking for in a MERGE statement exists in SQL Server but is NOT valid in Oracle:
MERGE INTO table1 t1
USING table2 t2
ON ( t1.id = t2.id )
WHEN MATCHED THEN
UPDATE SET phone_number = 123456
WHEN NOT MATCHED BY SOURCE THEN
UPDATE SET phone_number = 555555;
db<>fiddle here

Oracle: Check for existence and map a new column

Would like to check the existence of an id from table_1 in table_2 and based on that trying to create a new column mapped_value.
table_2 has huge number of records with duplicate id's and also has a non-unique index on it.
SQL> drop table table_1
Table dropped.
SQL> create table table_1(id varchar2(10),value varchar2(10))
Table created.
SQL> insert into table_1
(select '100','ABC' from dual
union all
select '101','DEF' from dual
union all
select '103','GHI' from dual
)
3 rows created.
SQL> commit
Commit complete.
SQL> select * from table_1
ID VALUE
---------- ----------
100 ABC
101 DEF
103 GHI
3 rows selected.
SQL> drop table table_2
Table dropped.
SQL> create table table_2(id varchar2(10),value varchar2(10),day date)
Table created.
SQL> insert into table_2
(select '100','ABC',sysdate from dual
union all
select '100','ABC',sysdate from dual
union all
select '100','ABC',sysdate from dual
union all
select '101','DEF',sysdate from dual
union all
select '101','DEF',sysdate from dual
union all
select '101','DEF',sysdate from dual
)
6 rows created.
SQL> commit
Commit complete.
SQL> select * from table_2
ID VALUE DAY
---------- ---------- ---------
100 ABC 18-SEP-18
100 ABC 18-SEP-18
100 ABC 18-SEP-18
101 DEF 18-SEP-18
101 DEF 18-SEP-18
101 DEF 18-SEP-18
6 rows selected.
Trying below but its getting duplicate records for ids 100 and 101.
I know,shouldn't use outer join as there are duplicates.
I want to get the desired output but without duplicates by leveraging the non-unique index on table_2.
How do go about this?
SQL> select t1.*,case when t2.id is null then '***EMPTY****' else t2.id end as mapped_value
from table_1 t1,table_2 t2
where t1.id = t2.id(+)
ID VALUE MAPPED_VALUE
---------- ---------- ------------
100 ABC 100
100 ABC 100
100 ABC 100
101 DEF 101
101 DEF 101
101 DEF 101
103 GHI ***EMPTY****
7 rows selected.
If I understand that correctly, an EXISTS in a CASE might be what you're after.
SELECT t1.id,
t1.value,
CASE
WHEN EXISTS (SELECT *
FROM table_2 t2
WHERE t2.id = t1.id) THEN
t1.id
ELSE
'***EMPTY***'
END mapped_value
FROM table_1 t1;

Return results from a table match on exact number of rows

I have two tables A and B, that are in a many to many relationship in a third table. What A want to achieve is get the "repeating" A rows based on B. For example:
table A table B table A_B
---------- ---------- ----------
1 A 1 A
2 B 1 B
3 C 2 A
4 D 2 B
5 3 A
3 B
3 C
4 A
4 D
5 A
What I want is, when searching table A_B by lets say '1', to get only 2, although 3 has both A and B and 4 has A, same goes for 5 too, it matches A but only A so it should be ignored as well. I've tried some suggestions form similar questions with cross join but I had no luck. I am trying to achieve this with just selects and joins, without stored procedures or temporary tables. Any suggestions is welcomed, thank you.
Repeat all base table rows for EACH left join row match
I want my output to look like:
table A_B
----------
2 A
2 B
Or if possible it would be even better if it matches the A_id by which the search is being done
table A_B
----------
1 A
1 B
2 A
2 B
However, the B_id column is not as important so if it is only
table A_B
----------
2
or
table A_B
----------
1
2
is acceptable as well.
EDIT 1:
Until now this is what I've came up with, although a bit unclean but it gets the expected result
select
A_id
from
tableA_B
where
A_id in
(
select
A_id
from
tableA_B
group by
A_id
having
count (A_id) IN (
select
count (A_id)
from
tableA_B
where
A_id = 1
)
)
AND
B_id IN (
select
B_id
from
tableA_B
where
A_id = 1
)
group by
A_id
Basically process of elimination, step by step. It would be ideal if it took only one step.
EDIT 2:
I'm sorry I left out some important information, my B values can be repeated for instance
table A table B table A_B
---------- ---------- ----------
1 A 1 A
2 B 1 B
3 c 2 A
4 D 2 B
5 AB 3 A
6 3 B
3 C
4 A
4 D
5 A
6 AB
so using XML path may return incorrect values. Because in my case it will return 6 as well which is incorrect. I apologies for leaving out this information.
Other solution which use INTERSECT could be:
CREATE TABLE tableA_B (A_id INT, B_id VARCHAR(8))
GO
INSERT INTO tableA_B VALUES
(1,'A'),(1,'B'),(2,'A'),(2,'B'),(3,'A'),(3,'B'),(3,'C'),(4,'A'),(4,'D'),(5,'A')
GO
DECLARE #x INT = 1;
SELECT A_id FROM tableA_B ab1
LEFT JOIN (
SELECT B_id FROM tableA_B
WHERE A_id=#x
) ab2 ON ab1.B_id=ab2.B_id
GROUP BY ab1.A_id
HAVING COUNT(*)=(SELECT COUNT(*) FROM tableA_B WHERE A_id=#x)
INTERSECT
SELECT A_id FROM tableA_B ab1
JOIN (
SELECT B_id FROM tableA_B
WHERE A_id=#x
) ab2 ON ab1.B_id=ab2.B_id
GROUP BY ab1.A_id
HAVING COUNT(*)=(SELECT COUNT(*) FROM tableA_B WHERE A_id=#x)
DROP TABLE tableA_B
GO
Try this,
declare #A_B table(col int,col2 varchar(30))
insert into #A_B VALUES
(1 ,'A') ,(1 ,'B') ,(2 ,'A') ,(2 ,'B') ,(3 ,'A') ,(3 ,'B')
,(3 ,'C') ,(4 ,'A') ,(4 ,'D') ,(5 ,'A'),(6 ,'AB')
declare #i int=1
declare #007 char(1)='-'
;with CTE as
(
select col,col2
,(select #007+col2 from #A_B y
where col=x.col for xml path(''))ConcateCol
from #A_B x
--where col=#i
)
select col,col2
from cte c
where
exists(select * from cte c1
where col=#i and c.ConcateCol=c1.ConcateCol)
you can further maniplate to get whatever desire output
;With tableA(ID)
AS
(
Select 1 uNION ALL
Select 2 uNION ALL
Select 3 uNION ALL
Select 4
)
, tableB(VAL)
As
(
SELECT 'A' UNION ALL
SELECT 'B' UNION ALL
SELECT 'C' UNION ALL
SELECT 'D'
)
SELECT ID,VAL FROM
(
SELECT *,ROW_NUMBER()OVER(PARTITION BY ID ORDER BY ID)AS Seq FROM tableA
CROSS JOIN tableB
)Dt
WHERE ID In (SELECT Id From tableA where id in(1,2) ) AND Dt.Seq<3
OutPut
table A_B
----------
1 A
1 B
2 A
2 B