What is wrong with my PLSQL dynamic code? - sql

I'm learning PLSQL since 3 or 4 months, and now I'm practicing Dynamic SQL and I've write a simple procedure to update last name of an employee with Dynamic SQL. I don´t know what is the problem because when I call the procedure and insert the parameters I receive an error.
CREATE OR REPLACE PROCEDURE upd_emp_dyn(
p_tname VARCHAR2,
p_lname employees.last_name%TYPE,
p_empid employees.employee_id%TYPE)
IS
v_dyn_stmt VARCHAR2(200) := 'UPDATE ' || p_tname ||
' SET last_name = ' ||
p_lname || ' WHERE employee_id = ' ||
p_empid;
v_confirm_stmt VARCHAR2(200) := 'SELECT * FROM ' || p_tname ||
' WHERE employee_id = ' ||
p_empid;
BEGIN
EXECUTE IMMEDIATE v_dyn_stmt;
DBMS_OUTPUT.PUT_LINE(v_confirm_stmt);
END upd_emp_dyn;
/
SHOW ERRORS;
SET SERVEROUTPUT ON
BEGIN
upd_emp_dyn('employees', 'Rooney', 120);
END;
/
Error que empieza en la línea: 2 del comando :
BEGIN
upd_emp_dyn('employees', 'Rooney', 120);
END;
Informe de error -
ORA-00904: "ROONEY": invalid identifier
ORA-06512: at "HR.UPD_EMP_DYN", line 14
ORA-06512: at line 2
00904. 00000 - "%s: invalid identifier"
*Cause:
*Action:

The best practice (for both performance and security) is to use parameters:
CREATE OR REPLACE PROCEDURE upd_emp_dyn(
p_tname VARCHAR2,
p_lname employees.last_name%TYPE,
p_empid employees.employee_id%TYPE)
IS
v_dyn_stmt VARCHAR2(200) := 'UPDATE ' || p_tname ||
' SET last_name = :1 WHERE employee_id = :2';
v_confirm_stmt VARCHAR2(200) := 'SELECT * FROM ' || p_tname ||
' WHERE employee_id = :1';
BEGIN
EXECUTE IMMEDIATE v_dyn_stmt using p_lname, p_empid;
DBMS_OUTPUT.PUT_LINE(v_confirm_stmt);
END upd_emp_dyn;
/
SHOW ERRORS;
SET SERVEROUTPUT ON
BEGIN
upd_emp_dyn('employees', 'Rooney', 120);
END;
/
For select statements use following:
declare
result my_table%rowtype;
id number := 123;
begin
execute immediate 'select * from my_table where id = :1' into result using id;
end;
/
Also, it helps you to avoid typo errors (like which caused this question). And of course, you should avoid the use of dynamic SQL if it is possible to use static.

Instead of declaring table name as separate variable as parameter in your procedure..you can write a cursor for employees table & call that cursor in your dynamic query to print the output. So your dynamic sql in your proc will be more simple.

Related

Oracle Cursor within a Package not working - ORA 06512

I am trying to build a package that will take in a table of table names and either drop from or delete those tables. I am using dynamic sql, and dropping or deleting the tables works, but I need both the procedures to loop through all of the table names passed back to it.
I've tried mulitple ways - including trying to create a FOR Loop and a cursor. Here is a similar function I wrote in PostgreSQL that works but I'm having trouble translating it to Oracle.
Here is my function in PostgreSQL that works:
CREATE OR REPLACE FUNCTION drop_tables_for_stnd_mod_build(tablenames text)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
tab_name RECORD;
BEGIN
FOR tab_name IN EXECUTE 'SELECT table_name FROM ' || tablenames
LOOP
EXECUTE 'DROP TABLE ' || tab_name.table_name || ' CASCADE';
END LOOP;
END;
$function$
;
And the procedure I'm writing as part of a package in Oracle
CREATE OR REPLACE PACKAGE BODY stnd_build_table_cleanup
AS
PROCEDURE drop_tables(table_in CLOB)
IS
TYPE cur_type is REF CURSOR;
c cur_type;
query_string VARCHAR(300);
loop_string VARCHAR(300);
table_name VARCHAR(100);
BEGIN
loop_string := 'SELECT tablenames FROM :table';
OPEN c FOR loop_string USING table_in;
LOOP
FETCH c INTO table_name;
query_string := 'DROP TABLE ' || table_name || ' CASCADE CONSTRAINTS';
-- dbms_output.PUT_LINE (query_string);
EXECUTE IMMEDIATE query_string;
EXIT WHEN c%NOTFOUND;
END LOOP ;
CLOSE c;
END drop_tables;
Here is the error I get when I try to call my function: Error report -
ORA-00903: invalid table name
ORA-06512: at "AMS_NYS.STND_BUILD_TABLE_CLEANUP", line 13
ORA-06512: at line 2
00903. 00000 - "invalid table name"
*Cause:
*Action:
Thanks!
Here's one possibility. Note that I coded this as a standalone procedure for simplicity.
CREATE OR REPLACE TYPE table_type IS TABLE OF VARCHAR2(128);
CREATE OR REPLACE PROCEDURE drop_tables(tables_to_drop_in table_type)
IS
BEGIN
FOR i IN tables_to_drop_in.FIRST .. tables_to_drop_in.LAST LOOP
--DBMS_OUTPUT.PUT_LINE(tables_to_drop_in(i));
EXECUTE IMMEDIATE 'DROP TABLE ' || tables_to_drop_in(i) || ' CASCADE CONSTRAINTS';
END LOOP;
END drop_tables;
DECLARE
tables_to_drop table_type;
BEGIN
tables_to_drop := table_type('TBL1','TBL2', 'TBL3');
drop_tables(tables_to_drop);
END;

Assigning multiple fields in a loop using execute immediate

I am using PLPDF's libraries to create spreadsheets for various files - I am trying to get a procedure written to take the values of each field and one-by-one insert them into the spreadsheet. This operation can include many different tables going into many different spreadsheets, so just doing an export is not going to cut it.
This example is has two cursors created from tables - the USER_TAB_COLUMNS to select the column names, and the actual view query to pull the data. I have one loop to go through the data record by record, and second to go field-by-field within the record.
Before doing the actual writing to the spreadsheet blob, I'm simply running the dbms_output.putline to make sure that I am getting the data needed.
declare
q_str varchar2(100);
this_vc varchar2(3000);
cursor diet_table is
select * from vcars_diet where nhp_id = 8573;
cursor diet_stru is
select 'begin :1 := i.' || column_name || '; end;' line_o_code from user_tab_columns where table_name = 'VCARS_DIET';
begin
for i in diet_table loop
DBMS_OUTPUT.PUT_LINE ('--------------------------------------------------------');
for h in diet_stru loop
DBMS_OUTPUT.PUT_LINE ('Varchar Value for i: "' || h.line_o_code || '"');
EXECUTE IMMEDIATE (h.line_o_code) USING out this_vc;
DBMS_OUTPUT.PUT_LINE ('Varchar Value for i.' || h.line_o_code || ' is: '||this_vc);
end loop;
end loop;
end;
The fields in the diet table are:
NHP_ID
DATE_TIME
DIET_NO
FORM_NAME
DATA_TYPE
The results are:
ORA-06550: line 1, column 13:
PLS-00201: identifier 'I.NHP_ID' must be declared
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
ORA-06512: at line 33
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
I have copied this PL/SQL code from Connor McDonald's solution from this AskTom article.
It uses type dynamic SQL to parse any SQL query and converts the column names as well as values into a collection. I've used a sample query on employees table in HR schema. Replace it with your query.
set serverout on size 999999
set verify off
declare
p_query varchar2(32767) :=
q'{select * from employees
where rownum = 1
}';-- Here you put your query
l_theCursor integer default dbms_sql.open_cursor;
l_columnValue varchar2(4000);
l_status integer;
l_descTbl dbms_sql.desc_tab;
l_colCnt number;
n number := 0;
procedure p(msg varchar2) is
l varchar2(4000) := msg;
begin
while length(l) > 0 loop
dbms_output.put_line(substr(l,1,80));
l := substr(l,81);
end loop;
end;
begin
execute immediate
'alter session set nls_date_format=''dd-mon-yyyy hh24:mi:ss'' ';
dbms_sql.parse( l_theCursor, p_query, dbms_sql.native );
dbms_sql.describe_columns( l_theCursor, l_colCnt, l_descTbl );
for i in 1 .. l_colCnt loop
dbms_sql.define_column(l_theCursor, i, l_columnValue, 4000);
end loop;
l_status := dbms_sql.execute(l_theCursor);
while ( dbms_sql.fetch_rows(l_theCursor) > 0 ) loop
for i in 1 .. l_colCnt loop
dbms_sql.column_value( l_theCursor, i, l_columnValue );
p( 'Value for '|| l_descTbl(i).col_name
|| ' is: ' ||
l_columnValue );
end loop;
dbms_output.put_line( '-----------------' );
n := n + 1;
end loop;
if n = 0 then
dbms_output.put_line( chr(10)||'No data found '||chr(10) );
end if;
end;
/
This gives the output:
Value for EMPLOYEE_ID is: 198
Value for FIRST_NAME is: Donald
Value for LAST_NAME is: OConnell
Value for EMAIL is: DOCONNEL
Value for PHONE_NUMBER is: 650.507.9833
Value for HIRE_DATE is: 21-jun-2007 00:00:00
Value for JOB_ID is: SH_CLERK
Value for SALARY is: 2600
Value for COMMISSION_PCT is:
Value for MANAGER_ID is: 124
Value for DEPARTMENT_ID is: 50
-----------------
PL/SQL procedure successfully completed.

How to use dynamic where clause in EXECUTE IMMEDIATE

I have a table with columns insert,select,where clause,dynamic where clause,group by clause.
Using procedure i need to execute insert into statement and also use dynamic where clause.
I tried the following one however it is giving me an error missing expression.
create or replace PROCEDURE dynamicWhereClause(Datee IN DATE,processId IN NUMBER)
IS
processName VARCHAR2(100);
tablePrefix CONFIG_DETAILS.SOURCE_TABLE%Type;
sourceTableType CONFIG_DETAILS.SOURCE_TABLE_TYPE%Type;
insertClause CONFIG_DETAILS.INSERT_CLAUSE%Type;
selectClause CONFIG_DETAILS.SELECT_CLAUSE%Type;
whereClause CONFIG_DETAILS.WHERE_CLAUSE%Type;
onUpdateClause CONFIG_DETAILS.ON_UPDATE_CLAUSE%Type;
groupByClause CONFIG_DETAILS.GROUP_BY_CLAUSE%Type;
orderByClause CONFIG_DETAILS.ORDER_BY_CLAUSE%Type;
isDynamicWhereClause CONFIG_DETAILS.IS_DYNAMIC_WHERE_CLAUSE%Type;
tableName VARCHAR2(50);
Process_Date DATE;
processQuery VARCHAR2(6000 BYTE);
CURSOR Process_Report IS
select NAME,SOURCE_TABLE,SOURCE_TABLE_TYPE,INSERT_CLAUSE,SELECT_CLAUSE,WHERE_CLAUSE,ON_UPDATE_CLAUSE,GROUP_BY_CLAUSE,ORDER_BY_CLAUSE,IS_DYNAMIC_WHERE_CLAUSE FROM
CONFIG_DETAILS where ID=processId;
BEGIN
OPEN Process_Report;
LOOP
FETCH Process_Report INTO processName,tablePrefix,sourceTableType,insertClause,selectClause,whereClause,onUpdateClause,groupByClause,orderByClause,isDynamicWhereClause;
EXIT when Process_Report%NOTFOUND;
tableName := getSourceTableName(tablePrefix,sourceTableType,processDate);
Process_Date := processDate;
processQuery := insertClause || selectClause ||' from ' || tableName ||' ' ||
nvl(whereClause,'') ||''||nvl(groupByClause,'') ||''||nvl(orderByClause,'') ||''||nvl(onUpdateClause,'');
dbms_output.put_line(processQuery);
IF isDynamicWhereClause = 'Y'
THEN
dbms_output.put_line(processQuery);
EXECUTE IMMEDIATE processQuery USING Process_Date;
ELSE
EXECUTE IMMEDIATE processQuery;
END IF;
END LOOP;
CLOSE Process_Report;
END;
While executing the proc it is giving me the below error.
Error report -
ORA-00936: missing expression
ORA-06512: at "Mytest.dynamicWhereClause", line 44
ORA-06512: at line 1
00936. 00000 - "missing expression"
Please assist me further
Thanks
Your questions is not 100% clear . you saying after DATEE is being empty, are you assigning a variable after date ? the below is example of how using execute immediate with a variable. Note how is the bind variable :i is showing in the print.
set serveroutput on size 1000
/
declare t number(4) :=10;
txt varchar2(100);
begin
txt :='INSERT INTO TAB (ID) values (:i)';
dbms_output.put_line(t || ' ' || txt);
execute immediate txt using T;
end;
/
PL/SQL procedure successfully completed
10 INSERT INTO TAB (ID) values (:i)

parameter i PL/SQL procedure

I am trying to execute a PL/SQL procedure without success.
It shows me "inexistent table". What can I do?
CREATE OR REPLACE PROCEDURE INTEGRATION(tableName varchar2) IS
BEGIN
MERGE INTO tableName alert
USING ...
you can try with execute immediate statement and concatenate the tablename passed from procedure
see example from oracle site
CREATE OR REPLACE PROCEDURE delete_rows (
table_name IN VARCHAR2,
condition IN VARCHAR2 DEFAULT NULL) AS
where_clause VARCHAR2(100) := ' WHERE ' || condition;
v_table VARCHAR2(30);
BEGIN
-- first make sure that the table actually exists; if not, raise an exception
SELECT OBJECT_NAME INTO v_table FROM USER_OBJECTS
WHERE OBJECT_NAME = UPPER(table_name) AND OBJECT_TYPE = 'TABLE';
IF condition IS NULL THEN where_clause := NULL; END IF;
EXECUTE IMMEDIATE 'DELETE FROM ' || v_table || where_clause;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE ('Invalid table: ' || table_name);
END;
/
BEGIN
delete_rows('employees_temp', 'employee_id = 111');
END;
/
for other info see oracle site
execute immediate

Passing table name and column name dynamically to PL/SQL Stored procedure

I am trying to pass table name and column name to a stored procedure in oracle , but it gives me following error: table or view does not exist
Below is the code:
create or replace procedure jz_dynamic_sql_statement
(p_table_name in varchar2,
p_col1_name in varchar2,
p_check_result out integer)
as
v_error_cd est_runtime_error_log.error_cd%type;
v_error_msg est_runtime_error_log.error_msg%type;
v_sql varchar2(1024);
v_result number(10);
begin
v_result := 0;
v_sql := 'select count(*) from ' || p_table_name ||' WHERE COLUMNNAME=' || p_col1_name;
execute immediate v_sql into v_result;
p_check_result := v_result;
end;
If the error coming back says the table does not exist then that means the table you pass in does not exist or the user that the procedure runs under cannot access it.
You could add a dbms_output.put_line statement to display the query that you are building and then try running it yourself, before you attempt the execute immediate. Then you know what errors you need to fix.
dbms_output.put_line('query : '||v_sql);
Be sure to turn on dbms_output.
Also, from what it looks like you are trying to do, you will need to pass the column name AND column value. Unless the tables you are querying will ALWAYS have the column name "COLUMNNAME".
Try this:
v_sql := 'select count(*) from ' || p_table_name ||' WHERE COLUMNNAME=''' || p_col1_name|| '''';