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

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

Related

with XMLDIFF, how to compare only the fields that my xml elements have in common?

introduction:
I have query using a pipeline function. I won't change the names of the returned columns but I will add other columns.
I want to compare the result of the old query with the new query (syntaxal always the same (select * from mypipelinefunction) , but I have changed the pipeline function )
I have used "select *" instead of "select the name of the columns" because there is a lot names.
code:
the code example is simplified to focus on the problem addressed in the title. (no pipeline function. Only two "identic" queries are tested. The second query has one more column that the first.
SELECT
XMLDIFF (
XMLTYPE.createXML (
DBMS_XMLGEN.getxml ('select 1 one, 2 two from dual')),
XMLTYPE.createXML (
DBMS_XMLGEN.getxml ('select 1 one from dual')))
from dual.
I want that XMLDIFF to say that there is no difference because the only columns that I care about are the colums that are in common.
In short I would like to have this result
<xd:xdiff xsi:schemaLocation="http://xmlns.oracle.com/xdb/xdiff.xsd http://xmlns.oracle.com/xdb/xdiff.xsd" xmlns:xd="http://xmlns.oracle.com/xdb/xdiff.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
</xd:xdiff>
instead of this result
<xd:xdiff xsi:schemaLocation="http://xmlns.oracle.com/xdb/xdiff.xsd http://xmlns.oracle.com/xdb/xdiff.xsd" xmlns:xd="http://xmlns.oracle.com/xdb/xdiff.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><xd:delete-node xd:node-type="element" xd:xpath="/ROWSET[1]/ROW[1]/TWO[1]"/></xd:xdiff>
Is this possible to force XMLdiff to compare only the columns that are in commun?
code
Another way to fix this problem would be to have a shortcut in TOAD that transform select * from t in select first_column, ......last_column from t. And it should work even if t is a pipeline function
If you only care about certain columns then wrap your query in a outer-query to only output the columns you care about:
SELECT XMLDIFF (
XMLTYPE.createXML (
DBMS_XMLGEN.getxml (
'SELECT one FROM (select 1 one, 2 two from dual)'
)
),
XMLTYPE.createXML (
DBMS_XMLGEN.getxml (
'SELECT one FROM (select 1 one from dual)'
)
)
) AS diff
FROM DUAL;
Which outputs:
DIFF
<xd:xdiff xsi:schemaLocation="http://xmlns.oracle.com/xdb/xdiff.xsd http://xmlns.oracle.com/xdb/xdiff.xsd" xmlns:xd="http://xmlns.oracle.com/xdb/xdiff.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><?oracle-xmldiff operations-in-docorder="true" output-model="snapshot" diff-algorithm="global"?></xd:xdiff>
db<>fiddle here

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

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

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

Pivoting rows into columns dynamically in Oracle

I have the following Oracle 10g table called _kv:
select * from _kv
ID K V
---- ----- -----
1 name Bob
1 age 30
1 gender male
2 name Susan
2 status married
I'd like to turn my keys into columns using plain SQL (not PL/SQL) so that the resulting table would look something like this:
ID NAME AGE GENDER STATUS
---- ----- ----- ------ --------
1 Bob 30 male
2 Susan married
The query should have as many columns as unique Ks exist in the table (there aren't that many)
There's no way to know what columns may exist before running the query.
I'm trying to avoid running an initial query to programatically build the final query.
The blank cells may be nulls or empty strings, doesn't really matter.
I'm using Oracle 10g, but an 11g solution would also be ok.
There are a plenty of examples out there for when you know what your pivoted columns may be called, but I just can't find a generic pivoting solution for Oracle.
Thanks!
Oracle 11g provides a PIVOT operation that does what you want.
Oracle 11g solution
select * from
(select id, k, v from _kv)
pivot(max(v) for k in ('name', 'age', 'gender', 'status')
(Note: I do not have a copy of 11g to test this on so I have not verified its functionality)
I obtained this solution from: http://orafaq.com/wiki/PIVOT
EDIT -- pivot xml option (also Oracle 11g)
Apparently there is also a pivot xml option for when you do not know all the possible column headings that you may need. (see the XML TYPE section near the bottom of the page located at http://www.oracle.com/technetwork/articles/sql/11g-pivot-097235.html)
select * from
(select id, k, v from _kv)
pivot xml (max(v)
for k in (any) )
(Note: As before I do not have a copy of 11g to test this on so I have not verified its functionality)
Edit2: Changed v in the pivot and pivot xml statements to max(v) since it is supposed to be aggregated as mentioned in one of the comments. I also added the in clause which is not optional for pivot. Of course, having to specify the values in the in clause defeats the goal of having a completely dynamic pivot/crosstab query as was the desire of this question's poster.
To deal with situations where there are a possibility of multiple values (v in your example), I use PIVOT and LISTAGG:
SELECT * FROM
(
SELECT id, k, v
FROM _kv
)
PIVOT
(
LISTAGG(v ,',')
WITHIN GROUP (ORDER BY k)
FOR k IN ('name', 'age','gender','status')
)
ORDER BY id;
Since you want dynamic values, use dynamic SQL and pass in the values determined by running a select on the table data before calling the pivot statement.
Happen to have a task on pivot. Below works for me as tested just now on 11g:
select * from
(
select ID, COUNTRY_NAME, TOTAL_COUNT from ONE_TABLE
)
pivot(
SUM(TOTAL_COUNT) for COUNTRY_NAME in (
'Canada', 'USA', 'Mexico'
)
);
First of all, dynamically pivot using pivot xml again needs to be parsed. We have another way of doing this by storing the column names in a variable and passing them in the dynamic sql as below.
Consider we have a table like below.
If we need to show the values in the column YR as column names and the values in those columns from QTY, then we can use the below code.
declare
sqlqry clob;
cols clob;
begin
select listagg('''' || YR || ''' as "' || YR || '"', ',') within group (order by YR)
into cols
from (select distinct YR from EMPLOYEE);
sqlqry :=
'
select * from
(
select *
from EMPLOYEE
)
pivot
(
MIN(QTY) for YR in (' || cols || ')
)';
execute immediate sqlqry;
end;
/
RESULT