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

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

Related

How to join large subset of data with smaller subset data

I have three tables in SQL Server
TABLE_A - contains 500 rows
TABLE_B - contains 1 million rows
TABLE_C - contains 1 million rows
I want to select the rows from TABLE_B and TABLE_C join with TABLE_A based on a row number position from TABLE_B and TABLE_C tables.
Below is my sample query:
SELECT TOP (50), *
INTO ##tempResult
FROM TABLE_A
LEFT JOIN
(SELECT *
FROM
(SELECT
memberID,
ROW_NUMBER() OVER (PARTITION BY TABLE_A.member_id ORDER BY TABLE_A EM.UTupdateDate DESC) AS rowNum,
FROM
TABLE_B
JOIN
TABLE_C ON TABLE_B.memberID = TABLE_C.memberID
)
) AS TABLE_subset
WHERE
TABLE_subset.rowNum <=2
) AS TABLE_INC ON TABLE_A.memberID = TABLE_INC.memberID
WHERE TABLE_A.colA = 'XYZ'
Here the TABLE_subset is joining entire records in TABLE_B and TABLE_C, but I want to join only the top 50 records with TABLE_A.
Is there any way to achieve this ?
Your question and query doesn't match exactly, but CROSS APPLY is probably your friend here.
The general idea is:
select TOP 50 *
from tableA a
CROSS APPLY (
SELECT TOP 2 b.id, c.otherid
from tableB b
inner join tableC c
ON c.id = b.id
where b.id = a.id -- Here you match field between A and B
order by b.date DESC -- order by something
) data
Now just need to adapt to your needs

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

Joining with a distinct column

How do I join a table with a distinct value in a SQL view?
My code looks like below
Select a, b, c, d from TableA inner join TableB on TableA.account = TableB.account
I want TableB.account to have distinct values when I join the table.
The selected fields(a,b,c,d) do not have to be distinct values.
SELECT distinct_foo.thing
, bar.other_things
FROM (
SELECT DISTINCT
thing
FROM foo
) AS distinct_foo
INNER
JOIN bar
ON bar.thing = distinct_foo.thing
;

oracle sql join not correct

I want to sum two tables. Both tables contain the number of rowas grouped by a category:
number category
5 A
4819 B
1 C
number category
12043 B
I tried the following:
select a.category, a.number + b.number as number
from a
right join b
on a.category = b.category
I get:
number category
4 16862
I don't understand why I don't get all three categories. I trief left join instead of right join.
The problem with you right join is that a table should appear on the right side of the join. Or, using a left join, we could write:
SELECT
a.category,
a.number + COALESCE(b.number, 0) AS number
FROM tablea a
LEFT JOIN tableb b
ON a.category = b.category;
But this answer assumes that the a table would contain every category which you want to appear in your result set. For a more general solution, we might have to use a full outer join:
SELECT
COALESCE(a.category, b.category) AS category,
COALESCE(a.number, 0) + COALESCE(b.number, 0) AS number
FROM tablea a
FULL OUTER JOIN tableb b
ON a.category = b.category;
You can use union all and aggregation:
select category ,sum(number) value
from (
select category, number from tablea
union all select category, number from tableb
)
group by category
Another option is to do a full outer join: this would allow values coming from both sides of the join:
select
coalesce(ta.category, tb.category) number,
sum(coalesce(ta.number, 0) + coalesce(tb.number, 0)) value
from tablea ta
full outer join tableb tb on ta.category = tb.category
group by coalesce(ta.category , tb.category )
I should have used nvl(). This works:
select a.category, nvl(a.number, 0) + nvl(b.number, 0) as number
from a
right join b
on a.category = b.category

Value present in more than one table

I have 3 tables. All of them have a column - id. I want to find if there is any value that is common across the tables. Assuming that the tables are named a.b and c, if id value 3 is present is a and b, there is a problem. The query can/should exit at the first such occurrence. There is no need to probe further. What I have now is something like
( select id from a intersect select id from b )
union
( select id from b intersect select id from c )
union
( select id from a intersect select id from c )
Obviously, this is not very efficient. Database is PostgreSQL, version 9.0
id is not unique in the individual tables. It is OK to have duplicates in the same table. But if a value is present in just 2 of the 3 tables, that also needs to be flagged and there is no need to check for existence in he third table, or check if there are more such values. One value, present in more than one table, and I can stop.
Although id is not unique within any given table, it should be unique across the tables; a union of distinct id should be unique, so:
select id from (
select distinct id from a
union all
select distinct id from b
union all
select distinct id from c) x
group by id
having count(*) > 1
Note the use of union all, which preserves duplicates (plain union removes duplicates).
I would suggest a simple join:
select a.id
from a join
b
on a.id = b.id join
c
on a.id = c.id
limit 1;
If you have a query that uses union or group by (or order by, but that is not relevant here), then you need to process all the data before returning a single row. A join can start returning rows as soon as the first values are found.
An alternative, but similar method is:
select a.id
from a
where exists (select 1 from b where a.id = b.id) and
exists (select 1 from c where a.id = c.id);
If a is the smallest table and id is indexes in b and c, then this could be quite fast.
Try this
select id from
(
select distinct id, 1 as t from a
union all
select distinct id, 2 as t from b
union all
select distinct id, 3 as t from c
) as t
group by id having count(t)=3
It is OK to have duplicates in the same table.
The query can/should exit at the first such occurrence.
SELECT 'OMG!' AS danger_bill_robinson
WHERE EXISTS (SELECT 1
FROM a,b,c -- maybe there is a place for old-style joins ...
WHERE a.id = b.id
OR a.id = c.id
OR c.id = b.id
);
Update: it appears the optimiser does not like carthesian joins with 3 OR conditions. The below query is a bit faster:
SELECT 'WTF!' AS danger_bill_robinson
WHERE exists (select 1 from a JOIN b USING (id))
OR exists (select 1 from a JOIN c USING (id))
OR exists (select 1 from c JOIN b USING (id))
;