Result set different from query than from stored procedure in Oracle - oracle

I have a sp that is giving me wrong results. That sp returns a cursor which is missing some rows. Now
When we fire the query within the sp directly in a query window, correct results come up. The only difference in this query is that instead of a cursor, the query directly outputs the result set; i.e. no cursor is opened when we fire the query directly.
When we recompile the sp, correct results come up for some time, and then it is back to missing rows after some time.
Have not been able to make much progress on this. Any pointers on what to look for, how to make progress etc. are much appreciated. I can paste the sp in here if needed, and probably the sp can be rewritten in some other way to make the problem go away, but I am more interested in knowing root cause - what could be causing this strange problem?
SP
CREATE OR REPLACE PROCEDURE "SP_GETTAXSETTINGCHANNELINFO" (v_bid varchar2,
p_rdTaxSetting out SYS_REFCURSOR) as
vT_bid varchar2(100);
begin
vT_bid := v_bid;
Open p_rdTaxSetting for
Select taxmappingid, CHANNELNAME,PROPERTYNAME, PROPERTYID, TAXTYPE, ISPARTIALLYINCLUSIVE, PARTIALLYINCLUSIVEVALUE
FROM (
WITH ChannelData AS (
SELECT ONDEMAND_SELECTED_SOURCES AS OnDemandChannels,
SCHEDULED_SELECTED_SOURCES AS ScheduledChannels
FROM SUB_ORDER S
INNER JOIN CONTACT C ON S.Supplier_ID = C.Contact_ID
WHERE C.BID = vT_bid
AND S.STATUS='Complete' and current_ind = 1
),
Property_Data As (
SELECT
SC.PROPERTY_NUM AS PropertyID
,SC.Company_Name as PropertyName
,SC.competitor_number as PropertyOrder
FROM SUB_ORDER S
INNER JOIN CONTACT C ON S.Supplier_ID = C.Contact_ID
INNER JOIN SUB_ORDER_COMPETITOR SC ON S.Order_Id = SC.Order_ID
WHERE C.BID = vT_bid
AND S.STATUS='Complete' and current_ind = 1
),
ODChannel as (
select regexp_substr (OnDemandChannels, '[^,]+', 1, rn) as Channel
from (Select OnDemandChannels From ChannelData)
cross join
(select rownum rn
from (select max (length (regexp_replace (OnDemandChannels, '[^,]+'))) + 1 max_value
from (Select OnDemandChannels From ChannelData))
connect by level <= max_value)
where regexp_substr (OnDemandChannels, '[^,]+', 1, rn) is not null
),
SCDChannel As (
select regexp_substr (ScheduledChannels, '[^,]+', 1, rn) as Channel
from (Select ScheduledChannels From ChannelData)
cross join
(select rownum rn
from (select max (length (regexp_replace (ScheduledChannels, '[^,]+'))) + 1 max_value
from (Select ScheduledChannels From ChannelData))
connect by level <= max_value)
where regexp_substr (ScheduledChannels, '[^,]+', 1, rn) is not null
),
ChaData1 As (
Select
CASE lower(Channel)
WHEN 'galileo' then 'GDS'
else Channel
end AS Channel
from ODChannel
),
ChaData2 As (
Select CASE lower(Channel)
WHEN 'galileo' then 'GDS'
else Channel
end AS Channel FROM SCDChannel
),
PropData As (
Select Distinct PropertyID,PropertyName from Property_Data
order by PropertyID
),
AllChannel AS (
Select Channel From ChaData1
union
Select Channel From ChaData2
),
Prop_Cha_Data As (
Select Distinct Channel,PropertyID,PropertyName from AllChannel,Property_Data
order by Channel,PropertyID
),
Tax_Channel_Prop_Exists AS ( select
CASE lower(CHANNELNAME)
WHEN 'galileo' then 'GDS'
else CHANNELNAME
end AS CHANNELNAME,
PropertyName As PROPERTYNAME, TCP.PROPERTYID As PROPERTYID,TAXTYPE,
ISPARTIALLYINCLUSIVE, PARTIALLYINCLUSIVEVALUE,taxmappingid
from TAXSETTING_CHANNEL_PROPERTY TCP
INNER JOIN AllChannel PC On TRIM (CASE lower(TCP.CHANNELNAME)
WHEN 'galileo' then 'GDS'else TCP.CHANNELNAME end) = TRIM(PC.Channel)
INNER JOIN PropData CP On TCP.PROPERTYID = CP.PropertyID
where TCP.BID=vT_bid
)
Select Distinct taxmappingid, CHANNELNAME,PROPERTYNAME, PROPERTYID, TAXTYPE, ISPARTIALLYINCLUSIVE, PARTIALLYINCLUSIVEVALUE from Tax_Channel_Prop_Exists
UNION ALL
select DISTINCT 0 As taxmappingid, PCD.Channel As CHANNELNAME,
PCD.PropertyName As PROPERTYNAME ,
PCD.propertyid As PROPERTYID,
-1 As TAXTYPE,
0 As ISPARTIALLYINCLUSIVE,
'' As PARTIALLYINCLUSIVEVALUE
FROM Prop_Cha_Data PCD
WHERE NOT EXISTS (
Select taxmappingid FROM Tax_Channel_Prop_Exists E
WHERE E.channelname = PCD.Channel AND
E.propertyid = PCD.propertyid )
)
Order by PROPERTYNAME,CHANNELNAME ;
end SP_GetTaxSettingChannelInfo;

Related

Oracle adding a subquery in a CTE

I have the following setup, which works fine and generates output as expected.
I'm trying to add the locations subquery into the CTE so my output will have a random location_id for each row.
The subquery is straight forward and should work but I am getting syntax errors when I try to place it into the 'data's CTE. I was hoping someone could help me out.
CREATE TABLE employees(
employee_id NUMBER(6),
emp_name VARCHAR2(30)
);
INSERT INTO employees(
employee_id,
emp_name
) VALUES
(1, 'John Doe');
INSERT INTO employees(
employee_id,
emp_name
) VALUES
(2, 'Jane Smith');
INSERT INTO employees(
employee_id,
emp_name
) VALUES
(3, 'Mike Jones');
CREATE TABLE locations AS
SELECT level AS location_id,
'Door ' || level AS location_name
FROM dual
CONNECT BY level <=
with rws as (
select level rn from dual connect by level <= 5 ),
data as ( select e.*,round (dbms_random.value(1,5)
) n from employees e)
select employee_id,
emp_name,
trunc (sysdate) + dbms_random.value (0, 5) AS random_date
from rws
join data d on rn <= n
order by employee_id;
-- trying to make this work
with rws as ( select level rn from dual connect by level <= 5 ),
data as ( select e.*, loc.location_id = (
select location_id
from locations order by dbms_random.value()
fetch first 1 row only
),
round (dbms_random.value(1,5)
) n from employees e )
select employee_id,
emp_name,
trunc (sysdate) + dbms_random.value (0, 5) AS random_date
from rws
join data d on rn <= n
order by employee_id;
You need to alias the subquery column expression, rather than trying to assign it to a [variable] name. So instead of this:
with rws as ( select level rn from dual connect by level <= 5 ),
data as ( select e.*, loc.location_id = (
select location_id
from locations order by dbms_random.value()
fetch first 1 row only
),
round (dbms_random.value(1,5)
) n from employees e )
you would do this:
with rws as (
select level rn
from dual
connect by level <= 5
),
data as (
select e.*,
(
select location_id
from locations
order by dbms_random.value()
fetch first 1 row only
) as location_id,
round (dbms_random.value(1,5)) as n
from employees e
)
db<>fiddle
But yes, you'll get the same location_id for each row, which probably isn't what you want.
There are probably better ways to avoid it (or to approach whatever you're actually trying to achieve) but one option is to force the subquery to be correlated by adding something like:
where location_id != -1 * e.employee_id
db<>fiddle
although that might be expensive. It's probably worth asking a new question about that specific aspect.
I am getting the same location_id for every employee_id, which I don't want either.
The subquery is in the wrong place then; move it to the main query, and correlate against both ID and n:
with rws as (
select level rn
from dual
connect by level <= 5
),
data as (
select e.*,
round (dbms_random.value(1,5)) as n
from employees e
)
select d.employee_id,
d.emp_name,
(
select location_id
from locations
where location_id != -1 * d.employee_id * d.n
order by dbms_random.value()
fetch first 1 row only
) as location_id,
trunc (sysdate) + dbms_random.value (0, 5) AS random_date
from rws r
join data d on r.rn <= d.n
order by d.employee_id;
db<>fiddle
Or move the location part to a new CTE, I suppose, with its own row number; and join that on one of your other generated values.

Adding filters in subquery from CTE quadruples run time

I am working on an existing query for SSRS report that focuses on aggregated financial aid data split out into 10 aggregations. User wants to be able to select students included in that aggregated data based on new vs. returning and 'selected for verification.' For the new/returning status, I added a CTE to return the earliest admit date for a student. 2 of the 10 data fields are created by a subquery. I have been trying for 3 days to get the subquery to use the CTE fields for a filter, but they won't work. Either they're ignored or I get a 'not a group by expression' error. If I put the join to the CTE within the subquery, the query time jumps from 45 second to 400 seconds. This shouldn't be that complicated! What am I missing? I have added some of the code... 3 of the chunks work - paid_something doesn't.
with stuStatus as
(select
person_uid, min(year_admitted) admit_year
from academic_study
where aid_year between :AidYearStartParameter and :AidYearEndParameter
group by person_uid)
--- above code added to get student information not originally in qry
select
finaid_applicant_status.aid_year
, count(1) as fafsa_cnt --works
, sum( --works
case
when (
package_complete_date is not null
and admit.status is not null
)
then 1
else 0
end
) as admit_and_package
, (select count(*) --does't work
from (
select distinct award_by_aid_year.person_uid
from
award_by_aid_year
where
award_by_aid_year.aid_year = finaid_applicant_status.aid_year
and award_by_aid_year.total_paid_amount > 0 )dta
where
(
(:StudentStatusParameter = 'N' and stuStatus.admit_year = finaid_applicant_status.aid_year)
OR
(:StudentStatusParameter = 'R' and stuStatus.admit_year <> finaid_applicant_status.aid_year)
OR :StudentStatusParameter = '%'
)
)
as paid_something
, sum( --works
case
when exists (
select
1
from
award_by_person abp
where
abp.person_uid = fafsa.person_uid
and abp.aid_year = fafsa.aid_year
and abp.award_paid_amount > 0
) and fafsa.requirement is not null
then 1
else 0
end
) as paid_something_fafsa
from
finaid_applicant_status
join finaid_tracking_requirement fafsa
on finaid_applicant_status.person_uid = fafsa.person_uid
and finaid_applicant_status.aid_year = fafsa.aid_year
and fafsa.requirement = 'FAFSA'
left join finaid_tracking_requirement admit
on finaid_applicant_status.person_uid = admit.person_uid
and finaid_applicant_status.aid_year = admit.aid_year
and admit.requirement = 'ADMIT'
and admit.status in ('M', 'P')
left outer join stuStatus
on finaid_applicant_status.person_uid = stuStatus.person_uid
where
finaid_applicant_status.aid_year between :AidYearStartParameter and :AidYearEndParameter
and (
(:VerifiedParameter = '%') OR
(:VerifiedParameter <> '%' AND finaid_applicant_status.verification_required_ind = :VerifiedParameter)
)
and
(
(:StudentStatusParameter = 'N' and (stuStatus.admit_year IS NULL OR stuStatus.admit_year = finaid_applicant_status.aid_year ))
OR
(:StudentStatusParameter = 'R' and stuStatus.admit_year <> finaid_applicant_status.aid_year)
OR :StudentStatusParameter = '%'
)
group by
finaid_applicant_status.aid_year
order by
finaid_applicant_status.aid_year
Not sure if this helps, but you have something like this:
select aid_year, count(1) c1,
(select count(1)
from (select distinct person_uid
from award_by_aid_year a
where a.aid_year = fas.aid_year))
from finaid_applicant_status fas
group by aid_year;
This query throws ORA-00904 FAS.AID_YEAR invalid identifier. It is because fas.aid_year is nested too deep in subquery.
If you are able to modify your subquery from select count(1) from (select distinct sth from ... where year = fas.year) to select count(distinct sth) from ... where year = fas.year then it has the chance to work.
select aid_year, count(1) c1,
(select count(distinct person_uid)
from award_by_aid_year a
where a.aid_year = fas.aid_year) c2
from finaid_applicant_status fas
group by aid_year
Here is simplified demo showing non-working and working queries. Of course your query is much more complicated, but this is something what you could check.
Also maybe you can use dbfiddle or sqlfiddle to set up some test case? Or show us sample (anonimized) data and required output for them?

sql placeholder rows

I have an apex item P_USERS which can have a value higher than the amount of rows returning from the query below.
I have a classic report which has the following query:
select
first_name,
last_name
from accounts
where account_role = 'Author'
order by account_nr;
I want placeholder rows to be added to the query (first_name = null, last_name = null etc.), if the total rows from the query is lesser than the value in the apex_item P_USERS.
Any tips on how to achieve this? Maybe with a LEFT join?
If you have more result than the minima you defined, you must add the rest with union.
Here is what you could try to adapt to your case:
SELECT i,c FROM (
select rownum i, c from (
select 'a' c from dual union all select 'b' from dual union all select 'd' from dual union all select 'be' from dual
)), (Select Rownum r From dual Connect By Rownum <= 3)
where (i(+)= r)
union select i,c from (select rownum i, c from (
select 'a' c from dual union all select 'b' from dual union all select 'd' from dual union all select 'be' from dual
)) where i>3
You may try to use a LEFT JOIN.
First, create a list of number until the limit you want like suggested here:
-- let's say you want 300 records
Select Rownum r From dual Connect By Rownum <= 300
Then you can use this to left join and have empty records:
SELECT C, R FROM
( select rownum i, c from (select 'a' c from dual union all select 'b' from dual) )
, ( Select Rownum r From dual Connect By Rownum <= 300)
where i(+)= r order by r
The above gives you an ordered list starting with 'a', 'b', then null until the end.
So you could adapt it to your case so:
SELECT F,L FROM
( select rownum i, f, l from (
select first_name f, last_name l
from accounts where account_role = 'Author'
order by account_nr) )
, ( Select Rownum r From dual Connect By Rownum <= 300)
where i(+)= r

How to use 'EXIST' in a simple oracle query

I have a table called ‘MainTable’ with following data
Another table called ‘ChildTable’ with following data (foreighn key Number)
Now I want to fetch those records from ‘ChildTable’ if there exists at least one ‘S’ status.
But if any other record for this number id ‘R’ then I don’t want to fetch it
Something like this-
I tried following
Select m.Number, c.Status from MainTable m, ChildTable c
where EXISTS (SELECT NULL
FROM ChildTable c2
WHERE c2.status =’S’ and c2.status <> ‘R’
AND c2.number = m.number)
But here I am getting record having ‘R’ status also, what I am doing wrong?
You can try something like this
select num, status
from
(select id, num, status,
sum(decode(status, 'R', 1, 0)) over (partition by num) Rs,
sum(decode(status, 'S', 1, 0)) over (partition by num) Ss
from child_table) t
where t.Rs = 0 and t.Ss >= 1
-- and status = 'S'
Here is a sqlfiddle demo
The child records with 'R' might be associated with a maintable record that also has another child record with status 'S' -- that is what your query is asking for.
Select
m.Number,
c.Status
from MainTable m
join ChildTable c on c.number = m.number
where EXISTS (
SELECT NULL
FROM ChildTable c2
WHERE c2.status =’S’
AND c2.number = m.number) and
NOT EXISTS (
SELECT NULL
FROM ChildTable c2
WHERE c2.status =’R’
AND c2.number = m.number)
WITH ChildrenWithS AS (
SELECT Number
FROM ChildTable
WHERE Status = 'S'
)
,ChildrenWithR AS (
SELECT Number
FROM ChildTable
WHERE Status = 'R'
)
SELECT MaintTable.Number
,ChildTable.Status
FROM MainTable
INNER JOIN ChildTable
ON MainTable.Number = ChildTable.Number
WHERE MainTable.Number IN (SELECT Number FROM ChildrenWithS)
AND MainTable.Number NOT IN (SELECT Number FROM ChildrenWithR)

Retrieving page that contains specific row

I have a procedure that returns multiple rows on some criteria and in specific order. These rows are separated into few pages (50 rows per page).
How can I retrieve all rows from page having some specific row.
I've created a query the query that do this work, but it is not optimized and have huge impact on performance. Help me please to optimize it or give an alternative to it:
select *
from
(
select file_id, row_number() over (order by rownum) rn
from my_table
)
where trunc(rn/50) = (
select trunc(rn/50) from
(select t.*, rownum rn from my_table t)
where file_id = 29987);
You have to adjust it a little...
with tab as
(
-- the
-- row_number() over (order by rownum) rn
-- should be here
select level + 1000 as val
, level/50 as rn_50
from dual
connect by
level < 140
)
, val as
(
select rn_50
from tab
where val = 1004 -- pg 1
--where val = 1051 -- pg 2
--where val = 1101 -- pg 3
)
select *
from tab t
where rn_50 >= (select floor(rn_50) from val)
and rn_50 <= (select ceil (rn_50) from val)
;

Resources