First of all, I usually try to replicate my problem in some dummy data to make it easier to ask here but this at time it won't be possible. So please forgive me for this.
So. My problem is the following. I have a Oracle Database and on it I have seven tables on which I want to perform a select doing LEFT JOIN between then. So. This is the raw script
select turn_on_off.ID as Event_ID,controllers.ID as Ctrl_ID,Ctrl,SubCtrl,Turn_OFF,Turn_ON,DiscountPCT,Reason,Observation,Comments
from
sagi_un
left join
sagi_industrial
on
sagi_un.ID = sagi_industrial.UN_ID
left join
sagi_plant
on
sagi_industrial.ID = sagi_plant.INDUSTRIAL_ID
left join
sagi_area
on
sagi_plant.ID = sagi_area.PLANT_ID
left join
sagi_area_ctrl_map
on
sagi_area.ID = sagi_area_ctrl_map.AREA_ID
left join
controllers
on
controllers.ID = sagi_area_ctrl_map.CTRL_ID
left join
turn_on_off
on
sagi_area_ctrl_map.CTRL_ID = turn_on_off.CTRL_ID
left join
justification
on
turn_on_off.ID = justification.Event_ID
left join
reasons
on
reasons.ID = justification.REASON_ID
where
Turn_OFF > TO_DATE('10-07-2022','DD-MM-YYYY') and
sagi_un.UN = 'Q 2 RS' and
sagi_industrial.Industrial = 'Olefinas' and
sagi_plant.plant = 'OLE-2' and
sagi_area.area = 'Area Quente'
at the end of the query I have a where clause where I set up some filtering for my data. This works fine. To make our live easier when accessing this data I created a pipelined function which receive the filtering parameter and should return the query result. This is the code that generate the function
create or replace FUNCTION "GET_OFF_INTERVALS2"
(
UN IN VARCHAR2
, INDUSTRIAL IN VARCHAR2
, PLANTA IN VARCHAR2
, AREA IN VARCHAR2
, MAX_DATE IN DATE
) RETURN OFF_INTERVAL_TABLE PIPELINED IS
type ref0 is ref cursor;
cur0 ref0;
out_rec OFF_INTERVAL
:= OFF_INTERVAL(NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
BEGIN
open cur0 for
select turn_on_off.ID as Event_ID,controllers.ID as Ctrl_ID,Ctrl,SubCtrl,Turn_OFF,Turn_ON,DiscountPCT,Reason,Observation,Comments
from
sagi_un
left join
sagi_industrial
on
sagi_un.ID = sagi_industrial.UN_ID
left join
sagi_plant
on
sagi_industrial.ID = sagi_plant.INDUSTRIAL_ID
left join
sagi_area
on
sagi_plant.ID = sagi_area.PLANT_ID
left join
sagi_area_ctrl_map
on
sagi_area.ID = sagi_area_ctrl_map.AREA_ID
left join
controllers
on
controllers.ID = sagi_area_ctrl_map.CTRL_ID
left join
turn_on_off
on
sagi_area_ctrl_map.CTRL_ID = turn_on_off.CTRL_ID
left join
justification
on
turn_on_off.ID = justification.Event_ID
left join
reasons
on
reasons.ID = justification.REASON_ID
where
Turn_OFF > max_date and
sagi_un.UN = UN and
sagi_industrial.Industrial = INDUSTRIAL and
sagi_plant.plant = PLANTA and
sagi_area.area = AREA;
loop
fetch cur0 into
out_rec.event_id,
out_rec.ctlr_id,
out_rec.ctrl,
out_rec.subctrl,
out_rec.turn_on,
out_rec.turn_off,
out_rec.discount_pct,
out_rec.reason,
out_rec.observation,
out_rec.comments;
exit when cur0%NOTFOUND;
pipe row(out_rec);
end loop;
close cur0;
RETURN;
END GET_OFF_INTERVALS2;
and I call this function like this.
select * from TABLE(GET_OFF_INTERVALS2('Q 2 RS','Olefinas','OLE-2','Area Quente',TO_DATE('10-07-2022','DD-MM-YYYY')));
You can see that the filtering parameters are the same.
The problem is that the function seems to be not considering the AREA parameter. In the data I am testing there are two areas (Area Quente and Area Fria) but the pipeline function is returning the data for both areas instead of the area I am requesting. It is like that line was commented. I think that I am doing something really stupid here but I am not able to see what it is. If someone can take a look on it an point me out my mistake I would be very gratefull. thanks!
CodePudding user response:
Your filter conditions turn the LEFT [OUTER] JOIN
into an INNER JOIN
as the WHERE
condition requires that there be a matched row and cannot match a NULL
row.
select turn_on_off.ID as Event_ID,
controllers.ID as Ctrl_ID,
Ctrl,
SubCtrl,
Turn_OFF,
Turn_ON,
DiscountPCT,
Reason,
Observation,
Comments
from sagi_un
INNER JOIN sagi_industrial
on sagi_un.ID = sagi_industrial.UN_ID
INNER JOIN sagi_plant
on sagi_industrial.ID = sagi_plant.INDUSTRIAL_ID
INNER JOIN sagi_area
on sagi_plant.ID = sagi_area.PLANT_ID
INNER JOIN sagi_area_ctrl_map
on sagi_area.ID = sagi_area_ctrl_map.AREA_ID
INNER JOIN turn_on_off
on sagi_area_ctrl_map.CTRL_ID = turn_on_off.CTRL_ID
left join controllers
on controllers.ID = sagi_area_ctrl_map.CTRL_ID
left join justification
on turn_on_off.ID = justification.Event_ID
left join reasons
on reasons.ID = justification.REASON_ID
where Turn_OFF > TO_DATE('10-07-2022','DD-MM-YYYY')
and sagi_un.UN = 'Q 2 RS'
and sagi_industrial.Industrial = 'Olefinas'
and sagi_plant.plant = 'OLE-2'
and sagi_area.area = 'Area Quente'
If you want to LEFT OUTER JOIN
then you need to include the filter in the ON
condition of the JOIN
:
select turn_on_off.ID as Event_ID,
controllers.ID as Ctrl_ID,
Ctrl,
SubCtrl,
Turn_OFF,
Turn_ON,
DiscountPCT,
Reason,
Observation,
Comments
from sagi_un
LEFT OUTER JOIN sagi_industrial
on ( sagi_un.ID = sagi_industrial.UN_ID
AND sagi_industrial.Industrial = 'Olefinas')
LEFT OUTER JOIN sagi_plant
on ( sagi_industrial.ID = sagi_plant.INDUSTRIAL_ID
AND sagi_plant.plant = 'OLE-2' )
LEFT OUTER JOIN sagi_area
on ( sagi_plant.ID = sagi_area.PLANT_ID
AND sagi_area.area = 'Area Quente' )
LEFT OUTER JOIN sagi_area_ctrl_map
on sagi_area.ID = sagi_area_ctrl_map.AREA_ID
LEFT OUTER JOIN controllers
on controllers.ID = sagi_area_ctrl_map.CTRL_ID
LEFT OUTER JOIN turn_on_off
on ( sagi_area_ctrl_map.CTRL_ID = turn_on_off.CTRL_ID
AND Turn_OFF > TO_DATE('10-07-2022','DD-MM-YYYY') )
LEFT OUTER JOIN justification
on turn_on_off.ID = justification.Event_ID
LEFT OUTER JOIN reasons
on reasons.ID = justification.REASON_ID
where sagi_un.UN = 'Q 2 RS'
As for your function, NEVER give PL/SQL variables the same name as an SQL column (if you do then the local SQL scope will be considered before the outer PL/SQL scope so you are effectively using WHERE sagi_un.UN = sagi_un.UN
in the comparisons and not WHERE sagi_un.UN = plsql_UN
):
CREATE FUNCTION GET_OFF_INTERVALS2
(
v_UN IN sagi_un.UN%TYPE
, v_INDUSTRIAL IN sagi_industrial.Industrial%TYPE
, v_PLANT IN sagi_plant.plant%TYPE
, v_AREA IN sagi_area.area%TYPE
, v_MAX_DATE IN turn_on_off.Turn_OFF%TYPE
) RETURN OFF_INTERVAL_TABLE PIPELINED
IS
cur0 REF CURSOR;
out_rec OFF_INTERVAL
:= OFF_INTERVAL(NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
BEGIN
open cur0 for
select turn_on_off.ID as Event_ID,
controllers.ID as Ctrl_ID,
Ctrl,
SubCtrl,
Turn_OFF,
Turn_ON,
DiscountPCT,
Reason,
Observation,
Comments
from sagi_un
LEFT OUTER JOIN sagi_industrial
on ( sagi_un.ID = sagi_industrial.UN_ID
AND sagi_industrial.Industrial = v_industrial)
LEFT OUTER JOIN sagi_plant
on ( sagi_industrial.ID = sagi_plant.INDUSTRIAL_ID
AND sagi_plant.plant = v_plant )
LEFT OUTER JOIN sagi_area
on ( sagi_plant.ID = sagi_area.PLANT_ID
AND sagi_area.area = v_area )
LEFT OUTER JOIN sagi_area_ctrl_map
on sagi_area.ID = sagi_area_ctrl_map.AREA_ID
LEFT OUTER JOIN controllers
on controllers.ID = sagi_area_ctrl_map.CTRL_ID
LEFT OUTER JOIN turn_on_off
on ( sagi_area_ctrl_map.CTRL_ID = turn_on_off.CTRL_ID
AND Turn_OFF > v_max_date )
LEFT OUTER JOIN justification
on turn_on_off.ID = justification.Event_ID
LEFT OUTER JOIN reasons
on reasons.ID = justification.REASON_ID
where sagi_un.UN = v_un;
loop
fetch cur0 into
out_rec.event_id,
out_rec.ctlr_id,
out_rec.ctrl,
out_rec.subctrl,
out_rec.turn_on,
out_rec.turn_off,
out_rec.discount_pct,
out_rec.reason,
out_rec.observation,
out_rec.comments;
exit when cur0%NOTFOUND;
pipe row(out_rec);
end loop;
close cur0;
EXCEPTION
WHEN NO_DATA_NEEDED THEN
close cur0;
END GET_OFF_INTERVALS2;
/
Note: You should also catch the NO_DATE_NEEDED
exception so you can close the cursor if the function is stopped calling before all the rows are read.
CodePudding user response:
AS per ANSI SQL
SQL LEFT JOIN In Sql, LEFT JOIN selects all the records from the left column (table 1) and all the records from the right table that satifies the given condition. If there is no match in the right table for the given condition then result will be NULL for those columns in the right table.
Hence check the sides(left/right) where sage_area table is used in the left join(s)