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

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.

Related

Print a json structure from a dynamic query

I want to create function that return a CLOB with a json content.
But first i want to print the result (see query code) to see how it looks like.
The result must be a json, that will contain the information for 1 record just, this record select query, must be created dynamically.
So I try the next query, to resolve this problem. but i get an error.
Query
declare
v_stmt VARCHAR2(1000);
l_sep varchar2(1) := chr(10);
l_tab varchar2(1) := chr(9);
up_column VARCHAR2(50);
low_column VARCHAR2(50);
cur_asc SYS_REFCURSOR;
Vcdogcur SYS_REFCURSOR;
id_ccd number;
first boolean := true;
begin
id_ccd := 174;
v_stmt := 'select ';
open cur_asc for
SELECT column_name as up_column
,lower(column_name) as low_column
FROM USER_TAB_COLUMNS
WHERE table_name = upper('my_table');
loop
FETCH cur_asc
INTO up_column, low_column;
EXIT WHEN cur_asc%NOTFOUND;
if not first then
v_stmt := v_stmt || ',';
end if;
first := false;
v_stmt := v_stmt || l_sep || l_tab ||up_column || ' as ' || low_column;
END LOOP;
--v_stmt := v_stmt || l_sep || 'from my_table' || l_sep ||'where id = ' || id_ccd || ';';
v_stmt := v_stmt || l_sep || 'from my_table' || l_sep ||'where id = ' || id_ccd;
DBMS_OUTPUT.PUT_LINE('salida: ' || v_stmt);
CLOSE cur_asc;
--v_stmt := v_stmt || l_sep || 'from my_table' || l_sep ||'where id = ' || id_ccd;
--DBMS_OUTPUT.PUT_LINE('salida: ' || v_stmt);
open Vcdogcur for v_stmt;
APEX_JSON.initialize_clob_output;
APEX_JSON.open_object;
APEX_JSON.write('my_table', Vcdogcur);
APEX_JSON.close_object;
DBMS_OUTPUT.put_line(APEX_JSON.get_clob_output);
CLOSE Vcdogcur;
end;
/
DBMS output
salida: select
CREATED as created,
CREATED_BY as created_by,
UPDATED as updated,
UPDATED_BY as updated_by,
ID as id,
TEXT as text,
PART as part,
CODE as code,
DATE as date,
PERCENTAGE as percentage
from my_table
where id = 174
{
"my_table":[
{
"CREATED":"2020-06-04T08:10:09.000000000Z"
,"CREATED_BY":"APEX_APP"
,"UPDATED":"2020-06-04T10:10:09.295632000Z"
,"UPDATED_BY":"APEX_APP"
,"ID":174
,"TEXT":"ALL"
,"PART":2770
,"CODE":2212
,"DATE":"2017-01-01T00:00:00.000000000Z"
,"PERCENTAGE":-4.98
}
]
}
Error
Informe de error -
ORA-01001: cursor no válido
ORA-06512: en línea 44
01001. 00000 - "invalid cursor"
*Cause:
*Action:
Can somebody help me to resolve this?
Best Regards

debug oracle procedure

I have a oracle stored procedure, inputs is
p_processId = 5660
p_featureClassName ='future_parcels'
p_DrawnGeometry = MDSYS.SDO_GEOMETRY(2003, 2400000, null,
MDSYS.SDO_ELEM_INFO_ARRAY(1, 1003, 1, 27, 2003, 1),
MDSYS.SDO_ORDINATE_ARRAY(8439212.4589933194220066070556640625,
4540074.1191380098462104797363281250, 8439201.7998965308070182800292968750, 4540058.374383729882538318634033203125, 8439315.814959609881043434143066406250, 4540058.374466760084033012390136718750, 8439319.9868115298449993133544921875, 4540067.6362674199044704437255859375, 8439323.231370599940419197082519531250, 4540081.528450479730963706970214843750, 8439328.328618479892611503601074218750, 4540097.737373660318553447723388671875, 8439330.6458750404417514801025390625, 4540129.691041340120136737823486328125, 8439332.959363190457224845886230468750, 4540149.486472180113196372985839843750, 8439227.984901839867234230041503906250, 4540148.055711519904434680938720703125, 8439225.43722324073314666748046875, 4540138.489620880223810672760009765625, 8439223.1199580393731594085693359375, 4540118.576599569991230964660644531250, 8439220.802692830562591552734375, 4540098.663578259758651256561279296875, 8439212.4589933194220066070556640625, 4540074.1191380098462104797363281250, 8439252.6796298995614051818847656250, 4540086.660427900031208992004394531250, 8439243.2996299006044864654541015625, 4540125.440427900291979312896728515625, 8439299.99962989985942840576171875, 4540138.040427899919450283050537109375, 8439310.49962989985942840576171875, 4540098.280427900142967700958251953125, 8439252.6796298995614051818847656250, 4540086.660427900031208992004394531250))
Procedure:
CREATE OR REPLACE
PROCEDURE get_DrawnGeometries (
p_processId IN INTEGER
, p_featureClassName IN VARCHAR2
, p_DrawnGeometry IN SDO_GEOMETRY
, p_prm_val OUT VARCHAR2
)
IS
PRAGMA AUTONOMOUS_TRANSACTION;
CURSOR featClassAttributes_cur (featureClassName IN VARCHAR2) IS
SELECT
gac.id
, lower(gac.column_name) AS column_name
, gac.view_u_pk
, gac.data_type
, gas.naziv
, gat.table_name
FROM geo_atrib_columns gac
JOIN geo_atrib_tables gat ON gat.table_name = gac.table_name
JOIN geo_app_slojevi gas ON gas.naziv = gat.layer_name
WHERE gas.naziv = featureClassName
AND gat.aktivan = 1
AND gac.aktivan = 1
AND gac.view_iu_write = 1
ORDER BY gac.view_order;
vc_sql CLOB;
vc_sql2 CLOB;
vc_sql3 CLOB;
v_columns VARCHAR2(1000);
v_tableName VARCHAR2(1000);
v_whereClause VARCHAR2(1000);
v_whereClause2 VARCHAR2(1000);
v_g_col_name VARCHAR2(30);
v_tolerance VARCHAR2(20);
v_separator VARCHAR2(16) := ' || ''' || glob_var.v_strSeparator || ''' || ';
p_id INTEGER;
v_mssg VARCHAR2(500) := NULL;
BEGIN
vc_sql := 'SELECT #columns# FROM #table_name# WHERE #where_clause#';
vc_sql2 := 'SELECT #columns# FROM #table_name# WHERE #where_clause2#';
vc_sql3 := vc_sql;
FOR rt_att IN featClassAttributes_cur(p_featureClassName) LOOP
IF rt_att.data_type = 'N' THEN
v_columns := v_columns || v_separator || ' nvl(gt_util.num2CharPointSep(' || rt_att.column_name|| '), ''NULL'')';
ELSE
v_columns := v_columns || v_separator || ' nvl(to_char(' || rt_att.column_name|| '), ''NULL'')';
END IF;
IF v_tableName IS NULL THEN
v_tableName := rt_att.table_name;
END IF;
--dbms_output.put_line(v_tableName);
END LOOP;
v_columns := ltrim(v_columns, v_separator);
v_g_col_name := gt_util.get_layer_g_col(v_tableName);
v_tolerance := gt_util.num2CharPointSep(gt_util.get_layer_tollerance(v_tableName, v_g_col_name));
--v_whereClause := 'ins_id = {0} AND SDO_GEOM.RELATE(:geom, ''EQUAL+INSIDE+CONTAINS+COVEREDBY+COVERS+OVERLAPBDYINTERSECT'', ' || v_g_col_name || ', ' || v_tolerance || ') = ''EQUAL+INSIDE+CONTAINS+COVEREDBY+COVERS+OVERLAPBDYINTERSECT''';
v_whereClause := 'ins_id = {0} AND SDO_GEOM.RELATE(:geom, ''EQUAL'', ' || v_g_col_name || ', ' || v_tolerance || ') = ''EQUAL''';
v_whereClause2 := 'ins_id = {0} AND gt_util.geom_is_equal(:geom, ' || v_g_col_name || ', ' || v_tolerance || ') = ''TRUE''';
v_whereClause := replace(v_whereClause, '{0}', to_char(p_processId));
v_whereClause2 := replace(v_whereClause2, '{0}', to_char(p_processId));
vc_sql := replace(vc_sql, '#columns#', v_columns);
vc_sql := replace(vc_sql, '#table_name#', v_tableName);
vc_sql := replace(vc_sql, '#where_clause#', v_whereClause);
vc_sql2 := replace(vc_sql2, '#columns#', v_columns);
vc_sql2 := replace(vc_sql2, '#table_name#', v_tableName);
vc_sql2 := replace(vc_sql2, '#where_clause2#', v_whereClause2);
dbms_output.put_line(vc_sql);
dbms_output.put_line(vc_sql2);
BEGIN
EXECUTE IMMEDIATE vc_sql INTO p_prm_val USING p_DrawnGeometry;
EXCEPTION WHEN NO_DATA_FOUND THEN
EXECUTE IMMEDIATE vc_sql2 INTO p_prm_val USING p_DrawnGeometry;
WHEN TOO_MANY_ROWS THEN
vc_sql3 := replace(vc_sql3, '#columns#', 'id');
vc_sql3 := replace(vc_sql3, '#table_name#', v_tableName);
vc_sql3 := replace(vc_sql3, '#where_clause#', v_whereClause);
vc_sql3 := 'SELECT * FROM (' || vc_sql3 || ' ORDER BY id DESC) WHERE ROWNUM = 1';
EXECUTE IMMEDIATE vc_sql3 INTO p_id USING p_DrawnGeometry;
EXECUTE IMMEDIATE 'DELETE FROM ' || v_tableName || ' WHERE id = ' || p_id;
COMMIT;
v_mssg := 'Identical geometry already exists. Drawn geometry is deleted. If you want to delete new geometry please delete the old one first';
RAISE error_util.ep_UsrExc;
--dbms_output.put_line(vc_sql2);
END;
EXCEPTION WHEN error_util.ep_UsrExc THEN
ERROR_UTIL.log_and_raise(SQLCODE, SQLERRM, v_mssg, DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
RAISE;
END get_DrawnGeometries;
how I can check result and debug it output ?
when I can call it returned errors
Procedure execution failed ORA-06550: line 1, column 233: PLS-00382:
expression is of wrong type ORA-06550: line 1, column 213: PL/SQL:
Statement ignored
In PL/SQL Developer you can right click on procedure name, click test and start debbuging

Generate change scripts from 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
;

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.