Generate change scripts from SQL - sql

I want to change database development from creating change scripts to automatically generating change scripts from the declarative defitinitions. How would I go about doing that for PL/SQL on an Oracle DB?

You can find a short introduction at section "Version Datamodel" here on stackoverflow. I've worked on this interesting area for many years, starting in 1994 on Oracle7 to allow automatic overnight going in production. Not all nights I got my sleep...
For Invantive Producer, we have a boot-package that runs on Oracle 11 and up and takes care of automatic upgrades, but it is over 8.000 lines. The boot package is called by PL/SQL when bootstrapping and later on takes it instructions from a repository with software definitions. The repository is described at Technical Reference Manual, also a Diagram is available and might give some inspiration.
I can give you a sample of such as procedure, please let me know what types of objects you would like to automatically generate DDL for, then I can add things when necessary.
procedure verify_index
( p_table_name varchar2
, p_index_name varchar2
, p_index_unique boolean default null
, p_column_list varchar2
)
;
and
procedure verify_index
( p_mode pls_integer
, p_table_name varchar2
, p_index_name varchar2
, p_index_unique boolean default null
, p_column_list varchar2
)
is
begin
verify_index
( get_table_name(p_mode, p_table_name)
, get_index_name(p_mode, p_index_name)
, case
when p_mode = g_history_mode
then false
else p_index_unique
end
, p_column_list
|| case
when p_mode = g_history_mode
then ',h_date_starts,h_date_ends'
else ''
end
)
;
end;
--
-- Verify existence of an index with the indicated column list.
--
procedure verify_index
( p_table_name varchar2
, p_index_name varchar2
, p_index_unique boolean default null
, p_column_list varchar2
)
is
l_index_exists_anywhere boolean;
l_index_exists_on_table boolean;
l_create_index boolean;
l_ist_column_list varchar2(4000);
l_soll_column_list varchar2(4000);
l_story varchar2(4000);
l_stmt varchar2(4000);
begin
l_story := 'Reason: ';
--
-- Determine whether to recreate the index.
-- In the process, drop the current index when not correct.
--
l_index_exists_anywhere := index_exists(p_index_name, null, null);
l_index_exists_on_table := index_exists(p_index_name, null, p_table_name);
if l_index_exists_on_table
then
l_ist_column_list := index_column_list(p_index_name, true);
l_soll_column_list := lower(p_column_list);
if identical_character_sequence(l_ist_column_list, l_soll_column_list)
then
-- Column lists are equal. Just check uniqueness.
if index_exists(p_index_name, p_index_unique, p_table_name)
then
null; -- Do nothing.
l_create_index := false;
l_story := l_story || ' index ' || p_index_name || ' already exists on the correct table with the correct column list ' || l_soll_column_list || '. Do nothing.';
else
drop_index_existing(p_index_name);
l_create_index := true;
l_story := l_story || ' index ' || p_index_name || ' has the correct column list ' || l_soll_column_list || ' but on a different table. Recreate index.';
end if;
else
drop_index_existing(p_index_name);
l_create_index := true;
l_story := l_story || ' index ' || p_index_name || ' has the incorrect column list ' || l_ist_column_list || ' but should have ' || l_soll_column_list || '. Recreate index.';
end if;
elsif l_index_exists_anywhere
then
drop_index_existing(p_index_name);
l_create_index := true;
l_story := l_story || ' index ' || p_index_name || ' exists on the wrong table. Recreate index.';
else
l_ist_column_list := null;
l_create_index := true;
l_story := l_story || ' index ' || p_index_name || ' does not yet exist. Create index.';
end if;
--
-- Now l_create_index describes whether to create the index or not.
--
if l_create_index
then
/* Only uncomment this when itgen_log is available. During bootstrapping it is not possible
to use itgen_log. */
-- itgen_log.debug('Create index ' || p_index_name || '. ' || l_story);
l_stmt := 'create'
|| case
when p_index_unique
then ' unique'
else ''
end
|| ' index '
|| p_index_name
|| ' on '
|| p_table_name
|| '('
|| p_column_list
|| ')'
;
execute_dynamic_sql(l_stmt);
end if;
end
;

Related

How can I pass column and table name as parameters to a SQL stored procedure

CREATE OR REPLACE FUNCTION getIdFromNameParameter
(columnName VARCHAR2, tableName VARCHAR2, whereColumn VARCHAR2, parameterColumn VARCHAR2)
RETURN VARCHAR2
AS
idCatc VARCHAR2(50);
BEGIN
execute immediate 'SELECT ' || columnName || ' INTO ' || idCatc || ' FROM ' || tableName || ' WHERE ' || whereColumn || ' = ' || parameterColumn;
RETURN idCatc;
END;
/
I get this warning:
Warning: Function created with compilation errors
For one thing, the into is part of execute immediate:
CREATE OR REPLACE FUNCTION getIdFromNameParameter (
in_columnName VARCHAR2,
in_tableName VARCHAR2,
in_whereColumn VARCHAR2,
in_parameterColumn VARCHAR2)
RETURN VARCHAR2
AS
idCatc VARCHAR2(50);
BEGIN
execute immediate 'SELECT ' || in_columnName || ' FROM ' || in_tableName || ' WHERE ' || in_whereColumn || ' = ' || in_parameterColumn
INTO idCatc;
RETURN idCatc;
END;
You were also using + for string concatenation.
If you create a function like this, ensure it's safe from sql injection with something like the following. This uses dbms_assert to sanitize the inputs against thing like ';drop table xyz;'
CREATE OR REPLACE FUNCTION getidfromnameparameter (
in_columnname VARCHAR2,
in_tablename VARCHAR2,
in_wherecolumn VARCHAR2,
in_parametercolumn VARCHAR2
) RETURN VARCHAR2 AS
idcatc VARCHAR2(50);
BEGIN
EXECUTE IMMEDIATE 'SELECT '
|| sys.dbms_assert.qualified_sql_name(in_columnName)
|| ' FROM '
|| sys.dbms_assert.sql_object_name(in_tableName)
|| ' WHERE '
|| sys.dbms_assert.qualified_sql_name(in_whereColumn)
|| ' = '
|| sys.dbms_assert.enquote_literal(in_parametercolumn)
INTO idcatc;
RETURN idcatc;
END;
/

How to set variable equal to empty XML node in SQL Developer

I am getting a 'method dispatch on NULL SELF argument is disallowed' error when running the below code. I manually went through my XML file and found that 'OtherLocation' is sometimes blank. I commented out the line associated with that node and that fixed the error. I am working with a large XML file that holds event information. Some events supplied information whereas other's did not.
I need to include the information if it is there and set the variable to ' ' if not.
How do I go about that?
WHILE l_xml.existsNode('/ResultOfEvent/Items/Event[' || v_count || ']') = 1 LOOP
l_eventid := l_xml.extract('/ResultOfEvent/Items/Event[' || v_count || ']/EventId/text()').getStringVal();
l_eventname := l_xml.extract('/ResultOfEvent/Items/Event[' || v_count || ']/EventName/text()').getStringVal();
l_organizationid := l_xml.extract('/ResultOfEvent/Items/Event[' || v_count || ']/OrganizationId/text()').getStringVal();
l_organizationname := l_xml.extract('/ResultOfEvent/Items/Event[' || v_count || ']/OrganizationName/text()').getStringVal();
l_description := l_xml.extract('/ResultOfEvent/Items/Event[' || v_count || ']/Description/text()').getStringVal();
--l_otherlocation := l_xml.extract('/ResultOfEvent/Items/Eventt[' || v_count || ']/OtherLocation/text()').getStringVal();
insert into xml_test values(l_eventid, l_eventname, l_organizationid, l_organizationname, l_otherlocation, l_description);
v_count := v_count + 1;
END LOOP;
EDIT
Upon doing more research on xml.extract I found that it will return a null xmltype if the xml element is empty. Is there a way to do a check for null before attempting to add? If the element is empty then, I need to insert ' ' into the corresponding table.
Ok after some time, I was finally able to come up with a working solution. The altered code below produces no error.
WHILE l_xml.existsNode('//ResultOfEvent/Items/Event[' || v_count || ']') = 1 LOOP
l_eventid := l_xml.extract('//ResultOfEvent/Items/Event[' || v_count || ']/EventId/text()').getStringVal();
l_eventname := l_xml.extract('//ResultOfEvent/Items/Event[' || v_count || ']/EventName/text()').getStringVal();
l_organizationid := l_xml.extract('//ResultOfEvent/Items/Event[' || v_count || ']/OrganizationId/text()').getStringVal();
l_organizationname := l_xml.extract('//ResultOfEvent/Items/Event[' || v_count || ']/OrganizationName/text()').getStringVal();
v_otherlocation := l_xml.extract('//ResultOfEvent/Items/Event[' || v_count || ']/OtherLocation/text()');
if v_otherlocation IS NULL then
l_otherlocation := ' ';
else
l_otherlocation := v_otherlocation.getStringVal();
end if;
l_description := l_xml.extract('//ResultOfEvent/Items/Event[' || v_count || ']/Description/text()').getStringVal();
insert into XMLPROCESSEDTABLE values(l_eventid, l_eventname, l_organizationid, l_organizationname, l_otherlocation, l_description);
v_count := v_count + 1;
END LOOP;
Creating a new variable v_otherlocation that is equal to the xmltype is what fixed the problem. The error I was getting was because of the getStringVal(). If l_xml.extract returned null then getStringVal() was trying to pull a value out of a null object.

Finding specific data in Oracle Tables

I needed to find a value for a column in my oracle database but i don't know which
table or column it's stored in
How can I search for a specific or like %% data as I do in
select * from SYS.dba_source
is there a table like that
Column Name ID Data Type Null? Comments
OWNER 1 VARCHAR2 (30 Byte) Y
NAME 2 VARCHAR2 (30 Byte) Y Name of the object
TYPE 3 VARCHAR2 (12 Byte) Y
Type of the object:
"TYPE", "TYPE BODY", "PROCEDURE", "FUNCTION",
"PACKAGE", "PACKAGE BODY" or "JAVA SOURCE"
LINE 4 NUMBER Y Line number of this line of source
TEXT 5 VARCHAR2 (4000 Byte) Y Source text
LINK: pl/sq to find any data in a schema
Imagine, there are a few tables in your schema and you want to find a specific value in all columns within these tables. Ideally, there would be an sql function like
select * from * where any(column) = 'value';
Unfortunately, there is no such function.
However, a PL/SQL function can be written that does that. The following function iterates over all character columns in all tables of the current schema and tries to find val in them.
create or replace function find_in_schema(val varchar2)
return varchar2 is
v_old_table user_tab_columns.table_name%type;
v_where Varchar2(4000);
v_first_col boolean := true;
type rc is ref cursor;
c rc;
v_rowid varchar2(20);
begin
for r in (
select
t.*
from
user_tab_cols t, user_all_tables a
where t.table_name = a.table_name
and t.data_type like '%CHAR%'
order by t.table_name) loop
if v_old_table is null then
v_old_table := r.table_name;
end if;
if v_old_table <> r.table_name then
v_first_col := true;
-- dbms_output.put_line('searching ' || v_old_table);
open c for 'select rowid from "' || v_old_table || '" ' || v_where;
fetch c into v_rowid;
loop
exit when c%notfound;
dbms_output.put_line(' rowid: ' || v_rowid || ' in ' || v_old_table);
fetch c into v_rowid;
end loop;
v_old_table := r.table_name;
end if;
if v_first_col then
v_where := ' where ' || r.column_name || ' like ''%' || val || '%''';
v_first_col := false;
else
v_where := v_where || ' or ' || r.column_name || ' like ''%' || val || '%''';
end if;
end loop;
return 'Success';
end;

pl/sql - can collection loop through column names?

The output from the below code is:
|LAT|MISC|SID|NO
MIN_LENGTH|1|2|1|1
MAX_LENGTH|6|6|4|2
The output is as I expect, but is there anyway to loop through the columns using an index (ie. j) instead of doing RESULTS(I).MAX_LENGTH , RESULTS(I).MAX_LENGTH etc ? The concern is that when adding extra columns to the 'R_RESULT_REC' record, another loop is required.
set serveroutput on
DECLARE
TYPE R_RESULT_REC IS RECORD
(COL_NAME VARCHAR2(100),
MIN_LENGTH NUMBER,
MAX_LENGTH NUMBER
);
TYPE tr_RESULT IS TABLE OF R_RESULT_REC;
RESULTS TR_RESULT := TR_RESULT();
v_counter NUMBER := 1;
BEGIN
FOR J IN (SELECT DISTINCT COLUMN_NAME FROM ALL_TAB_COLUMNS
WHERE OWNER = 'SYSTEM'
and TABLE_NAME = 'SPECCHAR')
LOOP
RESULTS.EXTEND;
RESULTS(V_COUNTER).COL_NAME := J.COLUMN_NAME;
EXECUTE IMMEDIATE 'SELECT MIN(LENGTH('||J.COLUMN_NAME||')),
MAX(LENGTH('||J.COLUMN_NAME||'))
FROM '||'SYSTEM'||'.'||'SPECCHAR' INTO
RESULTS(V_COUNTER).MIN_LENGTH,
RESULTS(V_COUNTER).MAX_LENGTH;
V_COUNTER := V_COUNTER + 1;
END LOOP;
FOR I IN RESULTS.FIRST .. RESULTS.LAST LOOP
IF I = RESULTS.LAST THEN
DBMS_OUTPUT.PUT_LINE(RESULTS(I).COL_NAME);
ELSIF I = RESULTS.FIRST THEN
DBMS_OUTPUT.PUT(' |'||RESULTS(I).COL_NAME||'|');
ELSE
DBMS_OUTPUT.PUT(RESULTS(I).COL_NAME||'|');
END IF ;
END LOOP;
FOR I IN RESULTS.FIRST .. RESULTS.LAST LOOP
IF I = RESULTS.LAST THEN
DBMS_OUTPUT.PUT_LINE(RESULTS(I).MIN_LENGTH);
ELSIF I = RESULTS.FIRST THEN
DBMS_OUTPUT.PUT('MIN_LENGTH|'||RESULTS(I).MIN_LENGTH||'|');
ELSE
DBMS_OUTPUT.PUT(RESULTS(I).MIN_LENGTH||'|');
END IF ;
END LOOP;
FOR I IN RESULTS.FIRST .. RESULTS.LAST LOOP
IF I = RESULTS.LAST THEN
DBMS_OUTPUT.PUT_LINE(RESULTS(I).MAX_LENGTH);
ELSIF I = RESULTS.FIRST THEN
DBMS_OUTPUT.PUT('MAX_LENGTH|'||RESULTS(I).MAX_LENGTH||'|');
ELSE
DBMS_OUTPUT.PUT(RESULTS(I).MAX_LENGTH||'|');
END IF ;
END LOOP;
end;
This uses DBMS_SQL, so it's pretty snarly to read. The main reason I saw to use it was that I could get columnar descriptions of a SQL statement and to a buffer-based, not object-based fetch.
Rather than making calls to DBMS_OUTPUT during the processing, it builds a table of records for output, using associative arrays for simplicity.
It could further be refined to have an array or parsable list of functions to apply to each function, but that seems excess to current requirements. The nature of the code would require editing if new aggregation functions are being added.
Call overview (2c + a + s):
3 loops;
2 loops over column list (c),
1 loop over number of analytic functions (a).
1 SQL statement against table data (s).
OP's call overview (c*s + a + 1):
1 loop, executing a sql statement against table data per column (c*s)
a+1 loops, where a is the number of analytic functions
Test data:
1 select min(length(GP_ID)), max(length(GP_ID)),
2 min(length(GGP_ID)), max(length(GGP_ID)),
3 min(length(OBJECT_NAME)), max(length(OBJECT_NAME))
4* from AMUSCH.GP
SQL> /
MIN(LENGTH(GP_ID)) MAX(LENGTH(GP_ID)) MIN(LENGTH(GGP_ID))
MAX(LENGTH(GGP_ID)) MIN(LENGTH(OBJECT_NAME)) MAX(LENGTH(OBJECT_NAME))
1 7 1
4 9 41
Code:
declare
p_owner varchar2(30);
p_table_name varchar2(30);
TYPE OUTPUT_TAB_TYPE IS TABLE OF VARCHAR2(32767) index by binary_integer;
OUTPUT_TAB OUTPUT_TAB_TYPE;
l_columns_tab dbms_sql.desc_tab;
l_columns_cur integer;
l_columns_sql varchar2(32767);
l_columns_cnt number;
l_minmax_sql varchar2(32767);
l_minmax_cur integer;
l_minmax_tab dbms_sql.desc_tab;
l_minmax_cnt number;
l_fetch_ok number;
l_fetch_value number;
begin
p_owner := 'AMUSCH';
p_table_name := 'GP';
output_tab(1) := lpad(' ', 20, ' ');
output_tab(2) := lpad('MIN_LENGTH', 20, ' ');
output_tab(3) := lpad('MAX_LENGTH', 20, ' ');
l_columns_sql := 'select * from ' || p_owner || '.' || p_table_name ||
' where 1 = 0';
l_columns_cur := dbms_sql.open_cursor;
dbms_sql.parse (l_columns_cur, l_columns_sql, dbms_sql.native);
dbms_sql.describe_columns (l_columns_cur, l_columns_cnt, l_columns_tab);
-- build the min/max sql statement
l_minmax_sql := 'select ' ;
for i in 1..l_columns_cnt
loop
l_minmax_sql := l_minmax_sql ||
' min(length(' || l_columns_tab(i).col_name || ')), ';
l_minmax_sql := l_minmax_sql ||
' max(length(' || l_columns_tab(i).col_name || ')), ';
end loop;
l_minmax_sql := substr(l_minmax_sql, 1,
length(l_minmax_sql) - 2); -- trim trailing comma
l_minmax_sql := l_minmax_sql || ' from ' || p_owner || '.' || p_table_name;
l_minmax_cur := dbms_sql.open_cursor;
dbms_sql.parse (l_minmax_cur, l_minmax_sql, dbms_sql.native);
dbms_sql.describe_columns (l_minmax_cur, l_minmax_cnt, l_minmax_tab);
for i in 1..l_minmax_cnt
loop
dbms_sql.define_column(l_minmax_cur, i, l_fetch_value);
end loop;
l_fetch_ok := dbms_sql.execute(l_minmax_cur);
loop
l_fetch_ok := dbms_sql.fetch_rows(l_minmax_cur);
exit when l_fetch_ok = 0;
-- loop over the columns selected over
for i in 1..l_columns_cnt
loop
output_tab(1) := output_tab(1) || '|' || l_columns_tab(i).col_name;
dbms_sql.column_value(l_minmax_cur, (2*i-1), l_fetch_value);
output_tab(2) := output_tab(2) || '|' ||
lpad(l_fetch_value, length(l_columns_tab(i).col_name), ' ');
dbms_sql.column_value(l_minmax_cur, (2*i), l_fetch_value);
output_tab(3) := output_tab(3) || '|' ||
lpad(l_fetch_value, length(l_columns_tab(i).col_name), ' ');
end loop;
end loop;
if dbms_sql.is_open(l_minmax_cur) then
dbms_sql.close_cursor (l_minmax_cur);
end if;
if dbms_sql.is_open (l_columns_cur) then
dbms_sql.close_cursor (l_columns_cur);
end if;
for i in output_tab.first..output_tab.last
loop
dbms_output.put_line(output_tab(i));
end loop;
end;
/
Results:
|GP_ID|GGP_ID|OBJECT_NAME
MIN_LENGTH| 1| 1| 9
MAX_LENGTH| 7| 4| 41
If you want to use the DBMS_SQL package (which is sometimes very complex), then there is a DBMS_SQL.COLUMN_VALUE function that may work for you.
update:
Or even better: DBMS_SQL.DESC_REC
you can refer to:
http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_sql.htm#i996963
notice example 8
I haven't tested it
update:
Perhaps what you really want is to loop on an Object type attributes and not a table column, so maybe you should try a different approach:
Make your type R_RESULT_REC an Object type in the DB and then you can loop on the query results:
SELECT attr_name
FROM user_type_attrs
WHERE type_name = 'R_RESULT_REC'
It's not like working with indexes but you still don't need to hard code the column names / type attributes
here is the code (based on yours):
CREATE OR REPLACE TYPE R_RESULT_REC AS OBJECT
(
COL_NAME VARCHAR2(100),
MIN_LENGTH NUMBER,
MAX_LENGTH NUMBER
);
/
and then:
DECLARE
TYPE tr_RESULT IS TABLE OF R_RESULT_REC;
RESULTS TR_RESULT := TR_RESULT();
v_counter NUMBER := 1;
v_max number;
v_min number;
BEGIN
FOR J IN (SELECT DISTINCT COLUMN_NAME
FROM ALL_TAB_COLUMNS
WHERE OWNER = 'SYSTEM'
and TABLE_NAME = 'SPECCHAR') LOOP
EXECUTE IMMEDIATE 'SELECT MIN(LENGTH(' || J.COLUMN_NAME || ')),
MAX(LENGTH(' || J.COLUMN_NAME || ')) FROM ' ||
'SPECCHAR'
INTO v_min, v_max;
RESULTS.EXTEND;
RESULTS(V_COUNTER) := new R_RESULT_REC(J.COLUMN_NAME, v_min, v_max);
V_COUNTER := V_COUNTER + 1;
END LOOP;
for r in (select attr_name
from all_type_attrs t
where t.owner = 'SYSTEM'
and t.type_name = 'R_RESULT_REC') loop
FOR I IN RESULTS.FIRST .. RESULTS.LAST LOOP
IF I = RESULTS.LAST THEN
execute immediate 'declare rec R_RESULT_REC := :0; begin' ||
' DBMS_OUTPUT.PUT_LINE(rec.' || r.attr_name || ');' ||
'end;'
using RESULTS(I);
ELSIF I = RESULTS.FIRST THEN
execute immediate 'declare rec R_RESULT_REC := :0; begin' ||
' DBMS_OUTPUT.PUT(''' || r.attr_name ||
'|'' || rec.' || r.attr_name || ' || ''|'');' ||
'end;'
using RESULTS(I);
ELSE
execute immediate 'declare rec R_RESULT_REC := :0; begin' ||
' DBMS_OUTPUT.PUT(rec.' || r.attr_name ||
' || ''|''); ' || 'end;'
using RESULTS(I);
END IF;
END LOOP;
end loop;
end;
If you'll add another attribute to the Record (and initiate it with values) , it will automatic display it.
Take advantage of Oracle's stats for this.
First, fully build stats on table using dbms_stats.gather_table_stats
Then, create the following function to help translate the raw low/high values that Oracle stores in all_tab_columns
create or replace function show_raw(i_raw raw, i_type varchar2)
return varchar2 is
l_varchar2 varchar2(32);
l_number number;
l_date date;
l_nvarchar2 nvarchar2(32);
l_rowid rowid;
l_char char;
begin
if (i_type = 'VARCHAR2') then
DBMS_STATS.CONVERT_RAW_VALUE(i_raw, l_varchar2);
return to_char(l_varchar2);
elsif(i_type = 'NUMBER') then
DBMS_STATS.CONVERT_RAW_VALUE(i_raw, l_number);
return to_char(l_number);
elsif(i_type = 'DATE') then
DBMS_STATS.CONVERT_RAW_VALUE(i_raw, l_date);
return to_char(l_date);
elsif(i_type = 'NVARCHAR2') then
DBMS_STATS.CONVERT_RAW_VALUE(i_raw, l_nvarchar2);
return to_char(l_nvarchar2);
elsif(i_type = 'ROWID') then
DBMS_STATS.CONVERT_RAW_VALUE(i_raw, l_rowid);
return to_char(l_rowid);
elsif(i_type = 'CHAR') then
DBMS_STATS.CONVERT_RAW_VALUE(i_raw, l_char);
return l_char;
else return 'Unknown type value';
end if;
end;
Then, just select the low/high values for each column:
select column_id,
column_name,
data_type,
show_raw(low_value, data_type) as min_val,
show_raw(high_value, data_type) as max_val
from all_tab_columns
where table_name = 'SOME_TABLE'
and owner = 'SOME_OWNER'
;

pl sql and dynamic sql

I am trying to create some dynamic sql using the following code block
firstSqlStatement := true;
updateText := 'UPDATE T_EMPLOYEES SET ';
if FIRSTNAME IS NOT NULL and FIRSTNAME > 0 THEN
updateText:=updateText || ' firstName=' || FIRSTNAME || ' ';
firstSqlStatement := false;
end if;
if MIDDLENAME IS NOT NULL and length(MIDDLENAME) > 0 THEN
if firstSqlStatement = false THEN
updateText:=updateText || ',';
end if;
updateText:=updateText || ' middleName=' || MIDDLENAME || ' ';
firstSqlStatement := false;
end if;
updateText:=updateText
|| ' where upper(id)=upper(' || ID ||');';
DBMS_OUTPUT.put_line(updateText);
EXECUTE IMMEDIATE updateText;
The statement never executes properly as there are missing single quotes around values.
Any ideas what i can do to make this small example work or is there any better way of doing this?
firstSqlStatement := true;
updateText := 'UPDATE T_EMPLOYEES SET ';
if FIRSTNAME IS NOT NULL and FIRSTNAME > 0 THEN
updateText:=updateText || ' firstName=''' || FIRSTNAME || ''' ';
firstSqlStatement := false;
end if;
if MIDDLENAME IS NOT NULL and length(MIDDLENAME) > 0 THEN
if firstSqlStatement = false THEN
updateText:=updateText || ',';
end if;
updateText:=updateText || ' middleName=''' || MIDDLENAME || ''' ';
firstSqlStatement := false;
end if;
updateText:=updateText || ' where upper(id)=upper(' || ID || ');';
DBMS_OUTPUT.put_line(updateText);
EXECUTE IMMEDIATE updateText;
use '''
Maybe you can do it this way.
declare
ll_employee_id number := 10;
lv_firstname varchar2(30) := 'Thomas';
lv_middlename varchar2(30) := null;
begin
update t_employees
set firstname = decode(lv_firstname, null, firstname, lv_firstname),
middlename = decode(lv_middlename, null, middlename, lv_middlename)
where employee_id = ll_employee_id;
end;
DECLARE
my_error exception;
sql_stmt VARCHAR2 (500);
v_char_field VARCHAR2 (500);
v_number_field NUMBER;
v_stmt_number NUMBER;
BEGIN
sql_stmt := 'UPDATE TABLE';
IF 1 = 1 -- REPLACE WITH CONDITION FOR SELECTION FIELD
THEN
sql_stmt := sql_stmt || 'field_1 = :1';
v_stmt_number := 1;
ELSIF 1 = 1 -- REPLACE WITH CONDITION FOR SELECTION FIELD
THEN
sql_stmt := sql_stmt || 'field_2 = :1';
v_stmt_number := 2;
ELSE
DBMS_OUTPUT.put_line ('Field unmanaged');
RAISE my_error;
END IF;
IF 1 = 1 -- REPLACE WITH CONDITION FOR SELECTION TYPE FIELD
THEN
EXECUTE IMMEDIATE sql_stmt USING v_char_field;
ELSIF 1 = 1 -- REPLACE WITH CONDITION FOR SELECTION TYPE FIELD
THEN
EXECUTE IMMEDIATE sql_stmt USING v_number_field;
ELSE
DBMS_OUTPUT.put_line ('Type Field unmanaged');
RAISE my_error;
END IF;
DBMS_OUTPUT.PUT_LINE ('STATEMENT NUMBER : ' || v_stmt_number);
DBMS_OUTPUT.PUT_LINE ('TOTAL RECORD UPDATE : ' || SQL%ROWCOUNT);
EXCEPTION
WHEN my_error
THEN
NULL;
WHEN OTHERS
THEN
DBMS_OUTPUT.PUT_LINE ('ERROR :' || SQLERRM);
END;
You can use multiple selections to compose your statement for selection fields to be updated and for the type.