WHERE clause to restrict rows left join - oracle

I don't understand why these two queries below fetch different count. Case 1 below fetches more rows while Case 2 fetches fewer rows. If the where clause is put outside, fewer records are fetched.
Case 1
SELECT COUNT(1)
FROM (
SELECT *
FROM (SELECT * FROM TABLE1 WHERE COL1 = 123) A
LEFT JOIN TABLE2 B ON B.COL2=A.COL4
LEFT JOIN TABLE3 C ON C.COL3=B.COL2
)
Case 2
SELECT COUNT(1)
FROM (
SELECT *
FROM (SELECT * FROM TABLE1 ) A
LEFT JOIN TABLE2 B ON B.COL2=A.COL4
LEFT JOIN TABLE3 C ON C.COL3=B.COL2
)
WHERE COL1 = 123

Theoretical explanation:
Consider a left outer join of tables A and B. A condition (filter) on table B has different effects if it is in the join condition (ON clause) vs. in the WHERE clause. EDIT: The filter on B being in the ON condition is equivalent to replacing B with a subquery where the filter is applied first (similar to the OP's example).
If it's in the ON clause, then the rows in table B are filtered for that condition, and then the left join is performed. Then the result of the query will include rows from A (with NULL for the B side) whenever there are no rows in B that satisfy the filter and match the row in A on the join condition.
On the other hand, if the filter on B comes later in the execution, in a WHERE clause, then the left join is performed first. Only then is the WHERE clause applied. The WHERE clause is very likely (depending on the conditions on B) to reject all the rows from A that didn't have a matching row in B - because for such rows, all the values from B are NULL.
In your case, assuming COL1 only exists in table B, then the condition COL1=123 in a WHERE clause will effectively cause the left join to produce the same result as an inner join: any rows from A that didn't have a match in B will come from the left join with COL1 as NULL, so they will fail the filter condition. When you put COL1=123 in the ON clause, that check is done BEFORE the "outer join" operation.

Related

Oracle: Joining two table with a common column plus a additional column(latest effective date) from second table to select other column

Joining two table with a common column plus an additional column (latest effective date) from the second table to select another column. I joined the tables with all conditions, but the resulting table has duplicate records as there are multiple records in Table2 for the same identifier, out of which I just need the record with the latest effective date. I am using Oracle.
Table1 (Column A, B, C, D);Table2 (C, Efft_date, X),Table3...
The result should be as below after Joining the tables: A, B, C, X and columns from other tables
Value of X depends on latest/max value of efft_date from Table2.
Other Info: There are other tables that are joined and other conditions in where clause.
Please help to join the tables without duplicates
So you need to further restrict your result set based on the max of table2.efft_date. So you need a clause like:
AND table2.efft_date = ( SELECT MAX( table2b.efft_date)
FROM table2 AS table2b
WHERE table2b.c = table2.c )
This assumes that table2 cannot have duplicate efft_date values.
You need to join to a subquery so that you get just 1 "latest date" for each value of column c. For this I recommend using row_number()
select t1.A, t1.B, t1.C, t1.D, t2.x, t2.efft_date
from table1 t1
inner join (
select c, x, efft_date, row_number() over(partition by c order by efft_date DESC) as rn
from table2
) t2 on t1.c = t2.c and t2.rn = 1
...
Note that by ordering the dates in descending values, the most recent date will be assigned the row number of 1. Hence the join condition and rn = 1 will only allow the most recent dates to be included in the result.
Changing the order to ascending date order would do the revers, just allow the earliest dates.

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.

Difference between standard outer left join and join using select

Is there any difference between these two statements:
-- Statement 1:
SELECT *
FROM Table1 t1
LEFT OUTER JOIN TABLE2 t2 on t1.id = t2.id
and
-- Statement 2:
SELECT *
FROM Table1 t1
LEFT OUTER JOIN (SELECT id, a, b, c FROM Table2) t2 on t1.id = t2.id
I'm not an expert but statement 2 just looks like poorly written sql, and like it would take much longer. I'm attempting to optimize a code block and it has many joins like the second one. Are they technically the same and I can just replace with the standard join statement 1?
Thanks!
Ps. This is Oracle, and working with 100's of millions of rows.
PSS. I'm doing my own detective work to figure out if they are the same, and time differences, was hoping an expert could explain if there is a technical difference what it is.
They are not same queries, with the lack of a criteria in the subquery that depends on whether the all columns and all column names of the TABLE2 is involved in the subquery. If the subquery involves all of the column names of the TABLE2 in the select list then they are the same query and the subquery is unnecessary. With subquery I refer to the part with a select statement after the join statement in the parens.
The first one uses the TABLE2 with its all columns, all those columns will be available in the result set where the criteria met.
However in the second one the table you make the JOIN is not the TABLE2 of yours but a table with just columns from TABLE2 specified in the subquery's SELECT list, namely id, a, b, and c. But it will have all the rows after this subquery since no criteria is enforced on it by a WHERE clause in the subquery.
You will have same number of rows with only selected columns participating from the TABLE2.
The second one is not necessarily the poorly written one. You could have a criteria to met before you JOIN to the TABLE2.

How to left join with conditions in Toad Data Point Query Builder?

I'm trying to build a query in Toad Data Point. I have a subquery that has a row number to identify the records I'm interested in. This subquery needs to be left joined onto the main table only when the row number is 1. Here's the query I'm trying to visualize:
SELECT distinct E.EMPLID, E.ACAD_CAREER
FROM PS_STDNT_ENRL E
LEFT JOIN (
SELECT ACAD_CAREER, ROW_NUMBER() OVER (PARTITION BY ACAD_CAREER ORDER BY EFFDT DESC) as RN
FROM PS_ACAD_CAR_TBL
) T on T.ACAD_CAREER = E.ACAD_CAREER and RN = 1
When I try to replicate this, the row number condition is placed in the global WHERE clause. This is not the intended functionality because it removes any records that don't have a match in the subquery effectively making it an inner join.
Here is the query it's generating:
SELECT DISTINCT E.EMPLID, E.ACAD_CAREER, T.RN
FROM SYSADM.PS_STDNT_ENRL E
LEFT OUTER JOIN
(SELECT PS_ACAD_CAR_TBL.ACAD_CAREER,
ROW_NUMBER ()
OVER (PARTITION BY ACAD_CAREER ORDER BY EFFDT DESC)
AS RN
FROM SYSADM.PS_ACAD_CAR_TBL PS_ACAD_CAR_TBL) T
ON (E.ACAD_CAREER = T.ACAD_CAREER)
WHERE (T.RN = 1)
Is there a way to get the query builder to place that row number condition on the left join instead of the global WHERE clause?
I found a way to get this to work.
Add a calculated field to the main table with a value of 1.
Join the row number to this new calculated field.
Now the query has the filter in the join condition instead of the WHERE clause so that it joins as intended. Here is the query it made:
SELECT DISTINCT E.EMPLID, E.ACAD_CAREER, T.RN
FROM SYSADM.PS_STDNT_ENRL E
LEFT OUTER JOIN
(SELECT PS_ACAD_CAR_TBL.ACAD_CAREER,
ROW_NUMBER ()
OVER (PARTITION BY ACAD_CAREER ORDER BY EFFDT DESC)
AS RN
FROM SYSADM.PS_ACAD_CAR_TBL PS_ACAD_CAR_TBL) T
ON (E.ACAD_CAREER = T.ACAD_CAREER) AND (1 = T.RN)

I want to update T1 using T3 column and by using three table relationships

UPDATE TABLE1 T1 SET T1.CENTERNAME=
(SELECT AC.CENTERNAME
FROM TABLE2 T2 INNER JOIN TABLET3 AN ON T2.CENTERID = T3.LOCATIONID
INNER JOIN TABLE1 T1 ON T3.LOG_ID = T1.LOGID W
HERE TRUNC(T1.ROW_DATE)='25-MAR-2014');
This gives the error 'ORA-01427: single-row subquery returns more than one row'.
The error message
ORA-01427: single-row subquery returns more than one row
means, er, the sub-query returns more than row. That is, this part of your statement ...
(SELECT AC.CENTERNAME
FROM TABLE2 T2 INNER JOIN TABLET3 AN ON T2.CENTERID = T3.LOCATIONID
INNER JOIN TABLE1 T1 ON T3.LOG_ID = T1.LOGID
WHERE TRUNC(T1.ROW_DATE)='25-MAR-2014')
returns more than row. The error occurs because the SET part of the UPDATE depends on the equality operator - SET T1.CENTERNAME= - so it can take only be one value.
Without more details about your data structure it is hard to be certain but I suspect what you really want is something like this
UPDATE TABLE1 T1
SET T1.CENTERNAME= (SELECT T2.CENTERNAME
FROM TABLE2 T2
INNER JOIN TABLE3 T3
ON T2.CENTERID = T3.LOCATIONID
WHERE T3.LOG_ID = T1.LOGID )
WHERE TRUNC(T1.ROW_DATE)='25-MAR-2014'
/
(I've tidied up your redaction to make the aliases consistent.)

Resources