Oracle converting pl/sql function to sql - sql

I need to convert this pl/sql to non pl/sql as we do not have access to functions and packages in oracle fusion, but currently I have no knowledge of pl/sql so I have no idea what this function does, can anyone help ?
FUNCTION numb(CNUMBER IN NUMBER) RETURN VARCHAR2 IS
TR NUMBER;
FR NUMBER;
fakat VARCHAR2(20) := 'فقط';
l_vc_ar_num VARCHAR2(32766);
BEGIN
IF cnumber > 999999999999.99 THEN
RETURN 'Please enter a number less than 1 Trillion (1,000,000,000,000.00).';
END IF;
TR := TRUNC(CNUMBER, 0);
FR := (ROUND(CNUMBER, 2) - TRUNC(CNUMBER, 0)) * 100;
--if fractional part exists then do not append fakat
--EXCEPTION IS 1 halala where we have to append fakat
IF FR > 0 THEN
IF TR = 0 THEN
IF FR = 1 THEN
l_vc_ar_num := fraction(FR) || ' ' || fakat;
ELSE
l_vc_ar_num := fraction(FR);
END IF;
ELSE
l_vc_ar_num := anumb(TR) || ' و' || fraction(FR);
END IF;
ELSE
l_vc_ar_num := anumb(TR) || ' ' || fakat;
END IF;
RETURN REPLACE(REPLACE(l_vc_ar_num, ' ', ' '), ' ', ' ');
EXCEPTION
WHEN OTHERS THEN
RETURN 'function numb. Others. Ex.' || SQLERRM;
END;

You may ask for grant execute on that function if it is in the schema of a different user than that with which your application connects to the database.
Were it something rather simple, the logic might have been implemented using a case statement. Yet, in that function I can see calls to fraction and anumb, which I suppose are some other functions, which I cannot imagine what they are doing.

WITH tmp AS (
SELECT
CNUMBER
, TRUNC(CNUMBER, 0) TR
, (ROUND(CNUMBER, 2) - TRUNC(CNUMBER, 0)) * 100 FR
, 'فقط' fakat
FROM
(
SELECT
123332 CNUMBER
FROM dual
)
)
SELECT
CASE
WHEN CNUMBER > 999999999999.99 THEN
'Please enter a number less than 1 Trillion (1,000,000,000,000.00).'
ELSE
REPLACE(
REPLACE(
CASE WHEN FR > 0 THEN
CASE WHEN TR = 0 THEN
CASE WHEN FR = 1 THEN
fraction(FR) || ' ' || fakat
ELSE
fraction(FR)
END
ELSE
anumb(TR) || ' و' || fraction(FR)
END
ELSE
anumb(TR) || ' ' || fakat
END
, ' ', ' '
)
, ' ', ' '
)
END numb
FROM tmp;

Related

How can i turn this pl/sql into a procedure

I had to write this query for an assignement. So we have a database and we are pulling information from it, this is going to work with some back end c# eventually. Is there anything i can do , knowing im going to reuse this, in order to make it better and more adaptable when the day comes when i have to connect it all.
set serveroutput on
DECLARE
LV_DATE HVK_RESERVATION.RESERVATION_START_DATE%TYPE;
LV_SERV VARCHAR(100);
CURSOR LCUR_RES IS
SELECT *
FROM HVK_RESERVATION R
INNER JOIN HVK_PET_RESERVATION PR
ON R.RESERVATION_NUMBER = PR.RES_RESERVATION_NUMBER
INNER JOIN HVK_PET P
ON P.PET_NUMBER = PR.PET_PET_NUMBER
INNER JOIN HVK_OWNER OW
ON OW.OWNER_NUMBER = P.OWN_OWNER_NUMBER
WHERE R.RESERVATION_START_DATE < LV_DATE
AND R.RESERVATION_END_DATE > LV_DATE;
CURSOR LCUR_SERVICE(PET_RES_NUM NUMBER) IS
SELECT *
FROM HVK_SERVICE S
INNER JOIN HVK_PET_RESERVATION_SERVICE PRS
ON PRS.SERV_SERVICE_NUMBER = S.SERVICE_NUMBER
AND PRS.PR_PET_RES_NUMBER = PET_RES_NUM;
BEGIN
LV_DATE := TO_DATE('&logdate', 'yy-mm-dd');
DBMS_OUTPUT.PUT_LINE('Kennel log for ' || '' || LV_DATE);
DBMS_OUTPUT.PUT_LINE('-------------------------------');
FOR I IN LCUR_RES LOOP
DBMS_OUTPUT.PUT_LINE('Run:' || '' || I.RUN_RUN_NUMBER || ' ' ||
'Pet: ' || '' || I.PET_NAME || ' ' ||
I.OWNER_LAST_NAME || ' Pet Reservation: ' || '' ||
I.PET_RES_NUMBER);
DBMS_OUTPUT.PUT_LINE('Reservation start/end ' || ' ' ||
I.RESERVATION_START_DATE || ' ' ||
I.RESERVATION_END_DATE);
DBMS_OUTPUT.PUT('Services : ');
FOR X IN LCUR_SERVICE(I.PET_RES_NUMBER) LOOP
DBMS_OUTPUT.PUT(X.SERVICE_DESCRIPTION || ' ');
END LOOP;
DBMS_OUTPUT.PUT_LINE('');
FOR LREC_LOG IN (SELECT *
FROM HVK_KENNEL_LOG KL
WHERE KL.PR_PET_RES_NUMBER = I.PET_RES_NUMBER
) LOOP
DBMS_OUTPUT.PUT_LINE('Notes: ' || '' ||
LREC_LOG.KENNEL_LOG_SEQUENCE_NUMBER || ' ' ||
'Log Note: ' || '' || LREC_LOG.KENNEL_LOG_NOTES);
END LOOP;
DBMS_OUTPUT.PUT_LINE(' ');
END LOOP;
END;
It it supposed to output the run number , reservation number , pet name , and any relate notes.
you can replace DECLARE with CREATE OR REPLACE PROCEDURE my_proc(in_logdate in date) IS.
in that case my_proc will be the name of your procedure.
you should also use a parameter instead of &logdate
so e.g. parameter name in_logdate of type date
...
LV_DATE := in_logdate;
...

ORA-01722: invalid number in execute immediate

I am running the following script in SQL Developer but getting the below error -
ORA-01722: invalid number
ORA-06512: at line 85
The data passed to this query is coming from a different table.
DATA BEING PASSED -
BAN - 945170744, ACTV_BILL_SEQ_NO- 12 , SUBSCRIBER_NO - 1234567891, DISCOUNT_CD - NULL, PRIOD_CVRG_ND_DATE - 04-JAN-20,
Note - Ban is defined as NUMBER(9) in both the tables.
SCRIPT -
SET SERVEROUTPUT ON
DECLARE
V_HIERARCHY_ID NUMBER(10);
V_NODE_CYCLE NUMBER(2);
V_BILL_CYCLE NUMBER(2);
V_DISCOUNT_CD CHAR(9);
V_EXP_DATE DATE;
V_COUNT NUMBER(3);
v_val_Done_by varchar2(25) ;
v_ban NUMBER(9);
v_comments varchar2(20);
v_success varchar2(10) :='SUCCESS';
v_yesnoind varchar2(1) :='Y';
v_comments_1 varchar2(30) :='DISCOUNT EXPIRED';
v_comments_2 varchar2(30) :='DISCOUNT NOT FOUND';
v_issue_Desc varchar2(50) :='NEW RECORD MISSING';
BEGIN
DBMS_OUTPUT.put_line ('BAN: '|| :1 || ' ACTV_BILL_SEQ_NO : ' || :2 || ' SUBSCRIBER: '|| :3 ||
'COLUMN_NAME : ' || :4|| ' COLUMN_DATA: '|| :5|| 'DIFF_DATA : ' || :6 || ' SOC: ' ||:7|| '
FEATURE_CODE: ' ||:8|| ' FTR_REVENUE_CODE: ' ||:9|| ' PRIOD_CVRG_ST_DATE: ' ||:10|| '
PRIOD_CVRG_ND_DATE: ' ||:11|| ' ACTV_REASON_CODE: ' ||:12|| 'BALANCE_IMPACT_CODE: '|| :13 || '
SOURCE_APPL_CODE : ' || :14 || ' DISCOUNT_CD: '|| :15 || ' BILL_MEDIA : ' || :16|| 'BILL_FORMAT : '
|| :17 || ' PRODUCT_TYPE: ' ||:18|| ' FTR_TYPE ' ||:19 || ' VAL_ID ' ||:20 );
v_ban :=:1;
V_DISCOUNT_CD :=:15;
V_EXP_DATE :=:11;
dbms_output.put_line(V_DISCOUNT_CD);
dbms_output.put_line(V_EXP_DATE);
select 'comments_'|| :20 into v_comments from dual;
select 'val_done_by_'|| :20 into v_val_Done_by from dual;
DBMS_OUTPUT.Put_line(v_val_Done_by);
DBMS_OUTPUT.Put_line(v_comments);
SELECT HIERARCHY_ID INTO V_HIERARCHY_ID FROM BILLING_ACCOUNT WHERE BAN =:1;
IF V_HIERARCHY_ID IS NULL
THEN
SELECT COUNT(*) INTO V_COUNT FROM BAN_DISCOUNT WHERE BAN =:1 AND TRIM(DISCOUNT_CODE) =
V_DISCOUNT_CD AND EXPIRATION_DATE < V_EXP_DATE;
IF V_COUNT >= 1
THEN
EXECUTE IMMEDIATE 'UPDATE BL_DIFF_CATEGORY SET VALIDATION_STS=:2,' || v_val_Done_by || ' = :3, '
|| v_comments || ' = :4 WHERE BAN = :1 AND DIFF_TYPE = :5'
using v_issue_Desc,v_comments_1,v_yesnoind,v_success,v_ban;
COMMIT;
ELSE
EXECUTE IMMEDIATE 'UPDATE BL_DIFF_CATEGORY SET VALIDATION_STS=:2,' || v_val_Done_by || ' = :3, '
|| v_comments || ' = :4 WHERE BAN = :1 AND DIFF_TYPE = :5'
using v_issue_Desc,v_comments_2,v_yesnoind,v_success,v_ban;
COMMIT;
END IF;
ELSE
-- NODE LEVEL DISCOUNT CHECK
SELECT COUNT(*) INTO V_COUNT FROM CH_OBJECT_ATTRIBUTES
WHERE
OBJ_ID IN ( SELECT ARC_FATHER_ID FROM CH_ARCS WHERE ARC_CHILD_ID = V_HIERARCHY_ID)
AND ATTR_NAME = 'Discount Plans'
AND ATTR_VALUE = V_DISCOUNT_CD
AND VALID_TO < V_EXP_DATE;
-- BAN LEVEL DISCOUNT CHECK
SELECT COUNT(*) INTO V_COUNT FROM CH_OBJECT_ATTRIBUTES
WHERE
OBJ_ID = V_HIERARCHY_ID
AND ATTR_NAME = 'Discount Plans'
AND ATTR_VALUE = V_DISCOUNT_CD
AND VALID_TO < V_EXP_DATE;
-- SUBSCRIBER LEVEL DISCOUNT CHECK
SELECT COUNT(*) INTO V_COUNT FROM CH_OBJECT_ATTRIBUTES
WHERE
OBJ_ID IN ( SELECT ARC_CHILD_ID FROM CH_ARCS WHERE ARC_FATHER_ID = V_HIERARCHY_ID)
AND ATTR_NAME = 'Discount Plans'
AND ATTR_VALUE = V_DISCOUNT_CD
AND VALID_TO < V_EXP_DATE;
IF V_COUNT >= 1
THEN
EXECUTE IMMEDIATE 'UPDATE BL_DIFF_CATEGORY SET VALIDATION_STS=:2,' || v_val_Done_by || ' = :3, '
|| v_comments || ' = :4 WHERE BAN = :1 AND DIFF_TYPE = :5'
using v_issue_Desc,v_comments_1,v_yesnoind,v_success,v_ban;
COMMIT;
ELSE
(I am facing the above error in this ELSE part in below line as it is going in this line 85)
EXECUTE IMMEDIATE ''UPDATE BL_DIFF_CATEGORY SET VALIDATION_STS=:2,'' || v_VAL_DONE_BY || '' =
:3, '' || v_comments || '' = :4 WHERE BAN = :1 AND DIFF_TYPE = :5''
using v_issue_Desc,v_comments_2,v_yesnoind,v_success,v_ban;
COMMIT;
END IF;
END IF;
END;
(I am facing the above error in this ELSE part in below line as it is going in this line 85)
EXECUTE IMMEDIATE ''UPDATE BL_DIFF_CATEGORY SET VALIDATION_STS=:2,'' || v_VAL_DONE_BY || '' = :3, '' || v_comments || '' = :4 WHERE BAN = :1 AND DIFF_TYPE = :5''
using v_issue_Desc,v_comments_2,v_yesnoind,v_success,v_ban;
There seems to be some discrepancy between orders of the placeholders in the string and the parameters in the USING clause. For instance BAN = :1 is the fourth placeholder but v_ban is the fifth variable passed to the EXECUTE IMMEDIATE statement, whilst the value actually assigned to BAN is v_success.
Perhaps you simply need to review the placeholders and re-order the parameters? If so, remember to do the same for the statement in the IF branch.
Incidentally, the names of the placeholders in the string don't matter, but if you're going to use numbers you might as well put them in numeric order.
DISCOUNT_CD - NULL <== Null is not a valid number -- you need a coalesce here
eg
SELECT COUNT(*) INTO V_COUNT FROM CH_OBJECT_ATTRIBUTES
WHERE
OBJ_ID IN ( SELECT ARC_CHILD_ID FROM CH_ARCS WHERE ARC_FATHER_ID = V_HIERARCHY_ID)
AND ATTR_NAME = 'Discount Plans'
AND ATTR_VALUE = COALESCE(V_DISCOUNT_CD,0)
AND VALID_TO < V_EXP_DATE;

how to += in Postgres 9.3

i am having a trouble to reassign a value, my teacher told me to use something like
var = var + (rest of the query)
but it aint working at all, here it is my code:
create or replace function recorrertablas()
returns void as
$BODY$
declare cursorx cursor for select id, nombre as nombres from tablas;
declare cursory refcursor;
declare rec record;
declare rec2 record;
declare consulta varchar;
--declare consulta2 varchar;
begin
open cursorx;
loop
fetch cursorx into rec;
exit when not found;
consulta= 'create table ' || rec.nombres;
--consulta2= '';
open cursory for select * from atributos where idtabla = rec.id;
loop
fetch cursory into rec2;
exit when not found;
consulta = consulta + '(' || rec2.id || ', ' || rec2.idtabla || ', ' || rec2.nombre || ', ' ||rec2.tipodedatos ;
if rec2.claveprimaria = 1 then
consulta= consulta +', constraint pk_ ' || rec.nombres || '( ' || rec.id || '));';
else
consulta = consulta +');';
end if;
end loop;
close cursory;
end loop;
execute consulta;
close cursorx;
end;
$BODY$
language plpgsql volatile;
If you need to concatenate things use || operator or concat() function like below.
You've been using this operator, but for some reason you mixed it with +.
SELECT 'string' || 'part2';
or
SELECT concat('string', 'part2');
For your specific case:
consulta = consulta || '(' || rec2.id || ', ' || rec2.idtabla || ', ' || rec2.nombre || ', ' ||rec2.tipodedatos ;
if rec2.claveprimaria = 1 then
consulta= consulta || ', constraint pk_ ' || rec.nombres || '( ' || rec.id || '));';
else
consulta = consulta || ');';
When using || operator if any value evaluates to NULL the whole output will be NULL, but when using concat() function it will omit NULL values and still concatenate other arguments.

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'
;