Perform multiple left joins using Linq - linq

I have four tables. I want to perform multiple left join on a table as follows.
from t1 in table1
join t2 in table2 on <condition1>
into join1
from subJoin1 in join1.DefaultIfEmpty()
join t3 in table3 on <condition2>
into join2
from subjoin2 in join2.DefaultIfEmpty()
join t4 in table4 on <condition3>
into join3
from subjoin3 in join3.DefaultIfEmpty()
select new
{
Field1 = t1.some_field,
Field2 = t2.some_field,
Field3 = t3.some_field,
Field4 = t4.some_field,
}
My problem is the first left join alone works fine, but when left join is done consecutively, the t1 data row is lost if a mapping cannot be found in t3, t4 etc.
Please help.

You do not need into (at least right away)!
Something like
from t1 in table1
join t2 in table2 on <inner_join_condition>
from t3 in table3.Where(<left_outer_join_condition>).DefaultIfEmpty()
...
select new
{
Field1 = t1.some_field,
Field2 = t3.some_field,
...
}
works usually pretty well!

Related

Spring-data-JPA : Join query best practices

I have two tables T1 and T2.
I have to fetch the record from Table T1 where anotherColumn is null in T2 or not exists in T2.
Table T1 Entity relation with T2
#OneToMany(mappedBy="t2")
private List<T2> t2s;
Table T2 Entity relation with T1
#ManyToOne
#JoinColumn(name="pId")
private T1 t1;
In the above scenario, it should return 2nd and 3rd records from Table T1.
#Query("select t1 from T1 t1 where NOT EXISTS (select t2 from T2 t2 where t1.id = t2.pId) OR EXISTS (select t2 from T2 t2 where t1.id = t2.pId OR t2.anotherColumn=null)")
public List<T2> findDisconnected();
Since I'm using inner subqueries it is taking more time.
Could please someone helps me,
1) How can I optimize the above query?
2) What is the best way to use join queries in Spring-data-jpa?
Is this what you are looking for
select * from T1 t1 full join T2 t2 on t1.id = t2.pId where t2.anotherColumn is NULL
here you are full joining two tables and fetching all the records which have a null value in another column.

USING multiple 'OR' conditions in JOIN component in Oracle data Integrator 12c

I want to use multiple 'OR' conditions in JOIN component in Oracle data Integrator 12c.
Conditions to be taken care when doing the above task is:
Say table T1 and T2, I need to take left outer join on T1(i.e. I need all the records from T1 for multiple satisfied join conditions specified in JOIN component in ODI 12c)
For example:
a. For table T1, T2: say conditions c1, c2, c3. T1 Left outer join T2.
b. I want to get the data in table say T3: Ensuring all records from T1 PLUS all records from T2 for all the conditions satisfied(namely c1,c2,c3).
Sample query:
select T1.*
from T1 LEFT OUTER JOIN T2
ON (C1 OR C2 OR C3);
Kindly help me on this at the earliest.
Thanks in advance!
You can try either query both will get you all the rows from T1 that either matched with T2 columns respectively or didn't have any match with T2.
Using UNION
SELECT DISTINCT *
FROM (
SELECT T1.*
FROM T1
LEFT OUTER JOIN T2 ON T1.day = T2.day
UNION
SELECT T1.*
FROM T1
LEFT OUTER JOIN T2 ON T1.month = T2.month
UNION
SELECT T1.*
FROM T1
LEFT OUTER JOIN T2 ON T1.yearly = T2.yearly
) as T3;
Using OR (NOTE: displaying T2 columns just to show that LEFT JOIN is working on each condition)
SELECT T1.*, T2.*
FROM T1
LEFT OUTER JOIN T2 ON
(T1.day = T2.day OR T1.month = T2.month OR T1.yearly = T2.yearly)
Sample Run
I have 4 records in T1 and 3 records in T2. Records in T1 are such that 3 rows
match with exactly 1 column in T2 and 4th row doesnt match any records in T2.
Output of both the queries gets what you need.

replace a union query involving a left join

I have a left join and a Union .
I was thinking if it is possible to replace the union with a join
select t1.col1,t1.col2,t2.col1,
from t1
left join t2 on (t1.col1 = t2.col1)
union
select t3.col1,t3.col2,t2.col1
t2.col1,
from t3
left join t2 on (t3.col1 = t2.col1)
I am on Oracle 12c can this query be re-written without the union ?
I tried this but the results don't match the union query's results
select t1.col1,t3.col2,t2.col1,
from t1
left join t2 on (t1.col1 = t2.col1)
left join t3 on (t3.col1 = t2.col1 and t1.col1 = t2.col1)
Any ideas is this even possible ?
Here are a couple of alternatives for you:
Do the union of t1 and t3 first, before joining to t2:
select t.col1, t.col2, t2.col1
from (select col1, col2
from t1
union
select col1, col2
from t3) t
left outer join t2 on (t.col1 = t2.col1);
Use a full outer join of t1 and t3 first, before joining to t2:
select t.col1, t.col2, t2.col1
from (select coalesce(t1.col1, t3.col1) col1,
coalesce(t1.col2, t3.col2) col2
from t1
full outer join t3 on t1.col1 = t3.col1
and t1.col2 = t3.col2) t
left outer join t2 on (t.col1 = t2.col1);
N.B. Both are untested, since you neglected to provide any sample data in any of the three tables. You'll have to test these approaches yourself.
ETA: Also, I believe both these approaches should work for the example as given. If your real world scenario differs from that in your question, it's possible neither approach will be of use.
ETA2: Given you're wishing to tweak your query for readability, I'd go with my first proposed solution (assuming it works for you, of course!), since the union deduplicates the rows from tables t1 and t3 before joining to t2 - that way, you're reducing the work needed to do the join. Also, the union has less to check too (2 cols instead of 3).
It may have a union in it, but it's sure as heck nicer to read than my second suggestion! At least, in my opinion *{;-)

Subquery in group by (ORACLE 9i)

I have a query that looks similar to the one below (albeit more complicated). When running it I get the following error: ORA-22818: Subquery expressions not allowed here in my group by statement.
What is the best way for me to get around this issue?
SELECT table1.ID
NVL(fget_office(fget_last_catc_id_by_date((SELECT MAX(table3.date) FROM table3 INNER JOIN table1 ON table1.ID = table3.id),table1.NUM), fget_max_split_line_no('FILL',(SELECT MAX table3.tc_id) FROM table3 INNER JOIN table1 ON table1.ID = table3.ID INNER JOIN table4 ON table3.tc_id = table3.tc_id))), table1.distribution) "OFFICE", --eper.DISTRIBUTION "OFFICE",
table1.name
FROM table1
LEFT JOIN table2
ON table1.ID = table2.ID
WHERE table1.company in ('CP01', 'CP02')
GROUP BY table1.ID,
NVL(fget_office(fget_last_catc_id_by_date((SELECT MAX(table3.date) FROM table3 INNER JOIN table1 ON table1.ID = table3.id),table1.NUM), fget_max_split_line_no('FILL',(SELECT MAX table3.tc_id) FROM table3 INNER JOIN table1 ON table1.ID = table3.ID INNER JOIN table4 ON table3.tc_id = table3.tc_id))), table1.distribution),
table1.name
Your code sample looks like you're using GROUP BY just to pull distinct rows. In that case, try this:
SELECT DISTINCT
table1.ID
NVL(fget_office(fget_last_catc_id_by_date((SELECT MAX(table3.date) FROM table3 INNER JOIN table1 ON table1.ID = table3.id),table1.NUM), fget_max_split_line_no('FILL',(SELECT MAX table3.tc_id) FROM table3 INNER JOIN table1 ON table1.ID = table3.ID INNER JOIN table4 ON table3.tc_id = table3.tc_id))), table1.distribution) "OFFICE", --eper.DISTRIBUTION "OFFICE",
table1.name
FROM table1
LEFT JOIN table2
ON table1.ID = table2.ID
WHERE table1.company in ('CP01', 'CP02')
In case you really are doing aggregation in your "real" query, a quick workaround would be to use a Common Table Expression (CTE), which is supported in Oracle 9i. This example assumes you're summing a column named some_value:
WITH x AS (
SELECT table1.ID
NVL(fget_office(fget_last_catc_id_by_date((SELECT MAX(table3.date) FROM table3 INNER JOIN table1 ON table1.ID = table3.id),table1.NUM), fget_max_split_line_no('FILL',(SELECT MAX table3.tc_id) FROM table3 INNER JOIN table1 ON table1.ID = table3.ID INNER JOIN table4 ON table3.tc_id = table3.tc_id))), table1.distribution) "OFFICE", --eper.DISTRIBUTION "OFFICE",
table1.name,
some_value
FROM table1
LEFT JOIN table2
ON table1.ID = table2.ID
WHERE table1.company in ('CP01', 'CP02')
)
SELECT ID, OFFICE, name, SUM(some_value)
FROM x
GROUP BY ID, Office, name
It looks to me like the results of those functions are directly or indirectly determined by the values of table1.
If so, you can perform the distinct operation on a simple set of data from table1 and table2 and apply the functions afterwards. This would reduce the number of calls to the functions and improve efficiency.
with cte1 as (
select
table1.id
table1.num
table1.distribution,
table1.name
from
table1 left join table2 on (table1.id = table2.id)
where
table1.company in ('CP01', 'CP02'))
select
cte1.id,
coalesce(
fget_office(
fget_last_catc_id_by_date(
(select max(table3.date)
from table3 inner join cte1 on cte1.id = table3.id),
cte1.num),
fget_max_split_line_no(
'FILL',
(select max(table3.tc_id)
from table3 inner join cte1 on cte1.id = table3.id
inner join table4 on table3.tc_id = table3.tc_id))),
table1.distribution) office
cte1.name
from cte1
/
You might as well get used to using Coalesce() instead of Nvl() -- it's ANSI compliant, more flexible, and features short-circuit evaluation so it's handy of your codebase has a lot of PL/SQL functions that get called in SQL.

left outer join on nullable field with between in join condition (Oracle)

I have two tables as: table1 with fields c1 and dt(nullable); table2 with fields start_dt, end_dt and wk_id. Now I need to perform left outer join between the table1 and table2 to take wk_id such that dt falls between start_dt and end_dt. I applied following condition but some wk_id which shouldn't be NULL are pulled NULL and some rows get repeated.
where nvl(t1.dt,'x') between nvl(t2.start_dt(+), 'x') and nvl(t2.end_dt(+), 'x');
What is wrong with the condition?
select *
from table1 t1
left join table2 t2
on t1.dt between t2.start_dt and t2.end_dt
I recommend you try the new ANSI join syntax.
Also, are you just using 'x' as an example? Or are the dt columns really stored as strings?
It seems you are missing the part "table1 left outer join table2 on table1.some_field = table2.some_field"
Something like this:
select t1.c1, t1.dt, t2.start_dt, t2.end_dt, t2.wk_id
from table1 t1 left outer join table2 t2
on t1.some_field1 = t2.some_field1
where nvl(t1.dt,'x')
between nvl(t2.start_dt, 'x') and
nvl(t2.end_dt, 'x')

Resources