SQLSearcher
asked on
SQL Left select, to get a zero when no data is returned
Hello Experts Exchange
I am using Oracle to select data from a database to get a sum of a value, but I want a record when no data is returned.
This is my query;
But the query does not work, how do I change so that if no data in the SITE_INCOME table I still get a record stating zero.
Regards
SQLSearcher
I am using Oracle to select data from a database to get a sum of a value, but I want a record when no data is returned.
This is my query;
Select DH.DH_Position,L.LOC_NAME, DH.DH_Name,
sum(NVL(si.si_income1,0)) as Value
From LOCATION_GROUP_TYPES gt
inner join LOCATION_GROUPS g
on gt.LGT_ID = g.LG_LGT_ID
inner join locations l
on g.LG_LOC_ID = l.LOC_ID
Left join SITE_INCOME si
on si.si_loc_id = l.loc_id
inner join Department_Headings DH
on DH.DH_Position = 1
Where (LOC_DATE_CLOSED is null or to_char(LOC_DATE_CLOSED,'YYYY') >= 2016)
and gt.LGT_REGION is not null
and si.si_year = 2016
and si.si_week Between 10 and 10 and gt.LGT_DESCRIPTION = 'NORTH WEST AND NORTH MIDLANDS' and L.LOC_NAME = 'CHORLTON'
group by DH.DH_Position,L.LOC_NAME, DH.DH_Name
But the query does not work, how do I change so that if no data in the SITE_INCOME table I still get a record stating zero.
Regards
SQLSearcher
Without knowing your data structure I can't be certain but...
What I'm seeing is that your LEFT JOIN for SITE_INCOME is dependent upon two INNER JOINS on LOCATION_GROUPS and locations. My guess is that your join is omitting the record you want because of one or both of those inner joins.
@slightwv - All LEFT joins are OUTER joins. The OUTER keyword is optional.
What I'm seeing is that your LEFT JOIN for SITE_INCOME is dependent upon two INNER JOINS on LOCATION_GROUPS and locations. My guess is that your join is omitting the record you want because of one or both of those inner joins.
@slightwv - All LEFT joins are OUTER joins. The OUTER keyword is optional.
>>The OUTER keyword is optional.
Thanks for the correction. There is the one new thing I learned today... I can stop learning for the day!
Thanks for the correction. There is the one new thing I learned today... I can stop learning for the day!
When you do this in the WHERE clause
AND si.si_year = 2016
AND si.si_week BETWEEN 10 AND 10
you are effectively negating the OUTER JOIN, and turning it into an INNER JOIN.
The reason this happens is the WHERE clause is applied after the join conditions are resolved.
So, if you don't have an SI row from the join, you will get NULL values returned by the outer join,
But, your where clause will then reject those rows because NULL can never satisfy those conditions.
The same is true for conditions that compare results between tables in the where clause; but the explicit value comparisons make it more obvious
To fix this, move those conditions from the WHERE into the ON conditions of the outer join
AND si.si_year = 2016
AND si.si_week BETWEEN 10 AND 10
you are effectively negating the OUTER JOIN, and turning it into an INNER JOIN.
The reason this happens is the WHERE clause is applied after the join conditions are resolved.
So, if you don't have an SI row from the join, you will get NULL values returned by the outer join,
But, your where clause will then reject those rows because NULL can never satisfy those conditions.
The same is true for conditions that compare results between tables in the where clause; but the explicit value comparisons make it more obvious
To fix this, move those conditions from the WHERE into the ON conditions of the outer join
Try this -
Select DH.DH_Position,L.LOC_NAME, DH.DH_Name,
[b]sum(si.si_income1[/b]) as Value
From LOCATION_GROUP_TYPES gt
inner join LOCATION_GROUPS g
on gt.LGT_ID = g.LG_LGT_ID
inner join locations l
on g.LG_LOC_ID = l.LOC_ID
[b]left join
(select si_loc_id, nvl(si_income1,0) si_income1
from site_income) si
[/b]on si.si_loc_id = l.loc_id
inner join Department_Headings DH
on DH.DH_Position = 1
Where (LOC_DATE_CLOSED is null or to_char(LOC_DATE_CLOSED,'YYYY') >= 2016)
and gt.LGT_REGION is not null
and si.si_year = 2016
and si.si_week Between 10 and 10 and gt.LGT_DESCRIPTION = 'NORTH WEST AND NORTH MIDLANDS' and L.LOC_NAME = 'CHORLTON'
group by DH.DH_Position,L.LOC_NAME, DH.DH_Name;
Apparently bold can not be embedded in code, which is what I attempted to do. I also didn't notice that the references to si.year and si.week would have to be included in the subquery aliased as si.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
I hate messing up so badly but the si. qualifier should be removed from the subquery :-(
using the subquery method doesn't help
if there is no row, the NVL won't be executed, so a 0 won't be generated
LEFT JOIN
(select si_loc_id, nvl(si_income1,0) si_income1
doesn't address the problem.if there is no row, the NVL won't be executed, so a 0 won't be generated
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
inner join Department_Headings DH on DH.DH_Position = 1
How many rows does this return?
if only 1 then I would suggest using CROSS JOIN instead
if it can return more than one it will multiply the SUM() results
Where (LOC_DATE_CLOSED is null or to_char(LOC_DATE_CLOSED,'Y YYY') >= 2016)
I assume this column comes from Locations L and I assume it is a date
to_char(...'YYYY') returns a string
2016 is an integer
so you are converting a date to a string and comparing to an integer
that isn't efficient
this would be more efficient
Where (L.LOC_DATE_CLOSED is null or L.LOC_DATE_CLOSED >= to_date('2016-01-01','YYYY -MM-DD')
(if L.LOC_DATE_CLOSED is a timestamp then use to_timestamp()
and your original where clause directly references the left joined table,
and si.si_year = 2016
and si.si_week Between 10 and 10
so EVERY ROW MUST meet those 2 predicates, and hence NULL from that table is suppressed
To fix this either alter the where clause like this:
and ( (si.si_year = 2016 and si.si_week Between 10 and 10) OR si.si_year IS NULL)
OR (preferred) move those predicates into the join (this is shown in comments above)
How many rows does this return?
if only 1 then I would suggest using CROSS JOIN instead
if it can return more than one it will multiply the SUM() results
Where (LOC_DATE_CLOSED is null or to_char(LOC_DATE_CLOSED,'Y
I assume this column comes from Locations L and I assume it is a date
to_char(...'YYYY') returns a string
2016 is an integer
so you are converting a date to a string and comparing to an integer
that isn't efficient
this would be more efficient
Where (L.LOC_DATE_CLOSED is null or L.LOC_DATE_CLOSED >= to_date('2016-01-01','YYYY
(if L.LOC_DATE_CLOSED is a timestamp then use to_timestamp()
and your original where clause directly references the left joined table,
and si.si_year = 2016
and si.si_week Between 10 and 10
so EVERY ROW MUST meet those 2 predicates, and hence NULL from that table is suppressed
To fix this either alter the where clause like this:
and ( (si.si_year = 2016 and si.si_week Between 10 and 10) OR si.si_year IS NULL)
OR (preferred) move those predicates into the join (this is shown in comments above)
ASKER
Thank you for your help.
...
Left outer join SITE_INCOME si
...