JOIN on column only if NOT NULL - sql

I'm in the process of re-writing an old SQL query and have troubles making sense out of it. It contains several conditions of the form
SELECT ...
FROM a, b, c
WHERE
c.id = ...
AND (
a.x_id IS NULL
OR a.x_id = c.x_id
)
AND b.id = a.b_id (+)
Can this query be rewritten using proper JOIN syntax? Is it equivalent to the following or will it produce different results under certain circumstances?
SELECT ...
FROM b
LEFT JOIN a
ON b.id = a.b_id
LEFT JOIN c
ON a.x_id = c.x_id
WHERE c.id = ...
The original query is 100 lines long and spans 5 tables, plus several joins over "virtual tables" (i.e. where conditions of the form x.z_id = y.z_id), which makes it hard to break down into more manageable bits or debug.

if you want same result as you have in first query - you must make left join only with table a, like this :
SELECT ...
FROM b, c
LEFT JOIN a
ON b.id = a.b_id and b.id = a.b_id
WHERE
c.id = ... b.c_id
or if you want the same style with all tables, you can use inner join with table b, like this :
SELECT ...
FROM c
INNER JOIN b
on b.c_id = c.id
LEFT JOIN a
ON b.id = a.b_id
WHERE
c.id = ...
in my both query we select data from table b where column is not null

Related

Access SQL - Joins on multple tables

I have come across a Join Error in Access SQL, when using multiple "ON" criteria's. I am unable to perform an ON clause on 2 different tables, for example:
select *
from
(((A
left join B on a.id = b.id)
left join c on c.id = b.id)
left join D
on (d.id = b.id) and (d.id = a.id)
That final join statement causes an error because I link table D on table B first, and then link Table D on Table A. If I choose to instead link table D on Table B again, then it resolves. However, I need to join it this way due to the certain data I need to link Table D on from both tables.
How can I more efficiently structure my query to achieve my results?
you may try this select * from A left join B on a.id = b.id left join c on c.id = b.id left join D on d.id = b.id and d.id = a.id
It's somewhat difficult to tell what you're trying to do exactly, but most likely, this is what you want:
select *
from
(((A
left join B on a.id = b.id)
left join C on c.id = b.id)
left join D on d.id = a.id)
Since you're trying a LEFT JOIN, there is no reason to link multiple ids to eachother.

Does inner join order and where as an impact on performance [duplicate]

This question already has answers here:
Order Of Execution of the SQL query
(7 answers)
Closed 8 years ago.
Are these 2 queries equivalent in performance ?
select a.*
from a
inner join b
on a.bid = b.id
inner join c
on b.cid = c.id
where c.id = 'x'
and
select a.*
from c
inner join b
on b.cid = c.id
join a
on a.bid = b.id
where c.id = 'x'
Does it join all the table first then filter the condition, or is the condition applied first to reduce the join ?
(I am using sql server)
The Query Optimizer will almost always filter table c first before joining c to the other two tables. You can verify this by looking into the execution plan and see how many rows are being taken by SQL Server from table c to participate in the join.
About join order: the Query Optimizer will pick a join order that it thinks will work best for your query. It could be a JOIN b JOIN (filtered c) or (filtered c) JOIN a JOIN b.
If you want to force a certain order, include a hint:
SELECT *
FROM a
INNER JOIN b ON ...
INNER JOIN c ON ...
WHERE c.id = 'x'
OPTION (FORCE ORDER)
This will force SQL Server to do a join b join (filtered c). Standard warning: unless you see massive performance gain, most times it's better to leave the join order to the Query Optimizer.
Read about http://www.bennadel.com/blog/70-sql-query-order-of-operations.htm
The execution order is FROM then WHERE, in this case or in any other cases I don't think the WHERE clause is executed before the JOINS .
select a.*
from (select * from c where c.id = 'x') c
inner join b
on b.cid = c.id
inner join a
on a.bid = b.id
This can create difference in execution.

Stuck on multiple table filtering query

Ok so I will try to simplify the problem that I have.
I have 4 tables:
TableA:
OneID
TableB:
OneID (FK to TableA)
TwoID (FK to TableC)
TableC:
TwoID
ThreeID (FK to TableD)
TableD:
ThreeID
I need a query to retrieve data from all 4 of these tables.
The query criteria is:
want to inner join tables A, B, C
want to join above result with table D with the following conditions:
if a record is in Table D but not in Table C, then it must always be present in the results
otherwise if a record is in Table D and Table C, then it should only be present if it is in the result of the A,B,C join
The scenario you have described is not really possible (or at least they are not really logical)
if a record is in Table D but not in Table C, then it must always be present in the results
The only way a record could be "in Table D and not in Table C" is if the foreign key is null in table D, with no link from table D to tables A or B there is no other way you could define a record as being present in D and not in C:
otherwise if a record is in Table D and Table C, then it should only be present if it is in the result of the A,B,C join
Again, the only way this could happen is with NULLABLE foreign keys. Regardless I think any of the below will get you the results you require:
SELECT A.OneID, B.TwoID, c.ThreeID, D.FourID
FROM D
LEFT JOIN (C
INNER JOIN B
ON B.TwoID = C.TwoID
INNER JOIN A
ON A.OneID = B.OneID)
ON C.ThreeID = D.ThreeID;
Or
SELECT A.OneID, B.TwoID, c.ThreeID, D.FourID
FROM A
INNER JOIN B
ON B.OneID = A.OneID
INNER JOIN C
ON C.TwoID = B.TwoID
RIGHT JOIN D
ON D.ThreeID = C.ThreeID
Or
SELECT A.OneID, B.TwoID, c.ThreeID, D.FourID
FROM A
INNER JOIN B
ON B.OneID = A.OneID
INNER JOIN C
ON C.TwoID = B.TwoID
INNER JOIN D
ON D.ThreeID = C.ThreeID
UNION ALL
SELECT NULL, NULL, NULL, FourID
FROM D
WHERE ThreeID IS NULL;
Examples on SQL Fiddle
The first two have the same execution plan, it is just a matter of preference, I personally dislike using RIGHT JOIN because it makes queries feel like they in the wrong order i.e bottom to top, but this is purely my preference. The last query may perform better depending on the cardinality of your data and any indexes you have
EDIT
With your revised criteria I think the easiest way to implement this is with a UNION ALL:
SELECT A.OneID, B.TwoID, c.ThreeID, d3 = D.ThreeID
FROM A
INNER JOIN B
ON B.OneID = A.OneID
INNER JOIN C
ON C.TwoID = B.TwoID
INNER JOIN D
ON D.ThreeID = C.ThreeID
UNION ALL
SELECT NULL, NULL, NULL, ThreeID
FROM D
WHERE NOT EXISTS (SELECT 1 FROM C WHERE C.ThreeID = D.ThreeID);
Example on SQL Fiddle
I am not 100% sure I understood you correctly but I will try to help anyway. It seems that you need to do FULL OUTER JOIN on table D:
SELECT
*
FROM
TableA AS A INNER JOIN
TableB AS B ON B.A_Id = A.Id INNER JOIN
TableC AS C ON C.B_Id = B.Id FULL OUTER JOIN
TableD AS D ON D.C_Id = C.Id
If I have misunderstood your requirements and you need more complicated criteria, you could just do FULL OUTER JOIN on all the tables and put extra conditions in WHERE part:
SELECT
*
FROM
TableA AS A FULL OUTER JOIN
TableB AS B ON B.A_Id = A.Id FULL OUTER JOIN
TableC AS C ON C.B_Id = B.Id FULL OUTER JOIN
TableD AS D ON D.C_Id = C.Id
WHERE
--if a record is in Table D but not in Table C, then it must always be present in the results
(D.Id IS NOT NULL AND C.Id IS NULL) OR
(
--otherwise if a record is in Table D and Table C, then it should only be present if it is in the result of the A,B,C join
(D.Id IS NOT NULL AND C.Id IS NOT NULL) AND
--want to inner join tables A, B, C
(A.Id IS NOT NULL AND B.Id IS NOT NULL AND B.Id IS NOT NULL)
)

How to use oracle outer join with a filter where clause

If i write a sql:
select *
from a,b
where a.id=b.id(+)
and b.val="test"
and i want all records from a where corresponding record in b does not exist or it exists with val="test", is this the correct query?
You're much better off using the ANSI syntax
SELECT *
FROM a
LEFT OUTER JOIN b ON( a.id = b.id and
b.val = 'test' )
You can do the same thing using Oracle's syntax as well but it gets a bit hinkey
SELECT *
FROM a,
b
WHERE a.id = b.id(+)
AND b.val(+) = 'test'
Note that in both cases, I'm ignoring the c table since you don't specify a join condition. And I'm assuming that you don't really want to join A to B and then generate a Cartesian product with C.
Move the condition into the JOIN clause and use the ANSI standard join pattern.
SELECT NameYourFields,...
FROM A
LEFT OUTER JOIN B
ON A.ID = B.ID
AND B.VAL = 'test'
INNER JOIN C
ON ...
A LEFT OUTER JOIN is one of the JOIN operations that allow you to specify a join clause. It preserves the unmatched rows from the first (left) table, joining them with a NULL row in the shape of the second (right) table.
So you can do as follows :
SELECT
FROM a LEFT OUTER JOIN b
ON a.id = b.id
--Note that you have used double quote "test" which is not used for varchar in SQL you should use single quote 'test'
AND b.val = 'test';
SELECT * FROM abc a, xyz b
WHERE a.id = b.id
AND b.val = 'test'

How I can retrieve all records with 1-2 queries using regular SQL and Linq-To-Entities?

I uploaded simplified SQL ERD on Picasa.
I tried to do it but I manage to it with more than 2 queries.
I need to retrieve all records from table D with specific E_Id of table E and specific TypeId of table A
UPDATED: I need also that all records from B that mapped to TypeId will be alse retrieved even if not all of them are mapped in table C (some kind of LEFT-RIGHT JOIN)
I need to do it at most 2 queries and then convert it to Linq-To-Entites statement
If it can be done with 1 query I prefer with 1 query
Thank you
SQL query:
SELECT D.*
FROM D
INNER JOIN C ON C.Id = D.C_Id
INNER JOIN B ON B.Id = C.B_Id
INNER JOIN A ON A.Id = B.A_Id
WHERE D.E_Id = #eId AND A.TypeId = #typeId
SELECT B.*
FROM B
INNER JOIN A ON A.Id = B.A_Id
WHERE A.TypeId = #typeId
Linq directly (if navigation properties don't exists):
var query = from d in context.D
join c in context.C on c.Id equals d.cId
join b in context.B on b.Id equals c.bId
join a in context.A on a.Id equals b.aId
where d.eId == eId && a.TypeId == typeId
select d;
var query2 = from b in context.B
join a in context.A on a.Id equals b.aId
where a.TypeId == typeId
select b;
Linq if navigation properties are correctly set up:
var query = from d in context.D
where d.E.Id == eId &&
d.C.B.A.TypeId == typeId
select d;
var query2 = from b in context.B
where b.A.TypeId == typeId
select b;
Any reason why the below won't work for you?
SELECT D.*
FROM D
INNER JOIN C
ON C.Id = D.C_Id
INNER JOIN B
ON B.Id = C.B_Id
INNER JOIN A
ON A.Id = B.A_Id
WHERE D.E_id = <The E_Id> AND A.Typeid = <The Typeid>