How to use part of a query inside a variable Oracle Function - sql

I'm trying to use a part of a query into a variable , because I have many ifs to prepare the query , but I have not used something like this.
FUNCTION f_rel_vendas_importacao(vTP_DADOS in varchar2, nCODIGO in number, dDT_COMPRA_INI in DATE, dDT_COMPRA_FIM in DATE, dDT_EFETIVACAO_INI in DATE, dDT_EFETIVACAO_FIM in DATE) RETURN number is
nRetorno number(14,2);
vSTRING VARCHAR2(2000);
begin
IF vTP_DADOS = 'VL_COMISSAO' THEN
vSTRING := 'SUM( DECODE( CPF.CD_MOEDA, 1, CPF.VL_COTACAO_UNIT * CPF.QTDE_COMPRA, ( SELECT Imp_Pack.fu_converte_moeda_ORACLE( CPF.CD_MOEDA, 1, CDT.DT_RECEBIMENTO, CPF.VL_COTACAO_UNIT, 1) * CPF.QTDE_COMPRA FROM dual ) ) )';
END IF;
IF vTP_DADOS = 'QTD_VENDA_SELECTCHEMIE_PERIODO' THEN
vSTRING := 'count(*)';
END IF;
SELECT
vSTRING
INTO
nRetorno
FROM
COMPRA_PROD_FORN CPF,
COMPRA_DATA CDT
WHERE
(CDT.DT_RECEBIMENTO >= dDT_COMPRA_INI AND CDT.DT_RECEBIMENTO <= dDT_COMPRA_FIM) AND
CDT.CD_COMPRA = CPF.CD_COMPRA AND
CDT.CD_TP_DATA = 7 AND
CPF.CD_FORNECEDOR = nCODIGO;
Return nRetorno;
end
f_rel_vendas_importacao;

When you SELECT vString, the result is the content of the variable vString. It doesn't make any attempt to interpret that content as a column or expression. You are best off placing those expressions directly into the query.
IF vTP_DADOS = 'VL_COMISSAO' THEN
SELECT
SUM( DECODE( CPF.CD_MOEDA, 1,
CPF.VL_COTACAO_UNIT * CPF.QTDE_COMPRA,
( SELECT Imp_Pack.fu_converte_moeda_ORACLE( CPF.CD_MOEDA, 1,
CDT.DT_RECEBIMENTO, CPF.VL_COTACAO_UNIT, 1) *
CPF.QTDE_COMPRA
FROM dual ) ) )
INTO
nRetorno
FROM
COMPRA_PROD_FORN CPF,
COMPRA_DATA CDT
WHERE
(CDT.DT_RECEBIMENTO >= dDT_COMPRA_INI AND
CDT.DT_RECEBIMENTO <= dDT_COMPRA_FIM) AND
CDT.CD_COMPRA = CPF.CD_COMPRA AND
CDT.CD_TP_DATA = 7 AND
CPF.CD_FORNECEDOR = nCODIGO;
END IF;
IF vTP_DADOS = 'QTD_VENDA_SELECTCHEMIE_PERIODO' THEN
SELECT count(*)
INTO
nRetorno
FROM
COMPRA_PROD_FORN CPF,
COMPRA_DATA CDT
WHERE
(CDT.DT_RECEBIMENTO >= dDT_COMPRA_INI AND
CDT.DT_RECEBIMENTO <= dDT_COMPRA_FIM) AND
CDT.CD_COMPRA = CPF.CD_COMPRA AND
CDT.CD_TP_DATA = 7 AND
CPF.CD_FORNECEDOR = nCODIGO;
END IF;

Related

Save the output of a procedure in snowflake in a variable

I have following procedure:
create or replace procedure todays_delivery_amount(targetkey_variable varchar)
returns number
language sql
as
$$
begin
if ((SELECT MONTHLY_DELIVERED_AMOUNT FROM test.process.msv_month_amount where TARGET_KEY = '20') = 0)
then
return ((SELECT monthly_target_amount from test.process.msv_month_amount)/3) ;
else
return ((SELECT monthly_target_amount from test.process.msv_month_amount where TARGET_KEY = '20') - (SELECT MONTHLY_DELIVERED_AMOUNT from test.process.msv_month_amount where TARGET_KEY = '20')) /
(SELECT (SELECT DATEDIFF(DAY,CONCAT(LEFT(current_date(), 8), '01')::date, CONCAT(LEFT(current_date(), 8), (SELECT datediff(dd,current_date(),dateadd(mm,1,current_date()))))::date+1)
- DATEDIFF(WEEK,CONCAT(LEFT(current_date(), 8), '01')::date, CONCAT(LEFT(current_date(), 8), (SELECT datediff(dd,current_date(),dateadd(mm,1,current_date()))))::date+1)) - RIGHT(current_date() -1, 2)::number + CAST(Round((day( current_date() ) +6)/7,0) as VARCHAR)::number);
end if;
end;
$$
;
UNSET todays_amount;
call todays_delivery_amount('10');
Now I want to do two things:
First I would like to save the output of the procedure in the variable todays_amount
So I tried this:
SET todays_amount = call todays_delivery_amount('10');
But this does not work.
And second:
Instead of where TARGET_KEY = '20' i would like to do where TARGET_KEY = targetkey_variable
But this does not work.
It appears that you can't set it directly from the call statement, but you can do this:
UNSET todays_amount;
call todays_delivery_amount('10');
set todays_amount = (select TODAYS_DELIVERY_AMOUNT from table(result_scan(last_query_id())));

Stored Procedure (rookie programmer), What's wrong with my code?

I'm trying to create a stored procedure that will update a related row in the BugIt table given an expense report number ErepNo and expense category number EXNo.
The procedure must contain 3 input parameters EXNo ,ErepNo and the 'rUpAm' and with one boolean output parameter aRes.
If the ErepStatus is equal to 'Approved' then the stored value rUpAm is added to BIAct,the output parameter is set to TRUE and the row is updated.
Else the ErepStatus is equal to 'Denied' or 'Pending' an application error is raised and the output parameter is set to FALSE.
Here is what I have tried so far
CREATE OR REPLACE PROCEDURE StoredProcedure1
(tEXNo IN BugIt.EXNo%TYPE,
tErepNo IN ER.ErepNo%TYPE,
rUpAm IN INTEGER,
aRes OUT BOOLEAN ) IS
FOUND BOOLEAN := False;
tErepStatus ER.ErepStatus%TYPE;
tBIAct BugIt.BIAct%TYPE;
CURSOR ERCur(TmpErepNo ExpI.ErepNo%TYPE)is
SELECT ExpI.EXNo, ER.ErepStatus
FROM ER, BugIt, ExpI
WHERE ER.ErepNo = tErepNo
AND BugIt.EXNo = ExpI.EXNo
AND ER.ErepNo = ExpI.ErepNo
ORDER BY ErepStatus DESC;
BEGIN
OPEN ERCur(tErepNo);
LOOP
FETCH ERCur INTO tErepStatus, tBIAct;
EXIT WHEN ERCur%NOTFOUND;
IF tErepStatus = 'APPROVED' THEN
FOUND := TRUE;
END IF;
IF tErepStatus = 'PENDING' OR tErepStatus = 'DENIED' THEN
FOUND := False;
EXIT;
END IF;
END LOOP;
CLOSE ERCur;
IF FOUND THEN
tBIAct := tBIAct + rUpAm;
aRes := TRUE;
UPDATE BugIt
Set BIAct = tBIAct
WHere EXNo = tEXNo;
ElSE
tBIAct := tBIAct;
aRes := False;
END IF;
EXCEPTION
WHEN OTHERS THEN aRes := False;
raise_application_error(-20001, 'Database error');
END;
/
Here is my test case to implement the code that I have, which keeps coming up with errors(below)
---test--
SET Serveroutput on;
SELECT ExpI.EXNo, ER.ErepStatus
FROM ER, BugIt, ExpI
WHERE ER.ErepNo = 21
AND BugIt.EXNo = ExpI.EXNo
AND BugIt.ErepNo = ExpI.ErepNo
ORDER BY ErepStatus DESC;
-- Test script
DECLARE
Result BOOLEAN;
aBIAct BugIt.BIAct%TYPE;
BEGIN
StoredProcedure1(2, 2, 3, Result);
IF Result then
dbms_output.put_line('updated element to the BugIt table');
ELSE
dbms_output.put_line('Row not updated to the BugIt table');
END IF;
END;
/
ERROR at line 1:
ORA-20001: Database error
ORA-06512: at "SYSTEM.SPROLLUPEXPITEM", line 51
ORA-06512: at line 6
I think you want:
CREATE OR REPLACE PROCEDURE StoredProcedure1 (
tEXNo IN BugIt.EXNo%TYPE,
tErepNo IN ER.ErepNo%TYPE,
rUpAm IN INTEGER,
aRes OUT BOOLEAN
)
IS
tErepStatus ER.ErepStatus%TYPE;
BEGIN
SELECT ER.ErepStatus
INTO tErepStatus
FROM ER
INNER JOIN ExpI
ON ( ER.ErepNo = ExpI.ErepNo )
INNER JOIN BugIt
ON ( BugIt.EXNo = ExpI.EXNo )
WHERE ER.ErepNo = tErepNo
AND ER.ErepStatus IN ( 'APPROVED', 'PENDING', 'DENIED' )
ORDER BY ErepStatus DESC
FETCH FIRST ROW ONLY;
aRes := tErepStatus = 'APPROVED';
IF aRes THEN
UPDATE BugIt
SET BIAct = BIAct + rUpAm
WHERE EXNo = tEXNo;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
aRes := FALSE;
END;
/
Which, for the sample data:
CREATE TABLE ER ( ErepNo, Erepstatus ) AS
SELECT 1, 'APPROVED' FROM DUAL UNION ALL
SELECT 2, 'PENDING' FROM DUAL UNION ALL
SELECT 3, 'DENIED' FROM DUAL UNION ALL
SELECT 3, 'APPROVED' FROM DUAL;
CREATE TABLE ExpI ( ErepNo, EXNo ) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 2 FROM DUAL UNION ALL
SELECT 3, 3 FROM DUAL;
CREATE TABLE BugIt ( EXNo, BIAct ) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 2 FROM DUAL UNION ALL
SELECT 3, 3 FROM DUAL;
Then:
DECLARE
aRes BOOLEAN;
BEGIN
StoredProcedure1( 1, 1, 1, aRes );
IF ares THEN
DBMS_OUTPUT.PUT_LINE( 'Changed' );
ELSE
DBMS_OUTPUT.PUT_LINE( 'Not Changed' );
END IF;
END;
/
Outputs Changed and:
SELECT * FROM BugIt;
Gives:
EXNO | BIACT
---: | ----:
1 | 2
2 | 2
3 | 3
And:
DECLARE
aRes BOOLEAN;
BEGIN
StoredProcedure1( 3, 3, 1, aRes );
IF ares THEN
DBMS_OUTPUT.PUT_LINE( 'Changed' );
ELSE
DBMS_OUTPUT.PUT_LINE( 'Not Changed' );
END IF;
END;
/
Outputs Not Changed and the BugIt table has not changed.
db<>fiddle here

How to concat variable to other variable in postgresql?

In php connecting variable using . (dot), like $a = "tes", $b="b", if
I connect $a.$b it becomes "tesb". I want this but in postgres
I have tried using dot and + but wrong
my code
CREATE OR REPLACE FUNCTION make_ctx_contoh (rl varchar, dat varchar)
RETURNS numeric AS $total$
declare
date_before date;
dan text;
os_l numeric;
BEGIN
date_before = (DATE_TRUNC('month', NOW())
+ '0 MONTH'::INTERVAL
- '1 DAY'::INTERVAL)::DATE;
if rl = 'nasional' THEN
dan = '';
ELSEIF rl = 'kanwil' THEN
dan = 'AND LOWER(a."KANWIL") = knw';
ELSEIF rl = 'kc' THEN
dan = 'AND LOWER(a."KC") = kac';
END IF;
SELECT
SUM("a"."OUTSTANDING") into os_l
FROM
"public".tbl_nominatif_hasil AS "a"
WHERE
"a"."BUSS_DATE" = date_before AND
"a"."COLLDET" = '1 ' + dan; RETURN os_l;
END; $total$ LANGUAGE plpgsql;
When I run select make_ctx_contoh('kanwil','2'); that shows error like :
ERROR: operator does not exist: unknown + text LINE 6:
"a"."COLLDET" = '1 ' + dan
From what I could understand from your code, you want to add conditions to the where clause based on certain criteria.
So, convert your select query into a AND OR logic.
SELECT
SUM("a"."OUTSTANDING") into os_l
FROM "public".tbl_nominatif_hasil AS "a"
WHERE "a"."BUSS_DATE" = date_before AND
"a"."COLLDET" = 1
AND ( rl = 'nasional' OR
( rl = 'kanwil' AND LOWER(a."KANWIL") = knw) OR
( rl = 'kc' AND LOWER(a."KC") = kac')
)

Procedure takes several hours to complete

I'm trying to generate a report that give me the percentage of each grade for each letter for each term for each subject. The final report has subject codes like MAT, ENC, etc. as the row headers and then the terms and letter grades as the column headers. I'm currently looping through each subject, then each term, then each grade and it takes 2+ hours to complete. What is the correct/more efficient way of accomplishing this. Feel free to ask if you have any questions. Thanks for your help!!
v_subj varchar2(5);
v_grade varchar2(5);
v_termloop varchar2(6);
c_total number;
c_dfw number;
c_gradetotal number;
c_gradedfw number;
v_output varchar2(1000);
Cursor Get_Terms is
select distinct ssbsect_term_code from ssbsect
where ssbsect_term_code >= 201230
and ssbsect_term_code not like '%5'
order by 1;
Cursor Get_Subj is
Select distinct ssbsect_subj_code from ssbsect
where ssbsect_term_code >= 201230
and ssbsect_insm_code = 'DL'
and ssbsect_term_code not like '%5'
order by 1;
Cursor Get_Grade is
select distinct msvcrse_grde_code from msvcrse
where msvcrse_grde_code in ('A','B','C','D','F','S','U','W')
order by 1;
Cursor Get_Total is
select count(msvcrse_pidm) from msvcrse
join ssbsect on msvcrse_crn = ssbsect_crn
and msvcrse_Term_code = ssbsect_term_code
where msvcrse_term_code = v_termloop
and ssbsect_insm_code = 'DL'
and msvcrse_crse_numb < '3000'
and msvcrse_subj_code = v_subj
and msvcrse_gmod_code <> 'D';
Cursor Get_GradeTotal is
select count(msvcrse_pidm) from msvcrse
join ssbsect on msvcrse_crn = ssbsect_crn
and msvcrse_Term_code = ssbsect_term_code
where msvcrse_term_code = v_termloop
and ssbsect_insm_code = 'DL'
and msvcrse_grde_code like '%'||v_grade||'%'
and msvcrse_crse_numb < '3000'
and msvcrse_subj_code = v_subj
and msvcrse_gmod_code <> 'D';
Begin
Open Get_Subj;
Loop
Fetch Get_Subj into v_subj;
Exit when Get_Subj%NOTFOUND;
v_output := '';
v_output := v_subj;
Open Get_Terms;
Loop
Fetch Get_Terms into v_termloop;
Exit when Get_Terms%NOTFOUND;
Open Get_Grade;
Loop
Fetch Get_Grade into v_grade;
Exit when Get_grade%NOTFOUND;
Open Get_Total;
Fetch Get_Total into c_total;
Close Get_Total;
Open Get_GradeTotal;
Fetch Get_GradeTotal into c_gradetotal;
Close Get_GradeTotal;
if c_total = 0 then
c_total := NULL;
end if;
v_output := v_output ||'|'||nvl(100*(round(c_gradetotal/c_total,3)),0);
--dbms_output.put_line(v_subj||'|'||v_termloop||'|'||v_insm||'|'||nvl(100*(round(c_dfw/c_total,3)),0));
End Loop;
Close Get_Grade;
End Loop;
Close Get_Terms;
dbms_output.put_line(v_output);
I change a little bit your code, but the result should be the same.
Please merge the cursors get_grade and get_gradetotal, makes no sense have two in this code.
DECLARE
c_total NUMBER;
v_output VARCHAR2 ( 1000 );
CURSOR get_subj IS
SELECT DISTINCT ssbsect_subj_code
FROM ssbsect
WHERE ssbsect_term_code >= 201230
AND ssbsect_insm_code = 'DL'
AND ssbsect_term_code NOT LIKE '%5'
ORDER BY 1;
CURSOR get_terms IS
SELECT DISTINCT ssbsect_term_code
FROM ssbsect
WHERE ssbsect_term_code >= 201230
AND ssbsect_term_code NOT LIKE '%5'
ORDER BY 1;
CURSOR get_total ( lci_termloop IN ssbsect.msvcrse_term_code%TYPE
, lci_subj IN msvcrse.msvcrse_subj_code%TYPE ) IS
SELECT COUNT ( msvcrse_pidm )
FROM msvcrse
JOIN ssbsect
ON msvcrse_crn = ssbsect_crn
AND msvcrse_term_code = ssbsect_term_code
WHERE msvcrse_term_code = lci_termloop
AND ssbsect_insm_code = 'DL'
AND msvcrse_crse_numb < '3000'
AND msvcrse_subj_code = lci_subj
AND msvcrse_gmod_code <> 'D';
CURSOR get_grade IS
SELECT DISTINCT msvcrse_grde_code
FROM msvcrse
WHERE msvcrse_grde_code IN ('A', 'B', 'C', 'D', 'F', 'S', 'U', 'W')
ORDER BY 1;
CURSOR get_gradetotal ( lci_termloop IN ssbsect.msvcrse_term_code%TYPE
, lci_grade IN msvcrse.msvcrse_grde_code%TYPE
, lci_subj IN msvcrse.msvcrse_subj_code%TYPE ) IS
SELECT COUNT ( msvcrse_pidm ) AS msvcrse_pidm
FROM msvcrse
JOIN ssbsect
ON msvcrse_crn = ssbsect_crn
AND msvcrse_term_code = ssbsect_term_code
WHERE msvcrse_term_code = lci_termloop
AND ssbsect_insm_code = 'DL'
AND msvcrse_grde_code LIKE '%' || lci_grade || '%'
AND msvcrse_crse_numb < '3000'
AND msvcrse_subj_code = lci_subj
AND msvcrse_gmod_code <> 'D';
BEGIN
FOR rec_subj IN get_subj LOOP
v_output := rec_subj.ssbsect_subj_code;
FOR rec_term IN get_terms LOOP
OPEN get_total ( lci_termloop => rec_term.ssbsect_term_code
, lci_subj => rec_subj.ssbsect_subj_code );
FETCH get_total INTO c_total;
CLOSE get_total;
FOR rec_grade IN get_grade LOOP
<<grade_total>>
FOR rec_gradetotal IN get_gradetotal ( lci_termloop => rec_term.ssbsect_term_code
, lci_grade => rec_grade.msvcrse_grde_code
, lci_subj => v_output ) LOOP
IF c_total = 0 THEN
c_total := NULL;
v_output := v_output || '|';
CONTINUE grade_total; -- <-- AVOID WORK
END IF;
v_output := v_output || '|' || NVL ( 100 * ( ROUND ( rec_gradetotal.msvcrse_pidm / c_total, 3 ) ), 0 ); -- <-- PLEASE MAKE THIS CODE BETTER
--dbms_output.put_line(rec_subj.ssbsect_subj_code||'|'||rec_term.ssbsect_term_code||'|'||v_insm||'|'||nvl(100*(round(c_dfw/c_total,3)),0));
END LOOP grade_total;
END LOOP;
END LOOP;
--DBMS_OUTPUT.put_line ( rec_subj.ssbsect_subj_code );
END LOOP;
END;
I change all code without compile, please make sure the types used int the cursor.
I guess 50% of improvement, but you need test.
Let me know whether you have any question and please use table alias in SQL and be careful with loops.
Thank you.

How to make a wrapper to return something other than ref cursor

I've got the following PL SQL function that returns a ref cursor but the application i am using does not support ref cursors. How can i make this code return something other than ref cursor
FUNCTION getADedIcWarningsProv(p_hos_id IN work_entity_data.hos_id%TYPE
,p_date IN DATE
)
RETURN eOdatatypes_package.eOrefcur
IS
v_refcur eOdatatypes_package.eOrefcur;
BEGIN
OPEN v_refcur FOR
SELECT IF_type IF_type
,COUNT(*) number_infected
FROM (SELECT DISTINCT bd.PT_id PT_id
,CASE WHEN NVL(O_package.get_O_code_property(pw.warning_code,'Setl'),'N') = 'Y'
THEN cd.description
ELSE 'Other'
END IF_type
FROM PT_ad pad
,BD_details bd
,PT_warnings pw
,codes cd
WHERE bd.current_record = 'Y'
AND bd.BD_location IS NOT NULL
AND bd.BD_status IN (SELECT code
FROM codes
WHERE prog_code IN (1, 1, 2)
AND code_type = 4)
AND bd.AD_no = pad.AD_no
AND pad.hos_id = p_hos_id
AND pw.PT_id = bd.PT_id
AND pw.warning_to IN ('D','Q')
AND p_date BETWEEN pw.applies_start
AND NVL(pw.applies_end,p_date)
AND NVL(O_package.get_O_code_property(pw.warning_code,'INFT'),'Y') = 'N'
AND pw.warning_code = cd.code)
GROUP BY IF_type
ORDER BY IF_type;
RETURN v_refcur;
END getADedIcWarningsProv;
OUTPUT:
IF_TYPE NUMBER_IF
---------------------------------------- ---------------
C 2
M 6
Other 4
3 rows selected
You can use a pipeline function to return a result set one record at a time, but in a way that the SQL engine can understand.
create or replace package WrapperSample is
type TResultRow is record(
if_type codes.cd%type
,number_infected Integer);
type TResultRowList is table of TResultRow;
function GetADedIcWarningsProv
(
p_hos_id in work_entity_data.hos_id%type
,p_date in date
) return TResultRowList
pipelined;
end WrapperSample;
/
create or replace package body WrapperSample is
function GetADedIcWarningsProv
(
p_hos_id in work_entity_data.hos_id%type
,p_date in date
) return TResultRowList
pipelined is
v_refcur eOdatatypes_package.eOrefcur;
currentRow TResultRow;
begin
v_refcur := YourSchema.getADedIcWarningsProv(p_hos_id, p_date);
loop
fetch v_refcur
INTO currentRow;
exit when v_refcur%NotFound;
pipe row(currentRow);
end loop;
close v_refcur;
return;
end;
end WrapperSample;
/
Using this package, you can select your ref cursor:
SELECT if_type
,number_infected
FROM table(WrapperSample.getADedIcWarningsProv(1, 2))