How to run a single query over all the tables in a user schema in ORACLE - sql

Let say I have tables A,B,C,......,Z and I want to count all the entries in the tables and combine the results like so:
SELECT 'A' AS A, count(*) from USER.A UNION
.
.
.
SELECT 'J' AS J, count(*) from USER.J UNION
.
.
SELECT 'Z' AS 'Z' COUNT(*) from USER.Z
I want to avoid all above hassle. How can I do this in a smart way?.

I tend to do this by writing a query using the user_tables Meta information View and first time selecting text that is actually an sql query:
SELECT 'select '''||TABLE_NAME||''', count(*) from '||TABLE_NAME||'union all' FROM USER_TABLES
Running that in your query tool will produce a grid of rows that are actually sql queries in their own right. Copying them out of the results grid, pasting them into the query window (remove the trailing UNION ALL) and running them again produces data for each table
select 'a', count(*) from a union all
select 'b', count(*) from b union all ....
To get more involved and include column names, there's a USER_TAB_COLUMNS view that cites info about columns (e.g. You could write a query that generates queries that search any varchar columns for a particular value)
If you get really involved it can be cleaner to use placeholders in the string and REPLACE them:
SELECT REPLACE(REPLACE(REPLACE(
'select ''{o}.{t}'' as tname, ''{c}'' as cname
from {o}.{t} where
{c} like ''hello%'' or
{c} like ''goodbye%'' union all'
--to add more placeholders copy paste these lines
--and ensure there is the same number of REPLACE
--as there are numbers of lines
, '{t}', TABLE_NAME)
, '{c}', COLUMN_NAME)
, '{o}', OWNER)
FROM ALL_TAB_COLUMNS
WHERE DATA_TYPE = 'VARCHAR'
This is massively cleaner than starting and stopping your string all the time with || concatenation. I've deliberately uppercase the outer query and lowercase the inner query so you can tell which part is which
To add more placeholders, put additional REPLACE( at the top and copy paste in place the lines underneath that begin with a comma. Example copy , '{t}', table_name) paste and change to , '{d}', data_type) add another REPLACE( at the top and now you have a {d} placeholder you can use anywhere in the first string (the sql query pattern) to signify the data type

Related

Cant able to understand the below query ,any one explain?

select table_name,
to_number(
extractvalue(
xmltype(
dbms_xmlgen.getxml(
'select count(*) c from '||table_name
))
,'/ROWSET/ROW/C')
) count
from user_tables
order by table_name;
I know it is giving the total count of row of each table . But what to know how it working ?
Examining the query starting from innermost part is best way to understand:
'select count(*) c from ' || table_name
creates a string containing query selecting the number of records from the table referred to by table_name, so if table_name contains xyz the query will be select count(*) from xyz.
xmltype(dbms_xmlgen.getxml(<query>))
executes the dynamically generated query, producing an XML result.
to_number(extractvalue(<xml>, '/ROWSET/ROW/C'))
fetches a specific value from the XML generated before, following a specific path. We need to assume the XML looks like<ROWSET><ROW><C>value</C></ROW></ROWSET>. The extracted value, yet a string, is then converted to a number.
select table_name, <number> count from user_tables order by table_name
is what finally remains...
Break it down:
'select count(*) c from '||table_name gives you a string containing a query against a specific table (from user_tables);
dbms_xmlgen.getxml('select count(*) c from '||table_name) uses the dbms_xmlgen package to run that dynamic query string and returns the result as an XML document, but as a CLOB;
xmltype(dbms_xmlgen.getxml('select count(*) c from '||table_name)) converts that CLOB to actual XML;
extractvalue(..., '/ROWSET/ROW/C') extracts the C node value from that XML (there is only one per document, and there is one document per table), as a string;
to_number(...) just converts that string to a number.
db<>fiddle with three dummy tables, showing the intermediate steps.
However, the version you have seems to have originated with Laurent Schneider in 2007, and things have moved on a bit since then. The extractvalue() function is deprecated so you should use XMLQuery instead, and you can skip a step by using getxmltype() instead of xmltype(getxml()):
select table_name,
to_number(
xmlquery(
'/ROWSET/ROW/C/text()'
passing dbms_xmlgen.getxmltype('select count(*) c from '||table_name)
returning content
)
) count
from user_tables
order by table_name;
Or (as #Padders mentioned) you could use XMLTable, with a CTE or inline view to provide the XML; which perhaps makes this example a bit more obscure, but is useful if you have more than one value to extract:
select t.table_name, x.count
from (
select table_name,
dbms_xmlgen.getxmltype('select count(*) c from '||table_name) as xml
from user_tables
) t
cross apply xmltable (
'/ROWSET/ROW'
passing t.xml
columns count number path 'C'
) x
order by table_name;
The principal is the same though, and I've included those versions in an expanded db<>fiddle.
(Incidentally, I'm not advocating using a keyword like count as a column alias - it's better to avoid those; I'm just sticking with the alais from your original query.)

How can I union multiple tables from a database that end with same characters in their name?

I am trying to union over 30 tables in my database without listing them all. They all end with the same last four characters in their name.
I've tried listing them all but would need a more efficient way to identify all tables that end with the four characters. I've tried searching for a way to search the name of the tables.
select * from [dbo].[AL2019CLFC]
union
select * from [dbo].[AR2019CLFC]
union
select * from [dbo].[AZ2019CLFC]
.....
I expect a way to identify the last four characters 'CLFC' from all the tables and union them.
I would suggest querying from the system list of tables and building out your sql statement. Then copying the results back into your querying window. Technically you could make a stored procedure that would build up a string and then run executesql to get the results.
Note: the examples below will give a trailing UNION keyword which should be removed. Again, if you wanted to get fancier, you could sql around it, but hopefully this will be enough to get you going.
MS SQL Server:
select
'SELECT * FROM ' + name + ' UNION ' query
from sys.tables
where name like '%CLFC'
Oracle:
select
'SELECT * FROM ' || table_name || ' UNION ' query
from user_tables
where table_name like '%CLFC'

concat all rows in one rows

Hi i need to concat all rows from my table.
I have this query select * from table1; this table contains 400 fields
i cannot do this select column1 ||','||column2||','||.....from table1
can someone help e to fix it using select * from table1 to concatinate all rows.
And thank you.
In Oracle (and similar in other DBMS) you could use system tables.... and do this in two steps:
Assuming you want to combine all the columns into 1 column for X rows...
STEP 1:
SELECT LISTAGG(column_Name, '|| Chr(44)||') --this char(44) adds a comma
within group (order by column_ID) as Fields
--Order by column_Id ensures they are in the same order as defined in db.
FROM all_tab_Cols
WHERE table_name = 'YOURTABLE'
and owner = 'YOUROWNER'
--Perhaps exclude system columns
and Virtual_Column = 'NO'
STEP 2:
Copy the results into a new SQL statement and execute.
The would look something like Field1|| Chr(44)||Field2|| Chr(44)||Field3
SELECT <results>
FROM YOURTABLE;
which would result in a comma separated list of values in 1 column for all rows of YOURTABLE
If the length of all the columns (along with , space and ||) would exceed the 4000 characters allowed... we can use a clob data type instead through the use of XML objects...
* Replace step 1 with *
SELECT RTRIM(XMLAGG(XMLELEMENT(Column_ID,Column_Name,'|| Chr(44)||').extract('//text()') order by Column_ID).GetClobVal(),'|| Chr(44)||') fields
FROM all_tab_Cols
WHERE table_name = 'YOURTABLENAME'
and owner = 'YOUROWNER'
--Perhaps exclude system columns
and Virtual_Column = 'NO';
Syntax for the above attributed to This Oracle thread but updated for your needs.

one query for many similar tables

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

simplify multiple sql queries different variable

I am trying to better automate my queries so I don't have to change the table name and where clause each time. Right now this is what I do:
Years 2014, 2013, etc. I might out these variables into a table. Also doing this on Oracle.
Colors: Red, Green, etc
select count(*) from Apples_2014
where Type = 'Red'
;
select count(*) from Apples_2014
where Type = 'Green'
;
select count(*) from Apples_2013
where Type = 'Red'
;
select count(*) from Apples_2013
where Type = 'Green'
;
Is there a simpler way to do this so I have only one query and then it gets run multiple times but with the different parameters?
Also through some research I saw I can use && which then creates a popup each time in Toad. This isn't really efficient though but its kinda works.
ADDITION (original answer below. Editing after seeing your comment on your post.)
Sounds like you actually want do to grouping.
Running something like the below will give counts for all tables and all values at once in a single result set.
select count(*) as total, 'TABLENAME' as tablename, value
from Tablename
group by value;
UNION ALL
select count(*) as total, 'TABLENAME2' as tablename, value
from Tablename2
group by value
***********************************88
Original answer:
you can use bind variables in Oracle - it will then prompt for each value.
select count(*) from TABLE
where Type = :value
assuming your table structures are very similar, you can union and add a paramater to handle the table name changes without switching to use dynamic sql (dynamic sql is just writing it as a concatenated string - not very efficient.).
So like this....
select total
from (
select count(*) as total, 'TABLENAME' as tablename
from Tablename
where type = :value;
UNION ALL
select count(*) as total, 'TABLENAME2' as tablename
from Tablename2
where type = :value
) a
where a.tablename = :tablename