Issue combining inner and outer joins in Oracle - sql

I'm trying to simplify a query against an Oracle database that uses multiple inner joins against the same table. I know the multiple joins against the same table can be cleaned up with outer joins, but I'm failing to elicit the same results.
Below are the tables listing only the columns needed for the query. The "link" column in table X maps to the ID, ID2, and ID3 columns in tables A, B, and C respectively.
**Table A**
id
**Table B**
id
id2
nm
**Table C**
id3
nm
**Table X**
cfgnum
link
Existing (working query):
SELECT COUNT(*)
FROM A,
X,
X X1,
X X2,
B,
C
WHERE X.cfgnum = 9999
AND A.id = X.link
AND X.cfgnum = X1.cfgnum
AND X.cfgnum = X2.cfgnum
AND B.id2 = X1.link
AND B.id = A.id
AND C.id3 = X2.link
AND C.nm = B.nm;
The below was my attempt to rewrite, but it returns substantially different results. With the comments in place it does return the right number of rows against the linking X table.
SELECT COUNT(*)
FROM X
LEFT JOIN A ON X.link = A.id
LEFT JOIN B on X.link = B.id2 --AND A.id = B.id
LEFT JOIN C on X.link = C.id3 --AND B.nm = C.nm
WHERE cfgnum = 9999
AND COALESCE (A.id, B.id2, C.id3) IS NOT NULL;
Any help is appreciated!
UPDATE
Apologies, new at this, and it appears I did not provide enough information. Table X groups rows from other tables under a "cfgnum". Each "cfgnum" can link to numerous rows in numerous tables.
Hopefully the below sample data makes sense.
**Table A**
id
1
2
7
**Table B**
id id2 nm
3 1 name1
4 3 name2
**Table C**
id3 nm
5 name2
6 name3
**Table X**
cfgnum link
9998 1
9998 2
9998 3
9999 1
9999 3
9999 4
9999 6
9999 7
9999 8
My thought was using left joins to X would minimize the number of joins required to X. Notice the "working" query has X three times in the from clause. If I'm offbase, please let me know.
Thanks again.

There are no left joins in your previous query. So you can try this -
SELECT COUNT(*)
FROM A
JOIN X ON A.id = X.link
JOIN X1 ON X.cfgnum = X1.cfgnum
JOIN X2 ON X.cfgnum = X2.cfgnum
JOIN B ON B.id2 = X1.link
AND B.id = A.id
JOIN C ON C.id3 = X2.link
AND C.nm = B.nm
WHERE X.cfgnum = 9999

Updated:
If you have just one-to-one relationship, ie you have just 1 link to each of A,B,C for specific cfgnum, you can use aggregation:
SELECT COUNT(*)
FROM (select cfgnum,
max(decode(rownum,1,link)) link1,
max(decode(rownum,2,link)) link2,
max(decode(rownum,3,link)) link3
from X
WHERE cfgnum = 9999
group by cfgnum -- just in case if you need to remove the predicate "cfgnum=9999"
) x1
join A on A.id in (link1,link2,link3)
join B on B.id2 in (link1,link2,link3) and B.id = A.id
join C on C.id3 in (link1,link2,link3) and C.nm = B.nm;

Related

Hive left join - conditions on where

Given two tables
Table A
idA
v1
1
1
2
1
3
2
and
Table B
idB
v2
v3
1
1
a
2
2
b
2
1
a
I want to get all the values from Table A, plus the information on Table B (v3) where the two ids should be the same. This is easy - left outer join!
select *
from A
left join B on A.idA = B.idB
However, what if I need to get v1 = v2 ? I thought that I could just use where
select *
from A
left join B on A.idA = B.idB
where B.id is null or A.v1 = B.v2
Unfortunately, this removes all rows from the left table (A) that did not match any on B (in this example, idA = 3). Any solution?
EDIT: as #irnerd point out, the problem as stated is very simple (just extend the on clause). The actual issue comes when v1 becomes a timestamp, that as to be between v2 and v4 (timestamps) as in
select *
from A
left join B on A.idA = B.idB and a.v1 between b.v2 and b.v4
The previous query works fine in Oracle, but in Hive I get error...
Just extend the join clause to qualify the second join criteria
select *
from A
left join B on A.idA = B.idB
and A.v1 = B.v2

Left join but maintain values where NULL may result in Microsoft SQL

I have two tables:
Table A:
id names
1 a
2 b
3 c
and Table B:
id names
1 x
2 y
I'd like to perform a left join of Table B on Table A that results in the following table:
id names
1 x
2 y
3 c
How can I do this in Microsoft SQL?
You could use COALESCE:
SELECT a.id, COALESCE(b.name, a.name) AS name
FROM tab1 a
LEFT JOIN tab2 b
ON a.id = b.id
I think you just want coalesce():
select a.id, coalesce(b.name, a.name) as name
from a left join
b
on a.id = b.id;

Postgresql: inner join on 3 tables if data in 2. table is of no interest

Assuming I have the following three tables:
a
------
id
p1
b_id
b
------
id
c_id
other_value
c
------
id
p2
To get all the p1 and p2 values, I would write the following query:
SELECT
a.p1, c.p2
FROM
a
INNER JOIN
b ON (a.b_id = b.id)
INNER JOIN
c ON (b.c_id = c.id)
Now, since I'm not really interested in the other_value inside of b, I wondered if there is a simpler way to find the corresponding p2 value for each a entry.
(The reason for this question is that I'm running into performance problems on a database with a similar, but more complex, structure)
If you really insist on avoiding the b table in the outer scope, here is a nice ugly solution:
SELECT *
-- a.p1, c.p2
FROM a,c
WHERE EXISTS (
SELECT 42
FROM b
WHERE b.id = a.b_id
AND b.c_id = c.id
);
The same, using JOIN syntax (yields exactly the same query plan)
SELECT *
FROM a
JOIN c ON EXISTS (
SELECT 42
FROM b
WHERE b.id = a.b_id
AND b.c_id = c.id
);

Getting data from one table to another table using join

I have a table name "a"
Id name
1 abc
2 xyz
3 mmm
4 xxx
and Other table name is "b"
Id suId
3 2
3 1
My requirement is get detail from "a" table from "b" table where id=3. Any help?
SELECT a.Id, a.name, b.Id, b.suId FROM b JOIN a ON b.suId = a.Id WHERE b.Id = 3;
I wont recommend join for this kind of scenarios(you want all details from Table A whose ids are in Table B suId column, where Table B id should be 3., Its bad English but hope you got me and may be i got you too.)
SELECT a.name FROM a
WHERE
a.id IN(SELECT b.suId FROM b WHERE b.id = 3);
If you want to use join only then,
SELECT a.name FROM a,b
WHERE a.id = b.suId
AND
b.id = 3;
Simple answer:
SELECT a.Id,a.name FROM a,b
WHERE a.Id=b.suId AND b.Id=3
It will give you the result:
Id Name
1 abc
2 xyz
See result in SQL Fiddle
This should get the job done:
SELECT * FROM table_a a JOIN table_b b ON b.suId = a.Id WHERE b.Id = 3;
You can try this...You can try different JOIN clauses like INNER JOIN, LEFT OUTER JOIN or just simply JOIN etc. You will get different number of rows depending on field connections from 1 table to the other.
SELECT T1.*
FROM a T1
INNER JOIN b T2
ON T1.Id = T2.Id
WHERE T1.Id='3'

Bidirectional outer join

Suppose we have a table A:
itemid mark
1 5
2 3
and table B:
itemid mark
1 3
3 5
I want to join A*B on A.itemid=B.itemid both right and left ways. i.e. result:
itemid A.mark B.mark
1 5 3
2 3 NULL
3 NULL 5
Is there a way to do it in one query in MySQL?
It's called a full outer join and it's not supported natively in MySQL, judging from its docs. You can work around this limitation using UNION as described in the comments to the page I linked to.
[edit] Since others posted snippets, here you go. You can see explanation on the linked page.
SELECT *
FROM A LEFT JOIN B ON A.id = B.id
UNION ALL
SELECT *
FROM A RIGHT JOIN B ON A.id = B.id
WHERE A.id IS NULL
Could do with some work but here is some sql
select distinct T.itemid, A.mark as "A.mark", B.mark as "B.mark"
from (select * from A union select * from B) T
left join A on T.itemid = A.itemid
left join B on T.itemid = B.itemid;
This relies on the left join, which returns all the rows in the original table (in this case this is the subselect table T). If there are no matches in the joined table, then it will set the column to NULL.
This works for me on SQL Server:
select isnull(a.id, b.id), a.mark, b.mark
from a
full outer join b on b.id = a.id