one query for many similar tables - sql

I have an Oracle database with many tables that have identical structure (columns are all the same). The table names are similar also. The names of the tables are like table_1, table_2, table_3...
I know this isn't the most efficient design, but I don't have the option of changing this at this time.
In this case, is it possible to make a single sql query, to extract all rows with the same condition across multiple tables (hundreds of tables) without explicitly using the exact table name?
I realize I could use something like
select * from table_1 UNION select * from table_2 UNION select * from table_3...select * from table_1000
But is there a more elegant sql statement that can be run that extracts from all matching table names into one result without having to name each table explicitly.
Something like
select * from table_%
Is something like that possible? If not, what is the most efficient way to write this query?

You can use dbms_xmlgen to query tables using a pattern, which generates an XML document as a CLOB:
select dbms_xmlgen.getxml('select * from ' || table_name
|| ' where some_col like ''%Test%''') as xml_clob
from user_tables
where table_name like 'TABLE_%';
You said you wanted a condition, so I've included a dummy one, where some_col like '%Test%'.
You can then use XMLTable to extract the values back as relational data, converting the CLOB to XMLType on the way:
select x.*
from (
select xmltype(dbms_xmlgen.getxml('select * from ' || table_name
|| ' where some_col like ''%Test%''')) as xml
from user_tables
where table_name like 'TABLE_%'
) t
cross join xmltable('/ROWSET/ROW'
passing t.xml
columns id number path 'ID',
some_col varchar2(10) path 'SOME_COL'
) x;
SQL Fiddle demo which retrieves one matching row from each of two similar tables. Of course, this assumes your table names follow a useful pattern like table_%, but you suggest they do.
This is the only way I know to do something like this without resorting to PL/SQL (and having searched back a bit, was probably inspired by this answer to count multiple tables). Whether it's efficient (enough) is something you'd need to test with your data.

This is kind of messy and best performed in a middle-tier, but I suppose you could basically loop over the tables and use EXECUTE IMMEDIATE to do it.
Something like:
for t in (select table_name from all_tables where table_name like 'table_%') loop
execute immediate 'select blah from ' || t.table_name;
end loop;

You can write "select * from table_1 and table_2 and tabl_3;"

Related

Required to create an empty table by joining multiple tables in Oracle DB

I got an error while creating an empty table by joining two tables.
I have two tables tab1 & tab2 and there are many common columns names in both tables.
I tried this:
create table tab3 as
select * from tab1 a, tab2 b where a.id = b.id and 1=2;
This gave ORA-00957: duplicate column name. As I mentioned above there are many common columns name between these two tables. If I prepare a create table statement by writing around 500 column names one by one then it will consume lots of time. Please help me out here.
The simple answer is, don't use *. Or is that the whole point, to avoid writing five lines of column names?
One way to avoid these conflicts, but that assumes that you are joining on all columns with the same name in both tables and on no other columns, is to do something like
create table new_table as
select *
from table_a natural join table_b
where null is not null
;
(As you can guess, as an aside, I prefer null is not null to 1 = 2; the parser seems to prefer it too, as it will rewrite 1 = 2 as null is not null anyway.)
Will you need to control the order of the columns in the new table? If you do, you will need to write them out completely in the select clause, regardless of which join syntax you choose to use.
That's an interesting question.
The only idea I have to offer it to let another query to compose the query you need
select 'select ' || listagg(col_name, ', ') within group(order by 1) || 'from tab1 a,tab2 b where (a.id=b.id) and 1=2'
from (select 'a.' || column_name col_name from user_tab_cols where table_name = 'TAB1'
union all
select 'b.' || column_name from user_tab_cols where table_name = 'TAB2')
Please be aware for subqueries you need to specify table names in the upper case

How to get several records searching on the whole database

My question is, is it possible to list all the columns from the whole database not just in specific tables based on 3 different criteria which ones are in an "OR" relationship. so for example I have database called "Bank" and I have 3 criterias "Criteria1; Criteria2; Criteria3" and if any of them is true so the relation between them should be OR and not AND than I will get back all the columns matching the criterias and the output put should provide "account_id" or "customer_id" from the same table.
How do I procced in this case?
It is possible, but you probably don't want to do it. Anyway, you could write a stored procedure that finds all tables that contain the columns you want:
select distinct table_name from user_tab_cols utc
where exists (select * from user_tab_cols where table_name = utc.table_name
and column_name = 'ACCOUNT_ID')
and exists (select * from user_tab_cols where table_name = utc.table_name
and column_name = 'CUSTOMER_ID');
Given the tables you could run a query where you append table name and your criteria:
execute immediate 'select account_id, customer_id from agreement where '
|| your_criteria_here;
A bit messy, inefficient and treat this as pseudo-code. However, if you really want to do this for an ad-hoq query it should point you in the right direction!

SQL inner join based on table name pattern

On this legacy SQL Database with hundreds of tables, I need to do a inner join on all tables whose name follows a format:
barX_foo_bazX
barX_foo_bazY
barZ_foo_bazZ
I would like to inner join all tables with foo in their name
I am not sure this is possible at all.
Clearly, with this syntax it is not (but it may help understand what I'm aiming at):
USE [LegacyDB_Name]
SELECT *
FROM '%_foo_%' inner join '%_foo_%'
where my_stuff_is(some condition)
Any Suggestions? Ideas on how I can do this? Maybe there is an easier path this young padawan is not seeing...
Many Thanks!
I am not sure this is possible at all.
Nope, table names cannot contain or use wildcards, they must be strings.
My advice would be to find whatever program makes these select queries and include whatever pattern matching you need in the queries in there.
But your finished query must contain table names as strings.
Maybe the simplest way to do this is to declare a cursor based on the below query and build a dynamic sql query. Research tsql cursor and dynamic sql execution and it should be fairly simple.
SELECT *
FROM information_schema.tables
Where Table_Type = 'Base Table' And Table_Name Like '%_foo_%'
If your tables all have the same structure (i.e. columns), then you could do this in two steps.
Generate the SQL statement:
select 'UNION ALL SELECT ''' + table_name + ''' AS table_name, * FROM '
+ table_name AS stmt
from information_schema.tables
where table_type = 'BASE TABLE'
and table_catalog = 'LegacyDB_Name'
and table_name LIKE '%foo%';
The output will be something like:
stmt
--------------------------------------------------------------------
UNION ALL SELECT 'barX_foo_bazX' AS table_name, * FROM barX_foo_bazX
UNION ALL SELECT 'barX_foo_bazY' AS table_name, * FROM barX_foo_bazY
UNION ALL SELECT 'barX_foo_bazZ' AS table_name, * FROM barX_foo_bazZ
From this output, copy the SQL rows and remove the first 2 words (UNION ALL) from the first line. This is a valid SQL statement.
Execute the SQL statement derived above
If you need this SQL more often, then create a view for it:
CREATE OR REPLACE VIEW all_foo AS
SELECT 'barX_foo_bazX' AS table_name, * FROM barX_foo_bazX
UNION ALL SELECT 'barX_foo_bazY' AS table_name, * FROM barX_foo_bazY
UNION ALL SELECT 'barX_foo_bazZ' AS table_name, * FROM barX_foo_bazZ;
Now you can query like
SELECT * FROM all_foo WHERE ...

Vertica Dynamic Max Timestamp from all Tables in a Schema

System is HP VERTICA 7.1
I am trying to create a SQL query which will dynamically find all particular tables in a specific schema that have a Timestamp column named DWH_CREATE_TIMESTAMP from system tables. (I have completed this part successfully)
Then, pass this list of tables to an outer query or some kind of looping statement which will select the MAX(DWH_CREATE_TIMESTAMP) and TABLE_NAME from all the tables in the list (200+) and union all the results together into one list.
The expected output is a 2 column table with all said tables with that TS field and the max of each value. Tables are constantly being created and dropped, so the point is to make everything totally dynamic where no TABLE_NAME values are ever hard-coded.
Any idea of Vertica specific ways to accomplish this without UDF's would be greatly appreciated.
Inner Query (working):
select distinct(table_name)
from columns
where column_name = 'DWH_CREATE_TIMESTAMP'
and table_name in (select DISTINCT(table_name) from all_tables where schema_name = 'PTG_DWH')
Outer Query (attempted - not working):
SELECT Max(DWH_CREATE_DATE) from
WITH table_name AS (
select distinct(table_name)
from columns
where column_name = 'DWH_CREATE_DATE' and table_name in (select DISTINCT(table_name) from all_tables where schema_name = 'PTG_DWH'))
SELECT MAX(DWH_CREATE_DATE)
FROM table_name
Thanks!!!
No way to do that in one SQL .
You can used the below method for node max timestamp columns values
select projections.anchor_table_name,vs_ros.colname,max(max_value) from vs_ros,vs_ros_min_max_values,storage_containers,projections where vs_ros.colname ilike 'timestamp'
and vs_ros.salstorageid=storage_containers.sal_storage_id
and vs_ros_min_max_values.rosid=vs_ros.rosid
and storage_containers.projection_name=projections.projection_name
group by projections.anchor_table_name,vs_ros.colname

Oracle: "grep" across multiple columns?

I would like to perform a like or regexp across several columns. These columns contain tags, keywords, etc. Something that's the equivalent of:
sqlplus scott/tiger #myquery.sql | grep somestring
Right now I have something like
select * from foo where c1 || c2 || c3 like '%somestring%'
but I'm hoping I can get something a bit more general purpose and/or optimized. Any clues appreciated!
Have you thought about using the Concatenated Datastore feature of Oracle Text?
Oracle Text lets you create full-text indexes on multiple columns in the same table. Or at least there's a process by which you can do this.
There's a good example document on the Oracle site I've used before:
http://www.oracle.com/technology/sample_code/products/text/htdocs/concatenated_text_datastore/cdstore_readme.html
Oracle Text searches are ridiculously fast. I think I'd look at keeping separate context indexes on each individual column so that you could apply relevance and priority to each column match.
Let me know if you'd like an example and I'll add something to the answer.
Hope this helps.
On 11G you could create a virtual column:
alter table foo add all_text varchar2(4000) generated always as (c1 ||','|| c2 ||','|| c3);
(See Oracle 11G new features).
Then query:
select * from foo where all_text like '%somestring%'
You could add an index on all_text if it helps performance too (see this answer for when it might help and when not).
Prior to 11G you could do the same thing but with a normal column, maintained via a trigger.
As a consultant I often have to search in poorly documented databases and have the need to have some handy scripts to find data. Here is two examples how to generate a select clause for searching data in all 'VARCHAR2' columns in a table:
Example1. Search part of string:
SELECT 'SELECT * FROM ' || min(TABLE_NAME) ||' WHERE ' || LISTAGG(COLUMN_NAME, ' like ''%somestring%'' or ') WITHIN GROUP (ORDER BY COLUMN_ID) || ' like ''%somestring%'';'
from ALL_TAB_COLUMNS
WHERE OWNER = 'YOUR_SCHEMA_NAME' -- Uppercase
AND TABLE_NAME = 'YOUR_TABLE_NAME' --Uppercase
AND DATA_TYPE LIKE 'VARCHAR2';
Example2. Search the entire value:
SELECT 'SELECT * FROM ' || min(TABLE_NAME) ||' WHERE ''somestring'' in (' || LISTAGG(COLUMN_NAME, ', ') WITHIN GROUP (ORDER BY COLUMN_ID) || ');'
from ALL_TAB_COLUMNS
WHERE OWNER = 'YOUR_SCHEMA_NAME' -- Uppercase
AND TABLE_NAME = 'YOUR_TABLE_NAME' --Uppercase
AND DATA_TYPE LIKE 'VARCHAR2';
Does regexp_like help.
http://www.psoug.org/reference/regexp.html
SELECT * FROM table WHERE REGEXP_LIKE(col1, <pattern>)
union
SELECT * FROM table WHERE REGEXP_LIKE(col2, <pattern>)
union
SELECT * FROM table WHERE REGEXP_LIKE(col3, <pattern>)
this should work. but i doubt if this would be any better in performance than your query. you might want to compare the performances of both. would really love to hear from you on your findings. :-)