Getting below error while executing in 12c oracle,but it is working fine when it was in oracle 10.2.0.4 :
Procedure proc_up_dts_product_cat_dynsql failed due to ORA-00920: invalid
relational operator
Here is the procedure :
CREATE OR REPLACE PROCEDURE SEAPROB.proc_up_dts_product_cat_dynsql(tablename IN varchar)
AS
dynamicsql varchar(8000);
ID int;
DTS_Segment_op varchar2(10);
DTS_Segment varchar2(15);
DTS_Segment_where varchar2(255);
DateEntered_op varchar2(10);
DateEntered varchar2(30);
DateEntered_where varchar2(255);
Svc_Name_op varchar2(10);
Svc_Name varchar2(100);
Svc_Name_where varchar2(255);
Product_Category varchar2(75);
Priority int;
combined_status_where varchar2(255);
refdate date ;
CURSOR PrivCursor
IS
SELECT
ID,
DTS_Segment_op,
Nvl(upper(trim(DTS_Segment)),' '),
DateEntered_op,
CASE WHEN dateentered='%' THEN dateentered
WHEN dateentered LIKE '%/%/____' THEN To_Char(To_Date(dateentered,'MM/DD/YYYY'),'YYYY-MM-DD')
WHEN dateentered LIKE '%/%/__' THEN To_Char(To_Date(dateentered,'MM/DD/YY'),'YYYY-MM-DD')
WHEN dateentered LIKE '''%/%/%'' % ''%/%/%''' THEN ''''||To_Char(To_Date(SubStr(dateentered,InStr(dateentered,'''',1,1)+1,InStr(dateentered,'''',1,2)-InStr(dateentered,'''',1,1)-1),'MM/DD/YY'),'YYYY-MM-DD')||''' and '''||To_Char(To_Date(SubStr(dateentered,InStr(dateentered,'''',1,3)+1,InStr(dateentered,'''',1,4)-InStr(dateentered,'''',1,3)-1),'MM/DD/YY'),'YYYY-MM-DD')||''''
ELSE dateentered END AS dateentered,
Svc_Name_op,
Nvl(upper(trim(Svc_Name)),' '),
Product_Category,
Priority
FROM tbl_dts_pt_lookup order by priority desc;
BEGIN
refdate := ADD_MONTHS(to_date(SYSDATE,'dd-mon-yy'),-6) ;
OPEN PrivCursor;
-- Loop through all the rows in the tbl_dts_category_lookup table
FETCH PrivCursor
INTO
ID,
DTS_Segment_op,
DTS_Segment,
DateEntered_op,
DateEntered,
Svc_Name_op,
Svc_Name,
Product_Category,
Priority;
WHILE PrivCursor%found
LOOP
-- Create dynamic SQL
--define case statements for where clause components
combined_status_where := ' where (DTS_Cm_DisputeStatus <>'|| '''C''' || ' OR ( DTS_Cm_DisputeStatus='|| '''C''' || ' AND DTS_CM_CLOSEDATE >= '''||refdate||'''))' ;
dts_segment_where := case when dts_segment='%' then ' and 1=1' else ' and NVL(trim(Replace(Upper(segment),chr(0),''' || ''')),''' || ' '') ' || dts_segment_op || ' ''' || dts_segment || '''' end;
svc_name_where := case when svc_name='%' then ' and 1=1' else ' and NVL(trim(Replace(Upper(dts_cm_servicename),chr(0),''' || ''')),''' || ' '') ' || svc_name_op || ' ''' || svc_name || '''' end ;
dateentered_where := case when dateentered='%' then ' and 1=1'
when dateentered_op='between' then ' and TO_CHAR(dts_cm_dateentered,''YYYY-MM-DD'') between ' || dateentered
else ' and TO_CHAR(dts_cm_dateentered,''YYYY-MM-DD'') ' || dateentered_op || ' ''' || dateentered || '''' end ;
dynamicsql := 'update '||tablename||' set product_cat_id=' || cast(id as varchar) ||', product_category =''' || product_category || '''';
--add where clause
dynamicsql := dynamicsql || combined_status_where || dts_segment_where || dateentered_where || svc_name_where;
EXECUTE IMMEDIATE dynamicsql;
COMMIT;
FETCH PrivCursor
INTO
ID,
DTS_Segment_op,
DTS_Segment,
DateEntered_op,
DateEntered,
Svc_Name_op,
Svc_Name,
Product_Category,
Priority;
END LOOP;
CLOSE PrivCursor;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Procedure proc_up_dts_product_cat_dynsql failed due to '||SQLERRM);
END proc_up_dts_product_cat_dynsql;
/
The database hurls ORA-00920: invalid relational operator when we submit a SQL statement with a syntax error in a WHERE clause. Usually it's a typo. This is extremely easy to do in dynamic SQL, because we cannot see the whole statement except at run time.
You haven't posted the generated update statement. Well you can't, as your exception handling doesn't display it. Pro tip: when working with dynamic SQL always log or display the generated statement, in the exception block if nowhere else:
dbms_output.put_line('generated statement:'|| dynamicsql);
So this is just a guess but this line looks suspicious ...
when dateentered_op='between' then ' and TO_CHAR(dts_cm_dateentered,''YYYY-MM-DD'') between ' || dateentered
... because it appears to generate a BETWEEN statement with no AND clause. The BETWEEN operator requires two bounds.
But it could be many things. You may be running with an unusual combination of parameters which generates an invalid statement. You need better logging.
Related
As part of a project, I'm working on data mapping of the peoplesoft records and fields in use at our company. There are more than 25K fields that I have to document but it gets tedious and will take a much more time than I have if I did it normal way. So, I wrote a stored procedure to reduce some of the work in documenting translate values.
Here is the code of my stored procedure:
CREATE OR REPLACE PROCEDURE SP_DATAMAPPINGINFO AS
TYPE EmpCurTyp IS REF CURSOR;
newrow_cursor EmpCurTyp;
txtable_cursor EmpCurTyp;
recname varchar2(30);
recdescr varchar2(200);
fieldnum number(3);
fieldname varchar2(30);
fieldescr varchar2(2000);
keyflag varchar2(1);
fieldtype varchar2(20);
distinctcount number(10);
query1_str varchar(300);
query2_str varchar(300);
query3_str varchar(300);
fieldvalue varchar2(200);
hyphen varchar2(5);
txvalue varchar2(200);
fielduse varchar2(500);
tablename varchar2(40);
intertxtabname varchar2(30);
txtablename varchar2(40);
CURSOR get_fields is
select A.RECNAME as "Record", A.RECDESCR as "Record Description"
, B.FIELDNUM as "FieldNum", B.FIELDNAME as "Field", C.DESCRLONG as "Field Description", CASE WHEN
EXISTS(select K.FIELDNAME FROM PSRECFLDDBKEYVW K WHERE K.RECNAME = A.RECNAME AND K.FIELDNAME=B.FIELDNAME)
THEN 'Y' ELSE 'N' END as "Key (Y/N)", DECODE (C.FIELDTYPE,
0, 'Character',
1, 'Long Char',
2, 'Number',
3, 'Signed Number',
4, 'Date',
5, 'Time',
6, 'DateTime',
8, 'Image/ Attachment',
9, 'Image Reference',
'Unknown') as "FieldType"
FROM PSRECDEFN A, PSRECFIELDDB B LEFT JOIN PSDBFIELD C ON (B.FIELDNAME = C.FIELDNAME)
WHERE B.RECNAME = A.RECNAME
AND A.RECNAME IN (select R.RECNAME from PSRECDEFN R, DBA_TABLES T
WHERE ('PS_'||R.RECNAME=T.TABLE_NAME)
AND T.NUM_ROWS > 0
AND R.RECTYPE=0)
order by A.RECNAME, B.FIELDNUM;
BEGIN
OPEN get_fields;
LOOP
FETCH get_fields INTO recname, recdescr, fieldnum, fieldname, fieldescr, keyflag, fieldtype;
fielduse := '';
tablename := 'PS_' || recname;
hyphen := ' - ';
fieldvalue := '';
txvalue := '';
intertxtabname := '';
txtablename := '';
if (fieldname <> '%EMPLID%' and fieldname <> '%DESCR%' and fieldname <> '%COMMENT%') THEN
query1_str := 'select RI.EDITTABLE FROM PSRECDEFN RD, PSRECFIELDDB RI WHERE RD.RECNAME = RI.RECNAME
AND RD.RECNAME = ' || recname || 'AND RI.FIELDNAME = ' || fieldname;
OPEN txtable_cursor FOR query1_str;
FETCH txtable_cursor INTO intertxtabname;
CLOSE txtable_cursor;
query2_str := 'select count(distinct T.' || fieldname || ') FROM ' || tablename;
IF (intertxtabname IS NOT NULL) THEN
txtablename := 'PS_' || intertxtabname;
query3_str := 'select distinct T.' || fieldname || ', TR.DESCR FROM ' || tablename || ' T left join ' || txtablename || ' TR ON T.'
|| fieldname || ' = TR.' || fieldname || ' order by T.' || fieldname;
ELSE
txtablename := '';
query3_str := 'select distinct DT.' || fieldname || ', DTR.XLATLONGNAME FROM ' || tablename || ' DT left join PSXLATITEM DTR on
(DTR.FIELDNAME = ''' || fieldname || ''' and DT.' || fieldname || ' = DTR.FIELDVALE) order by DT.' || fieldname;
END IF;
execute immediate query2_str into distinctcount;
if(distinctcount > 150) THEN
fielduse := 'More than 150';
ELSE
OPEN newrow_cursor FOR query3_str USING 'fieldname';
LOOP
FETCH newrow_cursor INTO fieldvalue, txvalue;
fielduse := fieldvalue || ' - ' || txvalue;
EXIT WHEN newrow_cursor%NOTFOUND;
END LOOP;
CLOSE newrow_cursor;
END IF;
ELSE
fielduse := 'SKIPPING';
END IF;
dbms_output.put_line(recname || ',' || recdescr || ',' || fieldnum || ',' || fieldname || ',' || fieldescr || ',' || keyflag || ',' || fieldtype || ',' || fielduse);
END LOOP;
CLOSE get_fields;
NULL;
END SP_DATAMAPPINGINFO;
The stored proc compiles without any errors but when I execute it, I get the following error:
Error starting at line : 1 in command -
BEGIN SP_DATAMAPPINGINFO; END;
Error report -
ORA-00933: SQL command not properly ended
ORA-06512: at "SYSADM.SP_DATAMAPPINGINFO", line 69
ORA-06512: at line 1
00000 - "SQL command not properly ended"
*Cause:
*Action:
Line 69 in the stored proc is OPEN txtable_cursor FOR query1_str;
I have tried using a bind variable but I still get the error and I might have used it incorrectly.
I may have other issues in the code. It'll be great if you can point those out. We are currently at Oracle 12c
Line 69 in the stored proc is OPEN txtable_cursor FOR query1_str;.
Obviously we can't run your code as we don't have your schema so we can't compile and validate your dynamic SQL. However, looking at your assembled string, the striking thing is the concatenation of the boilerplate text and the columns:
AND RD.RECNAME = ' || recname || 'AND RI.FIELDNAME = ' || fieldname
If you look closely you will spot that there is no space after the quote in 'AND RI.FIELDNAME which means the value of recname is concatenated with AND. That's your ORA-00933 right there.
Once you fix that you'll probably run into ORA-00936: missing expression. From the variable declarations we know recname and fieldname are strings but you are not treating them as strings. The code doesn't include any quotes around the variables so the executed query will treat them as columns or something. So that's something you need to fix:
AND RD.RECNAME = ''' || recname || ''' AND RI.FIELDNAME = ''' || fieldname ||''''
Later on you open the cursor like this:
OPEN newrow_cursor FOR query3_str USING 'fieldname';
So you are passing a parameter to a dynamic query which doesn't include any parameter placeholders. That should throw
ORA-01006: bind variable does not exist
Dynamic SQL is hard because it turns compilation errors into runtime errors. You need to develop a cool eye for looking at your code to spot them. One thing which will help is using logging to record the dynamic statement (DBMS_OUTPUT is better than nothing). Usually when we have the actual statement in front of our eyes it is easy to spot our bloomers. But if we can't at least we can run the query and let the compiler point them out.
I have already asked almost the same question before:
Finding non-numeric values in varchar column
And I received some great answer's and idea's to make not only fix the issue in my query but also make it much more efficient.
Old Query:
Select * From Table_Name
Where
(NOT REGEXP_LIKE(COLUMN_NAME, '^-?[0-9.]+$')
OR
LENGTH(Function_To_Fetch_Left_Component(COLUMN_NAME)) > (Precision-Scale)
OR
LENGTH(Column_Name) - LENGTH(REPLACE(Column_Name,'.','')) > 1)
New Query:
Select * From Table_Name
WHERE (Translate(column_name,'x0123456789-.','x') is not null
OR column_name = '.'
OR column_name = '-'
OR column_name = '-.'
OR Instr(column_name,'-') > 1
OR column_name like '%-%-%'
OR column_name like '%.%.%'
OR Instr(column_name||'.','.')-1 > (precision-scale) ) ;
Known limitations which are okay:
1.) Doesn't handle scientific notation. Its by design as I want to reject those.
2.) Doesn't check total precision (decimal component) however as while inserting oracle itself round's it off I am fine with it.
3.) Doesn't check for leading zero's.
Please help to see if the above query cover's everything and I haven't missing anything.
Another great solution by one Stackoverflow member however increased execution time by 2.5 times:
Function:
CREATE OR REPLACE function temp_is_number(p_test_value varchar2,
p_scale NUMBER,
p_precision NUMBER) return varchar2 is
l_result varchar2(1);
begin
execute immediate 'DECLARE l_number NUMBER; ' ||
'l_test_number NUMBER(' || p_precision || ',' || p_scale || '); ' ||
'BEGIN ' ||
' l_number := cast(:b1 as number); ' ||
' l_test_number := cast(:b1 as number); ' ||
' IF (l_number = l_test_number) THEN ' ||
' :b2 := ''Y''; ' ||
' ELSE ' ||
' :b2 := ''N''; ' ||
' END IF; ' ||
'EXCEPTION ' ||
' WHEN OTHERS THEN ' ||
' :b2 := ''N''; ' ||
'END;'
using in p_test_value, out l_result;
return l_result;
end;
/
Select Count(1) from Table_Name Where temp_is_number (Column_Name,scale,precision) = 'N';
How do I get the data(i.e rows) from the column_name I retrieved from SQL statement? (Completely new to PL/SQL).
Here is my code:
create or replace procedure com_coll_cur
as
type comColcur is ref cursor;
com_col_cur comColcur;
sql_stmt varchar2(4000);
type newtab_field is table of comColcur%TYPE;
begin
sql_stmt :=
'select column_name from all_tab_cols where table_name in (''TAB1'', ''TAB2'') ' ||
'group by column_name having count(*)>1';
open com_col_cur for sql_stmt;
loop
fetch com_col_cur into newtab_field;
exit when com_col_cur%NOTFOUND;
end loop;
close com_col_cur;
end;
What I'm trying to do here is first find the common columns between the two tables. This part only grabs column_name but I also want the data in that common columns. So I used cursor to "point" that common column_name and used loop(fetch) to get the data inside that common column_name. Finally, I want to create new table with this common columns only with their data.
I am new to everything here and any help will be appreciate it.
You don't understand how works cursors and fetch.
Fetch get the data from the cursor, so in your procedure example you get only column names, not the data in the columns. To get data you need another cursor - select from the target table or use the dynamic sql.
This is a code that do what you describe. It is not clear to me how you want to store data from two tables - subsequently or in another manner. Let's assume that we store them subsequently. Also this code suggests than columns with the same names have the same datatypes. Part of this code (to make datatype) I get from another stackoverflow post to save time to write it:
How do I get column datatype in Oracle with PL-SQL with low privileges?
dbms_output.put_line - used to print sql statements that we create
declare
cSql varchar2(4000);
cCols varchar2(4000);
cNewTableName varchar2(30) := 'AABBCC';
cTb1 varchar2(30) := 'TAB1';
cTb2 varchar2(30) := 'TAB2';
begin
for hc in (
select T.column_name, T.typ
from
(
select column_name,
data_type||
case when data_precision is not null and nvl(data_scale,0)>0 then '('||data_precision||','||data_scale||')'
when data_precision is not null and nvl(data_scale,0)=0 then '('||data_precision||')'
when data_precision is null and data_scale is not null then '(*,'||data_scale||')'
when char_length>0 then '('||char_length|| case char_used
when 'B' then ' Byte'
when 'C' then ' Char'
else null
end||')'
end||decode(nullable, 'N', ' NOT NULL') typ
from all_tab_cols
where table_name in (cTb1, cTb2) ) T
group by T.column_name, T.typ having count(*) > 1)
loop
cSql := cSql || case when cSql is null then null else ',' end || hc.column_name || ' ' || hc.typ;
cCols := cCols || case when cCols is null then null else ',' end || hc.column_name;
end loop;
if (cSql is not null) then
-- First drop table if it exists
for hc in (select * from all_objects where object_type = 'TABLE' and object_name = cNewTableName)
loop
execute immediate 'drop table ' || hc.object_name;
end loop;
-- create table
cSql := 'create table ' || cNewTableName || '(' || cSql || ')';
dbms_output.put_line(cSql);
execute immediate cSql;
-- insert data
cSql := 'insert into ' || cNewTableName || '(' || cCols || ') select ' || cCols || ' from ' || cTb1;
dbms_output.put_line(cSql);
execute immediate cSql;
cSql := 'insert into ' || cNewTableName || '(' || cCols || ') select ' || cCols || ' from ' || cTb2;
dbms_output.put_line (cSql);
execute immediate cSql;
end if;
end;
I'm trying to create a stored procedure where I'm passing select statement to for loop and i'm using dynamic table which is passing at runtime and getting below error:
LINE 23 PLS-00364: loop index variable 'I' use is invalid
LINE 19 PL/SQL: ORA-00942: table or view does not exist
CREATE OR REPLACE PROCEDURE CREATE_TEST(TBL_NM IN VARCHAR2)
IS
SRC_ID NUMBER(38);
SQL_Q VARCHAR2(250);
DEL_F VARCHAR2(250);
BEGIN
FOR I in (SELECT DEL_IND FROM TBL_NM)
LOOP
SRC_ID := SRC_FILE_ID_SEQ.NEXTVAL;
IF I.DEL_IND = 0
THEN
execute immediate 'INSERT INTO TEST_HIST ' || ' (a,b,c,d,e,DEL_IND) ' ||
' SELECT a,b,c,d,e, '|| 0 || ' || ' FROM ' || TBL_NM;
ELSIF I.DEL_IND = 1
THEN
execute immediate 'INSERT INTO JESTX_IGNR ' || ' (a,b,c,d,e,DEL_IND,SRC_ID_NO) ' ||
' SELECT a,b,c,d,e, '|| 2 ||' , '|| SRC_ID || ' FROM ' || TBL_NM;
END IF ;
END LOOP;
COMMIT;
END;
I call the procedure using:
EXEC CREATE_TEST('abc');
What you want is a REF CURSOR as you cannot use cursor for loop with dynamic sql.
I dont know the exact datatype for your column DEL_IND. Please declare accordingly.
Here is some more information on Oracle REF CURSORS.
Try below
CREATE OR REPLACE PROCEDURE CREATE_TEST(TBL_NM IN VARCHAR2)
IS
TYPE c1ref is REF CURSOR;
SRC_ID NUMBER(38);
SQL_Q VARCHAR2(250);
DEL_F VARCHAR2(250);
DEL_IND NUMBER(5);
BEGIN
vsql_text := 'select DEL_IND from ' || TBL_NM;
open c1ref for vsql_text;
LOOP
SRC_ID := SRC_FILE_ID_SEQ.NEXTVAL;
fetch c1ref into DEL_IND;
exit when c1ref%NOTFOUND;
IF (DEL_IND = 0)
THEN
execute immediate 'INSERT INTO TEST_HIST ' || ' (a,b,c,d,e,DEL_IND) ' ||
' SELECT a,b,c,d,e, '|| 0 || ' || FROM ' || TBL_NM;
ELSIF (DEL_IND = 1)
THEN
execute immediate 'INSERT INTO JESTX_IGNR ' || ' (a,b,c,d,e,DEL_IND) ' ||
' SELECT a,b,c,d,e, '|| 2 ||' , '|| SRC_ID || ' FROM ' || TBL_NM;
END IF ;
END LOOP;
CLOSE c1ref;
COMMIT;
END;
I have a table attribute_config with below columns:
table_name column_name key
Let us say is has below 2 rows
account accountphone accountnum
customer customernumber customerid
Key can be only accountnum or customerid.
I have to write code which will accept (i_accountnum,i_customerid) and;
fetch the respective values from columns mentioned in column_name in tables mentioned in table_name using the key in where condition.
For ex: select accountphone from account where accountnum = i_accountnum
select customernumber from customer where customerid = i_customerid
the complete query should be formed dynamically, whether to pass i_accountnum or i_customerid in the query also needs to be decided dynamically. if key - accountnum, i_accountnum will be passed to where condition.
I have been trying on these lines so far, this is not working, i know it is wrong.
declare
v_accountnum varchar2(20);
v_customerid varchar2(20);
v_attribute_value varchar2(20);
v_stmt varchar2(255);
begin
Account_Num := 'TestCustomer'; -- input to the function
v_customer_ref := 'TestAccount'; -- input to the function
for i in (Select * from attribute_config) loop
v_stmt := 'select ' || i.column_name || ' from ' || i.table_name ||' where ' || i.key|| ' = v_' || i.key;
execute immediate v_Stmt into v_attribute_value;
end loop;
end;
This will fix your code, but I do not see any advantage of using dynamic query when your code should accept 2 parameters(i_accountnum,i_customerid) - which is already static situation and fetch the relevant values, perhaps only in learning purposes.
declare
procedure fecth_values(i_accountnum account.accountnum%type,
i_customerid customer.customerid%type) return varchar2 is
v_attribute_value varchar2(20);
begin
for i in (select * from attribute_config) loop
execute immediate 'select ' || i.column_name || ' from ' ||
i.table_name || ' where ' || i.key || ' = ' || case when i.key = 'accountnum' then i_accountnum when i.key = 'customerid' then i_customerid end;
into v_attribute_value;
dbms_output.put_line(v_attribute_value);
end loop;
return null;
end;
begin
fecth_values(1, 1);
end;
Your where clause was wrong the i.key should be compared against the inputed values, not the 'v_' || i.key, which is undeclared when you execute your stmt.