EXECUTE IMMEDIATE fail - plsql

I'm trying to use "EXECUTE IMMEDIATE", when I run the procedure I keep getting -
ORA-00904: "NOV": invalid identifier.
I checked the column like 20 times, the name is right. the table has only 2 columns - acc, run1.
also, in the output I get [using the dbms_output.put_line (v_stat)]:
update LAHAD2
set run1 = 19-NOV-17
am I missing something?
create or replace procedure lahad is
v_sec_t varchar2 (100) := 'LAHAD2';
v_date date := trunc (sysdate+10);
v_stat varchar2 (500);
begin
v_stat :=
'update '|| v_sec_t|| '
set run1 = ' || v_date ;
dbms_output.put_line (v_stat);
EXECUTE IMMEDIATE v_stat;
end Lahad;

You must have this:
update LAHAD2 set run1 = '19-NOV-17'
So for this above, you must have
v_stat := 'update '|| v_sec_t|| ' set run1 = ''' || v_date ||'''';
It might also be safer to have a to_date around this. To expect:
update LAHAD2 set run1 = to_date('19-NOV-17', 'DD-MON-YY')
You need
v_stat := 'update '|| v_sec_t|| ' set run1 = to_date(''' || v_date ||''') , ''DD-MON-YY'')';
Hope you get the thing, especially this '' to input single ' in the output.

Related

DBMS_OUTPUT to a table with timestamp

I am deleting a massive table and want to delete the table in batches. I am deleting records older than 467 days. I want to insert the dbms_output status in the following procedure to be written to a table that has two columns such as the number of records to be deleted with timestamp columns:
CREATE OR REPLACE PROCEDURE delete_tab (tablename IN VARCHAR2, nrows IN NUMBER ) IS
sSQL1 VARCHAR2(2000);
sSQL2 VARCHAR2(2000);
nCount NUMBER;
BEGIN
DBMS_OUTPUT.enable (100000);
nCount := 0;
sSQL1:='delete from '|| tablename ||
' where ROWNUM < ' || nrows || ' and where cast(time_stamp as date) < sysdate - 467';
sSQL2:='select count(ROWID) from ' || tablename ||
' where cast(time_stamp as date) < sysdate - 467';
EXECUTE IMMEDIATE sSQL2 INTO nCount;
LOOP
EXECUTE IMMEDIATE sSQL1;
nCount := nCount-nrows;
DBMS_OUTPUT.PUT_LINE('Existing records to be deleted: ' || to_char(nCount));
commit;
EXIT WHEN nCount = 0;
END LOOP;
END delete_tab;
/
Let me know how I can add an insert statement within the block to write the progress.
Besides the missing Insert there are a couple other minor issues and a major issue with this code.
ssql1:='delete from '|| tablename ||
' where ROWNUM < ' || nrows || ' and where cast(time_stamp as date) < sysdate - 467';
If the above were valid then when executed it nrows-1 (not nrows as I suspect you are thinking) every time as long as that many rows still exist. However, it is not valid; the where clause is invalid. The proper format for the where clause is
"Where <condition> and <condition> ..."; do not repeat where.
ssql2:='select count(ROWID) from ' || tablename ||
' where cast(time_stamp as date) < sysdate - 467';
There is no reason to count the rowids, every row has exactly 1. ROWID is pseudo column telling Oracle basically where on the disk the row is located. You have probably heard that it is the fastest way to retrieve a specific - that is true - but it is not true selecting rowid itself is faster, it will if anything, be slower. It would require a full table scan and calculation of its value for every row in the table. The optimizer may notice what is happening and change this to select count() but why hope for that just start with count().
Now we get to the worst issue. Unless the total number of rows to delete is an exact multiple of the parameter nrows then the procedure becomes a never ending loop until it throws the exception ORA-22054: underflow error.
This is because result as the only exit condition is "exit when ncount = 0". Suppose you have 1002 rows to delete and nrows is 1000. The first iteration deletes 999 then reduces ncount to 2 (1002-1000). Then the second iteration deletes another 999 and reduces ncount to -998 (2-1000). The procedure continues iterating until eventually ncount gets so small it cannot be held any longer (something like -1*10^39 - 1).
You could change the exit condition to "exit when nCount < 1". But even that is not necessary, no need for the code to calculate the exit condition at all. Let Oracle tell it. The delete statement returns SQL%rowcount that contains the number of rows processed by the last DML statement. If no are processed it returns 0. So the exist condition becomes a simple "exit when sql%rowcount = 0". Lesson: always be careful with equal 0 conditions.
Taking all this into account the procedure becomes. (Also changed variable names as I do not like ssql1, ssql2 and reusing a variable for 2 things. When using such I get confused as to which is which, esp on large procedures.):
create or replace procedure delete_tab
( tablename in varchar2
, nrows in number
)
is
delete_sql varchar2(2000);
count_sql varchar2(2000);
deleted_count number;
expect_delete number;
begin
dbms_output.enable (100000);
count_sql := 'select count(*) from ' || tablename ||
' where time_stamp < current_timestamp - 467';
dbms_output.put_line('COUNT_SQL: ' || count_sql
);
execute immediate count_sql into expect_delete;
dbms_output.put_line('Existing records to be deleted: ' || to_char(expect_delete));
delete_sql := 'delete from '|| tablename ||
' where rownum <= ' || nrows ||
' and cast(time_stamp as date) < sysdate - 467';
dbms_output.put_line('DELETE_SQL: '
|| delete_sql
);
deleted_count := 0;
loop
execute immediate delete_sql;
exit when sql%rowcount = 0;
deleted_count := deleted_count+sql%rowcount;
commit;
end loop;
insert into delete_log( table_name, date_deleted, rows_deleted)
values (tablename, sysdate, deleted_count) ;
commit; -- commit the deletes in final loop and insert
end delete_tab;
See demo (includes example of danger with ncount = 0 and procedure has additional trace information). BTW in a production run my commit interval (nRows) would something like 500000 or larger.

PL/SQL Using CASE in WHERE clause

Good day Stackoverflow!
I have a query that is giving me an error: "Missing Right Parenthesis", at least, so says SQL Developer.
My query has a CASE statement within the WHERE clause that takes a parameter, and then executing a condition based on the value entered.
I've read that when using a CASE statement within a WHERE clause you have to surround the statement with parenthesis and assign it to a numeric value, e.g. "1", however doing so does not accomplish my goal.
My goal is to execute a condition in the CASE statement if that condition is met.
Would you mind taking a look and giving me some input please?
Thanks!
SELECT ...
FROM ....
WHERE 1 = 1
AND (
CASE :P_REPORT_PERIOD
WHEN 'SPRING'
THEN ((fiscal_year = (EXTRACT(YEAR FROM (SYSDATE))-1) AND period >=10) OR (fiscal_year = (EXTRACT(YEAR FROM (SYSDATE))) AND period < 4))
WHEN 'FALL'
THEN ((fiscal_year = (EXTRACT(YEAR FROM (SYSDATE))) AND period >=4) OR (fiscal_year = (EXTRACT(YEAR FROM (SYSDATE))) AND period < 10))
END
) = 1
Problem Solved, THANKS to all those that attempted finding a solution.
Solution: Rather than using a CASE statement, I just created a stored procedure, replaced the CASE with IF and built a VSQL string from my query.
Example:
VSQL := 'SELECT.....'
IF (v_rpt_pd = 'SPRING') THEN
VSQL := VSQL || '( ( AND EXTRACT(YEAR FROM (SYSDATE))-1 = fiscal_year and period >=10) or ';
VSQL := VSQL || ' ( AND EXTRACT(YEAR FROM (SYSDATE)) = fiscal_year and period <=3) )';
ELSE
VSQL := VSQL || '( ( AND EXTRACT(YEAR FROM (SYSDATE)) = fiscal_year and period >=4) or ';
VSQL := VSQL || ' ( AND EXTRACT(YEAR FROM (SYSDATE)) = fiscal_year and period <=9) )';
END IF;
VSQL := VSQL ||' GROUP BY fiscal_year, period
and so on, if you want the entire solution, DM me and I'll send you the code.
Cheers!
As per Tom the CASE syntax in WHERE CLAUSE is -
--Syntax
select * from <table name>
where 1=1
and
(case
when <BOOLEAN_EXPRESSION>
then <SCALAR_RETURN_VALUE>
...
ELSE <SCALAR_RETURN_VALUE>
end) = <SCALAR_VALUE> ;
Example:
--Query
WITH SAMPLE_DATA AS
(select 100 COL1,999 COL2 from DUAL UNION ALL
select 200 COL1,888 COL2 from DUAL
)
SELECT * FROM SAMPLE_DATA
WHERE 1=1
AND (
CASE COL2
WHEN 999 THEN 1
ELSE 0
END
) = 1 ;
-- Output:
100 999

Using parameters in procedures

I need to create a procedure that receives as a parameter a month and a year.
Inside the procedure I need to have a query to retrieve some values, that will have to take in account the parameters received.
create or replace procedure GET_REVS(MONTH in VARCHAR2,YEAR in varchar2) is
SELECT
*
FROM
REVENUS_TABLE
WHERE
Y_CODE IN ('YEAR')
AND M_CODE IN ()
Now, M_CODE should have the values since the start of the year until the month received by parameter.
Example if i receive in as parameter for the month a 4 i want my select to like this AND M_CODE IN ('1','2','3','4')
But if i receive MONTH = 3.. i need the select to have AND M_CODE IN ('1','2','3')
So what is the best way to do the procedure in order to be able to do that?
Thanks a lot
You could cast both M_CODE and MONTH as numbers and use BETWEEN operator or just <=:
CREATE OR REPLACE PROCEDURE get_revs(month IN VARCHAR2, year IN VARCHAR2) IS
BEGIN
....
SELECT *
FROM revenus_table
WHERE y_code = year
AND TO_NUMBER(m_code) BETWEEN 1 AND TO_NUMBER(month);
...
END;
You can do some thing like below example to make your procedure more dynamic
CREATE OR REPLACE PROCEDURE get_revs (
MONTH IN VARCHAR2,
YEAR IN VARCHAR2
)
IS
variable_name table_name%ROWTYPE;
v_sql VARCHAR2 (1000)
:= 'SELECT * FROM REVENUS_TABLE WHERE Y_CODE IN ('
|| MONTH
|| ')';
BEGIN
IF MONTH = 4 THEN
v_sql := v_sql || ' and M_CODE IN (''1'',''2'',''3'',''4'')';
END IF;
IF MONTH = 3 THEN
v_sql := v_sql || 'and M_CODE IN (''1'',''2'',''3'')';
END IF;
EXECUTE IMMEDIATE v_sql
INTO variable_name;
END;
Just an alternative to think about.
CREATE OR REPLACE PROCEDURE get_revs(month IN VARCHAR2, year IN VARCHAR2) IS
lv_in_clause VARCHAR2(100 CHAR);
p_ref sys_refcursor;
BEGIN
SELECT 'IN ('
||WMSYS.WM_CONCAT(A.NUM)
||')'
INTO lv_in_clause
FROM
(SELECT ''''
||LEVEL
||'''' NUM,
1 ID1
FROM DUAL
CONNECT BY LEVEL < to_number(MONTH)
)A
GROUP BY a.ID1;
OPEN p_ref FOR 'SELECT *
FROM revenus_table
WHERE y_code = '|| year
||' AND m_code '||lv_in_clause;
END;

Passsing two VARRAYS to a Procedure to pass them to a Index-By table

I try to pass two VARRAYs into a Procedure but when call it to test it I don't get any kind of respond from my database.
CREATE OR REPLACE PACKAGE PKG_TEST AS
TYPE PNO IS VARRAY(20) OF VARCHAR(20);
TYPE QTY IS VARRAY(20) OF INTEGER;
TYPE indexTest IS TABLE OF NUMBER INDEX BY VARCHAR2(20);
PROCEDURE blatest(i_PNO IN PNO, i_QTY IN QTY);
END PKG_TEST;
/
CREATE OR REPLACE PACKAGE BODY PKG_TEST AS
PROCEDURE blatest(i_PNO IN PNO , i_QTY IN QTY)
IS
V_COUNT_PNO INTEGER;
V_COUNT_QTY INTEGER;
bla_list indexTest;
name VARCHAR(20);
BEGIN
V_COUNT_PNO := i_PNO.COUNT;
V_COUNT_QTY := i_QTY.COUNT;
IF V_COUNT_PNO = V_COUNT_QTY THEN
FOR I IN 1..V_COUNT_PNO LOOP
bla_list(i_PNO(I)) := i_QTY(I);
END LOOP;
name := bla_list.FIRST;
WHILE name IS NOT null LOOP
dbms_output.put_line('Name: ' || name || ' is ' || TO_CHAR(bla_list(name)));
name := bla_list.NEXT(name);
END LOOP;
ELSE
dbms_output.put_line('Amount of Variables is not identical!');
END IF;
END blatest;
END PKG_TEST;
/
PKG_TEST.blatest(PKG_TEST.PNO('P123','P124'), PKG_TEST.QTY(2,3));
/
And if there is any easier way to fill in a Index-By table I dynamicly I am more than happy to read this ^^. Thanks in advance!
Call it as below, then you will get response:
set serveroutput on;
BEGIN
pkg_test.blatest (pkg_test.pno ('P123', 'P124'), pkg_test.qty (2, 3));
END;

Column exists but still get this error ORA-00904: invalid identifier

Anyone can help me with this issue
declare
lv2_sql VARCHAR2(32767);
cursor c_scv is
select financial_code, object_id, daytime from stream_category_version;
begin
for r_scv in c_scv LOOP
IF r_scv.financial_code = 'PURCHASE' THEN
lv2_sql := 'UPDATE stream_category_version ' || CHR(10) ||
'set REVN_PURCHASES_IND = ''Y'', last_updated_by = nvl(last_updated_by, created_by) ' || CHR(10) ||
'WHERE object_id = r_scv.object_id AND daytime = r_scv.daytime';
ecdp_dynsql.execute_statement(lv2_sql);
ELSIF r_scv.financial_code = 'SALE' THEN
lv2_sql := 'UPDATE stream_category_version ' || CHR(10) ||
'set REVN_SALES_IND = ''Y'', last_updated_by = nvl(last_updated_by, created_by) ' || CHR(10) ||
'WHERE object_id = r_scv.object_id AND daytime = r_scv.daytime';
ecdp_dynsql.execute_statement(lv2_sql);
END IF;
END LOOP;
end;
I have code as shown above, but i got error saying 'ORA-00904: R_SCV.DAYTIME: invalid identifier'. I have checked the table definition for 'stream_category_version' and found the column DAYTIME as shown below
SQL> desc stream_category_version
Name Type Nullable Default Comments
------------------ -------------- -------- ------- --------
OBJECT_ID VARCHAR2(32)
DAYTIME DATE
END_DATE DATE Y
NAME VARCHAR2(240) Y
FINANCIAL_CODE VARCHAR2(32) Y
SORT_ORDER NUMBER Y
COMMENTS VARCHAR2(2000) Y
Then i am confused with the error. Anyone can help me ?
Thanks in advance.
Shortly speaking - Oracle is case sensitive...
... probably during table creation column was typed UPPERCASE in quotation marks like that:
"DAYTIME"
and in your sql i see this column in lowercase
so you should verify your column name and best change it to version without quotation marks.
Other option is to call this column like that:
= r_scv.DAYTIME

Resources