This question already has answers here:
Querying an Oracle Database with Dynamic Table names
(1 answer)
dynamic table name without using cursors
(1 answer)
dynamic table name in cursor
(2 answers)
dynamic table name in select statement
(4 answers)
Closed 2 years ago.
without going into details - the tools we use for performance test, it spits out several tables (name changes with every new run) with different type of data. One table has list of all the table names and data type.
I am going to use oracle table as an example so it can be easily explained.
What I am wanting to do...
Query all_table like:
QueryA:
select table_name from all_tables where table_name like 'HZ_CUST_%'
Result will say: Table_name = 'HZ_CUST_ACCOUNTS'
Query using the QueryA result like:
Select * from [QUERYA_RESULT] WHERE creation_date > sysdate, account_number between '500000' and '599999'
I got something like this but not quite sure how to do this.
declare variable TABLE_NAME CHAR(10);
SELECT TABLE_NAME into :V_NAME from all_tables WHERE TABLE_NAME LIKE 'HZ_CUST_ACCOUNTS';
select *
from :V_NAME;
Thank you for the help.
The easiest solution for your problem given what you have expressed in the comment section is to use DYNAMIC SQL
declare
v_table_name varchar2(130);
v_sql clob;
begin
-- get the table name
select table_name into v_table_name from all_tables where table_name like 'HZ_CUST_%' ;
-- Build dynamic statement
v_sql := ' select * from '||v_table_name||' where creation_date > sysdate
and account_number between ''500000'' and ''599999'' ';
execute immediate v_sql;
end;
/
From this small version, you can expand this code to whatever you need to do, specialy with the output. Of course, depending on what you need to do with it. I could for example store the maximum value of a field based on this same sentence.
declare
v_table_name varchar2(130);
v_sql clob;
vmaxvalue pls_integer;
begin
-- get the table name
select table_name into v_table_name from all_tables where table_name like 'HZ_CUST_%' ;
-- Build dynamic statement
v_sql := ' select max(table_field) from '||v_table_name||' where creation_date > sysdate
and account_number between ''500000'' and ''599999'' ';
execute immediate v_sql into vmaxvalue;
end;
/
I don't believe you need for your case to use PTF ( Polymorphic Table Functions ) which are intended to evolve the concept of pipeline functions. However, as you did explain very well what you intent to do with the result of the query, you might want to have a look at them. Check this very good example of PTF ( Remember Oracle 18c or higher ).
Polymorphic Table Functions
Related
This question already has answers here:
Get counts of all tables in a schema
(6 answers)
Closed 2 years ago.
I have a Metadata table that contains the following info:
I would like to build a Query that shows the count (*) of each View per schema, something like
I am not quite sure how to start? in the matedata table only the name of the view per schema is stored.
do anyone has a suggestion please, thx
You'll have to query each view independently to get the real time count. You can generate SQL statements dynamically using below query:
select 'select count(*) as '||view_name||' from '||owner||'.'||view_name||';'
from all_views
where (owner ,view_name) in (select schema_name, view_name from metadata) ;
OR
You can do it dynamically using below PL/SQL block. Better to have results inserted in a table:
declare
v_cnt integer;
begin
for r in (select owner, view_name from all_views
where (owner ,view_name) in (select schema_name, view_name
from metadata))
loop
execute immediate 'select count(*) from ' || r.owner||'.'||view_name
into v_cnt;
insert into cnt_table(view_name,owner,total_count)
values (r.view_name,r.owner,v_cnt);
end loop;
end;
I have a table that store the name of other tables. Like
COL_TAB
--------------
TABLE_NAME
--------------
TAB1
TAB2
TAB3
What i want to do is that, i want to run a sql query on table like this,
SELECT * FROM (SELECT TABLE_NAME from COL_TAB WHERE TABLE_NAME = 'TAB1')
Thanks
An Oracle SQL query can use a dynamic table name, using Oracle Data Cartridge and the ANY* types. But before you use those advanced features, take a step back and ask yourself if this is really necessary.
Do you really need a SQL statement to be that dynamic? Normally this is better handled by an application that can submit different types of queries. There are many application programming languages and toolkits that can handle unexpected types. If this is for a database-only operation, then normally the results are stored somewhere, in which case PL/SQL and dynamic SQL are much easier.
If you're sure you've got one of those rare cases that needs a totally dynamic SQL statement, you'll need something like my open source project Method4. Download and install it and try the below code.
Schema Setup
create table tab1(a number);
create table tab2(b number);
create table tab3(c number);
insert into tab1 values(10);
insert into tab2 values(20);
insert into tab3 values(30);
create table col_tab(table_name varchar2(30), id number);
insert into col_tab values('TAB1', 1);
insert into col_tab values('TAB1', 2);
insert into col_tab values('TAB1', 3);
commit;
Query
select * from table(method4.dynamic_query(
q'[
select 'select * from '||table_name sql
from col_tab
where id = 1
]'));
Result:
A
--
10
You'll quickly discover that queries within queries are incredibly difficult. There's likely a much easier way to do this, but it may require a design change.
I don't have a database by hand to test this but I think you are looking for something like this:
DECLARE
-- Create a cursor on the table you are looking through.
CURSOR curTable IS
SELECT *
FROM MainTable;
recTable curTable%ROWTYPE;
vcQuery VARCHAR2(100);
BEGIN
-- Loop through all rows of MainTable.
OPEN curTable;
LOOP
FETCH curTable INTO recTable;
EXIT WHEN curTable%NOTFOUND;
-- Set up a dynamic query, with a WHERE example.
vcQuery := 'SELECT ColumnA, ColumnB FROM ' || recTable.Table_Name || ' WHERE 1 = 1';
-- Execute the query.
OPEN :dyn_cur FOR vcQuery;
END LOOP;
CLOSE curTable;
END;
/
Try this
CREATE OR REPLACE PROCEDURE TEST IS
sql_stmt VARCHAR2(200);
V_NAME VARCHAR2(20);
BEGIN
sql_stmt := 'SELECT * FROM ';
EXECUTE IMMEDIATE sql_stmt|| V_NAME;
END;
Update
select statement dont work in procedure.
in sql server you can try sql block
Declare #name varchar2(50)
Select #name='Select * from '+TABLE_NAME from COL_TAB WHERE TABLE_NAME = 'TAB1'
EXEC(#name);
I have a table as shown below. This table will be generated dynamically and I have no prior idea about what value it is going to hold.
------------------------------------------
TABLE_NAME COLUMN_NAME CHAR_LENGTH
------------------------------------------
EMPLOYEE COL1 100
EMPLOYEE COL2 200
EMPLOYEE COL3 300
EMPLOYEE COL4 400
Based on this table, I want to build a query in such a way that it would give me those columns, that contains data having char length greater than CHAR_LENGTH column value.
For example if COL2 contains data having char length 500 (>200), then query would give me COL2.
I don't have any draft code to show my attempt, as I have no idea how would I do this.
I don't think this is possible in pure SQL due to the dynamic nature of your requirement. You'll need some form of PL/SQL.
Assuming you're ok with simply outputting the desired results, here is a PL/SQL block that will get the job done:
declare
wExists number(1);
begin
for rec in (select * from your_dynamic_table)
loop
execute immediate 'select count(*)
from dual
where exists (select null
from ' || rec.table_name || ' t
where length(t.' || rec.column_name || ') > ' || rec.char_length || ')'
into wExists;
if wExists = 1 then
dbms_output.put_line(rec.column_name);
end if;
end loop;
end;
You'll also notice the use of the exists clause to optimize the query, so as not to iterate over the whole table unnecessarily, when possible.
Alternatively, if you want the results to be queryable, you can consider converting the code to a pipelined function.
select column_name
from (
select statement that builds the table output
) A
where char_length<length(column_name)
will that help?
You would need a procedure to achieve the same :
Here I am treating the all_tab_columns table in Oracle, which is a default table with much similar structure as your reference example.(Try select * from all_tab_columns). The structure of all_tab_columns is much like yours, except that you will never find a varchar record whose value has exceeded its data length(obvious database level constraint). Date fields may exceed data length, and do reflect in this procedure's output. I am searching all columns in EMPLOYEES whose size exceeds what is specified.
DECLARE
cursor c is select column_name,data_length,table_name from all_tab_columns where table_name=:Table_name;
V_INDEX_NAME all_tab_columns.column_name%type;
v_data_length all_tab_columns.data_length%type;
V_NUMBER PLS_INTEGER;
v_table_name all_tab_columns.table_name%type;
BEGIN
open c;
LOOP
FETCH c into v_index_name,v_data_length,v_table_name;
EXIT when c%NOTFOUND;
v_number :=0;
execute immediate 'select count(*) from '|| :Table_name ||' where length('||v_index_name||')>'||v_data_length into v_number;
if v_number>1 then
dbms_output.put_line(v_index_name||' has values greater than specified'||' '||V_INDEX_NAME||' '||v_data_length);
end if;
END LOOP;
close c;
END;
/
Replace all_tab_columns and its respective columns with the column name of your table.
DEFECTS : The table name is hardcoded. Trying to make the code generic execute immediate or any other trick. Will achieve soon.
EDIT : Defect fixed.
I have the following table name template, there are a couple with the same name and a number at the end: fmj.backup_semaforo_geo_THENUMBER, for example:
select * from fmj.backup_semaforo_geo_06391442
select * from fmj.backup_semaforo_geo_06398164
...
Lets say I need to select a column from every table which succeeds with the 'fmj.backup_semaforo_geo_%' filter, I tried this:
SELECT calle --This column is from the backup_semaforo_geo_# tables
FROM (SELECT table_name
FROM all_tables
WHERE owner = 'FMJ' AND table_name LIKE 'BACKUP_SEMAFORO_GEO_%');
But I'm getting the all_tables tables name data:
TABLE_NAME
----------
BACKUP_SEMAFORO_GEO_06391442
BACKUP_SEMAFORO_GEO_06398164
...
How can I achieve that without getting the all_tables output?
Thanks.
Presumably your current query is getting ORA-00904: "CALLE": invalid identifier, because the subquery doesn't have a column called CALLE. You can't provide a table name to a query at runtime like that, unfortunately, and have to resort to dynamic SQL.
Something like this will loop through all the tables and for each one will get all the values of CALLE from each one, which you can then loop through. I've used DBMS_OUTPUT to display them, assuming you're doing this in SQL*Plus or something that can deal with that; but you may want to do something else with them.
set serveroutput on
declare
-- declare a local collection type we can use for bulk collect; use any table
-- that has the column, or if there isn't a stable one use the actual data
-- type, varchar2(30) or whatever is appropriate
type t_values is table of table.calle%type;
-- declare an instance of that type
l_values t_values;
-- declare a cursor to generate the dynamic SQL; where this is done is a
-- matter of taste (can use 'open x for select ...', then fetch, etc.)
-- If you run the query on its own you'll see the individual selects from
-- all the tables
cursor c1 is
select table_name,
'select calle from ' || owner ||'.'|| table_name as query
from all_tables
where owner = 'FMJ'
and table_name like 'BACKUP_SEMAFORO_GEO%'
order by table_name;
begin
-- loop around all the dynamic queries from the cursor
for r1 in c1 loop
-- for each one, execute it as dynamic SQL, with a bulk collect into
-- the collection type created above
execute immediate r1.query bulk collect into l_values;
-- loop around all the elements in the collection, and print each one
for i in 1..l_values.count loop
dbms_output.put_line(r1.table_name ||': ' || l_values(i));
end loop;
end loop;
end;
/
May be a dynamic SQL in a PLSQL program;
for a in (SELECT table_name
FROM all_tables
WHERE owner = 'FMJ' AND table_name LIKE 'BACKUP_SEMAFORO_GEO_%')
LOOP
sql_stmt := ' SELECT calle FROM' || a.table_name;
EXECUTE IMMEDIATE sql_stmt;
...
...
END LOOP;
Hi everyone what I'm wondering if I can do is create a table that lists the record counts of other tables. It would get those table names from a table. So let's assume I have the table TABLE_LIST that looks like this
name
---------
sports_products <-- contains 10 records
house_products <-- contains 8 records
beauty_products <-- contains 15 records
I would like to write a statement that pulls the names from those tables to query them and coount the records and ultimately produce this table
name numRecords
------------------------------
sports_products 10
house_products 8
beauty_products 15
So I think I would need to do something like this pseudo code
select *
from foreach tableName in select name from table_list
select count(*) as numRecords
from tableName
loop
You can have a function that is doing this for you via dynamic sql.
However, make sure to declare it as authid current_user. You do not want anyone to gain some sort of privilege elevation by exploiting your function.
create or replace function SampleFunction
(
owner in VarChar
,tableName in VarChar
) return integer authid current_user is
result Integer;
begin
execute immediate 'select count(*) from "' || owner || '"."' || tableName || '"'
INTO result;
return result;
end;
One option is to simply keep your DB statistics updated, use dbms_stats package or EM, and then
select num_rows
from all_tables
where table_name in (select name from table_list);
I think Robert Giesecke solution will work fine.
A more exotic way of solving this is by using dbms_xmlgen.getxml.
See for example: Identify a table with maximum rows in Oracle