ORA-00936: missing expression - Line 62 - oracle

I've been trying to identify what's wrong with the Insert Statement in the Execute Immediate for few hours without luck. Made sure that I am not missing any commas or entering any incorrect character.
I have gone through all the answers on SO and other websites trying to figure out what could I be doing wrong but no luck.
Running this function results in the following error (error line starts with "-->" Please ignore it as its just for highlighting purpose):
ORA-00936: missing expression
ORA-06512: at "BDW_AMPS.COUNT_RECORDS", line 62
and here's the PL/SQL Code for the function:
CREATE OR REPLACE FUNCTION count_records (
p_test_case_id IN NUMBER,
p_table_name IN VARCHAR2
) RETURN VARCHAR2 IS
v_amt_recs INT;
v_test_result VARCHAR2(10);
v_threshold_val VARCHAR2(10);
v_test_suite_table VARCHAR2(100);
v_test_result_id NUMBER;
v_batch_id NUMBER;
v_report_id NUMBER;
v_test_seq_no NUMBER;
v_session_name VARCHAR2(100);
v_error_description VARCHAR2(100);
v_process_by VARCHAR2(100);
BEGIN
v_test_suite_table := 'bdw_amps.spares_bdw_test_suite';
v_process_by := 'INFORMATICA';
EXECUTE IMMEDIATE 'SELECT THRESHHOLD_VALUE FROM '
|| v_test_suite_table
|| ' WHERE TEST_CASE_ID = '
|| p_test_case_id
INTO v_threshold_val;
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM ' || p_table_name
INTO v_amt_recs;
EXECUTE IMMEDIATE 'SELECT BDW_AMPS.SPARES_TEST_SEQ_ID_SEQ.NEXTVAL FROM DUAL'
INTO v_test_result_id;
EXECUTE IMMEDIATE 'SELECT MAX(BATCH_ID) FROM BDW_AMPS.spares_bdw_session_audit
WHERE SESSION_NAME=(SELECT SESSION_NAME FROM '
|| v_test_suite_table
|| ' WHERE TEST_CASE_ID = '
|| p_test_case_id
|| ')'
INTO v_batch_id;
EXECUTE IMMEDIATE 'SELECT REPORT_ID FROM '
|| v_test_suite_table
|| ' WHERE TEST_CASE_ID = '
|| p_test_case_id
INTO v_report_id;
EXECUTE IMMEDIATE 'SELECT TEST_SEQ FROM '
|| v_test_suite_table
|| ' WHERE TEST_CASE_ID = '
|| p_test_case_id
INTO v_test_seq_no;
EXECUTE IMMEDIATE 'SELECT SESSION_NAME FROM '
|| v_test_suite_table
|| ' WHERE TEST_CASE_ID = '
|| p_test_case_id
INTO v_session_name;
IF
v_amt_recs > v_threshold_val
THEN
v_test_result := 'PASS';
--> EXECUTE IMMEDIATE 'INSERT INTO BDW_AMPS.spares_bdw_test_results(
TEST_RESULT_ID,
BATCH_ID,
REPORT_ID,
TEST_CASE_ID,
TEST_SEQ_NO,
TABLE_NAME,
SESSION_NAME,
TEST_RESULT,
PROCESS_DATE,
PROCESS_BY
)
VALUES
('|| v_test_result_id || ',
' || v_batch_id || ',
' || v_report_id || ',
' || p_test_case_id || ',
' || v_test_seq_no || ',
' || p_table_name || ',
' || v_session_name || ',
' || v_test_result || ',
SYSDATE,
' || v_process_by || '
)';
EXECUTE IMMEDIATE 'commit';
ELSE
v_test_result := 'FAIL';
v_error_description := 'Count: ' || v_amt_recs || ' is greater than threshold value: ' || v_threshold_val;
EXECUTE IMMEDIATE 'INSERT INTO BDW_AMPS.spares_bdw_test_results(
TEST_RESULT_ID,
BATCH_ID,
REPORT_ID,
TEST_CASE_ID,
TEST_SEQ_NO,
TABLE_NAME,
SESSION_NAME,
TEST_RESULT,
ERROR_DESCRIPTION,
PROCESS_DATE,
PROCESS_BY
)
VALUES (
'|| v_test_result_id || ',
' || v_batch_id || ',
' || v_report_id || ',
' || p_test_case_id || ',
' || v_test_seq_no || ',
' || p_table_name || ',
' || v_session_name || ',
' || v_test_result || ',
' || v_error_description || ',
SYSDATE,
' || v_process_by || '
)';
EXECUTE IMMEDIATE 'commit';
END IF;
RETURN v_test_result;
END;

Assuming that this is a simplified example of something that really does need to be dynamic, one issue is that the string values are not quoted. (If you'd had date values they would need special handling too.)
For example:
create table demo (numcol number, stringcol varchar2(20));
declare
l_num number := 123;
l_string varchar2(20) := 'Kittens';
l_sql long := 'insert into demo(numcol, stringcol) values ('||l_num||', '||l_string||')';
begin
dbms_output.put_line(l_sql);
execute immediate l_sql;
end;
/
Generated code:
insert into demo(numcol, stringcol) values (123, Kittens)
Fails with:
ORA-00984: column not allowed here
The error will vary depending on the contents of the string. For example, if it contains spaces:
declare
l_num number := 123;
l_string varchar2(20) := 'Kittens are cute';
l_sql long := 'insert into demo(numcol, stringcol) values ('||l_num||', '||l_string||')';
begin
dbms_output.put_line(l_sql);
execute immediate l_sql;
end;
/
Generated code:
insert into demo(numcol, stringcol) values (123, Kittens are cute)
ORA-00917: missing comma
or commas:
declare
l_num number := 123;
l_string varchar2(20) := 'Kittens, Puppies';
l_sql long := 'insert into demo(numcol, stringcol) values ('||l_num||', '||l_string||')';
begin
dbms_output.put_line(l_sql);
execute immediate l_sql;
end;
/
insert into demo(numcol, stringcol) values (123, Kittens, Puppies)
ORA-00913: too many values
You need to build the quoting:
declare
l_num number := 123;
l_string varchar2(20) := 'Kittens, Puppies';
l_sql long := 'insert into demo(numcol, stringcol) values ('||l_num||', '''||l_string||''')';
begin
dbms_output.put_line(l_sql);
execute immediate l_sql;
end;
/
so that you generate
insert into demo(numcol, stringcol) values (123, 'Kittens, Puppies')
(If the string could contain quote characters, that would need more work.)
It's worth always building the dynamic SQL as a variable and printing or logging it on failure, as it's usually pretty clear what the issue is when you can see the code.
Another point is that concatenating values like this is resource-intensive, as Oracle tries to cache SQL statements for reuse, so they will be individually parsed and optimised and take space in the cache, but they will never be reused. If this is going to be frequently run with different values, you should consider using bind variables via the using clause of execute immediate.

Your DML(INSERT) statements do not need EXECUTE IMMEDIATE statements. So, remove them after line 61 :
CREATE OR REPLACE FUNCTION count_records (
p_test_case_id IN NUMBER,
p_table_name IN VARCHAR2
) RETURN VARCHAR2 IS
v_amt_recs INT;
v_test_result VARCHAR2(10);
v_threshold_val VARCHAR2(10);
v_test_suite_table VARCHAR2(100);
v_test_result_id NUMBER;
v_batch_id NUMBER;
v_report_id NUMBER;
v_test_seq_no NUMBER;
v_session_name VARCHAR2(100);
v_error_description VARCHAR2(100);
v_process_by VARCHAR2(100);
BEGIN
v_test_suite_table := 'bdw_amps.spares_bdw_test_suite';
v_process_by := 'INFORMATICA';
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM ' || p_table_name
INTO v_amt_recs;
v_test_result_id := BDW_AMPS.SPARES_TEST_SEQ_ID_SEQ.NEXTVAL;
EXECUTE IMMEDIATE 'SELECT MAX(BATCH_ID) FROM BDW_AMPS.spares_bdw_session_audit
WHERE SESSION_NAME=(SELECT SESSION_NAME FROM '
|| v_test_suite_table
|| ' WHERE TEST_CASE_ID = '
|| p_test_case_id
|| ')'
INTO v_batch_id;
EXECUTE IMMEDIATE 'SELECT THRESHHOLD_VALUE, REPORT_ID, TEST_SEQ,SESSION_NAME FROM '
|| v_test_suite_table
|| ' WHERE TEST_CASE_ID = :caseId'
INTO v_threshold_val,v_report_id,v_test_seq_no,v_session_name
USING p_test_case_id;
IF
v_amt_recs > v_threshold_val
THEN
v_test_result := 'PASS';
INSERT INTO BDW_AMPS.spares_bdw_test_results(
TEST_RESULT_ID,
BATCH_ID,
REPORT_ID,
TEST_CASE_ID,
TEST_SEQ_NO,
TABLE_NAME,
SESSION_NAME,
TEST_RESULT,
PROCESS_DATE,
PROCESS_BY
)
VALUES
( v_test_result_id ,
v_batch_id ,
v_report_id ,
p_test_case_id ,
v_test_seq_no ,
p_table_name ,
v_session_name ,
v_test_result ,
SYSDATE,
v_process_by
);
commit;
ELSE
v_test_result := 'FAIL';
v_error_description := 'Count: ' || v_amt_recs || ' is greater than threshold value: ' || v_threshold_val;
INSERT INTO BDW_AMPS.spares_bdw_test_results(
TEST_RESULT_ID,
BATCH_ID,
REPORT_ID,
TEST_CASE_ID,
TEST_SEQ_NO,
TABLE_NAME,
SESSION_NAME,
TEST_RESULT,
ERROR_DESCRIPTION,
PROCESS_DATE,
PROCESS_BY
)
VALUES (
v_test_result_id ,
v_batch_id ,
v_report_id ,
p_test_case_id ,
v_test_seq_no ,
p_table_name ,
v_session_name ,
v_test_result ,
v_error_description ,
SYSDATE,
v_process_by
);
commit;
END IF;
RETURN v_test_result;
END;
while usage of them are right for SELECT statements because of dynamic table names.

Related

Stored procedure Is throwing ORA-20000: ORA-00913: too many values

The below code is used to create the stored procedure and this is how the stored procedure is called. Stored procedure is throwing ORA-20000: ORA-00913: too many values
BEGIN EDW_ODS_DELETE_RECORDS_PKG.MAIN('EDWFIN', 'PSP_LABOR_ADJUSTMENTS_F'); END;
create or replace
PACKAGE BODY "EDW_ODS_DELETE_RECORDS_PKG" AS
CON_EDW_IUD_FLAG CONSTANT CHAR := 'D';
CON_CURRENT_FLAG CONSTANT CHAR := 'N';
CON_DELETED_FLAG CONSTANT CHAR := 'Y';
PROCEDURE main (table_schema_in IN VARCHAR2, target_table_alias_in IN VARCHAR2) AS
v_source_schema VARCHAR2(30);
v_source_table_name VARCHAR2(30);
v_target_schema VARCHAR2(30);
v_target_table_name VARCHAR2(30);
v_source_table_key VARCHAR2(1000);
v_target_table_key VARCHAR2(1000);
v_table_type VARCHAR2(20);
v_last_deleted_date DATE;
v_delete_control_id NUMBER := 0;
v_target_table_alias VARCHAR2(30);
v_arc_target_table_name VARCHAR2(30);
BEGIN
SELECT SOURCE_TABLE_SCHEMA, SOURCE_TABLE_NAME, SOURCE_TABLE_KEY,
TARGET_TABLE_SCHEMA, TARGET_TABLE_NAME, TARGET_TABLE_KEY,
TABLE_TYPE, LAST_DELETED_DATE, DELETE_CONTROL_ID,
TARGET_TABLE_ALIAS,
ARC_TARGET_TABLE_NAME
INTO
v_source_schema, v_source_table_name, v_source_table_key,
v_target_schema, v_target_table_name, v_target_table_key,
v_table_type, v_last_deleted_date, v_delete_control_id,
v_target_table_alias,
v_arc_target_table_name
FROM EDW_ODS_DWH_DELETES_CONTROL
WHERE target_table_schema = table_schema_in AND target_table_alias = target_table_alias_in;
DBMS_OUTPUT.PUT_LINE ('Values : ' || v_last_deleted_date);
v_source_table_key := REPLACE(v_source_table_key, '~', ' || ''~'' || ');
v_target_table_key := REPLACE(v_target_table_key, '~', ' || ''~'' || ');
IF v_table_type = 'FACT' THEN
UPDATE_FACT_TABLES (v_source_schema, v_source_table_name, v_source_table_key,
v_target_schema, v_target_table_name, v_target_table_key,
v_last_deleted_date, v_delete_control_id,v_arc_target_table_name);
ELSE
UPDATE_DIMENSION_TABLES (v_source_schema, v_source_table_name, v_source_table_key,
v_target_schema, v_target_table_name, v_target_table_key,
v_last_deleted_date, v_delete_control_id, v_table_type);
END IF;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
ERROR_LOG(v_delete_control_id, table_schema_in, target_table_alias_in,
SQLERRM, DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
RAISE;
END main;
PROCEDURE UPDATE_FACT_TABLES (source_schema_in IN VARCHAR2, source_table_name_in IN VARCHAR2, source_table_key_in IN VARCHAR2,
target_schema_in IN VARCHAR2, target_table_name_in IN VARCHAR2, target_table_key_in IN VARCHAR2,
last_deleted_date_in IN DATE, delete_control_id_in IN NUMBER, arc_target_table_name IN VARCHAR2) AS
v_source_stage_table VARCHAR2(70);
v_target_fact_table VARCHAR2(70);
v_target_arc_table VARCHAR2(70);
query_fetch_deleted_recs CLOB;
query_insert_into_arc CLOB;
query_delete_from_fact CLOB;
v_delete_count NUMBER;
v_insert_count NUMBER;
v_last_deleted_date VARCHAR2(100) := TO_CHAR(last_deleted_date_in,'MM/DD/YYYY HH24:MI:SS');
BEGIN
v_source_stage_table := source_schema_in || '.' || source_table_name_in;
v_target_fact_table := target_schema_in || '.' || target_table_name_in;
v_target_arc_table := target_schema_in || '.' || arc_target_table_name;
query_fetch_deleted_recs := ' SELECT * FROM ' || v_source_stage_table ||
' STAGING_TABLE WHERE STAGING_TABLE.' || source_table_key_in || ' = FACT_TABLE.' || target_table_key_in ||
' AND NVL(STAGING_TABLE.EDW_IUD_FLAG, ''X'') = '''|| CON_EDW_IUD_FLAG ||
''' AND STAGING_TABLE.EDW_UPDATE_DATE_TIME >= TO_DATE('''|| v_last_deleted_date || ''', ''MM/DD/YYYY HH24:MI:SS'')';
DBMS_OUTPUT.PUT_LINE ('Query : ' || query_fetch_deleted_recs);
query_insert_into_arc := 'INSERT INTO ' || v_target_arc_table || ' SELECT * FROM ' || v_target_fact_table ||
' FACT_TABLE WHERE EXISTS (' || query_fetch_deleted_recs || ')';
query_delete_from_fact := 'DELETE FROM ' || v_target_fact_table ||
' FACT_TABLE WHERE EXISTS (' || query_fetch_deleted_recs || ')';
DBMS_OUTPUT.PUT_LINE ('Insert Query : ' || query_insert_into_arc);
EXECUTE IMMEDIATE query_insert_into_arc;
v_insert_count := SQL%ROWCOUNT;
EXECUTE IMMEDIATE query_delete_from_fact;
v_delete_count := SQL%ROWCOUNT;
COMMIT;
DBMS_OUTPUT.PUT_LINE ('Results : ' || v_insert_count ||v_delete_count);
--IF v_insert_count > 0 AND v_delete_count > 0 THEN
UPDATE_LAST_DELETED_DATE(v_source_stage_table, delete_control_id_in);
--END IF;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END UPDATE_FACT_TABLES;
PROCEDURE UPDATE_DIMENSION_TABLES (source_schema_in IN VARCHAR2, source_table_name_in IN VARCHAR2, source_table_key_in IN VARCHAR2,
target_schema_in IN VARCHAR2, target_table_name_in IN VARCHAR2, target_table_key_in IN VARCHAR2,
last_deleted_date_in IN DATE, delete_control_id_in IN NUMBER, table_type_in IN VARCHAR2) AS
v_source_stage_table VARCHAR2(70);
v_target_dim_table VARCHAR2(70);
query_fetch_deleted_recs CLOB;
query_update_dim1 CLOB;
query_update_dim2 CLOB;
query_update_dim6 CLOB;
v_update_count NUMBER;
v_last_deleted_date VARCHAR2(100) := TO_CHAR(last_deleted_date_in,'MM/DD/YYYY HH24:MI:SS');
BEGIN
v_source_stage_table := source_schema_in || '.' || source_table_name_in;
v_target_dim_table := target_schema_in || '.' || target_table_name_in;
query_fetch_deleted_recs := ' SELECT * FROM ' || v_source_stage_table ||
' STAGING_TABLE WHERE STAGING_TABLE.' || source_table_key_in || ' = DIM_TABLE.' || target_table_key_in ||
' AND NVL(STAGING_TABLE.EDW_IUD_FLAG, ''X'') = '''|| CON_EDW_IUD_FLAG ||
''' AND STAGING_TABLE.EDW_UPDATE_DATE_TIME >= TO_DATE('''|| v_last_deleted_date || ''', ''MM/DD/YYYY HH24:MI:SS'')';
IF table_type_in = 'DIMTYP1' THEN
query_update_dim1 := 'UPDATE ' || v_target_dim_table || ' DIM_TABLE' ||
' SET DIM_TABLE.DELETE_FLAG = ''' || CON_DELETED_FLAG || ''', DIM_TABLE.EDW_UPDATE_DATE_TIME = SYSDATE, DIM_TABLE.EDW_UPDATED_BY = ''ETL'''||
' WHERE (DIM_TABLE.DELETE_FLAG = ''N'' OR DIM_TABLE.DELETE_FLAG IS NULL)' ||
' AND EXISTS (' || query_fetch_deleted_recs || ')';
DBMS_OUTPUT.PUT_LINE ('Query : ' || query_update_dim1);
EXECUTE IMMEDIATE query_update_dim1;
v_update_count := SQL%ROWCOUNT;
ELSIF table_type_in = 'DIMTYP2' THEN
query_update_dim2 := 'UPDATE ' || v_target_dim_table || ' DIM_TABLE' ||
' SET DIM_TABLE.CURRENT_FLAG = ''' || CON_CURRENT_FLAG || ''', DIM_TABLE.EDW_END_DATE = TRUNC(SYSDATE), ' ||
' EDW_UPDATE_DATE_TIME = SYSDATE, EDW_UPDATED_BY = ''ETL'''||
' WHERE DIM_TABLE.CURRENT_FLAG = ''Y'' AND EXISTS (' || query_fetch_deleted_recs || ')';
EXECUTE IMMEDIATE query_update_dim2;
v_update_count := SQL%ROWCOUNT;
ELSIF table_type_in = 'DIMTYP6' THEN
query_update_dim6 := 'UPDATE ' || v_target_dim_table || ' DIM_TABLE' ||
' SET DIM_TABLE.EDW_END_DATE = TRUNC(SYSDATE) , ' ||
' DIM_TABLE.DELETE_FLAG = ''' || CON_DELETED_FLAG || ''', DIM_TABLE.EDW_UPDATE_DATE_TIME = SYSDATE, DIM_TABLE.EDW_UPDATED_BY = ''ETL'''||
' WHERE DIM_TABLE.EDW_END_DATE = TO_DATE(''12/31/2099'',''MM/DD/YYYY'') AND EXISTS (' || query_fetch_deleted_recs || ')';
EXECUTE IMMEDIATE query_update_dim6;
v_update_count := SQL%ROWCOUNT;
END IF;
COMMIT;
DBMS_OUTPUT.PUT_LINE ('Results : ' || v_update_count);
--IF v_update_count > 0 THEN
UPDATE_LAST_DELETED_DATE(v_source_stage_table, delete_control_id_in);
--END IF;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END UPDATE_DIMENSION_TABLES;
PROCEDURE UPDATE_LAST_DELETED_DATE (source_stage_table_in IN VARCHAR2, delete_control_id_in IN NUMBER) AS
query_max_updated_query CLOB;
v_max_updated_date DATE;
BEGIN
query_max_updated_query := 'SELECT MAX(STAGE_TABLE.EDW_UPDATE_DATE_TIME) FROM ' || source_stage_table_in || ' STAGE_TABLE ';
EXECUTE IMMEDIATE query_max_updated_query INTO v_max_updated_date;
UPDATE EDW_ODS_DWH_DELETES_CONTROL
SET EDW_UPDATE_DATE_TIME = SYSDATE, EDW_UPDATED_BY = 'ETL', LAST_DELETED_DATE = v_max_updated_date
WHERE DELETE_CONTROL_ID = delete_control_id_in;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END UPDATE_LAST_DELETED_DATE;
PROCEDURE ERROR_LOG (delete_record_key_in IN NUMBER, table_schema_in IN VARCHAR2, target_table_in IN VARCHAR2,
error_message_in IN VARCHAR2, error_backtrace_in IN VARCHAR2 := NULL) AS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO edw_ods_dwh_deletes_log VALUES (delete_record_key_in, target_table_in, table_schema_in, SYSDATE, 'ETL',
error_message_in, error_backtrace_in);
COMMIT;
RAISE_APPLICATION_ERROR(-20000, error_message_in);
EXCEPTION
WHEN OTHERS THEN
RAISE;
END ERROR_LOG;
END EDW_ODS_DELETE_RECORDS_PKG;
Your error is, as the ORA-00913 error message states, that in one of your statements you are passing too many values.
Cause:
As an example, if you have the table:
CREATE TABLE table_name (a NUMBER, b NUMBER);
And you do either of:
INSERT INTO table_name VALUES (1,2,3);
INSERT INTO table_name (a,b) VALUES (1,2,3);
Then you get the error:
ORA-00913: too many values
As you are trying to insert three values into two columns.
If you name all the columns:
INSERT INTO table_name (a,b,c) VALUES (1,2,3);
Then you will get a more expressive error message:
ORA-00904: "C": invalid identifier
Solution:
Go through every statement and explicitly name the columns you are inserting and make sure you are selecting the same number of columns.
For example, in the UPDATE_FACT_TABLES procedure you have:
query_insert_into_arc := 'INSERT INTO ' || v_target_arc_table || ' SELECT * FROM ' || v_target_fact_table ||
' FACT_TABLE WHERE EXISTS (' || query_fetch_deleted_recs || ')';
It would be better to be:
query_insert_into_arc := 'INSERT INTO ' || v_target_arc_table || ' (col1, col2, col3, col4)
|| ' SELECT other_col1, other_col2, other_col3, other_col3'
|| ' FROM ' || v_target_fact_table || ' FACT_TABLE'
|| ' WHERE EXISTS (' || query_fetch_deleted_recs || ')';
Then you know which columns are being inserted where and you will get a more detailed error message if you are trying to insert into a column that does not exist.
You also do not need to use:
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
As the default behaviour is to rollback the transaction when an exception is raised.
Additionally, you should not raise an exception in your autonomous transaction as you are already raising the exception in the non-autonomous procedure.
PROCEDURE ERROR_LOG (
delete_record_key_in IN NUMBER,
table_schema_in IN VARCHAR2,
target_table_in IN VARCHAR2,
error_message_in IN VARCHAR2,
error_backtrace_in IN VARCHAR2 := NULL
)
AS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO edw_ods_dwh_deletes_log (
delete_record_key, -- Give the columns their proper names
target_table,
table_schema,
change_date,
col1,
error_message,
error_backtrace
) VALUES (
delete_record_key_in,
target_table_in,
table_schema_in,
SYSDATE,
'ETL',
error_message_in,
error_backtrace_in
);
COMMIT;
-- Remove raising the exception.
END ERROR_LOG;

Gettig error PLS-00364

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;

Converting a dynamic query to use bind variables in Oracle 11g

I have a dynamic search query that I would like to convert to make use of bind variables. The dynamic portion of the of query is in the where clause, and using a series of if statements to build a string that is concatenated to the rest of the query string. That query is then used in the for clause of a openstatement, which the result set is the return parameter. I'm not really sure how to accomplish this.
Here is the stored procedure:
PROCEDURE run_search(i_unit_id IN lu_unit.fsu_id%TYPE,
i_equipment IN tbl_component.component%TYPE,
i_equipment_status IN tbl_component.equipment_status%TYPE,
i_equipment_type IN tbl_component.equipment_type%TYPE,
i_equipment_subtype IN tbl_component.equipment_sub_type%TYPE,
i_system_id IN tbl_component.system_id%TYPE,
i_association_code IN tbl_component_assc_code.assc_code%TYPE,
i_manufacturer IN lu_component_manu_model.equipment_manufacturer%TYPE,
i_manumodel IN lu_component_manu_model.equipment_model%TYPE,
o_results OUT sys_refcursor) AS
v_query VARCHAR2(32767) := '';
v_where VARCHAR2(32767) := ' 1= 1';
BEGIN
IF i_unit_id IS NOT NULL THEN
v_where := v_where || ' AND unit_id=''' || i_unit_id ||''' ';
END IF;
IF i_equipment IS NOT NULL THEN
v_where := v_where || ' AND lower(component) LIKE ''%' || lower(i_equipment) ||'%'' ';
END IF;
IF i_equipment_status IS NOT NULL THEN
v_where := v_where || ' AND equipment_status=''' || i_equipment_status ||''' ';
END IF;
IF i_equipment_type IS NOT NULL THEN
v_where := v_where || ' AND equipment_type=''' || i_equipment_type ||''' ';
END IF;
IF i_equipment_subtype IS NOT NULL THEN
v_where := v_where || ' AND equipment_sub_type=''' || i_equipment_subtype ||''' ';
END IF;
IF i_system_id IS NOT NULL THEN
v_where := v_where || ' AND system_id=''' || i_system_id || ''' ';
END IF;
IF i_association_code IS NOT NULL THEN
v_where := v_where || ' AND EXISTS ( select null from tbl_component_assc_code where assc_code = ''' || i_association_code || ''' and component_id = vcs.component_id )';
END IF;
IF i_manufacturer IS NOT NULL THEN
v_where := v_where || ' AND equipment_manufacturer=''' || i_manufacturer || ''' ';
END IF;
IF i_manuModel IS NOT NULL THEN
v_where := v_where || ' AND equipment_model=''' || i_manuModel || ''' ';
END IF;
v_query :=
' SELECT rownum, results.* '
||' FROM '
||' ( SELECT '
||' count(*) OVER () ' || ' as total_results, '
||''
||' site_id, site_display_name, '
||' unit_id, unit_display_name, '
||' system_id, system_display_name, '
||' component_id, component, component_description, equipment_description, '
||' equipment_status, equipment_model, equipment_serial_number, equipment_type, equipment_sub_type, '
||' template_ids '
||''
||' FROM vw_component_search '
||' WHERE ' || v_where
||' ORDER BY unit_display_name, component '
||' ) results '
;
OPEN o_results FOR v_query;
END run_search;
You can write the query without dynamically creating it so you include all the parameters and just ignore those which are NULL (please profile it to test whether there are any performance issues compared to a dynamic query):
PROCEDURE run_search(i_unit_id IN lu_unit.fsu_id%TYPE,
i_equipment IN tbl_component.component%TYPE,
i_equipment_status IN tbl_component.equipment_status%TYPE,
i_equipment_type IN tbl_component.equipment_type%TYPE,
i_equipment_subtype IN tbl_component.equipment_sub_type%TYPE,
i_system_id IN tbl_component.system_id%TYPE,
i_association_code IN tbl_component_assc_code.assc_code%TYPE,
i_manufacturer IN lu_component_manu_model.equipment_manufacturer%TYPE,
i_manumodel IN lu_component_manu_model.equipment_model%TYPE,
o_results OUT sys_refcursor)
AS
BEGIN
OPEN o_results FOR
SELECT rownum,
results.*
FROM ( SELECT count(*) OVER () as total_results,
site_id,
site_display_name,
unit_id,
unit_display_name,
system_id,
system_display_name,
component_id,
component,
component_description,
equipment_description,
equipment_status,
equipment_model,
equipment_serial_number,
equipment_type,
equipment_sub_type,
template_ids
FROM vw_component_search
WHERE ( i_unit_id IS NULL
OR unit_id= i_unit_id )
AND ( i_equipment IS NULL
OR lower(component) LIKE '%' || lower(i_equipment) || '%' )
AND ( i_equipment_status IS NULL
OR equipment_status= i_equipment_status )
AND ( i_equipment_type IS NULL
OR equipment_type= i_equipment_type )
AND ( i_equipment_subtype IS NULL
OR equipment_sub_type= i_equipment_subtype )
AND ( i_system_id IS NULL
OR system_id= i_system_id )
AND ( i_association_code IS NULL
OR EXISTS ( select null
from tbl_component_assc_code
where assc_code = i_association_code
and component_id = vcs.component_id ) )
AND ( i_manufacturer IS NULL
OR equipment_manufacturer= i_manufacturer )
AND ( i_manuModel IS NULL
OR equipment_model= i_manuModel )
ORDER BY unit_display_name, component
) results;
END run_search;
(I've not compiled the above code - so there may be some errors).
The best way is to avoid such headache at all. 'SP returning resultset' is a usual practise in a poor legacy things such as MSSQL2000, but in unnecessary and doubtful in Oracle.
If you wish to do this, I'd advice you to do something like this:
procedure MakeGarbage(value_mask varchar2) return sys_refcursor is
cur integer;
stmt varchar2(32000 byte);
type TParamTable is table of varchar2(1000) index by varchar2(20);
params TParamTable;
i varchar2(20);
begin
stmt := 'select * from table where 1 = 1 ';
if value_mask is not null then
stmt := stmt || ' and value like :value_mask ';
params('value_mask') := value_mask;
end if;
...
cur := dbms_sql.create_cursor;
dbms_sql.open_cursor(cur, stmt, dbms_sql.native);
i := params.first;
while i is not null loop
dbms_sql.bind_variable(i, params(i));
i := params.next(i);
end loop;
return dbms_sql.to_ref_cursor(cur);
end;
every reference to a PL/SQL variable is in fact a bind variable.
you can check this asktom link
https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:2320123769177
and check "Dynamic SQL" from this link http://www.akadia.com/services/ora_bind_variables.html

Spool the data from execute immediate within pl/sql block

Ho to spool the data from execute immediate in pl/sql.
Below is the code.
DECLARE
from_dt varchar(300):=&from_date;
to_dt varchar2(300):=&to_date;
account varchar2(200):=&account;
mbl_table varchar2(100);
dn_table varchar2(100);
id number;
Begin
select aid into id from account where upper(acode)=upper(account);
select tablename into mbl_table from partner_mbl where pid in (select pid from account where upper(acode)=upper(account));
select dn_tablename into dn_table from partner_mbl where pid in (select pid from account where upper(acode)=upper(account));
dbms_output.put_line(mbl_table);
FOR i IN (select 'p_'||to_char((ROWNUM-1 + to_date(from_dt, 'ddmonyyyy')),'ddmonyyyy') part FROM all_objects
WHERE ROWNUM-2 < to_date(to_dt, 'ddmonyyyy')-to_date(from_dt, 'ddmonyyyy'))loop
execute immediate 'Select a.pcode,A.Acode,To_Char(A.Mobile),a.msg,a.senderid,To_Char(A.Rts,'||'''dd/mm/yyyy hh24:mi:ss'''||'),To_Char(A.Sts,'||'''dd/mm/yyyy hh24:mi:ss'''||'),A.Statusflag,A.Statusid,To_Char(B.Lastts,'||'''dd/mm/yyyy hh24:mi:ss'''||'),B.Statusflag,B.Err_Des From '|| mbl_table ||' partition('||i.part||') A, '|| dn_table ||' partition('||i.part||') B Where a.aid='||id|| ' And A.Msgid = B.Msgid(+)';
end loop;
End;
how to spool the output of execute immediate into csv file
DECLARE
from_dt varchar(300) := &from_date;
to_dt varchar2(300) := &to_date;
account varchar2(200) := &account;
mbl_table varchar2(100);
dn_table varchar2(100);
id number;
/*define types*/
/* change the next varchar2(1000) with your types*/
t_pcode IS TABLE OF varchar2(1000);
v_pcode t_pcode;
t_Acode IS TABLE OF varchar2(1000);
v_Acode t_Acode;
t_Mobile IS TABLE OF varchar2(1000);
v_Mobile t_Mobile;
t_msg IS TABLE OF varchar2(1000);
v_msg t_msg;
t_senderid IS TABLE OF varchar2(1000);
v_senderid t_senderid;
t_Rts IS TABLE OF varchar2(1000);
v_Rts t_Rts;
t_Sts IS TABLE OF varchar2(1000);
V_Sts t_Sts;
t_Statusflag IS TABLE OF varchar2(1000);
v_Statusflag t_Statusflag;
t_Statusid IS TABLE OF varchar2(1000);
v_Statusid t_Statusid;
t_Lastts IS TABLE OF varchar2(1000);
v_Lastts t_Lastts;
t_Statusflag IS TABLE OF varchar2(1000);
v_Statusflag t_Statusflag;
t_Err_Des IS TABLE OF varchar2(1000);
v_Err_Des t_Err_Des;
/*end types*/
Begin
select aid into id from account where upper(acode) = upper(account);
select tablename
into mbl_table
from partner_mbl
where pid in
(select pid from account where upper(acode) = upper(account));
select dn_tablename
into dn_table
from partner_mbl
where pid in
(select pid from account where upper(acode) = upper(account));
dbms_output.put_line(mbl_table);
FOR i IN (select 'p_' ||
to_char((ROWNUM - 1 + to_date(from_dt, 'ddmonyyyy')),
'ddmonyyyy') part
FROM all_objects
WHERE ROWNUM - 2 < to_date(to_dt, 'ddmonyyyy') -
to_date(from_dt, 'ddmonyyyy')) loop
/* execute immediate 'Select a.pcode,A.Acode,To_Char(A.Mobile),a.msg,a.senderid,To_Char(A.Rts,' ||
'''dd/mm/yyyy hh24:mi:ss''' || '),To_Char(A.Sts,' ||
'''dd/mm/yyyy hh24:mi:ss''' ||
'),A.Statusflag,A.Statusid,To_Char(B.Lastts,' ||
'''dd/mm/yyyy hh24:mi:ss''' ||
'),B.Statusflag,B.Err_Des From ' || mbl_table ||
' partition(' || i.part || ') A, ' || dn_table ||
' partition(' || i.part || ') B Where a.aid=' || id ||
' And A.Msgid = B.Msgid(+)' ;
*/
execute immediate 'Select a.pcode,A.Acode,To_Char(A.Mobile),a.msg,a.senderid,To_Char(A.Rts,' ||
'''dd/mm/yyyy hh24:mi:ss''' || '),To_Char(A.Sts,' ||
'''dd/mm/yyyy hh24:mi:ss''' ||
'),A.Statusflag,A.Statusid,To_Char(B.Lastts,' ||
'''dd/mm/yyyy hh24:mi:ss''' ||
'),B.Statusflag,B.Err_Des From ' || mbl_table ||
' partition(' || i.part || ') A, ' || dn_table ||
' partition(' || i.part || ') B Where a.aid=' || id ||
' And A.Msgid = B.Msgid(+)'
BULK COLLECT INTO v_pcode, v_Acode, v_Mobile, v_msg, v_senderid,
v_Rts, V_Sts, v_Statusflag, v_Statusid, v_Lastts,
v_Statusflag, v_Err_Des;
for i.. v_pcode.count loop
dbms_output.put_line('pcode and msg values are: '||v_pcode(i)||' ' ||v_msg(i) ||' at line' || i );
/*you can get out other values with same way*/
end loop;
End;
you can also take a look at oracle documentation for "oracle collections", "execute immediate", "bulk collect".
my previous post could solve your problem however here is a simple example as additional info.
DECLARE
TYPE emp_typ IS TABLE OF scott.emp%ROWTYPE;
e_tab emp_typ;
BEGIN
EXECUTE IMMEDIATE 'SELECT * FROM emp'
BULK COLLECT INTO e_tab;
DBMS_OUTPUT.put_line('Dynamic EXECUTE: ' || e_tab.count);
for i in 1..e_tab.count loop
DBMS_OUTPUT.put_line( e_tab(i).ename ||':'|| e_tab(i).empno );
null;
end loop;
END;
/

Oracle: Search an entire DB for a string: Error ORA-00911

I have created the PL/SQL stored procedure below to search an entire Oracle11g database for a string (srchstr) and return the table and column where that string was found to a table called VALUESEARCHRESULTS.
The procedure has ran successfully in Oracle XE via SQL Developer as a user. However, when try to run it as user SYS in Oracle11g for schema ABC, I receive the following error:
ORA-00911: invalid character
Cause: identifiers may not start with any ASCII character other than letters and numbers. $#_ are also allowed after the first character. Identifiers enclosed by double quotes may contain any character other than a double quote. Alternative quotes (q"#...#") cannot use spaces, tabs, or carriage returns as delimiters. For all other contexts, consult the SQL Language Reference Manual.
Does anyone know why this may be? Please see my code below.
CREATE OR REPLACE PROCEDURE ABC.FIND_STRING(p_str IN VARCHAR2) authid current_user is
l_query clob;
srchstr varchar2(30) := '';
r_cname varchar2(30) := '';
l_case clob;
l_runquery boolean;
l_tname varchar2(30);
l_cname varchar2(30);
begin
dbms_application_info.set_client_info( '%' || upper(p_str) || '%' );
for x in (select * from user_tables)
loop
l_query := 'select ''' || x.table_name || ''', $$
from ' || x.table_name || '
where rownum = 1 and ( 1=0 ';
l_case := 'case ';
l_runquery := FALSE;
for y in ( select *
from user_tab_columns
where table_name = x.table_name
and (data_type in('CHAR', 'DATE', 'FLOAT', 'NCHAR', 'NUMBER', 'NVARCHAR2', 'VARCHAR2' )
or data_type like 'INTERVAL%' or data_type like 'TIMESTAMP%' )
)
loop
l_runquery := TRUE;
l_query := l_query || ' or upper(' || y.column_name ||
') like userenv(''client_info'') ';
l_case := l_case || ' when upper(' || y.column_name ||
') like userenv(''client_info'') then ''' ||
y.column_name || '''';
end loop;
if ( l_runquery )
then
l_case := l_case || ' else NULL end';
l_query := replace( l_query, '$$', l_case ) || ')';
begin
execute immediate l_query into l_tname, l_cname;
r_cname := l_cname;
dbms_application_info.read_client_info(srchstr);
insert into ABC.ValueSearchResults (resulttable, resultcolumn, searchstring) values (x.table_name, r_cname, srchstr);
dbms_output.put_line
( srchstr || ' found in ' || l_tname || '.' || l_cname );
exception
when no_data_found then
dbms_output.put_line
( srchstr || ' has no hits in ' || x.table_name );
end;
end if;
end loop;
end;
EDIT: The stored procedure above compiles without error. The code below executes the stored procedure by passing values from a table into the stored procedure. The error shows when the code below is ran:
BEGIN
FOR c IN (SELECT ControlValue FROM ABC.ControlValues) LOOP
ABC.FIND_STRING(c.ControlValue);
END LOOP;
END;
I have found a resolution for the issue I initially raised.
Cause of error: Not specifying a schema; only grabbing user-specific tables.
While the stored procedure would execute when deployed as user ABC, the error generated when running the stored procedure as a user other ABC. It appeared the same table name existed in multiple schemas. Thus, adding an OWNER variable specified the schema associated with the table name and eliminated the error.
Additionally, the procedure was originally searching for USER_TABLES. This limited the results to only the tables of the current schema. By replacing USER_TABLES with DBA_TABLES, the stored procedure's search spanned through all tables of the database.
See below for the corrected code:
CREATE OR REPLACE
PROCEDURE FIND_STRING(
p_str IN VARCHAR2) authid current_user
IS
l_query CLOB;
srchstr VARCHAR2(100) := '';
r_cname VARCHAR2(100) := '';
l_case CLOB;
l_runquery BOOLEAN;
l_tname VARCHAR2(100);
l_cname VARCHAR2(100);
BEGIN
dbms_application_info.set_client_info( '%' || upper(p_str) || '%' );
FOR x IN
(SELECT *
FROM dba_tables
WHERE table_name <> 'CONTROLVALUES'
AND table_name <> 'VALUESEARCHRESULTS'
AND tablespace_name <> 'SYSTEM'
AND tablespace_name <> 'SYSAUX'
AND tablespace_name <> 'TEMP'
AND tablespace_name <> 'UNDOTBS1'
)
LOOP
l_query := 'select ''' || x.owner || '.' || x.table_name || ''', $$
from ' || x.owner || '.' || x.table_name || '
where rownum = 1 and ( 1=0 ';
l_case := 'case ';
l_runquery := FALSE;
FOR y IN
(SELECT *
FROM dba_tab_columns
WHERE table_name = x.table_name
AND owner = x.owner
AND (data_type IN ( 'CHAR', 'DATE', 'FLOAT', 'NCHAR', 'NUMBER', 'NVARCHAR2', 'VARCHAR2' )
OR data_type LIKE 'INTERVAL%'
OR data_type LIKE 'TIMESTAMP%' )
)
LOOP
l_runquery := TRUE;
l_query := l_query || ' or upper(' || y.column_name || ') like userenv (''client_info'') ';
l_case := l_case || ' when upper(' || y.column_name || ') like userenv (''client_info'') then ''' || y.column_name || '''';
END LOOP;
IF ( l_runquery ) THEN
l_case := l_case || ' else NULL end';
l_query := REPLACE( l_query, '$$', l_case ) || ')';
BEGIN
EXECUTE immediate l_query INTO l_tname, l_cname;
r_cname := l_cname;
dbms_application_info.read_client_info(srchstr);
INSERT
INTO VALUESEARCHRESULTS
(
resulttable,
resultcolumn,
searchstring
)
VALUES
(
x.table_name,
r_cname,
srchstr
);
dbms_output.put_line ( srchstr || ' found in ' || l_tname || '.' || l_cname );
EXCEPTION
WHEN no_data_found THEN
dbms_output.put_line ( srchstr || ' has no hits in ' || x.owner || '.' || x.table_name );
END;
END IF;
END LOOP;
END;

Resources