Oracle - inner and left join - sql

create table t1 (v varchar2(500), n number);
Insert into T1 (V,N) values ('a',1);
Insert into T1 (V,N) values ('bb',2);
Insert into T1 (V,N) values ('c',3);
Insert into T1 (V,N) values ('d',4);
create table t2 (v varchar2(500), n number);
Insert into T2 (V,N) values ('a',1);
Insert into T2 (V,N) values ('bb',2);
select * from t1 join t2 on t1.v = t2.v
union all
select * from t1 LEFT join t2 on t1.v = t2.v ;
Output:
a 1 a 1
bb 2 bb 2
a 1 a 1
bb 2 bb 2
d 4 (null) (null)
c 3 (null) (null)
Can we get the same above output from single scan of T1 and T2 ie from single query without UNION ALL etc? Want to re-write the above Select query so that it scans the tables T1 and T2 only once and give the same result. See the LEFT join.
The output cant be changed as we are passing it further in the application, duplicate data is required as per the requirement.

" Want to re-write the above Select query so that it scans the tables T1 and T2 only once"
You could use subquery factoring . The WITH clauses read each table once and the UNION-ed queries read from them:
with cte1 as ( select * from t1 )
, cte2 as ( select * from t2 )
select * from cte1 join cte2 on cte1.v = cte2.v
union all
select * from cte1 LEFT join cte2 on cte1.v = cte2.v ;
Here is a SQL Fiddle demo.

You can avoid excess joins and unions by doubling the rows:
select t1.*,t2.* from t1
left join t2 on t1.v=t2.v
cross join (select 1 as dbl from dual
union select 2 as dbl from dual) dbl
where dbl=1 or t2.v is not null

Related

Union All but keep only duplicates from one table in T-SQL

I have two table which I would like to union. I need to keep only the duplicates from one of the two tables. I tried to find it, but could not find it anywhere. Hope somebody can help.
For example:
Table_1:
ID
Product
Amount
1
A
10
2
B
10
3
C
10
Table_2:
ID
Product
Amount
3
C
9
4
A
100
5
B
100
Desired result:
ID
Product
Amount
1
A
10
2
B
10
3
C
9
4
A
100
5
B
100
So always use the duplicates from table_2. In this example ID 3 is duplicate, so use the duplicate of table_2 with amount 9.
How to realize this with T-SQL? I used the code below:
Select * from Table_1 where Table_1.id != Table_2.id
Union All
Select * from Table_2
But then I receive the error:
'The multi-part identifier "Table_2.ID" could not be bound.'
Use not exists:
Select t1.*
from Table_1 t1
where not exists (select 1 from table_2 t2 where t2.id = t1.id)
Union All
Select t2.*
from Table_2 t2;
Try this:
SELECT T1.*
FROM #Table1 T1
WHERE T1.ID NOT IN (SELECT ID FROM #Table2)
UNION
SELECT T2.*
FROM #Table2 T2
I assume what you want is an EXISTS:
SELECT T1.ID,
T1.Product,
T1.Amount
FROM dbo.Table1 T1
WHERE NOT EXISTS (SELECT 1
FROM dbo.Table2 T2
WHERE T1.ID = T2.ID)
UNION ALL
SELECT T2.ID,
T2.Product,
T2.Amount
FROM dbo.Table2 T2;
A FULL OUTER JOIN, however, might also work if ID is unique in both tables:
SELECT ISNULL(T2.ID,T1.ID) AS ID,
ISNULL(T2.Product,T1.Product) AS Product,
ISNULL(T2.Amount,T1.Amount) AS Amount
FROM dbo.Table1 T1
FULL OUTER JOIN dbo.Table2 T2 ON T1.ID = T2.ID;
Union will give you the result. Union will always return unique values always. If you use union all you will get all with duplicates. Your answer would be to use union all.
SELECT
B.ID
,B.Product
,B.Amount
FROM
(
SELECT
A.ID
,A.Product
,A.Amount
,ROW_NUMBER() over (Partition BY ID, Product order by Amount ASC) AS [row_num]
FROM
(
SELECT
tb_1.*
FROM tb_1
UNION ALL
SELECT
tb_2.*
FROM tb_2
) AS A
) AS B
WHERE B.[row_num] = 1

Get the list of name column values which are not common in both the tables?

recently i gave an interview where the question was
suppose there are two tables in database.
Table T1 has a column named "name" in it and few other columns
Table T2 also has a column name "name" and few other columns
suppose table T1 has values in name column as
[n1,n2,n3,n4,n5]
and values in the "name" column of table T2 are
[n2,n4]
then output should be
[n1,n3,n5] as n2 and n4 are common in both tables
we needs to find the list of names which are not common in both the tables.
The solution that i provided him was using join in the below form
select name from table1 where name not in (select t1.name from table1 t1 join table2 t2 on t1.name=t2.name)
UNION
select name from table2 where name not in (select t1.name from table1 t1 join table2 t2 on t1.name=t2.name)
But he said there is still a better solution. I was not able to come up with any different and more efficient solution. What is the other efficient way to get the list of names if there is any?
If the NAME column does not have NULL values, there is also
select distinct(coalesce(a.name, b.name)) name
from table1 a
full join table1 b on a.name = b.name
where a.name is null or b.name is null
(Corrected WHERE condition, sorry...)
Use FULL OUTER JOIN:
SELECT DISTINCT(COALESCE(t1.NAME, t2.NAME)) AS NAME
FROM TABLE1 t1
FULL OUTER JOIN TABLE2 t2
ON t2.NAME = t1.NAME
WHERE t1.NAME IS NULL OR
t2.NAME IS NULL
A FULL OUTER JOIN is similar to a LEFT OUTER JOIN unioned with a RIGHT OUTER JOIN - it returns rows where data exists in the first table but not the second, or where it data exists in the second table but not the first. You could get the same effect by using
SELECT t1.NAME
FROM TABLE1 t1
LEFT OUTER JOIN TABLE2 t2
ON t2.NAME = t1.NAME
WHERE t2.NAME IS NULL
UNION
SELECT t2.NAME
FROM TABLE1 t1
RIGHT OUTER JOIN TABLE2 t2
ON t2.NAME = t1.NAME
WHERE t1.NAME IS NULL
and in fact the above is what you'd need to do if you were using a database which doesn't support the FULL OUTER JOIN syntax (e.g. MySQL, the last time I looked).
See this dbfiddle
Union the tables and return the values that don't have a count of 2:
create table t1 (
c1 int
);
create table t2 (
c1 int
);
insert into t1 values ( 1 );
insert into t1 values ( 3 );
insert into t2 values ( 2 );
insert into t2 values ( 3 );
commit;
select c1 only_in_one_table
from (
select 'T1' t, c1 from t1
union
select 'T2' t, c1 from t2
)
group by c1
having count(*) <> 2;
ONLY_IN_ONE_TABLE
1
2
I'm not a fan of not in with subqueries, because it behaves unexpectedly with null values. And the person asking would have to explain what "better" means. Your version is actually reasonable.
I might be inclined to approach this using aggregation:
select name
from ((select distinct name, 1 as in_table1, 0 as in_table2
from table1
) union all
(select distinct name, 0 as in_table1, 0\1 as in_table2
from table2
)
) t
group by name
having max(in_table1) <> max(in_table2);
In a real world case, you would probably have a separate table with all names. If so:
select n.*
from names n
where (not exists (select 1 from table1 t1 where t1.name = n.name) and
exists (select 1 from table2 t2 where t2.name = n.name
) or
(exists (select 1 from table1 t1 where t1.name = n.name) and
not exists (select 1 from table2 t2 where t2.name = n.name
);
This is usually the fastest approach because it does not involve any aggregation or duplicate removal.
If you want to use SET operator then find the solution as below:
CREATE TABLE TABLE1(NAME VARCHAR2(100));
CREATE TABLE TABLE2(NAME VARCHAR2(100));
INSERT INTO TABLE1 VALUES('A');
INSERT INTO TABLE1 VALUES('B');
INSERT INTO TABLE1 VALUES('C');
INSERT INTO TABLE2 VALUES('A');
INSERT INTO TABLE2 VALUES('B');
INSERT INTO TABLE2 VALUES('D');
SELECT
NAME
FROM
(
SELECT
NAME
FROM
TABLE1
UNION
SELECT
NAME
FROM
TABLE2
)
WHERE
NAME NOT IN (
SELECT
NAME
FROM
TABLE1
JOIN TABLE2 USING ( NAME )
);
Cheers!!
Yet another possible solution:
Find those that are in the first table but not in the second table using the MINUS operator (which is Oracle's implementation of the standard EXCEPT). Then UNION that with those that are in the second but not in the first.
(
select name
from t1
minus
select name
from t2
)
union all
(
select name
from t2
minus
select name
from t1
);
Given this setup:
create table t1
(
name varchar(10)
);
insert into t1 values ('Arthur');
insert into t1 values ('Zaphod');
create table t2
(
name varchar(10)
);
insert into t2 values ('Tricia');
insert into t2 values ('Zaphod');
This returns:
NAME
------
Arthur
Tricia
select id from((select id from table1)
union all
(select id from table2)) as t1
group by id having count(id)=1
Using the basic set operations the following query should work.
(
select name from table1
union all
select name from table2
)
minus
(
select name from table1
intersect
select name from table2
)
;
Regards
Akash

Need to get output of 2 tables in one column using joins only

I have this scenario question which needs to be solved using joins only. can't use except,intersect or union.
sample code:
--demo setup
create table t1 (id int)
insert into t1 values (1),(2),(3)
create table t2 (id int)
insert into t2 values (4),(5),(6)
--join
select t1.id,t2.id
from t1 full outer join
t2 on t1.id=t2.id
--after join i am getting
id id
----------- -----------
1 NULL
2 NULL
3 NULL
NULL 4
NULL 5
NULL 6
--But i need is
id
-----------
1
2
3
4
5
6
Can someone help me with this ? i know this can be easily done using union but this challenge needs to be solved using joins only.
any help is appreciated ....
--solved the challenge after adding case to the code.
select case when t1.id is null then t2.id else t1.id end as id
from t1 full outer join
t2 on t1.id=t2.id
you can use this code:
SELECT case when t1.id IS NULL THEN t2.id ELSE t1.id END AS id
FROM t1 FULL OUTER JOIN t2
ON t1.id=t2.id
As you said the Use From Union
select * from t1
union
select * from t2
Although UNION ALL is the better option, you can use this following script-
SELECT
CASE WHEN C.ID1 IS NULL THEN C.ID2 ELSE C.ID1 END V
FROM
(
SELECT * FROM
(
SELECT 'T1' TabName1,id ID1 FROM #t1
)A
FULL JOIN (
SELECT 'T2' TabName2,id ID2 FROM #t2
)B
ON A.TabName1 = B.TabName2
)C
ORDER BY 1

inner join two SELECT statements in ONE query AND show results from BOTH tables

I want the use 2 SELECT statements and an INNER JOIN from different queries BUT also want to show the two different results, from different tables in the same query. Like this..
SELECT column1 FROM earth e1 that is null
+
SELECT chair5 FROM space s1 that is not null
INNER JOIN space s1 ON e1.car = s1.truck
ORDER BY e1.column,s1.chair5
How do I show the results of two different queries while using an INNER JOIN?
Assume the table T1 contains values 'A','B','C' and table T2 contains values 'A','B','D'
You may query
select 'T1' as source, col from t1
union all
select 'T2' as source, col from t2
union all
select 'join T1,T2' as source, t1.col from t1 inner join t2
on t1.col= t2.col
order by 1,2
;
getting
SOURCE COL
---------- -----
T1 A
T1 B
T1 C
T2 A
T2 B
T2 D
join T1,T2 A
join T1,T2 B
The first column identifies the source: single query or a join
Alternatively, which I'll prefer you may get the same information (more compressed) using FULL OUTER JOIN
with fj as (
select t1.col t1_col, t2.col t2_col
from t1 full outer join t2
on t1.col= t2.col
)
select
case when t1_col is not null and t2_col is not null then 'both'
when t1_col is not null then 'T1 only'
when t2_col is not null then 'T2 only' end as source,
nvl(t1_col, t2_col) col
from fj
order by 1,2
.
SOURCE COL
------- ----------
T1 only C
T2 only D
both A
both B

How to get data from three or more tables

I need to display distinct data between three tables. How to do this requirement.
FirstTable:
9999999999
8888888888
7777777777
6666666666
5555555555
SecondTable:
7777777777
9999999999
ThirdTable:
8888888888
i want output in this format.
6666666666
5555555555
Use LEFT JOIN
SELECT T1."Col"
FROM Table1 T1
LEFT JOIN Table2 T2
ON T1."Col" = T2."Col"
LEFT JOIN Table3 T3
ON T1."Col" = T3."Col"
WHERE T2."Col" IS NULL
AND T3."Col" IS NULL
Output:
| COL |
--------------
| 6666666666 |
| 5555555555 |
See this SQLFiddle
For the data you gave us, you can try this:
select YourColumn from Table1
minus
select Yourcolumn from Table2
minus
select YourColumn from Table3
This however wouldn't give you entries that existed in Table 3 but not tables 1 ND 2. I second the suggestion that you improve the question.
try this...
Select yourColumn
from Table1
where yourColumn not in
( select yourColumn from Table2)
and yourColumn not in
(select yourColumn from table3)
Try:
SELECT Column FROM FirstTable
WHERE Column NOT IN
(SELECT Column from SecondTable UNION
SELECT Column from ThirdTable)
YOUR result doesn't show distinct data. It shows the data which is not IN on any OTHER table
So you can write like this
CREATE TABLE FirstTable (VALUE VARCHAR(10))
CREATE TABLE SecondTable (VALUE VARCHAR(10))
CREATE TABLE ThirdTable (VALUE VARCHAR(10))
INSERT INTO FirstTable VALUES(9999999999)
INSERT INTO FirstTable VALUES(8888888888)
INSERT INTO FirstTable VALUES(7777777777)
INSERT INTO FirstTable VALUES(6666666666)
INSERT INTO FirstTable VALUES(5555555555)
INSERT INTO SecondTable VALUES(9999999999)
INSERT INTO SecondTable VALUES(7777777777)
INSERT INTO ThirdTable VALUES(8888888888)
SELECT a1.vALUE FROM (SELECT a.vALUE FROM FirstTable a
LEFT OUTER JOIN SecondTable b ON A.VALUE=B.VALUE
WHERE b.VALUE IS NULL) a1
LEFT OUTER JOIN ThirdTable c ON A1.VALUE=C.VALUE
WHERE c.VALUE IS NULL
Contrary to popular inclination, I think this is a good question!
The following solution finds all unique values in three tables:
with middle_tab as(
select t1.id t1_id,t2.id t2_id,t3.id t3_id from tab1 t1
full outer join tab2 t2 on t1.id=t2.id
full outer join tab3 t3 on t1.id=t3.id
)
select coalesce(t1_id,t2_id,t3_id) unique_ids from (
select t1_id,t2_id,t3_id,NVL2(t1_id,0,1)+NVL2(t2_id,0,1)+NVL2(t3_id,0,1) b
from middle_tab)
where b>1
Here is SQL Fiddle Demo!