I'm wondering if it possible to debug similar statements in an easy way.
When I save the 'select string' in a variable , it become 'long' and I would need to split it in more variables. I' presenting the very simplified sample:
OPEN o_recordset FOR
'SELECT distinct
a, b, c
FROM t1,t2
WHERE'
|| CASE
WHEN i_use_ctr_id = 1 then ' a = b'
END
|| ' ORDER BY 1 ASC , DECODE('''||i_sort_order||''',null, '''', ''a'', '' NULLS LAST '', ''b'' ,'',2 ASC NULLS LAST'')'
;
I wish to see the select like this (i_use_ctr_id = 1, i_sort_order = a)
SELECT distinct
a, b, c
FROM t1,t2
WHERE a = b
END
ORDER BY 1 ASC , DECODE('a',null, '''', ''a'', '' NULLS LAST '', ''b'' ,'',2 ASC NULLS LAST'')'
;
Use a debug procedure that either writes to a file or inserts in a table (with an autonomous transaction).
For instance:
CREATE TABLE debug_t (ts timestamp default systimestamp, data VARCHAR2(4000));
CREATE OR REPLACE PROCEDURE debug_p (p VARCHAR2) IS
PRAGMA autonomous_transaction;
BEGIN
-- you should split p if length is > 4000
INSERT INTO debug_t (data) VALUES (p);
COMMIT;
END;
/
Then you can debug values by inserting a single line of code:
SQL> DECLARE
2 l_sql VARCHAR2(4000);
3 i_use_ctr_id NUMBER;
4 i_sort_order NUMBER;
5 BEGIN
6 l_sql := 'SELECT distinct
7 a, b, c
8 FROM t1,t2
9
10 WHERE'
11 || CASE
12 WHEN i_use_ctr_id = 1 then ' a = b'
13 END
14 || ' ORDER BY 1 ASC , DECODE('''||i_sort_order
15 ||''',null, '''', ''a'', '' NULLS LAST '', ''b'' ,'',2 ASC NULLS LAST'')'
16 ;
17 debug_p(l_sql); -- debug before opening cursor
18 END;
19 /
PL/SQL procedure successfully completed
SQL> select * from debug_t;
TS DATA
----------------- --------------------------------------------------------------------------------
11/09/13 11:52:30 SELECT distinct
a, b, c
FROM t1,t2
WHERE ORDER BY 1 ASC , DECODE('',null, '', 'a', ' NULLS LAST ', 'b' ,',2 ASC
Related
---These columns in v_array has counts in it but when i run this code ,its returns zero counts for all columns , where is my mistake ,please help me in it ----
set serveroutput on
declare
type t_list is table of varchar(50) index by PLS_INTEGER;
r_emp t_list;
type e_list is varray(4) of varchar(50);
v_array e_list;
begin
v_array:=e_list('CLAIRTY_ID','SERV_ACCT_NUMBER','BB_ACQUISITION_TYPE','ONT_ACQ_TYPE');
for i in 1..4 loop
select sum(case when v_array(i) is null then 1 else 0 end) into r_emp(i) from SZ_GOODS_DETAIL;
dbms_output.put_line( r_emp(i));
end loop;
end;
--------------output--------------------
0
0
0
0
PL/SQL procedure successfully completed.
You seem to be trying to count the number of null values in each of the columns included in your collection. When you do:
select sum(case when v_array(i) is null then 1 else 0 end) into r_emp(i) from SZ_GOODS_DETAIL;
you are counting how many rows in the table the collection element v_array(i) is null for - which is either going to always be zero (if the collection element is not null, as all your are) or the number of rows in the table (if the element is null). Whether that element is null is nothing to do with the data in the table.
If you want to evaluate the column value corresponding to the value of that collection element for each row in the table then you would either need evaluate the element name with something like:
select sum(case
when v_array(i) = 'CLAIRTY_ID' and CLAIRTY_ID is null then 1
when v_array(i) = 'SERV_ACCT_NUMBER' and SERV_ACCT_NUMBER is null then 1
when v_array(i) = 'BB_ACQUISITION_TYPE' and BB_ACQUISITION_TYPE is null then 1
when v_array(i) = 'ONT_ACQ_TYPE' and ONT_ACQ_TYPE is null then 1
else 0
end)
into r_emp(i)
... which makes the collection a bit pointless and probably isn't what you were thinking of, or use dynamic SQL to embed the element value as the column name:
declare
type t_list is table of number index by PLS_INTEGER;
r_emp t_list;
type e_list is varray(4) of varchar2(50);
v_array e_list;
v_stmt varchar2(255);
begin
v_array:=e_list('CLAIRTY_ID','SERV_ACCT_NUMBER','BB_ACQUISITION_TYPE','ONT_ACQ_TYPE');
for i in 1..4 loop
v_stmt := 'select sum(case when "' || v_array(i) || '" is null then 1 else 0 end) from SZ_GOODS_DETAIL';
-- just for debugging
dbms_output.put_line(v_stmt);
execute immediate v_stmt into r_emp(i);
dbms_output.put_line(r_emp(i));
end loop;
end;
/
Each iteration of the loop will construct and execute a dynamic statement like:
select sum(case when "CLAIRTY_ID" is null then 1 else 0 end) from SZ_GOODS_DETAIL
... which you can see in the debug output (which you can obviously remove once it's working).
You could also do the calculation using count(), subtracting the number of non-null values form the number of rows:
v_stmt := 'select count(*) - count("' || v_array(i) || '") from SZ_GOODS_DETAIL';
Or you could avoid PL/SQL and use an XML trick:
select column_value,
to_number(xmlquery('/ROWSET/ROW/C/text()'
passing xmltype(dbms_xmlgen.getxml(
'select count(case when "' || column_value || '" is null then 1 end) as c '
|| 'from SZ_GOODS_DETAIL'))
returning content)) as c
from sys.odcivarchar2list('CLAIRTY_ID','SERV_ACCT_NUMBER','BB_ACQUISITION_TYPE','ONT_ACQ_TYPE')
fiddle
What do you expect as a result? What does SZ_GOODS_DETAIL table have to do with it? I guess you wanted to compare collection contents to values stored in that table, but - your code doesn't do that.
Here's what I think you want (which doesn't have to be true):
Table contents:
SQL> SELECT * FROM sz_goods_detail;
ID NAME
---------- -------------------
1 CLAIRTY_ID
2 CLAIRTY_ID
3 BB_ACQUISITION_TYPE
SQL> SET SERVEROUTPUT ON
Modified procedure (see line #19):
SQL> DECLARE
2 TYPE t_list IS TABLE OF VARCHAR (50)
3 INDEX BY PLS_INTEGER;
4
5 r_emp t_list;
6
7 TYPE e_list IS VARRAY (4) OF VARCHAR (50);
8
9 v_array e_list;
10 BEGIN
11 v_array :=
12 e_list ('CLAIRTY_ID',
13 'SERV_ACCT_NUMBER',
14 'BB_ACQUISITION_TYPE',
15 'ONT_ACQ_TYPE');
16
17 FOR i IN 1 .. 4
18 LOOP
19 SELECT SUM (CASE WHEN v_array (i) = s.name THEN 1 ELSE 0 END)
20 INTO r_emp (i)
21 FROM sz_goods_detail s;
22
23 DBMS_OUTPUT.put_line (v_array(i) ||': '|| r_emp (i));
24 END LOOP;
25 END;
26 /
Result:
CLAIRTY_ID: 2
SERV_ACCT_NUMBER: 0
BB_ACQUISITION_TYPE: 1
ONT_ACQ_TYPE: 0
PL/SQL procedure successfully completed.
SQL>
If you want to use dynamic column names then you need to use dynamic SQL:
DECLARE
r_emp SYS.ODCINUMBERLIST := SYS.ODCINUMBERLIST();
v_array SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST(
'CLAIRTY_ID','SERV_ACCT_NUMBER','BB_ACQUISITION_TYPE','ONT_ACQ_TYPE'
);
BEGIN
DBMS_OUTPUT.ENABLE;
FOR i IN 1..v_array.COUNT LOOP
r_emp.EXTEND;
EXECUTE IMMEDIATE
'SELECT SUM(CASE WHEN "' || v_array(i) || '" IS NULL THEN 1 ELSE 0 END) FROM SZ_GOODS_DETAIL'
INTO r_emp(i);
dbms_output.put_line(v_array(i) || ': ' || r_emp(i));
END LOOP;
END;
/
Which, for the sample data:
CREATE TABLE SZ_GOODS_DETAIL (CLAIRTY_ID, SERV_ACCT_NUMBER, BB_ACQUISITION_TYPE, ONT_ACQ_TYPE) AS
SELECT 1, 1, 1, 1 FROM DUAL UNION ALL
SELECT 1, 1, 1, NULL FROM DUAL UNION ALL
SELECT 1, 1, NULL, NULL FROM DUAL UNION ALL
SELECT 1, NULL, NULL, NULL FROM DUAL;
Outputs:
CLAIRTY_ID: 0
SERV_ACCT_NUMBER: 1
BB_ACQUISITION_TYPE: 2
ONT_ACQ_TYPE: 3
fiddle
I need to count the number of null values of all the columns in a table in Oracle.
For instance, I execute the following statements to create a table TEST and insert data.
CREATE TABLE TEST
( A VARCHAR2(20 BYTE),
B VARCHAR2(20 BYTE),
C VARCHAR2(20 BYTE)
);
Insert into TEST (A) values ('a');
Insert into TEST (B) values ('b');
Insert into TEST (C) values ('c');
Now, I write the following code to compute the number of null values in the table TEST:
declare
cnt number :=0;
temp number :=0;
begin
for r in ( select column_name, data_type
from user_tab_columns
where table_name = upper('test')
order by column_id )
loop
if r.data_type <> 'NOT NULL' then
select count(*) into temp FROM TEST where r.column_name IS NULL;
cnt := cnt + temp;
END IF;
end loop;
dbms_output.put_line('Total: '||cnt);
end;
/
It returns 0, when the expected value is 6.
Where is the error?
Thanks in advance.
Counting NULLs for each column
In order to count NULL values for all columns of a table T you could run
SELECT COUNT(*) - COUNT(col1) col1_nulls
, COUNT(*) - COUNT(col2) col2_nulls
,..
, COUNT(*) - COUNT(colN) colN_nulls
, COUNT(*) total_rows
FROM T
/
Where col1, col2, .., colN should be replaced with actual names of columns of T table.
Aggregate functions -like COUNT()- ignore NULL values, so COUNT(*) - COUNT(col) will give you how many nulls for each column.
Summarize all NULLs of a table
If you want to know how many fields are NULL, I mean every NULL of every record you can
WITH d as (
SELECT COUNT(*) - COUNT(col1) col1_nulls
, COUNT(*) - COUNT(col2) col2_nulls
,..
, COUNT(*) - COUNT(colN) colN_nulls
, COUNT(*) total_rows
FROM T
) SELECT col1_nulls + col1_nulls +..+ colN_null
FROM d
/
Summarize all NULLs of a table (using Oracle dictionary tables)
Following is an improvement in which you need to now nothing but table name and it is very easy to code a function based on it
DECLARE
T VARCHAR2(64) := '<YOUR TABLE NAME>';
expr VARCHAR2(32767);
q INTEGER;
BEGIN
SELECT 'SELECT /*+FULL(T) PARALLEL(T)*/' || COUNT(*) || ' * COUNT(*) OVER () - ' || LISTAGG('COUNT(' || COLUMN_NAME || ')', ' + ') WITHIN GROUP (ORDER BY COLUMN_ID) || ' FROM ' || T
INTO expr
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = T;
-- This line is for debugging purposes only
DBMS_OUTPUT.PUT_LINE(expr);
EXECUTE IMMEDIATE expr INTO q;
DBMS_OUTPUT.PUT_LINE(q);
END;
/
Due to calculation implies a full table scan, code produced in expr variable was optimized for parallel running.
User defined function null_fields
Function version, also includes an optional parameter to be able to run on other schemas.
CREATE OR REPLACE FUNCTION null_fields(table_name IN VARCHAR2, owner IN VARCHAR2 DEFAULT USER)
RETURN INTEGER IS
T VARCHAR2(64) := UPPER(table_name);
o VARCHAR2(64) := UPPER(owner);
expr VARCHAR2(32767);
q INTEGER;
BEGIN
SELECT 'SELECT /*+FULL(T) PARALLEL(T)*/' || COUNT(*) || ' * COUNT(*) OVER () - ' || listagg('COUNT(' || column_name || ')', ' + ') WITHIN GROUP (ORDER BY column_id) || ' FROM ' || o || '.' || T || ' t'
INTO expr
FROM all_tab_columns
WHERE table_name = T;
EXECUTE IMMEDIATE expr INTO q;
RETURN q;
END;
/
-- Usage 1
SELECT null_fields('<your table name>') FROM dual
/
-- Usage 2
SELECT null_fields('<your table name>', '<table owner>') FROM dual
/
Thank you #Lord Peter :
The below PL/SQL script works
declare
cnt number :=0;
temp number :=0;
begin
for r in ( select column_name, nullable
from user_tab_columns
where table_name = upper('test')
order by column_id )
loop
if r.nullable = 'Y' then
EXECUTE IMMEDIATE 'SELECT count(*) FROM test where '|| r.column_name ||' IS NULL' into temp ;
cnt := cnt + temp;
END IF;
end loop;
dbms_output.put_line('Total: '||cnt);
end;
/
The table name test may be replaced the name of table of your interest.
I hope this solution is useful!
The dynamic SQL you execute (this is the string used in EXECUTE IMMEDIATE) should be
select sum(
decode(a,null,1,0)
+decode(b,null,1,0)
+decode(c,null,1,0)
) nullcols
from test;
Where each summand corresponds to a NOT NULL column.
Here only one table scan is necessary to get the result.
Use the data dictionary to find the number of NULL values almost instantly:
select sum(num_nulls) sum_num_nulls
from all_tab_columns
where owner = user
and table_name = 'TEST';
SUM_NUM_NULLS
-------------
6
The values will only be correct if optimizer statistics were gathered recently and if they were gathered with the default value for the sample size.
Those may seem like large caveats but it's worth becoming familiar with your database's statistics gathering process anyway. If your database is not automatically gathering statistics or if your database is not using the default sample size those are likely huge problems you need to be aware of.
To manually gather stats for a specific table a statement like this will work:
begin
dbms_stats.gather_table_stats(user, 'TEST');
end;
/
select COUNT(1) TOTAL from table where COLUMN is NULL;
I have to write a query which does below. I tried but couldn't write. Please help me.
I have table which returns below result set.
select *
from table1; --(rowid and ColumnName are columns of the table)
Output:
rowid ColumnName
------------------------------
1 Segment1
2 Segment2
I have another table which has below structure : (Segment1 and Segment2 are columns here)
select *
from table2;
Output:
appId Segment1 Segment2 Segment3
---------------------------------------------
a1 fld1 fld2 per
a2 cmp1 hcd4 klp
I need to write a query, which reads the "ColumnName" values from first table and retrieves column values in the second table.
That means, from the table1, I will know what are the available columns I the table2 and from table2, I will know what is the data stored against those columns.
Please let me know if I am not clear.
This query is in Oracle SQL
As mentioned in the comment you need a PLSQL block with dynamic sql. See below an example:
Tables:
create table table1 (row_id number,
ColumnName varchar2(100))
create table table2 (appId number,
Segment1 varchar2(100),
Segment2 varchar2(100),
Segment3 varchar2(100));
Insert all
into TABLE1 (ROW_ID, COLUMNNAME) Values (1, 'Segment1')
into TABLE1 (ROW_ID, COLUMNNAME) Values (2, 'Segment2')
into TABLE2 (APPID, SEGMENT1, SEGMENT2, SEGMENT3) Values (1, 'RRR', 'KKK', 'MMM')
into TABLE2 (APPID, SEGMENT1, SEGMENT2, SEGMENT3) Values (2, 'ZZZ', 'PPP', 'QQQ')
into TABLE2 (APPID, SEGMENT1, SEGMENT2, SEGMENT3) Values (3, 'LLL', 'NNN', 'DDD')
select * from dual;
Code:
DECLARE
var VARCHAR2 (1000);
v_sql VARCHAR2 (2000);
TYPE x_var IS TABLE OF VARCHAR2(1000);
z_var x_var;
num number:=0;
BEGIN
FOR rec IN ( SELECT DISTINCT columnname
FROM table1
ORDER BY 1)
LOOP
num := num +1;
if num = 1 then
var:= rec.columnname;
else
var := var || ' || '' , ''||' || rec.columnname;
end if;
END LOOP;
var := RTRIM (LTRIM (var, ','), ',');
v_sql := 'select '|| var ||' from table2';
EXECUTE IMMEDIATE v_sql BULK COLLECT INTO z_var;
FOR i IN 1 .. z_var.COUNT
LOOP
DBMS_OUTPUT.put_line (z_var(i));
END LOOP;
END;
Output:
SQL> /
RRR , KKK
ZZZ , PPP
LLL , NNN
Dynamic columns in a SQL statement are almost always a bad idea. There's usually a way to avoid these kind of problems and build a simpler solution.
But if this is one of those rare times when you really need to run dynamic SQL in SQL then you'll need to install and run something like my open source project Method4.
For example:
create table table1 as
select 1 id, 'Segment1' columnName from dual union all
select 2 id, 'Segment2' columnName from dual;
create table table2 as
select 'a1' appId, 'fld1' Segment1, 'fld2' Segment2, 'per' Segment3 from dual union all
select 'a2' appId, 'cmp1' Segment1, 'hcd4' Segment2, 'klp' Segment3 from dual;
select * from table(method4.dynamic_query(
q'[
select
'select appID, '
||listagg(columnName, ',') within group (order by id)
||' from table2'
sql_statement
from table1
]'
));
APPID SEGMENT1 SEGMENT2
----- -------- --------
a1 fld1 fld2
a2 cmp1 hcd4
There are a lot of downsides to running this way. The code is complicated, slow, and has some odd behavior. For an explanation of how this works, see this article
by Adrian Billington.
Will the below PL SQL block help your requirement.
BEGIN
FOR iter IN (
SELECT column_name
FROM all_tab_columns
WHERE upper(table_name) = 'table1'
AND UPPER(column_name) LIKE 'SEGMENT%'
)
LOOP
SELECT iter.column_name INTO temp_table FROM table1
dbms_output.put_line(temp_table.column_name);
END LOOP;
END;
/
Say you have tables like the following:
SQL> select * from someTable;
COLUMN1 COLUMN2 COLUMN3
---------- ---------- ----------
1 2 3
2 4 6
3 6 9
SQL> select * from tableOfColumns;
COLUMNN
-------
column1
column3
You may need something like the following:
SQL> declare
2 type tListOfResults is table of varchar2(1000);
3 vSQL varchar2(1000);
4 vResult tListOfResults ;
5 begin
6 select 'select ' || listagg (columnName, ' || '', '' || ') within group (order by columnName) || ' from someTable'
7 into vSQL
8 from tableOfColumns;
9 --
10 execute immediate vSQL bulk collect into vResult;
11 if vResult.count() > 0 then
12 for i in vResult.first .. vResult.last loop
13 dbms_output.put_line(vResult(i));
14 end loop;
15 end if;
16 end;
17 /
1, 3
2, 6
3, 9
PL/SQL procedure successfully completed.
I have column in my database where the values are coming like the following:
3862,3654,3828
In dummy column any no. of comma separated values can come. I tried with following query but it is creating duplicate results.
select regexp_substr(dummy,'[^,]+',1,Level) as dummycol
from (select * from dummy_table)
connect by level <= length(REGEXP_REPLACE(dummy,'[^,]+'))+1
I am not understanding the problem. Can anyone can help?
Works perfectly for me -
SQL> WITH dummy_table AS(
2 SELECT '3862,3654,3828' dummy FROM dual
3 )
4 SELECT trim(regexp_substr(dummy,'[^,]+',1,Level)) AS dummycol
5 FROM dummy_table
6 CONNECT BY level <= LENGTH(REGEXP_REPLACE(dummy,'[^,]+'))+1
7 /
DUMMYCOL
--------------
3862
3654
3828
SQL>
There are many other ways of achieving it. Read Split single comma delimited string into rows.
Update Regarding the duplicates when the column is used instead of a single string value. Saw the use of DBMS_RANDOM in the PRIOR clause to get rid of the cyclic loop here
Try the following,
SQL> WITH dummy_table AS
2 ( SELECT 1 rn, '3862,3654,3828' dummy FROM dual
3 UNION ALL
4 SELECT 2, '1234,5678' dummy FROM dual
5 )
6 SELECT trim(regexp_substr(dummy,'[^,]+',1,Level)) AS dummycol
7 FROM dummy_table
8 CONNECT BY LEVEL <= LENGTH(REGEXP_REPLACE(dummy,'[^,]+'))+1
9 AND prior rn = rn
10 AND PRIOR DBMS_RANDOM.VALUE IS NOT NULL
11 /
DUMMYCOL
--------------
3862
3654
3828
1234
5678
SQL>
Update 2
Another way,
SQL> WITH dummy_table AS
2 ( SELECT 1 rn, '3862,3654,3828' dummy FROM dual
3 UNION ALL
4 SELECT 2, '1234,5678,xyz' dummy FROM dual
5 )
6 SELECT trim(regexp_substr(t.dummy, '[^,]+', 1, levels.column_value)) AS dummycol
7 FROM dummy_table t,
8 TABLE(CAST(MULTISET
9 (SELECT LEVEL
10 FROM dual
11 CONNECT BY LEVEL <= LENGTH (regexp_replace(t.dummy, '[^,]+')) + 1
12 ) AS sys.OdciNumberList)) LEVELS
13 /
DUMMYCOL
--------------
3862
3654
3828
1234
5678
xyz
6 rows selected.
SQL>
Giving a PL/SQL example where parsing over a table with an ID and column name. This will parse and print out each ID and the parsed value which could then be inserted into a new table or used in some other way.
Input
Column_ID Column_Name
123 (3862,3654,3828)
Output
Column_ID Column_Name
123 3862
123 3654
123 3828
PL/SQL Code
declare
table_name1 varchar2(1000);
string_to_parse varchar2(2000); -- assign string to table name
string_length number := 0; -- string length for loop
string_value varchar2(2000); -- string value to store value in
column_id number;
begin
--some table in the format '123' as column_id, '(3862,3654,3828)' as column_name
--remove the parenthesis or other special characters if needed
update some_table t
set t.column_name = regexp_replace(t.column_name,'\(|\)','');
commit;
for i in (
select * from some_table
) loop
column_id := i.column_id; --assign the id of the colors
string_to_parse := i.column_name; -- assign string to be parsed
if string_to_parse is null then
--at this point insert into a new table, or do whatever else you need
dbms_output.put_line(column_id || ' ' || string_value);
else
--String to parse is the comma
string_to_parse := string_to_parse||',';
string_length := length(string_to_parse) - length(replace(string_to_parse,',',''));
-- Loop through string from parameter
for i in 1 .. string_length loop
-- [^,] matches any character except for the ,
select regexp_substr(string_to_parse,'[^,]+',1,i)
into string_value -- stores value into string_value
from dual; -- dual is a dummy table to work around
--at this point insert into a new table, or do whatever else you need
dbms_output.put_line(column_id || ' ' || string_value);
--clear out the string value
string_value := null;
end loop;
end if;
end loop;
end;
I have a series of history tables in an oracle 9 database. History_table_00 contains last months data, History_table_01 contains the month before, and History_table_02 the month before that. Next month, History_table_02 will automatically get renamed to history_table_03, history_table_01 renamed to history_table_02, history_table_00 renamed to history_table_01, and a new history_table_00 will be created to gather the newest history (I really hope I am making sense).
Anyway, I need to write a select statement that will dynamically select all history tables. I am hoping this won't be too complicated because they all share the same name, just appended with sequential number so I can discover the table names with:
select table_name from all_tables where table_name like 'HISTORY_TABLE_%';
My standard query for each table is going to be:
select id, name, data_column_1, data_column_2 from history_table_%;
What do I have to do to accomplish the goal of writing a sql statement that will always select from all history tables without me needing to go in every month and add the new table? Thanks for anything you guys can provide.
you can use ref cursor but i wouldn't recommend it.
it goes like this
create table tab_01 as select 1 a , 10 b from dual;
create table tab_02 as select 2 a , 20 b from dual;
create table tab_03 as select 3 a , 30 b from dual;
create or replace function get_all_history
return sys_refcursor
as
r sys_refcursor;
stmt varchar2(32000);
cursor c_tables is
select table_name
from user_tables
where table_name like 'TAB_%';
begin
for x in c_tables loop
stmt := stmt || ' select * from ' || x.table_name ||' union all';
end loop;
stmt := substr(stmt , 1 , length(stmt) - length('union all'));
open r for stmt;
return r;
end;
/
SQL> select get_all_history() from dual;
GET_ALL_HISTORY()
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
A B
---------- ----------
1 10
2 20
3 30
I would suggest you to define a view in which you select from all history tables using union all
and each time the tables are renamed you modify the view as well.
create OR replace view history_data as
SELECT id, name, data_column_1, data_column_2 FROM history_table_01
union all
SELECT id, name, data_column_1, data_column_2 FROM history_table_02
union all
SELECT id, name, data_column_1, data_column_2 FROM history_table_03
;
then you can simle SELECT * FROM history_data;
you can build the view dynamicaly with the help of the following statment:
SELECT 'SELECT id, name, data_column_1, data_column_2 FROM ' || table_name || ' union all '
FROM user_tables
WHERE table_name like 'HISTORY_TABLE_%'
The best idea is to do a dynamic SQL statement that builds up a large query for each table existing in the database. Give the following SQL query try. (please forgive my formatting, I am not sure how to do line-breaks on here)
DECLARE #table VARCHAR(255)
, #objectID INT
, #selectQuery VARCHAR(MAX)
SELECT #objectID = MIN(object_id)
FROM sys.tables
WHERE name LIKE 'history_table_%'
WHILE #objectID IS NOT NULL
BEGIN
SELECT #table = name
FROM sys.tables
WHERE object_id = #objectID
ORDER BY object_id
SELECT #selectQuery = ISNULL(#selectQuery + ' UNION ALL ', '') + 'select id, name, data_column_1, data_column_2 FROM ' + #table
SELECT #objectID = MIN(object_id)
FROM sys.tables
WHERE name LIKE 'tblt%'
AND object_id > #objectID
END
SELECT #selectQuery
--EXEC (#selectQuery)
A Possible Solution:
CREATE OR REPLACE PROCEDURE GET_HIST_DETAILS IS
DECLARE
QUERY_STATEMENT VARCHAR2(4000) := NULL;
CNT NUMBER;
BEGIN
select COUNT(table_name) INTO CNT from all_tables where table_name like 'HISTORY_TABLE_%';
FOR loop_counter IN 1..CNT
LOOP
IF LOOP_COUNTER <> CNT THEN
{
QUERY_STATEMENT := QUERY_STATEMENT || 'select id, name, data_column_1, data_column_2 from history_table_0' || loop_counter || ' UNION';
}
ELSE
{
QUERY_STATEMENT := QUERY_STATEMENT || 'select id, name, data_column_1, data_column_2 from history_table_0' || loop_counter ;
}
EXECUTE_IMMEDIATE QUERY_STATEMENT;
END LOOP;
END GET_DETAILS;
PS:I dont have Oracle installed , so havent tested it for syntax errors.