Find common values in many tables - sql

I have 5 tables and I want to find the common values between them in one column. The column name differs in two tables(account_number, account,account_id).
select * from db.table1 as a
INNER JOIN db.table2 as b
ON a.account = b.account_id
INNER JOIN db.table3 as c
ON a.account = c.account_number
INNER JOIN db.table4 as d
ON a.account = d.account_number
INNER JOIN db.table_5 as f
ON a.account = f.account_number;`
I tried the above with the idea of every time compare the last result of the inner join with the new one but it seems wrong.

Your method is okay, but it can return duplicates. Another method is to use union all:
select account
from ((select account, 'a' as which from db.table1) union all
(select account_id, 'b' as which from db.table2) union all
(select account_number, 'c' as which from db.table3) union all
(select account_number, 'd' as which from db.table4) union all
(select account_number, '3' as which from db.table5)
) t
group by account
having count(distinct which) = 5;
Note that this can be easily tweaked to get accounts that are in three or four tables rather than in all of them.

Related

Multiple tables joined to a table via single column

I am trying to create query, on below scenario.
with my skills I am able to join Table A,A1,B and A,A1,C and A,A1,D individually and union them.
Is there any better way to achieve same. I am using Oracle as Database.
It all depends on what they mean and if you need to know the columns the values are from.
This would get all the columns and you would have NULL values from the non-matching B, C, D tables:
SELECT *
FROM a1
INNER JOIN a ON a1.aid = a.id
LEFT OUTER JOIN b ON a.extid = b.extid
LEFT OUTER JOIN c ON a.extid = c.extid
LEFT OUTER JOIN d ON a.extid = d.extid
Or, this would get only the relevant values and give you the type they belong to in fewer columns:
SELECT *
FROM a1
INNER JOIN a ON a1.aid = a.id
INNER JOIN (
SELECT extid, 'B' AS type, pqr_col AS col1, qrs_col AS col2 FROM b
UNION ALL
SELECT extid, 'C', abc_col, bcd_col FROM c
UNION ALL
SELECT extid, 'D', xyz_col, yza_col FROM d
) bcd
ON a.extid = bcd.extid
Union was my first thought when I read your question. Though, for simplicity, you could first create a view and then join it to other table(s):
create view v_ext as
select b.extid, b.pqr_col, b.qrs_col from b
union all
select c.extid, c.abc_col, c.bcd_col from c
union all
select d.extid, d.xyz_col, d.yza_col from d;
select *
from a join a1 on a.id = a1.aid
join v_ext v on v.extid = a.extid;
you can try the query with 'with' clause. Something like below, I havent tested it though
with union_output as
( select b.extid, b.pqr_col, b.qrs_col from b
union
select c.extid, c.abc_col, c.bcd_col from c
union
select d.extid, d.xyz_col, d.yza_col from d)
select *
from a join a1 on a.id = a1.aid
join union_output uo on uo.extid = a.extid;
Select *from tableA A
Inner join tableA1 A1 on A1.A1ID=A.AID
Inner join tableB b on b.ExtID=A.ExtID
Inner join tableC c on c.ExtID=A.ExtID
Inner join tableD d on d.ExtID=A.ExtID

sql find parent table where sum of child column not equal to parent column

hey guys i am trying to find the a bill from billMaster where the sum of billDetails.total is not equal to billMaster.remainingAmount
NB
this is a one to many relationship where one bill can contain more billdetails
i tried the below query and got an sql error
/* SQL Error (1111): Invalid use of group function */
SELECT a.id AS billMAsterId FROM eBillMaster AS a JOIN eBillDetail AS b ON a.id = b.billId
WHERE SUM(b.total) != b.remainAmount GROUP BY a.id
SELECT a.remainAmount, a.id AS BillId FROM eBillMaster a JOIN (SELECT MAX(id) AS id FROM eBillMaster) b JOIN eBillDetail c ON (a.id - c.billId) WHERE SUM(c.total) != a.remainAmount
both queries returned the same error i gess its on how i used the sum on where close.
But the sad thing is that i cant sole the problem..
Any response will be appreciated.
now assume i want to get recent user bill that meets the above condition . Note the billMaster has a column called user_id. how will the new query look like.
An example would make thinks much easier, but I think this is what you want:
SELECT m.id
FROM eBillMaster AS m
JOIN (select billId, sum(total) sumTotal from eBillDetail group by billId) AS d ON m.id = d.billId
WHERE d.sumTotal != m.remainAmount;
Check it out here http://sqlfiddle.com/#!9/89dcfb/7
It's more efficient tou use a cross apply also known as a lateral join - depending on your sql dialect, you haven't tagged your database
select a.id as billMAsterId
from eBillMaster as a
cross apply (
select Sum(total) total
from eBillDetail as b
where b.billId = a.id
)b
where a.remainAmount != b.total
To do the same with having clause you can do
select a.id as billMAsterId
from eBillMaster as a
join eBillDetail as b on a.id = b.billId
group by a.id, a.remainAmount
having Sum(b.total) != a.remainAmount
You need to use the having clause and also group by remainamount
with ebillmaster (id) as(
select 1 from dual union all
select 2 from dual union all
select 3 from dual),
ebilldetail (billid, total, remainamount) as(
select 1, 4, 5 from dual union all
select 2, 3, 4 from dual union all
select 3, 3, 3 from dual union all
select 4, 1, 2 from dual)
SELECT
a.id AS billmasterid
FROM
ebillmaster a
JOIN ebilldetail b ON a.id = b.billid
GROUP BY
a.id,
b.remainamount
HAVING
SUM(b.total) != b.remainamount
This is standard SQL, Aggregated values are filtered in the HAVING clause.
SELECT a.id AS billMAsterId
FROM eBillMaster AS a
JOIN eBillDetail AS b ON a.id = b.billId
GROUP BY a.id, a.remainAmount
HAVING SUM(b.total) != a.remainAmount
Some Sql engines allow to omitt a.remainAmount from GROUP BY when eBillMaster.id is PK.
One more (exotic a bit) option
SELECT a.id AS billMAsterId
FROM eBillMaster AS a
JOIN eBillDetail AS b ON a.id = b.billId
GROUP BY a.id
HAVING SUM(b.total) != AVG(a.remainAmount)
Thank you every one for your feedback it led me to the correct path and the modified query is
SELECT m.propertyId, d.sumTotal, m.remainAmount, m.id
FROM eBillMaster AS m JOIN ( SELECT MAX(id) AS id FROM eBillMaster GROUP BY propertyId) b USING (id)
JOIN (select billId, sum(total) sumTotal from eBillDetail group by billId) AS d ON m.id = d.billId
WHERE d.sumTotal != m.remainAmount;

Easy way to get a single resultset from three identical tables?

The DB I'm working with has three tables with identical column layouts, OPEX, NOPEX and CAPEX. I would like to query all three for items with a matching AssetId and get a single result set so that I can process them all at the same time in my .Net code.
The twist is that I do need to know which table they came from.
I know I can do this with a series of CASE in the SELECT clause, perhaps using the ID column in each where it's non-zero to decide which of the tables it came from. But I would have to have one for each column and the tables are pretty wide.
Is there some other way to solve this problem?
In order to get them into one set, you would use a combination of UNION and EXISTS() checks. The UNION ALL will give you a single result set that contains data from all three tables, and the EXISTS check on each will confirm the table you are querying from has corresponding records in the other tables.
SELECT *, 'OPEX' AS table_name
FROM OPEX o
WHERE EXISTS (
SELECT 1
FROM NOPEX n
WHERE n.asset_id = o.asset_id)
AND EXISTS (
SELECT 1
FROM CAPEX c
WHERE c.asset_id = o.asset_id)
UNION ALL
SELECT *, 'NOPEX' AS table_name
FROM NOPEX n
WHERE EXISTS (
SELECT 1
FROM Opex o
WHERE o.asset_id = n.asset_id)
AND EXISTS (
SELECT 1
FROM CAPEX c
WHERE c.asset_id = n.asset_id)
UNION ALL
SELECT *, 'CAPEX' AS table_name
FROM CAPEX c
WHERE EXISTS (
SELECT 1
FROM Opex o
WHERE o.asset_id = c.asset_id)
AND EXISTS (
SELECT 1
FROM NOPEX n
WHERE n.asset_id = c.asset_id)
I guess you could also do INNER JOINs?
SELECT c.*, 'CAPEX' AS table_name
FROM CAPEX c
INNER JOIN OPEX o
ON o.asset_id = c.asset_id
INNER JOIN NOPEX n
ON n.asset_id = c.asset_id
UNION ALL
SELECT o.*, 'OPEX' AS table_name
FROM OPEX o
INNER JOIN CAPEX c
ON c.asset_id = o.asset_id
INNER JOIN NOPEX n
ON n.asset_id = o.asset_id
UNION ALL
SELECT n.*, 'NOPEX' AS table_name
FROM NOPEX n
INNER JOIN OPEX o
ON o.asset_id = n.asset_id
INNER JOIN CAPEX c
ON c.asset_id = n.asset_id
Similar answer to dfundako, but resolving sooner where AssetId is in all three tables and less hitting of the indexes on the related tables:
;with cte as (
select
AssetID
from (
select distinct
AssetID
from Opex
union all
select distinct
AssetID
from Nopex
union all
select distinct
AssetID
from Capex
) as AssetIDs
group by AssetId
having count(AssetId) = 3
)
select 'Opex', * from Opex as o
inner join cte
on o.AssetID = cte.AssetID
union all
select 'Nopex', * from Nopex as n
inner join cte
on n.AssetID = cte.AssetID
union all
select 'Capex', * from Capex as c
inner join cte
on c.AssetID = cte.AssetID

Inner Join and select other columns on that table

This is a part of my query and I have a union before this query. I want to select more column in table1 under a condition.
SELECT c.ID, c.Name, c.Position, c.Email, c.ContactNumber
FROM table1 c
INNER JOIN table2 a
ON c.ID = (SELECT foreignID FROM table2 WHERE a.Name = 'someName')
WHERE Dept = 'Something' --this will return nothing since in the inner join
--the condition returns a single column and it doesn't
--satisfy the WHERE Clause
I want to select the person that satisfies the ID (which works), including the people with a Dept of Something. Is there another way in solving this, or I really need to use UNION for this. Will this affect the performance of an App, specifically Mobile App?
If I Understood your requirement correctly then Use LEFT JOIN
SELECT c.ID, c.Name, c.Position, c.Email, c.ContactNumber
FROM table1 c
LEFT JOIN table2 a ON c.ID = a.foreignID AND a.Name = 'someName'
WHERE Dept = 'Something'

LEFT JOIN - How to join tables and include extra row even if you have right match

I have two tables
Table A
-------
ID
ProductName
Table B
-------
ID
ProductID
Size
I want to join these two tables
SELECT * FROM
(SELECT * FROM A)
LEFT JOIN
(SELECT * FROM B)
ON A.ID = B.ProductID
This is easy, I will get all rows from A multiplied by rows matched in B, and NULL fields if there is no match.
But here comes the tricky question, how can I get all rows from A with NULL fields for table B, even if there is a match, so I get an extra line with NULL values plus all the matches?
SELECT A.*
, B3.ID
, B3.ProductID
, B3.Size
FROM A
LEFT JOIN
(
SELECT ProductID as MatchID
, ID
, ProductID
, Size
FROM B
UNION ALL
SELECT ID
, null
, null
, null
FROM A A2
) B3
ON A.ID = B3.MatchID
Live example at SQL Fiddle.
Instead of using UNION ALL in a subquery as suggested by others, you could also (and I would) use UNION ALL at the outer level, which keeps the query simpler:
SELECT A.ID, A.ProductName, B.ID, B.Size
FROM A
INNER JOIN B
ON B.ProductID = A.ID
UNION ALL
SELECT A.ID, A.ProductName, NULL, NULL
FROM A
Since every join is going to be successful, we can switch to a full/inner join:
SELECT
*
FROM
A
INNER JOIN
(SELECT ID,ProductID,Size FROM B
UNION ALL
SELECT NULL,ID,NULL FROM A) B
ON
A.ID = B.ProductID
Now would be a very good time to switch to naming columns explicitly, rather than using SELECT *
Or, if, as per #Andomar's comment, you need all of the B columns to be NULL:
SELECT
A.ID,A.ProductName,
B.ID,B.ProductID,B.Size
FROM
A
INNER JOIN
(SELECT ID,ProductID,Size,ProductID as MatchID FROM B
UNION ALL
SELECT NULL,NULL,NULL,ID FROM A) B
ON
A.ID = B.MatchID