query with join of 3 tables - sql

I want a single query to produce the following results..
To result in records that are in table1 and in table2 and were not in table3.
There are more than 10,000 records in each table..so i am looking for an efficient one. In all the table Cono is the primary key..
In detail with tables.
TABLE 1:-
Cono
th-123
th-124
th-125
TABLE 2:-
Cono
th-234
th-245
th-256
TABLE 3:-
Cono
th-124
th-125
th-256
Now i want to have the following records
Result TABLE:-
Cono
th-123
th-234
th-245

Try this
WITH Table1 AS
(
SELECT 'th-123' CONO UNION
SELECT 'th-124' UNION
SELECT 'th-125'
)
,
Table2 AS
(
SELECT 'th-234' CONO UNION
SELECT 'th-245' UNION
SELECT 'th-256'
)
,
Table3 AS
(
SELECT 'th-124' CONO UNION
SELECT 'th-125' UNION
SELECT 'th-256'
)
SELECT CONO
FROM Table1
WHERE NOT EXISTS
(
SELECT 1
FROM Table3
WHERE TABLE1.CONO = TABLE3.CONO
)
UNION ALL
SELECT CONO
FROM Table2
WHERE NOT EXISTS
(
SELECT 1
FROM Table3
WHERE TABLE2.CONO = TABLE3.CONO
)

try this:
select t.cono from Table1 t WHERE NOT EXISTS (SELECT 1
FROM Table3 x WHERE x.cono=t.cono)
UNION
select t.cono from Table2 t WHERE NOT EXISTS (SELECT 1
FROM Table3 x WHERE x.cono=t.cono)

(SELECT t1.Cono FROM table1 t1
LEFT JOIN table3 t3
ON t3.Cono = t1.Cono
WHERE t3.Cono IS NULL)
UNION ALL
(SELECT t2.Cono FROM table2 t2
LEFT JOIN table3 t3
ON t3.Cono = t2.Cono
WHERE t3.Cono IS NULL)

Kind of vague tables and names, but here's what you can do if you REALLY wanna do it in one query:
SELECT Cono
FROM Table3
WHERE NOT EXISTS ( SELECT Cono
FROM TABLE1 as T
WHERE EXISTS ( SELECT *
FROM TABLE2
WHERE T.Cono = TABLE2.Cono));
This should select the values in table 3 that do not exist in the query that was created in parenthesis, which is a table made up of rows that are in tables 1 and 2.
Unfortunately, nesting and efficiency normally don't go hand-in-hand...

this one worked for me... and process fast:
select X.FID, c.id as CID
from
(
select a.id as FID from tbl1 a
union
select b.id as FID from tbl2 b
) as X
left outer join tbl3 c on FID = c.id
where
c.id is null
;

Try this (not tested):
; WITH all_data AS (
SELECT * FROM table1
UNION ALL
SELECT * FROM table2
)
SELECT *
FROM all_data ad
WHERE NOT EXISTS (
SELECT *
FROM table3 t3
WHERE ad.Cono = t3.Cono);

Related

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

SQL. Joining records from 3 or more tables based on dates

My tables look like this(just an example):
table1:
TIME |data1
1.01.2018|aaa
2.01.2018|bbb
table2:
TIME |data2
1.01.2018|abcd
2.01.2018|cd
table3:
TIME |data3
1.01.2018|
2.01.2018|d
Now what i would like to do is take the data from table2 and put it in the table1 where there are missing records. If there is no record in all tables for that date, then the data in data column is NULL. The data copied to table1 needs to be the same date as in the table2. and if there is no same date in table1 as it is in table2, that date is created.
So far i have tried with this code, but the data copied is not valid. If there is a missing day in some of the tables, that missing day is created in time column, but the data in data column is incorect.
SELECT DISTINCT table1.time,table1.data,table2.time,table2.data,table3.time,table3.data
FROM table1
LEFT JOIN table2 ON table1.time=table2.time
LEFT JOIN table3 ON table1.time=table3.time
EDIT
This is how the output should look like:
For example, there was no data in table3 for data3 for that specific date, so the cell is empty.
TIME |data1|data2|data3|
1.01.2018|aaa |abcd | |
2.01.2018|bbb | cd | d |
You need a full join instead of a left join. (you didn't specify the backend. Here the sample is with MS SQL) ie:
SELECT
coalesce(table1."time",table2."time", table3."time") as "time",
table1.data1,table2.data2,table3.data3
FROM table1
full JOIN table2 ON table1."time"=table2."time"
full JOIN table3 ON table1."time"=table3."time" ;
Output:
Time data1 data2 data3
1.01.2018 aaa abcd (null)
2.01.2018 bbb cd d
EDIT: The distinct in your code suggests you want only one row per day. Then it is not clear which column value should be used. Assuming any would do, you can do aggregation like this:
SELECT
coalesce(table1."time",table2."time", table3."time") as "time",
max(table1.data1) as data1,
max(table2.data2) as data2,
max(table3.data3) as data3
FROM table1
full JOIN table2 ON table1."time"=table2."time"
full JOIN table3 ON table1."time"=table3."time"
group by coalesce(table1."time",table2."time", table3."time");
Start the process with
SELECT [DISTINCT] TIME FROM TABLE1
UNION [DISTINCT]
SELECT [DISTINCT] TIME FROM TABLE2
UNION [DISTINCT]
SELECT [DISTINCT] TIME FROM TABLE3
And then LEFT JOIN the three tables to that.
Try this:
with cte as (Select time from table1 union Select time from table2 union Select time from table3)
SELECT cte.time,table1.data as data1,table2.data as data2,table3.data as data3
FROM cte
LEFT JOIN table1 ON table1.time=cte.time
LEFT JOIN table2 ON cte.time=table2.time
LEFT JOIN table3 ON cte.time=table3.time
Apply simple left join after union on.
DECLARE #t1 table
(
TIMEDate1 DATE
,Data1 VARCHAR(50)
)
DECLARE #t2 table
(
TIMEDate2 DATE
,Data2 VARCHAR(50)
)
DECLARE #t3 table
(
TIMEDate3 DATE
,Data3 VARCHAR(50)
)
insert #t1 (TIMEDate1,Data1 ) select getdate(),'aaa'
insert #t1 (TIMEDate1,Data1 ) select getdate()+1,'bbb'
insert #t2 (TIMEDate2,Data2 ) select getdate(),'abcd'
insert #t2 (TIMEDate2,Data2 ) select getdate()+1,'cd'
insert #t3 (TIMEDate3,Data3 ) select getdate(),''
insert #t3 (TIMEDate3,Data3 ) select getdate()+1,'d'
SELECT t.TIMEDate,t1.Data1,t2.Data2,t3.Data3
FROM (
SELECT distinct TIMEDate1 as TIMEDate FROM #t1
UNION
SELECT distinct TIMEDate2 as TIMEDate FROM #t2
UNION
SELECT distinct TIMEDate3 as TIMEDate FROM #t3
) t
left JOIN #t1 t1 ON t1.TIMEDate1 = t.TIMEDate
left JOIN #t2 t2 ON t2.TIMEDate2 = t.TIMEDate
left JOIN #t3 t3 ON t3.TIMEDate3 = t.TIMEDate
--order by t1.TIMEDate1 asc
The SQL below is just an alternative for inserting the missing dates into Table1.
--
-- insert those into Table1 that are in Table2 but not in Table1
--
insert into Table1 ("time", data1)
select "time", data2
from Table2 as t2
where not exists
(
select 1
from Table1 as t1
where t1."time" = t2."time"
);
--
-- insert those into Table1 that are in Table3 but not in Table1
--
insert into Table1 ("time", data1)
select "time", data3
from Table3 as t3
where not exists
(
select 1
from Table1 as t1
where t1."time" = t3."time"
);
Then add the dates that are still missing in Table1 after all that.
Below is an example for MS SQL Server :
;with RCTE_DATERANGE as
(
select min([time]) as dt, max([time]) as max_dt
from Table1
union all
select dateadd(day, 1, dt), max_dt
from RCTE_DATERANGE where dt < max_dt
)
insert into Table1 ([time])
select dt
from RCTE_DATERANGE AS rcte
where not exists
(
select 1
from Table1 as t1
where t1.[time] = rcte.dt
);
-- Just checking
;with RCTE_DATERANGE as
(
select min([time]) as dt, max([time]) as max_dt
from Table1
union all
select dateadd(day, 1, dt), max_dt
from RCTE_DATERANGE where dt < max_dt
)
select rcte.dt, t1.[time] as time1, t2.[time] as time2, t3.[time] as [time3], t1.data1, t2.data2, t3.data3
from RCTE_DATERANGE AS rcte
full join Table1 t1 on t1.[time] = rcte.dt
full join Table2 t2 on t2.[time] = rcte.dt
full join Table3 t3 on t3.[time] = rcte.dt;

SQL join to override particular column of left table

I have two tables namely, Table1 and Table2
Table1
custId--custName--custAge
c1--c1name--32
c2--c2name--41
c3--c3name--41
Table2
custId--verified--custName
c1--Y--c1FullName
c2--N--c2FullName
I need to join Table1 and Table2, So that if verified column is Y in table 2, I need custName from Table2 instead of Table1.
So, desired output is: (overwrite custName column from Table2 if verified column is Y for that custId)
custId--custName--custAge
c1--c1FullName--32
c2--c2name--41
c3--c3name--41
I wrote following query, which is not giving proper result. Please help.
select T1.custId, NVL(T2.custName, T1.custName),T1.custAge
from Table1 T1
left join Table2 T2 on T1.custId=T2.custId and T2.verified='Y'
You can use a CASE statement to achieve this :
SELECT tab1.custId,
CASE
WHEN (tab2.verified = 'Y')
THEN tab2.custName
ELSE tab1.custName
END AS CustName,
tab1.custAge
FROM Table1 tab1
LEFT JOIN Table2 tab2 ON tab1.custId = tab2.custId
See this here -> http://rextester.com/EVOMK25746 (This fiddle is built on SQL Server , however, the query should work on Oracle databases as well)
Hope this helps!!!
Try this query once..
select * into #tab1 from
(
select 'c1' custId,'c1name' custName,31 custAge
union all
select 'c2' ,'c2name' ,41
union all
select 'c3' ,'c3name' ,41
) as a
select * into #tab2 from
(
select 'c1' custId,'Y'verified,'c1FullName' custName
UNION ALL
SELECT 'c2','N','c2FullName '
) as a
SELECT T1.custId,CASE WHEN T2.verified='Y' THEN T2.custName ELSE T1.custName END AS CUSTNAME,T1.custAge FROM #tab1 T1
LEFT JOIN #tab2 T2 ON T1.custId=T2.custId
select custId, custName, custAge
from Table1
natural join
( select custId
from Table2
where verified='N' ) t2
union
select custId, custName, custAge
from ( select custId, custAge from Table1 ) t1
natural join
( select custId, custName
from Table2
where verified='Y' ) t2;

how to get a field value from more than 7 tables using where condition

I have a flat file which consists of a list of mobile numbers and their status as active or inactive.So my job is to write a SQL query such that i need to fetch a list of 7 tables for a particular msisdn and i have to get the status of the msisdn.So any suggestion how to write.
I cannot use joins.
Step 1: Do A BULK INSERT to Load the Text File
Step 2: Fetch Records from all the tables for a particular msisdn
Step 3: Join between Step 1 and Step 2 for a particular msisdn
Pseudo Code
CREATE TABLE TempTable
(
MSISDN INT,
MobileNumber VARCHAR (50) NOT NULL,
TheStatus varchar (10) NOT NULL
)
--Step 1:
BULK INSERT TempTable FROM 'D:\InputMobileNumbers.txt' WITH (FIELDTERMINATOR = '","')
-- Step 2:
INSERT INTO #TEMP
SELECT * FROM
(
SELECT MSISDN,MobileNumber FROM Table1 WHERE MSISDN = #MSISDN UNION All
SELECT MSISDN,MobileNumber FROM Table2 WHERE MSISDN = #MSISDN UNION All
.............................................................................
.............................................................................
SELECT MSISDN,MobileNumber FROM Table7 WHERE MSISDN = #MSISDN UNION All
)X
-- Step 3
SELECT tt.MSISDN,tt.MobileNumber,tt.TheStatus
FROM TempTable tt
JOIN #TEMP t ON tt.MSISDN = t.MSISDN
WHERE tt.MSISDN = #MSISDN
DROP TABLE #TEMP
DROP TABLE TempTable
Perhaps you will be using UNION on this.
SELECT *
FROM
(
SELECT mobileNo, moileStatus FROM table1
UNION
SELECT mobileNo, moileStatus FROM table2
UNION
SELECT mobileNo, moileStatus FROM table3
UNION
SELECT mobileNo, moileStatus FROM table4
UNION
SELECT mobileNo, moileStatus FROM table5
UNION
SELECT mobileNo, moileStatus FROM table6
UNION
SELECT mobileNo, moileStatus FROM table7
) allMobile
WHERE allMobile.mobileNo = ''
-- OR something like that
select t1.column1,t1.column2,...,t2.column4,..
from table1 t1
left outer join table2 t2 on t1.column1=t2.column3
left outer join table2 t3 on t2.column1=t3.column2
.....
where t1.active=1 and t2.column2>0 ....
order by t1.column1,.....

SQL: Getting possible values for 'IN' operator from two different tables

I have 3 tables with the following schema:
Table1 columns: ID MsgNum Msg
Table2 columns: ID MsgNum SystemId
Table3 columns: ID MsgNum IncidentNum
I want to get all items in Table 1 with MsgNum within a range of values selected from Table 2 and Table 3.
From Table2, the possible values for MsgNum is obtained using the following SQL:
SELECT Table2.MsgNum from Table2 WHERE SystemId = 'Sys-0001'
and from Table3,
SELECT Table3.MsgNum from Table3 WHERE IncidentNumber = 'Incident-0001'
Now, I will like to use a combination of MsgNum values obtained from the previous 2 SELECT statements to get the corresponding rows in Table1 as follows:
SELECT * from Table1 where MsgNum IN ( SELECT Table2.MsgNum from Table2 WHERE SystemId = 'Sys-0001' OR SELECT Table3.MsgNum from Table3 WHERE IncidentNumber = 'Incident-0001')
But this SQL fails using Interactive SQL. What am I doing wrong?
why don't you use EXISTS instead
SELECT * from Table1 t1
where EXISTS ( SELECT t2.MsgNum from Table2 t2
WHERE t2.SystemId = 'Sys-0001' AND t2.MsgNum = t1.MsgNum)
OR EXISTS (SELECT t3.MsgNum from Table3 t3
WHERE t3.IncidentNumber = 'Incident-0001' AND t3.MsgNum = t1.MsgNum)
Do you mean UNION ?
SELECT *
from Table1
where MsgNum IN
(
SELECT Table2.MsgNum from Table2 WHERE SystemId = 'Sys-0001'
UNION
SELECT Table3.MsgNum from Table3 WHERE IncidentNumber = 'Incident-0001'
)
Use the UNION command to put two queries together
SELECT * from Table1 where MsgNum IN (
SELECT Table2.MsgNum from Table2 WHERE SystemId = 'Sys-0001'
UNION
SELECT Table3.MsgNum from Table3 WHERE IncidentNumber = 'Incident-0001')
)