I am creating procedure in SqlDeveloper for pagination, searching and sorting. Below is my Procedure query and I am getting error at "OFFSET DisplayStart ROWS".
create or replace PROCEDURE XX_EMS_GET_ITEM_DESC_New(
DisplayLength INTEGER,
DisplayStart INTEGER,
SortCol INTEGER,
SortDir VARCHAR2,
SearchTxt VARCHAR2,
result_data OUT sys_refcursor)
AS
BEGIN
open sys_refcursor for
SELECT
COUNT(*) OVER () AS TotalCount, Item, description
FROM xx_dts_item_no_v
WHERE
(SearchTxt IS NULL OR
Item LIKE '%' || SearchTxt || '%' OR
Description LIKE '%' || SearchTxt || '%')
ORDER BY
CASE
WHEN SortDir = 'ASC' THEN
CASE SortCol
WHEN 0 THEN Item
WHEN 1 THEN Description
END
END desc,
CASE
WHEN SortDir = 'desc' THEN
CASE SortCol
WHEN 0 THEN Item
WHEN 1 THEN Description
END
END DESC
OFFSET DisplayStart ROWS
FETCH NEXT DisplayLength ROWS ONLY;
END XX_EMS_GET_ITEM_DESC_New;
Getting Below error while compiling procedure.
Error(9,5): PL/SQL: SQL Statement ignored
Error(33,5): PL/SQL: ORA-00933: SQL command not properly ended
Related
I don't understand behaviour of my code.
Why when I have
select min(i.column_name) into value_min from EMPLOYEES; in my code (see belowe) I get error but if i put in this palce string with correct name of table I get result
select min(SALARY) into value_min from EMPLOYEES;
declare
cursor c_column_name is select column_name, data_type from USER_TAB_COLUMNS where table_name = 'EMPLOYEES';
/***************************
(...)
SALARY NUMBER
(...)
***************************/
value_name_col varchar2(100);
value_min number;
value_max number;
value_result number;
type t_rekord is table of EMPLOYEES%ROWTYPE INDEX BY BINARY_INTEGER;
begin
for i IN c_column_name loop
--DBMS_OUTPUT.PUT_LINE(i.column_name);
if i.data_type = 'NUMBER' and i.column_name = 'SALARY' then
-- coment line get ERROR
-- select min(i.column_name) into value_min from EMPLOYEES;
-- select max(i.column_name) into value_max from EMPLOYEES;
-- code below get results
select min(SALARY) into value_min from EMPLOYEES;
select max(SALARY) into value_max from EMPLOYEES;
select round(dbms_random.value(value_min,value_max),2) into value_result from dual;
DBMS_OUTPUT.PUT_LINE(i.column_name || ' = ' || value_result);
end if;
end loop;
end;
Error which I get:
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at line 27
06502. 00000 - "PL/SQL: numeric or value error%s"
I prepare this for show you how my problem look like, i'ts not exactly what I do.
If you want to specify table or column names using a variable, you need dynamic SQL (e.g. with execute immediate) as Jeff Holt commented.
Try this instead:
execute immediate 'select min(' || i.column_name
|| '), max(' || i.column_name
|| ') from EMPLOYEES' into value_min, value_max;
You're getting a "numeric or value error" because it's doing select min('SALARY') and then it tries to save the output (the string 'SALARY') into value_min, which is a NUMBER data type.
I have the following SQL query that will return the name of a column in a specific table. Let's say it return 'USER_PK' as column name when it runs.
the query:
SELECT max(COLUMN_NAME)
FROM ALL_TAB_COLUMNS
WHERE OWNER= 'DW_01'
AND table_name='D_O_USERS'
AND COLUMN_NAME<>'USER_PK';
Now I would like to run the above query as part of a function but instead of running it and storing the value it returns in a variable (using INTO or attribution like initial_sql: = '...', followed by exec ) I would need to have it run inside one line of code as below (see part in bold)... So far I have been unsuccessful as it is interpreted as a string when using quotes ...
CREATE OR REPLACE function DW_01.EXECUTE_AUTO (db_schema IN VARCHAR2, db_table IN VARCHAR2, pk_name IN VARCHAR2, id_pk IN INTEGER) RETURN VARCHAR2
IS
result VARCHAR2(4000);
begin
EXECUTE IMMEDIATE 'select STANDARD_HASH( '|| **SELECT max( COLUMN_NAME) FROM ALL_TAB_COLUMNS WHERE OWNER='' || db_schema || '' AND table_name=''||db_table ||'' AND COLUMN_NAME<>'' ||pk_name ||'** ,''SHA512'' ) from '||db_table||' where '|| pk_name ||'='||id_pk into RESULT ;
return result;
end;
Many thanks in advance for your thoughts!
You need to amend you r code like below -
CREATE OR REPLACE function DW_01.EXECUTE_AUTO (db_schema IN VARCHAR2,
db_table IN VARCHAR2,
pk_name IN VARCHAR2,
id_pk IN INTEGER) RETURN VARCHAR2
IS
result VARCHAR2(4000);
begin
EXECUTE IMMEDIATE 'select STANDARD_HASH( ' || pk_name || ',256 )
from '||db_table||' where '|| pk_name ||'='||id_pk into RESULT;
return result;
end;
/
There are only a few ways to run dynamic SQL in SQL, and they're neither pretty nor fast. The function below uses DBMS_XMLGEN.GETXML to dynamically run a SQL statement.
create or replace function execute_auto(db_schema in varchar2, db_table in varchar2, pk_name in varchar2, id_pk in integer) return varchar2
is
v_column_name varchar2(128);
v_result varchar2(4000);
begin
select standard_hash(to_number(extractValue(xml_results, '/ROWSET/ROW/' || max_column)), 'SHA512') hash_value
into v_result
from
(
--Create a single XML file with the ROWIDs that match the condition.
select max(column_name) max_column, xmltype(dbms_xmlgen.getxml
('
select '||max(column_name)||'
from '||db_schema||'.'||db_table||'
where id = '||id_pk
)) xml_results
from all_tab_columns
where owner = db_schema
and table_name = db_table
and column_name <> pk_name
);
return v_result;
end;
/
For example, let's create this sample table with 100,000 rows:
--drop table test1;
create table test1(id number, a number, b number, constraint pk_test1 primary key(id));
insert into test1
select level, level, level from dual connect by level <= 100000;
commit;
This shows how to use the function;
select execute_auto(user, 'TEST1', 'ID', id) hash
from test1 where id = 1;
HASH
----
A36753F534728ED84A463ECB13750B8E920A7E4D90244258DE77D9800A0F3DAF8CBAD49602E960A2355933C689A23C30377CE10FC4B8E1F197739FF86C791022
In addition to problems with type conversion and SQL injection, the performance is terrible. Selecting all 100,000 rows this way takes 200 seconds on my machine.
select sum(length(execute_auto(user, 'TEST1', 'ID', id))) from test1;
Normally, running everything in a single SELECT statement is a good way to improve performance. But this extreme type of dynamic SQL will never run fast. You probably want to rethink your approach. Instead of trying to optimize the SQL inside a function that is run one-row-at-a-time, try to change the process to process once-per-table
I am trying to get the record data using function by passing values
please find the below
CREATE TABLE "TEST"
( "TEST_ID" NUMBER(9,0) NOT NULL ENABLE,
"TEST_DESC" VARCHAR2(30 BYTE),
"TEST_DATE" DATE
);
create or replace TYPE TEST_OBJ_TYPE IS OBJECT
(
TEST_ID NUMBER(9),
TEST_DESC VARCHAR(30),
dates date
);
create or replace TYPE TEST_TABTYPE AS TABLE OF TEST_OBJ_TYPE;
Using the above object and table type created the function as follows
create or replace FUNCTION GET_ROWS(dates date)RETURN TEST_TABTYPE
AS
V_Test_Tabtype Test_TabType;
table_name varchar2(30);
q1 varchar2(300);
BEGIN
table_name :='Test';
q1 := 'SELECT TEST_OBJ_TYPE(A.TEST_ID, A.TEST_DESC)FROM' || '
(SELECT TEST_ID, TEST_DESC FROM ' || table_name || ' where
TEST_DATE = '''||dates||''' ) A';
dbms_output.put_line(q1);
EXECUTE IMMEDIATE q1 BULK COLLECT INTO V_Test_TabType ;
RETURN V_Test_TabType;
EXCEPTION
WHEN OTHERS THEN
v_Test_TabType.DELETE;
RETURN v_Test_TabType;
END;
When I execute this the SQL is printing correctly but not giving the record value.
Error as follows:
select (GET_ROWS('01-08-18')) from dual
Error report -
ORA-02315: incorrect number of arguments for default constructor
ORA-06512: at "AMTEL_MIS.GET_ROWS", line 13
SELECT TEST_OBJ_TYPE(A.TEST_ID, A.TEST_DESC) FROM (SELECT TEST_ID, TEST_DESC FROM Test where TEST_DATE = '01-08-18' ) A
Please assist me further
Thanks in advance
Your type TEST_OBJ_TYPE is defined with three attributes: TEST_ID, TEST_DESC, DATES. However, your query populates the constructor with just two columns:
SELECT TEST_OBJ_TYPE(A.TEST_ID, A.TEST_DESC) FROM
You're missing a value for DATES and that's why Oracle hurls ORA-02315.
I have tried as per your suggestion but it's is giving me an error
ORA-00904: "A"."DATES": invalid identifier
Because of the convoluted way your function is written you need to include TEST_DATE (or dates) in both the subquery and the object constructor:
q1 := 'SELECT TEST_OBJ_TYPE(A.TEST_ID, A.TEST_DESC,A.TEST_DATE)FROM' || ' -- here!
(SELECT TEST_ID, TEST_DESC, TEST_DATE FROM ' -- and here!
|| table_name || ' where TEST_DATE = '''||dates||''' ) A';
If you do that your code will work. Here is a LiveSQL demo of your code with the fix. (Free Oracle login required).
As it seems likely that you will want to pass in the table name so here is a version of your code which does that:
create or replace function get_rows(dates date, p_table_name in varchar2)
return test_tabtype
as
v_test_tabtype test_tabtype;
q1 varchar2(300);
begin
q1 := 'select test_obj_type(a.test_id, a.test_desc,a.test_date) from'
|| '(select test_id, test_desc, test_date from '
|| p_table_name
|| ' where test_date = :1 ) a';
dbms_output.put_line(q1);
execute immediate q1
bulk collect into v_test_tabtype
using dates ;
return v_test_tabtype;
exception
when others then
v_test_tabtype.delete;
return v_test_tabtype;
end;
Note how much easier it is to understand the code when it is laid out with consistent use of case and regular indentation. Readability is a feature!
While creating below SP I am getting the error as
Error(30,11): PLS-00103: Encountered the symbol "UPDATE" when expecting one of the following: ( - + case mod new not null select with continue avg count current exists max min prior sql stddev sum variance execute forall merge time timestamp interval date pipe
Here is my SP
Procedure Update_Link_Details (
P_UDLINKID NVARCHAR2,
P_FOLDERSTRUCTURE NVARCHAR2,
TBL_UPD OUT SYS_REFCURSOR
)
AS
BEGIN
OPEN TBL_UPD FOR
Update Tb_Link_Print
set status='Start'
where LINKPRINTID= P_UDLINKID
and upper(FOLDERSTRUCTURE) LIKE '%' || upper(P_FOLDERSTRUCTURE) || %';
NULL;
END Update_Link_Details;
"I just want to update the table with that status ...Also I want to return the updated record"
That's two things, so it's two statements:
Procedure Update_Link_Details (
P_UDLINKID NVARCHAR2,
P_FOLDERSTRUCTURE NVARCHAR2,
TBL_UPD OUT SYS_REFCURSOR
)
AS
BEGIN
Update Tb_Link_Print
set status='Start'
where LINKPRINTID= P_UDLINKID
and upper(FOLDERSTRUCTURE) LIKE '%' || upper(P_FOLDERSTRUCTURE) || '%';
OPEN TBL_UPD FOR
select * from Tb_Link_Print
where LINKPRINTID= P_UDLINKID
and upper(FOLDERSTRUCTURE) LIKE '%' || upper(P_FOLDERSTRUCTURE) || '%';
END Update_Link_Details;
This is a trifle clunky. Here is a version which engineers away the duplication:
Procedure Update_Link_Details (
P_UDLINKID NVARCHAR2,
P_FOLDERSTRUCTURE NVARCHAR2,
TBL_UPD OUT SYS_REFCURSOR
)
AS
upd_rowids sys.dbms_debug.vc2coll;
BEGIN
Update Tb_Link_Print
set status='Start'
where LINKPRINTID= P_UDLINKID
and upper(FOLDERSTRUCTURE) LIKE '%' || upper(P_FOLDERSTRUCTURE) || '%'
returning rowidtochar(rowid) bulk collect into upd_rowids;
OPEN TBL_UPD FOR
select p.*
from Tb_Link_Print p
join table(upd_rowids) u
on p.rowid = chartorowid(u.column_value)
;
END Update_Link_Details;
It uses the RETURNING clause to capture the ROWIDs of the affected rows, then opens the Ref Cursor using those ROWIDs to restrict the result set.
I want to able to support this query:
select first_name from
employees
where hire_date between x and y;
I have create the below procedure with no error.
CREATE OR REPLACE PROCEDURE pr_show_col_table(
col IN VARCHAR2,
tab IN VARCHAR2,
date_col IN VARCHAR2,
dt2 IN VARCHAR2,
dt1 IN VARCHAR2)
IS
--
type typ_ref_cur IS ref CURSOR;
cur typ_ref_cur;
type l_record IS record (first_name employees.first_name%type);
l_rec l_record;
BEGIN
--
OPEN cur FOR
' Select '|| col || ' from ' || tab || ' where ' || date_col ||
' Between TRUNC(:startdt) and TRUNC(:enddt) '
USING dt2, NVL(dt1,dt2+1);
--
LOOP
FETCH cur INTO l_rec;
EXIT WHEN cur%notfound;
dbms_output.put_line('Name is '|| l_rec.first_name);
END LOOP;
CLOSE cur;
--
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line
('Error at line '||dbms_utility.format_error_backtrace|| chr(10)
|| 'Error Message '||dbms_utility.format_error_STACK);
END pr_show_col_table;
/
But when I am trying to run this procedure:
BEGIN
pr_show_col_table (
'first_name',
'employees',
'hire_date',
to_Date('30-DEC-1995','DD-MON-YYYY'),
TO_DATE('01-JAN-1995','DD-MON-YYYY'));
END;
I am receiving below error:
Error at line ORA-06512: at "HR.PR_SHOW_COL_TABLE", line 13
Error Message ORA-06502: PL/SQL: numeric or value error: character to number conversion error
How do I fix this?
The reason is that you're passing date values into a varchar parameter. Set the data type of the dt2,dt1 parameters to DATE instead of varchar2