Invalid identifier in Oracle SQL - sql

I haven't created any columns(PAYMENTTERM) in tables with double quotes but
still I'm getting following Error:
Error(26,9): PL/SQL: SQL Statement ignored
Error(27,29): PL/SQL: ORA-00904: "P"."PAYMENTTERM": invalid identifier
Please point out what wrong am i doing and what needs to be corrected:
CREATE OR REPLACE
PROCEDURE PAYTERMUPDATE
IS
RecordCount INT;
BEGIN
SELECT
count(1) INTO RecordCount
FROM
docmeta d
INNER JOIN temp_pay_term p ON d.XPROJECT_ID = p.PID
WHERE
lower(d.PAYMENTTERM) <> lower(p.PAYMENTTERM);
DBMS_OUTPUT.PUT_LINE('');
DBMS_OUTPUT.PUT_LINE('There were '
|| to_char(RecordCount)
|| ' records where payment term is mismatch.');
DBMS_OUTPUT.PUT_LINE('');
FOR X IN (
SELECT p.PID, p.PAYMENTTERM
FROM docmeta d, temp_pay_term p
WHERE d.XPROJECT_ID = p.PID AND d.PAYMENTTERM <> p.PAYMENTTERM)
LOOP
UPDATE docmeta
SET d.PAYMENTTERM = p.PAYMENTTERM
WHERE XPROJECT_ID = X.PID;
END LOOP;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-1000,
'Error occured, No payment term were updated');
END PAYTERMUPDATE;

In this line:
UPDATE docmeta
SET d.PAYMENTTERM = p.PAYMENTTERM
WHERE XPROJECT_ID = X.PID ;
You must add an alias on docmeta (d) and the p.PAYMENTTERM alias must be X
So, change in this way your query:
UPDATE docmeta d
SET d.PAYMENTTERM = X.PAYMENTTERM
WHERE XPROJECT_ID = X.PID ;

Use loop variable as alias to PAYMENTTERM that is X in you case, also declare alias for DOCMETA as d.

Try this?
LOOP
UPDATE docmeta d
SET d.PAYMENTTERM = p.PAYMENTTERM
WHERE XPROJECT_ID = X.PID ;
END LOOP;

Related

Error(13,34): PLS-00201: identifier 'D.BNDNG_TYP' must be declared

I wrote a procedure where i am trying to insert value from source to destination table, i used bulk collect in order to execute the data for large amount of data.
create or replace PROCEDURE TEST2 (
p_array_size IN NUMBER
) IS
CURSOR cur1 IS SELECT DISTINCT
*
FROM
test;
CURSOR cur3 IS SELECT * FROM test;
CURSOR cur2( BND_TYPE d.bndng_typ%TYPE, BND_VAL d.bndng_val%TYPE, FINANCIAL_INST_ID d.financial_institution_id%TYPE, PRDCT_ID d.prdct_id%TYPE,
PRDCT_SUB_ID d.prdct_sub_id%TYPE, INSTRUMENT_ID d.instrmnt_id%TYPE) IS SELECT
d.*
FROM
test1 d,
(
SELECT
b.prdct_id,
FROM
test2 a,
test1 b
WHERE
a.ir_id = b.ir_id
AND a.price_component_id = b.price_component_id
AND a.financial_institution_id = b.financial_institution_id
GROUP BY
b.prdct_id,
) e
WHERE
d.prdct_id = e.prdct_id
AND d.bndng_typ = BND_TYPE
AND d.bndng_val = BND_VAL
AND d.financial_institution_id = FINANCIAL_INST_ID
AND d.prdct_id = PRDCT_ID
AND d.prdct_sub_id = PRDCT_SUB_ID
AND d.instrmnt_id = INSTRUMENT_ID ;
TYPE loan_data_tbl IS TABLE OF cur1%rowtype INDEX BY PLS_INTEGER;
loan_data loan_data_tbl;
TYPE loanrate_tbl IS TABLE OF cur2%rowtype INDEX BY BINARY_INTEGER;
loan_rate loanrate_tbl;
BEGIN
DECLARE
v_noofDays NUMBER:=0;
currentDt DATE;
BEGIN
SELECT * INTO currentDt FROM dt;
BEGIN
IF cur1%Isopen Then
Close cur1;
End IF;
IF cur2%Isopen Then
Close cur2;
End IF;
OPEN cur1;
LOOP
FETCH cur1 BULK COLLECT INTO loan_data LIMIT p_array_size;
EXIT WHEN loan_data.COUNT = 0;
FOR i IN 1..loan_data.COUNT
LOOP
OPEN cur3;
OPEN cur2(loan_data(i).bndng_typ, loan_data(i).bndng_val,loan_data(i).financial_institution_id,
loan_data(i).prdct_id, loan_data(i).prdct_sub_id, loan_data(i).instrmnt_id);
loop
FETCH cur2 BULK COLLECT INTO loan_rate LIMIT p_array_size;
EXIT WHEN loan_rate.COUNT = 0;
FOR j IN 1..loan_rate.COUNT
LOOP
IF(cur3.POS_NUM = loan_data(i).POS_NUM AND cur3.POS_TYPE = loan_data(i).POS_TYPE
AND cur3.PRICE_COMPONENT_ID = loan_rate(j).PRICE_COMPONENT_ID
AND cur3.RPRTD_TILL_DT = loan_data(i).RPRTD_TILL_DT) THEN
update test SET SEQ_NUM=1,
WHERE SEQ_NUM=2;
ELSE
INSERT INTO test VALUES (
....
....
);
END IF;
COMMIT;
END LOOP;
END LOOP;
CLOSE cur2;
CLOSE cur1;
END LOOP;
END LOOP;
CLOSE cur1;
END;
END;
End ;
/
In above procedure, i removed some column names for security purpose.
I am getting two errors one is
PLS-00201: identifier 'D.BNDNG_TYP' must be declared and
PLS-00225: subprogram or cursor 'cur3' reference is out of scope
if any one can help to solve this.
In cursor, You cannot provide the type referring to alias from cursor query. You need to just provide the table name test1 instead of alias d.
CURSOR cur2( BND_TYPE test1.bndng_typ%TYPE,
BND_VAL test1.bndng_val%TYPE,
FINANCIAL_INST_ID test1.financial_institution_id%TYPE,
PRDCT_ID test1.prdct_id%TYPE,
PRDCT_SUB_ID test1.prdct_sub_id%TYPE,
INSTRUMENT_ID test1.instrmnt_id%TYPE)
IS SELECT
.....
This is untested as I don't have your tables and data, but as a first refactoring I would start with this type of structure:
create or replace procedure test2
as
cursor loan_data_cur
( bnd_type cr_loan_prima_rate_orig.bndng_typ%type
, bnd_val cr_loan_prima_rate_orig.bndng_val%type
, financial_inst_id cr_loan_prima_rate_orig.financial_institution_id%type
, prdct_id cr_loan_prima_rate_orig.prdct_id%type
, prdct_sub_id cr_loan_prima_rate_orig.prdct_sub_id%type
, instrument_id cr_loan_prima_rate_orig.instrmnt_id%type )
is
select d.*
from test1 d
join ( select b.prdct_id
from test2 a
join test1 b
on b.ir_id = a.ir_id
and b.price_component_id = a.price_component_id
and b.financial_institution_id = a.financial_institution_id
group by b.prdct_id ) e
on d.prdct_id = e.prdct_id
where d.bndng_typ = loan_data_cur.bnd_type
and d.bndng_val = loan_data_cur.bnd_val
and d.financial_institution_id = loan_data_cur.financial_inst_id
and d.prdct_id = loan_data_cur.prdct_id
and d.prdct_sub_id = loan_data_cur.prdct_sub_id
and d.instrmnt_id = loan_data_cur.instrument_id;
begin
for loan_data in (
select distinct *
from test
)
loop
for loan_rate in loan_data_cur
( loan_data.bndng_typ
, loan_data.bndng_val
, loan_data.financial_institution_id
, loan_data.prdct_id
, loan_data.prdct_sub_id
, loan_data.instrmnt_id )
loop
update test t set t.seq_num = 1
where t.seq_num = 2
and t.pos_num = loan_data.pos_num
and t.pos_type = loan_data.pos_type
and t.price_component_id = loan_rate.price_component_id
and t.rprtd_till_dt = loan_data.rprtd_till_dt;
if sql%rowcount = 0 then
insert into test values (x, y, z);
end if;
end loop;
end loop;
commit;
end test2;
I would also looking at doing the join from test to test1 as a single cursor instead of explicitly with two separate cursors, as the optimiser might find a more efficient method, such as a hash join.
Probably the update/insert combination could be written as a single merge. Once you have a merge working, you might find you can apply it to the whole table in one shot and not need any cursor loops at all.
Bulk-collecting into an array could be useful if the number of row-by-row updates causes performance problems (if it's more than a few thousand, say). If so, you would want to structure it so that you could apply the updates/inserts using a forall construction, not more loops.

Bulk Collect with Sum function

I am trying to use Bulk all and Forall in Oracle database:
Original code from Procedure is as below:
IF NVL(v_mc,0) != 0 THEN
FOR rec IN
(SELECT a.testid,
SUM(pct * NVL(cap,0))/v_mc lead1
BULK COLLECT INTO testids1, testids2
FROM testtable a
WHERE a.id = n_id
AND a.type =n_type
GROUP BY a.testid;
)
LOOP
UPDATE testtable
SET LEAD1 =ROUND(testids2(i),2)
WHERE tid = n_id
AND type = n_type
AND testid =testids1(i);
END LOOP;
END IF;
So In select statement , I am using Sum function and also using aliasing here .
Code , I have written which use Bulk collect and Forall is as follows:
PROCEDURE test
IS
TYPE test1Tab IS TABLE OF sh_rpt_temp_peer_wip.test1%TYPE;
TYPE test2Tab IS TABLE OF testtable.lead1%TYPE;
testids1 testidTab; --Error 1 and Error 2
testids2 LeadTab;
BEGIN
IF NVL(v_mc,0) != 0 THEN
SELECT testid,
SUM(pct * NVL(cap,0))/v_mc lead1
BULK COLLECT INTO testids1, testids2
FROM testtable a --Error 3
WHERE a.id = n_id
AND a.type =n_type
GROUP BY a.testid ORDER BY a.testid;
FORALL i IN testids1.FIRST..testids1.LAST
UPDATE testtable
SET LEAD1 =ROUND(testids2(i),2)
WHERE tid = n_id --Error 3
AND type = n_type
AND testid =testids1(i);
END IF;
END;
But while I am compiling procedure , I am getting multiple errors. I am very new to PL/SQL. Please let me know if I can retrieve calculated value as a Column in Bulk Collect?
I am getting below errors in procedure:
Error 1) PL/SQL: Item ignored
Error 2) component 'LEAD' must be declared
Error 3) expression is of wrong type
Please let me know what is wrong here
Thanks
As I identified that the collection type that you are referring is not in scope in the procedure, might be you have declared globally. I modified your code get a try it once, hopefully, it works for you.
PROCEDURE test
IS
TYPE test1Tab IS TABLE OF testtable.testid%TYPE;
TYPE test2Tab IS TABLE OF number;
testids1 test1Tab; //Error 1 and Error 2
testids2 test2Tab;
BEGIN
IF NVL(v_mc,0) != 0 THEN
SELECT testid,
SUM(pct * NVL(cap,0))/v_mc lead
BULK COLLECT INTO testids1, testids2
FROM testtable a //Error 3
WHERE a.id = n_id
AND a.type =n_type
GROUP BY a.testid ORDER BY a.testid;
FORALL i IN testids1.FIRST..testids1.LAST
UPDATE testtable
SET LEAD = ROUND(testids2(i),2)
WHERE tid = n_id //Error 3
AND type = n_type
AND testid = testids1(i);
END IF;
END;

How to use dynamic column names in an UPDATE or SELECT statement in a function?

In PostgreSQL 9.1, PL/pgSQL, given a query:
select fk_list.relname from ...
where relname is of type name (e.g., "table_name").
How do you get the appropriate value for "relname" that can be used directly in an UPDATE statement as:
Update <relname> set ...
within the PL/pgSQL script?
Using quote_ident(r.relname) as:
Update quote_ident(r.relname) Set ...
fails with:
syntax error at or near "("
LINE 55: UPDATE quote_ident(r.relname) ....
The complete code I am working with:
CREATE FUNCTION merge_children_of_icd9 (ocicd9 text,
ocdesc text, ncicd9 text, ncdesc text)
RETURNS void AS $BODY$
DECLARE
r RECORD;
BEGIN
FOR r IN
WITH fk_actions ( code, action ) AS (
VALUES ('a', 'error'),
('r', 'restrict'),
('c', 'cascade'),
('n', 'set null'),
('d', 'set default')
), fk_list AS (
SELECT pg_constraint.oid AS fkoid, conrelid, confrelid::regclass AS parentid,
conname, relname, nspname,
fk_actions_update.action AS update_action,
fk_actions_delete.action AS delete_action,
conkey AS key_cols
FROM pg_constraint
JOIN pg_class ON conrelid = pg_class.oid
JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid
JOIN fk_actions AS fk_actions_update ON confupdtype = fk_actions_update.code
JOIN fk_actions AS fk_actions_delete ON confdeltype = fk_actions_delete.code
WHERE contype = 'f'
), fk_attributes AS (
SELECT fkoid, conrelid, attname, attnum
FROM fk_list
JOIN pg_attribute ON conrelid = attrelid AND attnum = ANY(key_cols)
ORDER BY fkoid, attnum
), fk_cols_list AS (
SELECT fkoid, array_agg(attname) AS cols_list
FROM fk_attributes
GROUP BY fkoid
)
SELECT fk_list.fkoid, fk_list.conrelid, fk_list.parentid, fk_list.conname,
fk_list.relname, fk_cols_list.cols_list
FROM fk_list
JOIN fk_cols_list USING (fkoid)
WHERE parentid = 'icd9'::regclass
LOOP
RAISE NOTICE 'now in loop. relname is %', quote_ident(r.relname);
RAISE NOTICE 'cols_list[1] is %', quote_ident(r.cols_list[1]);
RAISE NOTICE 'cols_list[2] is %', quote_ident(r.cols_list[2]);
RAISE NOTICE 'now doing update';
UPDATE quote_ident(r.relname) SET r.cols_list[1] = ncicd9, r.cols_list[2] = ncdesc
WHERE r.cols_list[1] = ocicd9 AND r.cols_list[2] = ocdesc;
RAISE NOTICE 'finished update';
END LOOP;
RETURN;
END $BODY$ LANGUAGE plpgsql VOLATILE;
-- select merge_children_of_icd9('', 'aodm type 2', '', 'aodm, type 2');
I'm sure this kind of thing is done often, but I can't seem to find anything like it using PostgreSQL. Is there a better way?
In an UPDATE statement in PL/pgSQL, the table name has to be given as a literal. If you want to dynamically set the table name and the columns, you should use the EXECUTE command and paste the query string together:
EXECUTE 'UPDATE ' || quote_ident(r.relname) ||
' SET ' || quote_ident(r.cols_list[1]) || ' = $1, ' ||
quote_ident(r.cols_list[2]) || ' = $2' ||
' WHERE ' || quote_ident(r.cols_list[1]) || ' = $3 AND ' ||
quote_ident(r.cols_list[2]) || ' = $4'
USING ncicd9, ncdesc, ocicd9, ocdesc;
The USING clause can only be used for substituting data values, as shown above.
You need dynamic SQL with EXECUTE like #Patrick already provided.
However, both your function and the EXECUTE part can be much simpler. In particular, use format() to concatenate longer query strings safely (available since pg 9.1):
CREATE OR REPLACE FUNCTION merge_children_of_icd9 (_ocicd9 text, _ocdesc text
, _ncicd9 text, _ncdesc text)
RETURNS void
LANGUAGE plpgsql AS
$func$
DECLARE
_sql text;
BEGIN
FOR _sql IN
SELECT format('UPDATE %3$s SET %1$I = $3 , %2$I = $4
WHERE %1$I = $1 AND %2$I = $2'
, x.cols[1], x.cols[2], x.conrelid::regclass::text)
FROM (
SELECT c.conrelid, array_agg(a.attname ORDER BY a.attnum) AS cols
FROM pg_constraint c
JOIN pg_attribute a ON a.attrelid = c.conrelid
AND a.attnum = ANY(c.conkey)
WHERE c.confrelid = 'icd9'::regclass
AND c.contype = 'f'
GROUP BY c.oid, c.conrelid
ORDER BY c.oid
) x
LOOP
-- RAISE NOTICE '%', _sql; -- debug?
EXECUTE _sql
USING _ocicd9, _ocdesc, _ncicd9, _ncdesc;
END LOOP;
END
$func$;
The function errors out if the FK constraint does not span at least two columns or if the data type of the columns is not compatible with text. May or may not be as intended.
Details on how to pass identifiers safely and execute dynamic SQL:
Table name as a PostgreSQL function parameter
Define table and column names as arguments in a plpgsql function?

01403. 00000 - "no data found"

I'm updating my docmeta table but i get the
ORA-01403: no data found
ORA-06512: at line 25
01403. 00000 - "no data found" error
Below is the query. How can i get rid of this error?
DECLARE
varPayTerm varchar2(300);
BEGIN
FOR X IN(
SELECT
d.did, d.xproject_id
FROM
revisions r,
(SELECT DDOCNAME, MAX(DID) mDID
FROM
REVISIONS REV
WHERE
dcreatedate >='01-Jan-14'
GROUP BY
DDOCNAME
) RevLatestID,
docmeta d
--temp_project p
WHERE
RevLatestID.mdid = r.did
and d.did = r.did
)
loop
select paymentterm
into varPayTerm
from project where pid = X.xproject_id and paymentterm is not null;
update docmeta
set xpaymentterm= varPayTerm
where did=X.dID
and xproject_id = X.xproject_id;
END LOOP;
END ;
The SELECT...INTO statements is returning no rows
If it is valid for no rows to be returned, and you want to continue you can catch and ignore the NO_DATA_FOUND exception like this:
...
BEGIN
SELECT ... INTO...;
EXCEPTION
WHEN NO_DATA_FOUND THEN
paymentterm := 0;
-- or ant msg you want
then return 'no data here';
END;
...

IF EXISTS condition not working with PLSQL

I am trying to print the TEXT when condition is TRUE. The select code is perfectly working fine. It's showing 403 value when i only run select code. But I have to print some text when condition exists. What's the problem with following code.
BEGIN
IF EXISTS(
SELECT CE.S_REGNO FROM
COURSEOFFERING CO
JOIN CO_ENROLMENT CE
ON CE.CO_ID = CO.CO_ID
WHERE CE.S_REGNO=403 AND CE.COE_COMPLETIONSTATUS = 'C' AND CO.C_ID = 803
)
THEN
DBMS_OUTPUT.put_line('YES YOU CAN');
END;
Here is the error report:
Error report:
ORA-06550: line 5, column 1:
PLS-00103: Encountered the symbol "JOIN" when expecting one of the following:
) , with group having intersect minus start union where
connect
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
IF EXISTS() is semantically incorrect. EXISTS condition can be used only inside a SQL statement. So you might rewrite your pl/sql block as follows:
declare
l_exst number(1);
begin
select case
when exists(select ce.s_regno
from courseoffering co
join co_enrolment ce
on ce.co_id = co.co_id
where ce.s_regno=403
and ce.coe_completionstatus = 'C'
and ce.c_id = 803
and rownum = 1
)
then 1
else 0
end into l_exst
from dual;
if l_exst = 1
then
DBMS_OUTPUT.put_line('YES YOU CAN');
else
DBMS_OUTPUT.put_line('YOU CANNOT');
end if;
end;
Or you can simply use count function do determine the number of rows returned by the query, and rownum=1 predicate - you only need to know if a record exists:
declare
l_exst number;
begin
select count(*)
into l_exst
from courseoffering co
join co_enrolment ce
on ce.co_id = co.co_id
where ce.s_regno=403
and ce.coe_completionstatus = 'C'
and ce.c_id = 803
and rownum = 1;
if l_exst = 0
then
DBMS_OUTPUT.put_line('YOU CANNOT');
else
DBMS_OUTPUT.put_line('YES YOU CAN');
end if;
end;
Unfortunately PL/SQL doesn't have IF EXISTS operator like SQL Server. But you can do something like this:
begin
for x in ( select count(*) cnt
from dual
where exists (
select 1 from courseoffering co
join co_enrolment ce on ce.co_id = co.co_id
where ce.s_regno = 403
and ce.coe_completionstatus = 'C'
and co.c_id = 803 ) )
loop
if ( x.cnt = 1 )
then
dbms_output.put_line('exists');
else
dbms_output.put_line('does not exist');
end if;
end loop;
end;
/