Oracle function perform better than inline query - oracle

I have one long query which have a lot of let join with subqueries. It 's have some performance issue. I have just put subqueries inside new function and the function execute that subqueries and store the output in a collection (user defined table which contain user defined object). when I execute outer query with this function it's perform better than subqueries but we don't found the answer why it's happend.
Example : Existing Query
select *
from a
left join (select * from b left join c on b.something =c.something where b.something=`something`) d on a.something = d.something
Update the query
create object user_obj as ( column_name varchar, column_name varchar);
create or replace TYPE table_name AS TABLE OF user_obj;
create function test (something) as
report table_name := table_name();
Begin
for i in (select * from b left join c on b.something =c.something where
b.something='something')loop
report.EXTEND;
report(report.count) := (user_obj(i.firstcolumnvalue,
i.secondColumnValue))
end loop;
return report;
End
calling function by outer query
select * from a left join (select * from table(test(input_data))) d on a.something = d.something
When I executing through function it's give me better performance as compare to inline query
I want a reason why performance is better in function

Related

ORA-00947 not enough values with function returning table of records

So I'm trying to build a function that returns the records of items that are included in some client subscription.
So I've been building up the following:
2 types:
CREATE OR REPLACE TYPE PGM_ROW AS OBJECT
(
pID NUMBER(10),
pName VARCHAR2(300)
);
CREATE OR REPLACE TYPE PGM_TAB AS TABLE OF PGM_ROW;
1 function:
CREATE OR REPLACE FUNCTION FLOGIN (USER_ID NUMBER) RETURN PGM_TAB
AS
SELECTED_PGM PGM_TAB;
BEGIN
FOR RESTRICTION
IN ( SELECT (SELECT LISTAGG (ID_CHANNEL, ',')
WITHIN GROUP (ORDER BY ID_CHANNEL)
FROM (SELECT DISTINCT CHA2.ID_CHANNEL
FROM CHANNELS_ACCESSES CHA2
JOIN CHANNELS CH2
ON CH2.ID = CHA2.ID_CHANNEL
WHERE CHA2.ID_ACCESS = CMPA.ID_ACCESS
AND CH2.ID_CHANNELS_GROUP = CG.ID))
AS channels,
(SELECT LISTAGG (ID_SUBGENRE, ',')
WITHIN GROUP (ORDER BY ID_SUBGENRE)
FROM (SELECT DISTINCT SGA2.ID_SUBGENRE
FROM SUBGENRES_ACCESSES SGA2
JOIN CHANNELS_ACCESSES CHA2
ON CHA2.ID_ACCESS = SGA2.ID_ACCESS
JOIN CHANNELS CH2
ON CH2.ID = CHA2.ID_CHANNEL
WHERE SGA2.ID_ACCESS = CMPA.ID_ACCESS
AND CH2.ID_CHANNELS_GROUP = CG.ID))
AS subgenres,
CG.NAME,
A.BEGIN_DATE,
A.END_DATE,
CMP.PREVIEW_ACCESS
FROM USERS U
JOIN COMPANIES_ACCESSES CMPA
ON U.ID_COMPANY = CMPA.ID_COMPANY
JOIN COMPANIES CMP ON CMP.ID = CMPA.ID_COMPANY
JOIN ACCESSES A ON A.ID = CMPA.ID_ACCESS
JOIN CHANNELS_ACCESSES CHA
ON CHA.ID_ACCESS = CMPA.ID_ACCESS
JOIN SUBGENRES_ACCESSES SGA
ON SGA.ID_ACCESS = CMPA.ID_ACCESS
JOIN CHANNELS CH ON CH.ID = CHA.ID_CHANNEL
JOIN CHANNELS_GROUPS CG ON CG.ID = CH.ID_CHANNELS_GROUP
WHERE U.ID = USER_ID
GROUP BY CG.NAME,
A.BEGIN_DATE,
A.END_DATE,
CMPA.ID_ACCESS,
CG.ID,
CMP.PREVIEW_ACCESS)
LOOP
SELECT PFT.ID_PROGRAM, PFT.LOCAL_TITLE
BULK COLLECT INTO SELECTED_PGM
FROM PROGRAMS_FT PFT
WHERE PFT.ID_CHANNEL IN
( SELECT TO_NUMBER (
REGEXP_SUBSTR (RESTRICTION.CHANNELS,
'[^,]+',
1,
ROWNUM))
FROM DUAL
CONNECT BY LEVEL <=
TO_NUMBER (
REGEXP_COUNT (RESTRICTION.CHANNELS,
'[^,]+')))
AND PFT.ID_SUBGENRE IN
( SELECT TO_NUMBER (
REGEXP_SUBSTR (RESTRICTION.SUBGENRES,
'[^,]+',
1,
ROWNUM))
FROM DUAL
CONNECT BY LEVEL <=
TO_NUMBER (
REGEXP_COUNT (RESTRICTION.SUBGENRES,
'[^,]+')))
AND (PFT.LAUNCH_DATE BETWEEN RESTRICTION.BEGIN_DATE
AND RESTRICTION.END_DATE);
END LOOP;
RETURN SELECTED_PGM;
END FLOGIN;
I expect the function tu return a table with 2 columns containing all the records from table PROGRAMS_FT that are included in the user access.
For some reason, I'm getting compilation warning ORA-000947.
My understanding of the error code is that it occurs when the values inserted does not match the type of the object receiving the values, and I can't see how this can be the case here.
You're selecting two scalar values and trying to put them into an object. That doesn't happen automatically, you need to convert them to an object:
...
LOOP
SELECT PGM_ROW(PFT.ID_PROGRAM, PFT.LOCAL_TITLE)
BULK COLLECT INTO SELECTED_PGM
FROM PROGRAMS_FT PFT
...
(It's an unhelpful quirk of PL/SQL that it says 'not enough values' rather than 'too many values', as you might expect when you try to put two things into one; I'm sure I came up with a fairly convincing explanation/excuse for that once but it escapes me at the moment...)
I'm not sure your loop makes sense though. Assuming your cursor query returns multiple rows, each time around the loop you're replacing the contents of the SELECTED_PGM collection - you might think you are appending to it, but that's not how it works. So you will end up returning a collection based only on the final iteration of the loop.
Aggregating and then splitting the data seems like a lot of work too. You could maybe use collections for those; but you can probably get rid of the cursor and loop and combine the cursor query with the inner query, which would be more efficient and would allow you to do a single bulk-collect for all the combined data.

How to convert my TSQL query to LINQ

I am new to linq. I can't convert this SQL code to linq. Please help me. Thanks.
DECLARE #myHID BIGINT;
SET #myHID = 1;
WITH tblChild AS
(
SELECT *
FROM wbs.WBS w
WHERE w.ParentId = #myHID
UNION ALL
SELECT w2.*
FROM wbs.WBS w2
JOIN tblChild ON w2.ParentId = tblChild.hID
)
SELECT
tblChild.hID ,
Unit.ID, w3.wbsName + ' * ' + tblChild.wbsName as
structure ,
tblChild.FK_WbsBaseStructure_hID ,
tblChild.parentID ,
unitNumber ,
unitTitle ,
FK_UsageItem_ID,
usageTitle ,
nominalArea
FROM
tblChild
INNER JOIN
unit.Unit ON tblChild.hID = Unit.FK_WBS_hID
INNER JOIN
unit.UsageItem ON Unit.FK_UsageItem_ID = UsageItem.ID
LEFT JOIN
wbs.WBS w3 ON tblChild.parentID = w3.hID
Please convert this to linq code.
Thanks.
For translating SQL to LINQ query comprehension:
Translate FROM subselects as separately declared variables.
Translate each clause in LINQ clause order, leaving monadic operators (DISTINCT, TOP, etc) as functions applied to the whole LINQ query.
Use table aliases as range variables. Use column aliases as anonymous type field names.
Use anonymous types (new { }) for multiple columns
Left Join is simulated by using a into join_variable and doing another from from the join variable followed by .DefaultIfEmpty().
Replace COALESCE with the conditional operator and a null test.
SELECT * must be replaced with select range_variable or for joins, an anonymous object containing all the range variables.
SELECT fields must be replaced with select new { ... } creating an anonymous object with all the desired fields or expressions.
Proper FULL OUTER JOIN must be handled with an extension method.

how to convert child - parent sql query to linq or lambda expression

i have tsql query. it is work fine. i want to convert it to linq query or lambda expression
DECLARE #myHID BIGINT;
SET #myHID = 1;
WITH tblChild AS
(
SELECT *
FROM wbs.WBS w WHERE w.ParentId = #myHID
UNION ALL
SELECT w2.* FROM wbs.WBS w2 JOIN tblChild ON w2.ParentId =
tblChild.hID
)
SELECT
tblChild.hID, Unit.ID, w3.wbsName + ' * ' + tblChild.wbsName,
tblChild.FK_WbsBaseStructure_hID, tblChild.parentID, unitNumber,
unitTitle, FK_UsageItem_ID, usageTitle, nominalArea
FROM tblChild
inner join unit.Unit on tblChild.hID = Unit.FK_WBS_hID
inner join unit.UsageItem on Unit.FK_UsageItem_ID = UsageItem.ID
left join wbs.WBS w3 on tblChild.parentID = w3.hID
thank's
I didn't notice you are using a recursive CTE in your SQL. Unfortunately, there is no good direct translation of a recursive CTE. The recommended approach is to create a Stored Procedure or View based on the recursive SQL and access that.
For translating SQL to LINQ,
Translate subselects as separate variables
Translate each clause in LINQ clause order, leaving monadic operators (DISTINCT, TOP, etc) as functions applied to the whole LINQ query.
Use table aliases as range variables. Use column aliases as anonymous type field names.
Use anonymous types (new { }) for multiple columns
Left Join is simulated by using a join variable and doing another from from the join variable followed by .DefaultIfEmpty().
Replace coalesce with the conditional operator and a null test.
Use Concat for UNION ALL and Union for UNION.

PLSQL Loop taking lot of time to execute

I have a Oracle procedure which is updating below 10,000 records. if I run the normal SQL statement, it is returning the result immediately with in seconds(30).
same statment in procedure loop it is going to endlessly.
My loop statment below.
Note: data FIELD Data type is a clob not varchar2.
statment:
select
'LB_COPY_CHANGE-'||8 LAST_MODIFIED_BY,
rec.COR_ID_old,
rec.COR_ID_NEW,
replace(replace(replace(a.data,'''id'':'||rec.COR_ID_OLD||',','''id'':'||rec.COR_ID_NEW||','),''id':'||rec.COR_ID_OLD||',',''id':'||rec.COR_ID_NEW||','),'''id'':'||rec.COR_ID_OLD||',','''id'':'||rec.COR_ID_NEW||',') as data
from KPI_MET_FIELD_DATA a, CUSTOM_TEMP_TABLE_SESSION_1 rec
where A.cmf_fk_id in (145,146,147)
and TYPE_LB in (14,15,16)
and a.KDB_FK_ID in (
select distinct km.KDB_FK_ID
from KPI_MET_FIELD_DATA km , KPI_DET_BASE kp, KPI_REL_KPI_SCORECARD ksc, STR_DET_EMP_SCORECARD sc
where ksc.SDE_FK_ID=sc.SDE_PK_ID
and km.KDB_FK_ID = ksc.KDB_KPI_FK_ID
and km.is_deleted=0
and kp.kdb_pk_id = km.KDB_FK_ID
and kp.is_deleted=0
and km.cmf_fk_id in (145,146,147)
and sc.sdp_fk_id = 8)
and a.is_deleted=0
and (a.data like '%'||rec.COR_ID_OLD||'%');
FOR rec in (SELECT * FROM CUSTOM_TEMP_TABLE_SESSION where TYPE_LB in (14,15,16)) LOOP
update KPI_MET_FIELD_DATA
set LAST_MODIFIED_BY='LB_COPY_CHANGE-'||p2 ,
data = replace(replace(replace(data,'''id'':'||rec.COR_ID_OLD||',','''id'':'||rec.COR_ID_NEW||','),''id':'||rec.COR_ID_OLD||',',''id':'||rec.COR_ID_NEW||','),'''id'':'||rec.COR_ID_OLD||',','''id'':'||rec.COR_ID_NEW||',')
where cmf_fk_id in (145,146,147)
and KDB_FK_ID in (
select distinct km.KDB_FK_ID
from KPI_MET_FIELD_DATA km , KPI_DET_BASE kp, KPI_REL_KPI_SCORECARD ksc, STR_DET_EMP_SCORECARD sc
where ksc.SDE_FK_ID=sc.SDE_PK_ID
and km.KDB_FK_ID = ksc.KDB_KPI_FK_ID
and km.is_deleted=0
and kp.kdb_pk_id = km.KDB_FK_ID
and kp.is_deleted=0
and km.cmf_fk_id in (145,146,147)
and sc.sdp_fk_id = p2)
and is_deleted=0 ;
There are several weaknesses in your code.
WHERE KDB_FK_ID in (select distinct ... does not make any sense. There is no need to make DISTINCT for an IN () clause.
Use ANSI join syntax instead of old Oracle join syntax, it is less error-prone
But the main difference is, your loop does not contain join condition (a.data like '%'||rec.COR_ID_OLD||'%'), i.e. you update entire table KPI_MET_FIELD_DATA again and again for each row in CUSTOM_TEMP_TABLE_SESSION where TYPE_LB in (14,15,16)

PL/SQL: Loop through and alter REFCURSOR rows before returning to calling procedure

I have a refcursor in a function, declared like this:
my_cursor type_refcur_my
And populated as such:
OPEN my_cursor FOR
SELECT DISTINCT A.vegetable, A.animal, A.mineral, A.ID,
(SELECT DISTINCT SUBSTR(bcptr.bcptr_desc_l1,INSTR(bcptr.bcptr_desc_l1,')',-1)-3,3)
FROM doe D, ray R, me M
WHERE ...) ID
FROM artifacts A
ORDER BY vegetable, mineral;
RETURN my_cursor;
I need to perform an operation that involves another SELECT on all the rows in the recursor and use some logic to alter 2 of the column values before returning it. Something kind of like:
IF my_cursor.vegetable = (SELECT B.ID from vegetables B
WHERE my_cursor.vegetable = B.vegetable_description)
THEN
my_cursor.A.ID := B.ID
END IF;
My thought was to put this code after opening the cursor and before returning it. But this produces compile errors, and I cannot find an appropriate example online.I appreciate your help.

Resources