Relation "" already exist dynamic query - sql

I'm trying to execute a query within a loop which is within another loop, which is within an anonymous code block.
I'm trying to run this query
query := 'CREATE VIEW hopsPartialDistance AS '
||'SELECT AvgDistance '
||'FROM Distance '
||'WHERE PlanetOrigin = ' || rHops.PlanetOrigin
||' AND PlanetDestination = ' || rHops.PlanetDestination;
EXECUTE query;
But I keep getting the error
relation "hopspartialdistance" already exists
I then run
\d hopspartialdistance;
And the output is
Did not find any relation named "hopspartialdistance".
So I don't know where the error is coming from.
Here's the full function
DO
$$
DECLARE
routeDistance real := 0.0;
hopDistance real := 0.0;
rRoute record;
rHops record;
query text := '';
BEGIN
FOR rRoute IN
SELECT MonitoringKey FROM TradingRoute
LOOP
query := 'CREATE VIEW PortsOfCall AS '
||'SELECT PlanetID, VisitOrder '
||'FROM EnrichedCallsAt '
||'WHERE MonitoringKey = ' || rRoute.MonitoringKey
||' ORDER BY VisitOrder';
EXECUTE query;
CREATE VIEW Hops AS
SELECT A.PlanetID AS PlanetOrigin,
B.PLanetID AS PlanetDestination
FROM PortsOfCall A INNER JOIN PortsOfCall B ON A.VisitOrder + 1 = B.VisitOrder;
routeDistance = 0.0;
FOR rHops IN
SELECT PlanetOrigin, PlanetDestination FROM Hops
LOOP
query := 'CREATE VIEW hopsPartialDistance AS '
||'SELECT AvgDistance '
||'FROM Distance '
||'WHERE PlanetOrigin = ' || rHops.PlanetOrigin
||' AND PlanetDestination = ' || rHops.PlanetDestination;
EXECUTE query;
hopDistance = (SELECT SUM(AvgDistance) FROM hopsPartialDistance) ;
routeDistance = routeDistance + hopDistance;
END LOOP;
INSERT INTO RouteLength (RouteMonitoringKey, RouteTotalDistance)
SELECT rRoute.MonitoringKey, routeDistance;
DROP VIEW Hops
CASCADE;
DROP VIEW PortsOfCall
CASCADE;
END LOOP;
END;
$$;

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

Postgres Type of Parameter does not match

I am having an odd problem with Postgres (10.5). I have a function, generate_unique_name which takes in three text values. It works fine; however, calling this function seems to be an issue. When I call the function using:
SELECT generate_unique_name('basic', 'seeds', 'project=' || 2)
It works without issue. I can make the same call several times. Now, when I try the same call, but change the second parameter as below:
SELECT generate_unique_name('basic', 'queue', 'project=' || 2)
Then it seems to fail with the error:
ERROR: type of parameter 9 (text) does not match that when preparing
the plan (character varying) CONTEXT: PL/pgSQL function
generate_unique_name(text,text,text) line 12 at assignment SQL state:
42804
I have tried changing the query to:
SELECT generate_unique_name('basic'::text, 'queue'::text, ('project=' || 2)::text)
But this also fails. If I then kill the connection to postgres DB, and create a new one, and instead start with the second query, it now works, but the first stops functioning.
It seems like postgres decides to stop treating the parameters as text part way through, for no apparent reason. Am I missing something?
EDIT: Code for generate_unique_name
CREATE OR REPLACE FUNCTION public.generate_unique_name(
proposed_name text,
table_name text,
condition text)
RETURNS text
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
DECLARE
unique_name text;
name_counter integer;
r record;
names_to_check text[];
BEGIN
unique_name = proposed_name;
name_counter = 0;
FOR r IN EXECUTE 'SELECT name FROM ' || table_name || ' WHERE ' || condition LOOP
names_to_check = array_append(names_to_check, r.name::text);
END LOOP;
WHILE unique_name = ANY(names_to_check) LOOP
name_counter = name_counter + 1;
unique_name = proposed_name || ' (' || name_counter || ')';
END LOOP;
RETURN unique_name;
END;
$BODY$;
My guess is there's a value in the name column of the queue table that causes an issue with
names_to_check = array_append(names_to_check, r.name::text)
As Joe mentioned, the issue was with the array_append, which I could not figure out a way to fix. Instead, the generate_unique_names functions was changed to just query the DB continuously.
CREATE OR REPLACE FUNCTION generate_unique_name (proposed_name text, table_name text, condition text) RETURNS text AS
$BODY$
DECLARE
unique_name text;
name_counter integer;
not_unique boolean;
BEGIN
unique_name = proposed_name;
name_counter = 0;
EXECUTE 'SELECT COUNT(*)!=0 FROM ' || table_name || ' WHERE ' || condition || ' AND name = ''' || unique_name || '''' INTO not_unique;
WHILE not_unique LOOP
name_counter = name_counter + 1;
unique_name = proposed_name || ' (' || name_counter || ')';
EXECUTE 'SELECT COUNT(*)!=0 FROM ' || table_name || ' WHERE ' || condition || ' AND name = ''' || unique_name || '''' INTO not_unique;
END LOOP;
RETURN unique_name;
END;
$BODY$ LANGUAGE plpgsql;

How to validate and print message when there is no data? PL SQL

Using PL/SQL I'm trying to validate and print a message when there's no data found on a cursor. My main problem is that if I use the %NOTFOUND it gets printed multiple times or along when data is found.
Here is my code:
set SERVEROUTPUT ON
set verify off
DECLARE
rut int;
CURSOR Ejercicio2 (rut int)
IS
SELECT alumno.nombre,alumno.apellidoP,alumno.apellidoM,Ramo.descripcion,profesor.nombre
FROM alumno
INNER JOIN alumnoramo ON alumnoramo.cod_matricula = alumno.cod_matricula
INNER JOIN Ramo ON ramo.cod_ramo = alumnoramo.cod_ramo
INNER JOIN profesor ON profesor.cod_prof = ramo.cod_prof
WHERE alumno.cod_matricula = rut;
alumno_nombre VARCHAR2(45);
alumno_apellido VARCHAR2(45);
alumno_apellidoM VARCHAR2(45);
ramo_nombre VARCHAR2(45);
profesor_nombre VARCHAR2(45);
BEGIN
rut := '&rut';
OPEN Ejercicio2 (rut);
LOOP
FETCH Ejercicio2 INTO alumno_nombre,alumno_apellido,alumno_apellidoM,ramo_nombre,profesor_nombre;
EXIT WHEN Ejercicio2%NOTFOUND;
dbms_output.put_line('Nombre: ' || alumno_nombre);
dbms_output.put_line('Apellido: ' || alumno_apellido);
dbms_output.put_line('Apellido Materno: ' || alumno_apellidoM);
dbms_output.put_line('Ramo: ' || ramo_nombre);
dbms_output.put_line('Profesor: ' || profesor_nombre);
END LOOP;
CLOSE Ejercicio2;
END;
When the user inputs a "rut" it should normally print the found data and end. If the input "rut" and no data is returned, then it should print a message and ending the program. I haven't being able to do so.
use the below code block .let me know if you get some error.I just wrote it on notepad and it should as I expect.
set SERVEROUTPUT ON
set verify off
DECLARE
rut int;
CURSOR Ejercicio2 (rut int)
IS
SELECT alumno.nombre,alumno.apellidoP,alumno.apellidoM,Ramo.descripcion,profesor.nombre
FROM alumno
INNER JOIN alumnoramo ON alumnoramo.cod_matricula = alumno.cod_matricula
INNER JOIN Ramo ON ramo.cod_ramo = alumnoramo.cod_ramo
INNER JOIN profesor ON profesor.cod_prof = ramo.cod_prof
WHERE alumno.cod_matricula = rut;
TYPE Ejercicio2_typ IS TABLE OF Ejercicio2%ROWTYPE;
Ejercicio2_tbl Ejercicio2_typ;
BEGIN
rut := '&rut';
OPEN Ejercicio2 (rut);
FETCH Ejercicio2 BULK COLLECT INTO Ejercicio2_tbl;
IF Ejercicio2_tbl.count >0 THEN
FOR rec IN Ejercicio2_tbl.first..Ejercicio2_tbl.last LOOP
dbms_output.put_line('Nombre: ' || Ejercicio2_tbl(rec).alumno_nombre);
dbms_output.put_line('Apellido: ' || Ejercicio2_tbl(rec).alumno_apellido);
dbms_output.put_line('Apellido Materno: ' || Ejercicio2_tbl(rec).alumno_apellidoM);
dbms_output.put_line('Ramo: ' || Ejercicio2_tbl(rec).ramo_nombre);
dbms_output.put_line('Profesor: ' || Ejercicio2_tbl(rec).profesor_nombre);
END LOOP;
ELSE
dbms_output.put_line('No data found');
END IF;
CLOSE Ejercicio2;
END;
/
Happy coding.
Mark it as an answer if it satisfy your needs.
Just a variation and a clean way of handling the scenario. Hope it helps.
CREATE OR REPLACE TYPE obj IS OBJECT
(
nombre VARCHAR2(100),
apellidoP VARCHAR2(100)
apellidoM VARCHAR2(100)
descripcion VARCHAR2(100)
nombre VARCHAR2(100)
);
CREATE OR REPLACE TYPE tab IS TABLE OF obj;
SET SERVEROUTPUT ON
SET verify OFF
DECLARE
rut INT:=&Enter_rut;
tab1 tab;
BEGIN
SELECT obj(alumno.nombre,alumno.apellidoP,alumno.apellidoM,Ramo.descripcion,profesor.nombre) BULK COLLECT
INTO tab1
FROM alumno
INNER JOIN alumnoramo
ON alumnoramo.cod_matricula = alumno.cod_matricula
INNER JOIN Ramo
ON ramo.cod_ramo = alumnoramo.cod_ramo
INNER JOIN profesor
ON profesor.cod_prof = ramo.cod_prof
WHERE alumno.cod_matricula = rut;
IF tab1.EXISTS(1) THEN
FOR I IN tab1.FIRST..tab1.LAST
LOOP
dbms_output.put_line('Nombre: ' || tab1(i).nombre);
dbms_output.put_line('Apellido: ' || tab1(i).apellidoP);
dbms_output.put_line('Apellido Materno: ' || tab1(i).apellidoM);
dbms_output.put_line('Ramo: ' || tab1(i).descripcion);
dbms_output.put_line('Profesor: ' || tab1(i).nombre);
END LOOP;
ELSE
dbms_output.put_line('no data found for the Input');
END IF;
END;
You could set a Boolean variable in the loop, and then check at the end whether it was true or false:
set define on
accept rut number prompt 'Introduza un identificador de class: '
var rut number
exec :rut := &rut
declare
cursor ejercicio2
( cp_rut int )
is
select alumno.nombre as alumno_nombre
, alumno.apellidop as alumno_apellido
, alumno.apellidom as alumno_apellidom
, ramo.descripcion as ramo_nombre
, profesor.nombre as profesor_nombre
from alumno
join alumnoramo
on alumnoramo.cod_matricula = alumno.cod_matricula
join ramo
on ramo.cod_ramo = alumnoramo.cod_ramo
join profesor
on profesor.cod_prof = ramo.cod_prof
where alumno.cod_matricula = cp_rut;
l_encontro boolean := false;
begin
for r in ejercicio2(:rut)
loop
l_encontro := true;
dbms_output.put_line('Nombre: ' || r.alumno_nombre);
dbms_output.put_line('Apellido: ' || r.alumno_apellido);
dbms_output.put_line('Apellido Materno: ' || r.alumno_apellidom);
dbms_output.put_line('Ramo: ' || r.ramo_nombre);
dbms_output.put_line('Profesor: ' || r.profesor_nombre);
end loop;
if not l_encontro then
dbms_output.put_line('No se encontraron las classes por rut ' || :rut);
end if;
end;
/
(Excuse my Google Spanish.)
Or you could make it a numeric value initialised to 0 and report the number of rows processed.

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