Restricting a LEFT JOIN - sql

I have a table, let's call it "a" that is used in a left join in a view that involves a lot of tables. However, I only want to return rows of "a" if they also join with another table "b". So the existing code looks like
SELECT ....
FROM main ...
...
LEFT JOIN a ON (main.col2 = a.col2)
but it's returning too many rows, specifically ones where a doesn't have a match in b. I tried
SELECT ...
FROM main ...
...
LEFT JOIN (
SELECT a.col1, a.col2
FROM a
JOIN b ON (a.col3 = b.col3)) ON (a.col2 = main.col2)
which gives me the correct results but unfortunately "EXPLAIN PLAN" tells that doing it this way ends up forcing a full table scan of both a and b, which is making things quite slow. One of my co-workers suggested another LEFT JOIN on b, but that doesn't work because it gives me the b row when it's present, but doesn't stop returning the rows from a that don't have a match in b.
Is there any way to put the main.col2 condition in the sub-SELECT, which would get rid of the full table scans? Or some other way to do what I want?

SELECT ...
FROM ....
LEFT JOIN ( a INNER JOIN b ON .... ) ON ....

add a where (main.col2 = a.col2)
just do a join instead of a left join.

What if you created a view that gets you the "a" to "b" join, then do your left joins to that view?

Select ...
From Main
Left Join a on main.col2 = a.col2
where a.col3 in (select b.col3 from b) or a.col3 is null
you may also need to do some indexing on a.col3 and b.col3

First define your query between table "a" and "b" to make sure it is returning the rows you want:
Select
a.field1,
a.field2,
b.field3
from
table_a a
JOIN table_b b
on (b.someid = a.someid)
then put that in as a sub-query of your main query:
select
m.field1,
m.field2,
m.field3,
a.field1 as a_field1,
b.field1 as b_field1
from
Table_main m
LEFT OUTER JOIN
(
Select
a.field1,
a.field2,
b.field3
from
table_a a
JOIN table_b b
on (b.someid = a.someid)
) sq
on (sq.field1 = m.field1)
that should do it.
Ahh, missed the performance problem note - what I usually end up doing is putting the query from the view in a stored procedure, so I can generate the sub-queries to temp tables and put indexes on them. Suprisingly faster than you would expect. :-)

Related

Select statement with default values

When I have have a statement like the one below, is there a way to select all values from tableA including those that don't connect to tableB (with b.value being NULL or empty)? Thanks for any help.
SELECT DISTINCT a.value, b.value
FROM tableA a
LEFT JOIN tableB b ON ....
The behavior you described is exactly what a left join would do. There's nothing extra you need to do (except for completing the join condition you left out, of course).

join or merge two table based on id merge

I have two tables:
I am looking for the results like mentioned in the last.
I tried union (only similar col can be merged), left join, right join i am getting repeated fields in Null areas what can be other options where i can get null without column repeating
A full join would get all results from both tables.
select
A.ID,
A.ColA,
A.ColB,
B.ColC,
B.ColD
from TableA A
full join Table B on A.ID = B.ID
Here is a good post to understand joins
You can try distinct:
select distinct * from
tableA a,
tableB b
where a.id = b.id;
It will not give any duplicate tuples.

Select two columns one join

Given this SQL query:
select A.field1 as field
from A left join B
on A.field1 = B.field1
union all
select A.field2 as field
from A left join B
on A.field2 = B.field1;
Is there some way to get the same with few lines? i.e: is there another way to get two columns from a table A joined each one separated with a column from the table B, and then both put into the same resultset column?
The reason for needing that is the real query has a more complex join and where conditions, and for that is a bit big and, almost all, redundant.
Thank you in advance!
With the left join and assuming that the join to B doesn't produce duplicates, then your query is more simply written as:
select A.field1 as field from A
union all
select A.field2 from A;
The left join to B doesn't do anything in this case.
I assume your query is more complicated or you intend an inner join. You could do the union all before the join:
select A.field
from (select field1 from A union all
select field2 from A
) a join
B
on A.field = B.field1 ;
Whether or not this has worse or the same performance depends on the nature of the data.

Change join condition based on a condition - Oracle

assume we have two tables.
TabA(Acol1,Acol2,Acol3)
TabB(Bcol1,Bcol2.Ccol3)
Requirement is like, join two tables on Acol1,Bcol1 and if Acol3='C' then join based on Acol2=Bcol2 in addition to above join. Can we make this in single SQL query ? Is join is record wise or table wise ?
One solution I can get to is using Union, but I dont think this will be a optimized one. Any other solutions ?
Another solution I figured
SELECT A.*,B.* FROM TabA A
INNER JOIN TabB B ON A.Acol1 = B.BCol1
and case when A.Acol3='C' then A.ACol2 else '1' end =
case when A.Acol3='C' then B.BCol2 else '1' end ;
Any other solution without case and Union ?
Thanks in advance
If you want to join on TabA.col2 only when it is 'C' then in those case, TabB.col2 will also be 'C', as you are already joining from col1. So your output will be same which you get just by first join.
select a.*, b.* from tabA a join tabB b
on a.col1=b.col1
This should give you the same output anytime. Try creating a different scenario on values of 'C'. The result will always be a subset of your first join result.
Hmmm, after thinking about the question, I think you might just want a complicated on clause:
select a.*, b.*
from tabA a join
tabB b
on a.col1 = b.col1 and
(a.col3 <> 'C' or a.col2 = b.col2);
Note: the above assumes that a.col2 is not null (that condition is easily included if needed).
You may need to work out some examples by hand to see that the or method is equivalent to the case statement.

How can I implement SQL INTERSECT and MINUS operations in MS Access

I have researched and haven't found a way to run INTERSECT and MINUS operations in MS Access. Does any way exist
INTERSECT is an inner join. MINUS is an outer join, where you choose only the records that don't exist in the other table.
INTERSECT
select distinct
a.*
from
a
inner join b on a.id = b.id
MINUS
select distinct
a.*
from
a
left outer join b on a.id = b.id
where
b.id is null
If you edit your original question and post some sample data then an example can be given.
EDIT: Forgot to add in the distinct to the queries.
INTERSECT is NOT an INNER JOIN. They're different. An INNER JOIN will give you duplicate rows in cases where INTERSECT WILL not. You can get equivalent results by:
SELECT DISTINCT a.*
FROM a
INNER JOIN b
on a.PK = b.PK
Note that PK must be the primary key column or columns. If there is no PK on the table (BAD!), you must write it like so:
SELECT DISTINCT a.*
FROM a
INNER JOIN b
ON a.Col1 = b.Col1
AND a.Col2 = b.Col2
AND a.Col3 = b.Col3 ...
With MINUS, you can do the same thing, but with a LEFT JOIN, and a WHERE condition checking for null on one of table b's non-nullable columns (preferably the primary key).
SELECT DISTINCT a.*
FROM a
LEFT JOIN b
on a.PK = b.PK
WHERE b.PK IS NULL
That should do it.
They're done through JOINs. The old fashioned way :)
For INTERSECT, you can use an INNER JOIN. Pretty straightforward. Just need to use a GROUP BY or DISTINCT if you have don't have a pure one-to-one relationship going on. Otherwise, as others had mentioned, you can get more results than you'd expect.
For MINUS, you can use a LEFT JOIN and use the WHERE to limit it so you're only getting back rows from your main table that don't have a match with the LEFT JOINed table.
Easy peasy.
Unfortunately MINUS is not supported in MS Access - one workaround would be to create three queries, one with the full dataset, one that pulls the rows you want to filter out, and a third that left joins the two tables and only pulls records that only exist in your full dataset.
Same thing goes for INTERSECT, except you would be doing it via an inner join and only returning records that exist in both.
No MINUS in Access, but you can use a subquery.
SELECT DISTINCT a.*
FROM a
WHERE a.PK NOT IN (SELECT DISTINCT b.pk FROM b)
I believe this one does the MINUS
SELECT DISTINCT
a.CustomerID,
b.CustomerID
FROM
tblCustomers a
LEFT JOIN
[Copy Of tblCustomers] b
ON
a.CustomerID = b.CustomerID
WHERE
b.CustomerID IS NULL