ORA-01489 : result of string concatenation to long - sql

I am creating a dynamic query from a list of tables which are their in my table called : get_table_names
My Query :
SELECT listagg('SELECT '
|| ''''
|| tbl_name
|| ''''
|| ' AS TBL , COUNT(*) as CNT FROM '
|| 'TGT_MB.'
|| tbl_name
|| ' union all'
|| CHAR(10) ) WITH GROUP ( ORDER BY tbl_name) AS sql_str
FROM get_table_names;
My get_table_names as lots of table , at least 70 table names.
The query works fine for 10 tables but more then that it throws the error like below
ORA-01489 : result of string concatenation to long
Their is some option called EXTEND that option I cannot touch as I have low level privileges.
cannot make changes to it until DBA.
Any other work around would be much appreciated like using some XMLAGG or CLOB , BLOB

Can I respectively suggest that when posting a code snippet, make it sure it runs so that people can help you more easily. eg your code above took the following debugging before we could even start to see what you want to do
SQL> SELECT listagg('SELECT '
2 || ''''
3 || table_name
4 || ''''
5 || ' AS TBL , COUNT(*) as CNT FROM '
6 || 'TGT_MB.'
7 || table_name
8 || ' union all'
9 || CHAR(10) ) WITH GROUP ( ORDER BY table_name) AS sql_str
10 FROM dba_tables
11 where owner = 'SCOTT';
|| CHAR(10) ) WITH GROUP ( ORDER BY table_name) AS sql_str
*
ERROR at line 9:
ORA-00936: missing expression
SQL> SELECT listagg('SELECT '
2 || ''''
3 || table_name
4 || ''''
5 || ' AS TBL , COUNT(*) as CNT FROM '
6 || 'TGT_MB.'
7 || table_name
8 || ' union all'
9 || chr(10) ) WITH GROUP ( ORDER BY table_name) AS sql_str
10 FROM dba_tables
11 where owner = 'SCOTT';
|| chr(10) ) WITH GROUP ( ORDER BY table_name) AS sql_str
*
ERROR at line 9:
ORA-00923: FROM keyword not found where expected
Once those fixes are done, we end up here
SQL> SELECT listagg('SELECT '
2 || ''''
3 || table_name
4 || ''''
5 || ' AS TBL , COUNT(*) as CNT FROM '
6 || 'TGT_MB.'
7 || table_name
8 || ' union all'
9 || chr(10),'' ) WITHIN GROUP ( ORDER BY table_name) AS sql_str
10 FROM dba_tables
11 where owner = 'SCOTT';
SQL_STR
-------------------------------------------------------------------------------------
SELECT 'BONUS' AS TBL , COUNT(*) as CNT FROM TGT_MB.BONUS union all
SELECT 'DEPT' AS TBL , COUNT(*) as CNT FROM TGT_MB.DEPT union all
SELECT 'EMP' AS TBL , COUNT(*) as CNT FROM TGT_MB.EMP union all
SELECT 'EMP2' AS TBL , COUNT(*) as CNT FROM TGT_MB.EMP2 union all
SELECT 'SALGRADE' AS TBL , COUNT(*) as CNT FROM TGT_MB.SALGRADE union all
which ultimately blows up when we get too many tables
SQL> SELECT listagg('SELECT '
2 || ''''
3 || table_name
4 || ''''
5 || ' AS TBL , COUNT(*) as CNT FROM '
6 || 'TGT_MB.'
7 || table_name
8 || ' union all'
9 || chr(10),'' ) WITHIN GROUP ( ORDER BY table_name) AS sql_str
10 FROM dba_tables;
FROM dba_tables
*
ERROR at line 10:
ORA-01489: result of string concatenation is too long
Now even if we use a tool (eg
https://github.com/connormcd/listagg_clob) to get the results as a clob, then I imagine you are just going to fire that SQL at the database to get a count of all tables. If that is the case, then why do you need to LISTAGG at all, just build a script to do it, ie
SELECT 'SELECT '
|| ''''
|| table_name
|| ''''
|| ' AS TBL , COUNT(*) as CNT FROM '
|| 'TGT_MB.'
|| table_name
|| ' union all'
FROM dba_tables
Spool that to a file, trim the last union all and run it as a script.
Or even better...think about what benefit the output gives you. Why does anyone need the exact row count? Querying NUM_ROWS from USER_TABLES is probably sufficient.

Related

how to check for duplicate rows of all the columns

I want to check for duplicate rows . and see there column values .
if there were only few columns in my table - 2 for example - I would have done something like:
'''
select col1, col2 ,count(*)
from mytable
group by col1,col2
having count(*) > 1.
'''
but I have dozens of column in my table .... and using the above syntax is tedious to specify all the columns in the table.
trying another approach with select distinct ... will not identify for me the content of duplicated rows .
I tried somthing like
'''
select * , count (*)
from my table
group by *
'''
but that doesn't work.
Write a query which will write a query for you.
For example, "john smith" is a duplicate here:
SQL> select * from my_data order by 1;
FULL_NAME FIRST_NAME LAST_NAME
---------- -------------------- --------------------
h gonzalez h gonzalez
john smith john smith
john smith john smith
rudy chan rudy chan
Query uses user_tab_columns and aggregates all column names, concatenating them to the rest of a select statement:
SQL> SELECT 'select '
2 || LISTAGG (column_name, ', ') WITHIN GROUP (ORDER BY column_id)
3 || ', count(*) cnt '
4 || CHR (10)
5 || ' from '
6 || table_name
7 || CHR (10)
8 || ' group by '
9 || LISTAGG (column_name, ', ') WITHIN GROUP (ORDER BY column_id)
10 || CHR (10)
11 || ' having count(*) > 1;' statement_to_run
12 FROM user_tab_columns
13 WHERE table_name = 'MY_DATA'
14 GROUP BY table_name;
STATEMENT_TO_RUN
--------------------------------------------------------------------------------
select FULL_NAME, FIRST_NAME, LAST_NAME, count(*) cnt
from MY_DATA
group by FULL_NAME, FIRST_NAME, LAST_NAME
having count(*) > 1;
Now, copy/paste the above statement_to_run and get the result:
SQL> select FULL_NAME, FIRST_NAME, LAST_NAME, count(*) cnt
2 from MY_DATA group by
3 FULL_NAME, FIRST_NAME, LAST_NAME having count(*) > 1;
FULL_NAME FIRST_NAME LAST_NAME CNT
---------- -------------------- -------------------- ----------
john smith john smith 2
SQL>
Just write out all the columns.
there are dozens of columns ,about 30 , and there names look like : 'AGtrf-456F_RValue'
Copy-paste.
In SQL*Plus you can use the DESCRIBE command to describe a table and you can copy the column names from the table description.
Or you can list all the columns using:
SELECT '"' || column_name || '",'
FROM user_tab_columns
WHERE table_name = 'MY_DATA'
ORDER BY column_id;
And then copy-paste the output into your query into the SELECT and GROUP BY clauses.
Can you generate the query automatically.
Yes, but it usually is not worth it as it takes longer to write a query to generate the query than it does just to list the columns and copy-paste.
If you have lots of column names that require you to use quoted identifiers (i.e. they are mixed-case or use non-standard characters like -) then you can use:
SELECT EMPTY_CLOB()
|| 'SELECT '
|| LISTAGG('"' || column_name || '"', ',') WITHIN GROUP (ORDER BY column_id)
|| ', COUNT(1) FROM MY_DATA GROUP BY '
|| LISTAGG('"' || column_name || '"', ',') WITHIN GROUP (ORDER BY column_id)
|| ' HAVING COUNT(1) > 1;'
FROM user_tab_columns
WHERE table_name = 'MY_DATA'
ORDER BY column_id;
Which works unless you have too many columns and LISTAGG exceeds 4000 characters then you would need to use something like:
WITH columns (col, pos) AS (
SELECT '"' || column_name || '",',
column_id
FROM user_tab_columns
WHERE table_name = 'MY_DATA'
ORDER BY column_id
)
SELECT sql
FROM (
SELECT 'SELECT ' AS sql, 0 FROM DUAL
UNION ALL
SELECT col, pos FROM columns
UNION ALL
SELECT ' COUNT(1) FROM MY_DATA GROUP BY ', 10000 FROM DUAL
UNION ALL
SELECT col, 10000 + pos FROM columns
UNION ALL
SELECT '1 HAVING COUNT(1) > 1', 20000 FROM DUAL
ORDER BY 2
)
fiddle

Oracle dynamic query (return or select) result as table

How can I show the result of running a dynamic query as a table in the output?
I want to show the result of the following query as a table in the output.
my table :
create table myTable(ROW_NAME varchar(10),COLUMN_NAME varchar(10),COLUMN_NAME_VALUE varchar(10));
table data :
insert into myTable (ROW_NAME,COLUMN_NAME,COLUMN_NAME_VALUE)
select 'ROW1','COL1','R1C1' from dual
union all select 'ROW1','COL2','R1C2' from dual
union all select 'ROW1','COL3','R1C3' from dual
union all select 'ROW2','COL1','R2C1' from dual
union all select 'ROW2','COL2','R2C2' from dual
union all select 'ROW2','COL3','R2C3' from dual
union all select 'ROW3','COL1','R3C1' from dual
union all select 'ROW3','COL2','R3C3' from dual
union all select 'ROW3','COL3','R3C3' from dual
my dynamic query :
DECLARE
mycols VARCHAR2(1000);
sqlCommand varchar2(1000);
TYPE PivotCurTyp IS REF CURSOR;
pivot_cv PivotCurTyp;
type pivotted is record (row_name myTable.row_name%type, col1 myTable.column_name_value%type, col2 myTable.column_name_value%type, col3 myTable.column_name_value%type);
piv_rec pivotted;
BEGIN
select (select LISTAGG('''' || COLUMN_NAME || '''', ',') from myTable group by ROW_NAME FETCH FIRST 1 ROWS ONLY) into mycols from dual;
select Concat('select * from myTable pivot ( max (COLUMN_NAME_VALUE) for COLUMN_NAME in (',Concat(mycols,')) ORDER BY ROW_NAME')) into sqlCommand from dual;
DBMS_OUTPUT.PUT_LINE(sqlCommand);
OPEN pivot_cv FOR sqlCommand;
LOOP
FETCH pivot_cv INTO piv_rec;
EXIT WHEN pivot_cv%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('ROW_NAME: ' || piv_rec.ROW_NAME || ' COL1: ' ||
piv_rec.COL1 || ' COL2: ' || piv_rec.COL2 || ' COL3: ' || piv_rec.COL3);
END LOOP;
CLOSE pivot_cv;
END;
/
demo in db<>fiddle
Thanks for any help
Maybe I misunderstood, I guess what you want is this?
select 'ROW_NAME ' || t1.row_name || ' ' || listagg(t1.column_name || ': ' || t1.column_name_value, ' ')
within group(order by t1.column_name)
from myTable t1
group by t1.row_name
order by t1.row_name

How to quickly see what columns in a table have data?

We are currently undertaking a testing phase which requires us to see if there is any data in each column for each table. Now, the route that is long and labour-intensive is:
SELECT COUNT(Col1), COUNT(Col2)...FROM TABLE
Is there any easier way to do this? We can go down this route by concatenating each column name from our data lineage document with the COUNT() function, but we have a lot of tables and a lot of columns in each table, making this a bit unfeasible.
Essentially we just need a count of records in each column for each table, without having to write long COUNT(Col) queries.
Thanks
This query will return accurate results if the table statistics were recently gathered with the default value for ESTIMATE_PERCENT:
SELECT utab.table_name
, tcol.column_name
, utab.num_rows
from user_tables utab,
user_tab_cols tcol
where utab.table_name = tcol.table_name
and utab.num_rows > 0
and utab.num_rows = tcol.num_nulls;
You could use a dynamic query to build the queries. This will generate all the queries.
SELECT 'SELECT COUNT(' || t.column_name || ' ) FROM ' || t.owner || '.' || t.table_name || ';' FROM dba_tab_columns t
You can generate all the select statements like so:
SELECT CASE WHEN column_id = 1 AND column_id_desc != 1 THEN 'SELECT ''' || LOWER(owner) || '.' || LOWER(table_name) || ''' table_name, ' || CHR(10) || 'COUNT(' || LOWER(column_name) || ') ' || SUBSTR(LOWER(column_name), 1, 26) || '_cnt,'
WHEN column_id = 1 AND column_id_desc = 1 THEN 'SELECT ''' || LOWER(owner) || '.' || LOWER(table_name) || ''' table_name, ' || CHR(10) || 'COUNT(' || LOWER(column_name) || ') ' || SUBSTR(LOWER(column_name), 1, 26) || '_cnt FROM ' || LOWER(owner) || '.' || LOWER(table_name) || ';'
WHEN column_id_desc = 1 THEN ' COUNT(' || LOWER(column_name) || ') ' || SUBSTR(LOWER(column_name), 1, 26) || '_cnt' || CHR(10) || 'FROM ' || LOWER(owner) || '.' || LOWER(table_name) || ';'
ELSE ' COUNT(' || LOWER(column_name) || ') ' || SUBSTR(LOWER(column_name), 1, 26) || '_cnt,'
END sql_text
FROM (SELECT owner,
table_name,
column_name,
column_id,
row_number() OVER (PARTITION BY owner, table_name ORDER BY column_id DESC) column_id_desc
FROM all_tab_columns)
WHERE <predicates to filter on the tables you're interested in>
ORDER BY owner,
table_name,
column_id;
This goes through all the tables you're interested in plus their columns and outputs text that will, when taken together, form a select statement for each table.
The text that is output in the sql_text column depends on whether the column in the list is the first or last (or both!); this way you get the full statement which queries each table once, rather than one per table and column.
You can then copy and paste the results and run that as a script.
It's can help you
SELECT
a.table_name,
a.column_name
FROM
ALL_TAB_COLUMNS a
WHERE owner = '<your user>'
AND a.SAMPLE_SIZE = a.NUM_NULLS

Executing resulting rows for the result of Dynamic Native SQL query

I'm going mental over this. I'm fairly new to dynamic SQL, so I may just not be asking Google the right question, but here's what I'm trying to do... I have a query with dynamic SQL. When I run that query, it produces several rows. All of these rows (about 30) make up a single union query. I can copy all of those rows and paste into a new query and run - works fine, but what I need to do is run this all in a single query. I've looked up examples of using execute immediate and fetch, but I cannot seem to get them to actually spit out the data...they just end up saying something like "Executed Successfully", but doesn't actually produce any resulting rows. The resulting column name of the below SQL is "qry_txt" - instead of producing it at face value, I want to execute it as a query. Again, I may not be articulating this well, but I'm basically trying to turn 2 queries (with a manual copy/paste step involved) into a single query. Hope this makes sense...
Here's my SQL:
Select CASE when
lead(ROWNUM) over(order by ROWNUM) is null then
'SELECT '||''''||T.TABLE_NAME||''''||' as TABLE_NAME,'||''''||T.COLUMN_NAME||''''||' as COLUMN_NAME, cast('|| T.COLUMN_NAME ||' as
varchar2(100)) as SAMPLE_DATA ||
from rpt.'||T.TABLE_NAME ||' where '||T.COLUMN_NAME||' is not null and ROWNUM=1;'
else
'SELECT '||''''||T.TABLE_NAME||''''||' as TABLE_NAME,'||''''||T.COLUMN_NAME||''''||' as COLUMN_NAME, cast('|| T.COLUMN_NAME ||' as
varchar2(100)) as SAMPLE_DATA from rpt.'||T.TABLE_NAME ||' where '||T.COLUMN_NAME||' is not null and ROWNUM=1 union ' end as qry_txt
from all_tab_columns t where T.OWNER='rpt' and T.DATA_TYPE != 'BLOB' and T.DATA_TYPE != 'LONG' and T.TABLE_NAME = 'NME_DMN'
ORDER BY ROWNUM asc;
You cannot write a dynamic query in a SQL. You need to use PLSQL block to accomploish that. Please see how you can do it.
PS: Code is not tested.
declare
var1 <decalration same of column in select list> ;
var2 <decalration same of column in select list> ;
var3 <decalration same of column in select list> ;
....
varn ;
begin
for i in ( SELECT LEAD (ROWNUM) OVER (ORDER BY ROWNUM) COl1
FROM all_tab_columns t
WHERE T.OWNER = 'rpt'
AND T.DATA_TYPE != 'BLOB'
AND T.DATA_TYPE != 'LONG'
AND T.TABLE_NAME = 'NME_DMN'
ORDER BY ROWNUM ASC)
Loop
If i.col1 IS NULL Then
execute immediate 'SELECT '
|| ''''
|| T.TABLE_NAME
|| ''''
|| ' as TABLE_NAME,'
|| ''''
|| T.COLUMN_NAME
|| ''''
|| ' as COLUMN_NAME, cast('
|| T.COLUMN_NAME
|| ' as
varchar2(100)) as SAMPLE_DATA ||
from rpt.'
|| T.TABLE_NAME
|| ' where '
|| T.COLUMN_NAME
|| ' is not null and ROWNUM=1' into var1 , var2 ,var3 ....varn;
Else
execute immediate 'SELECT '
|| ''''
|| T.TABLE_NAME
|| ''''
|| ' as TABLE_NAME,'
|| ''''
|| T.COLUMN_NAME
|| ''''
|| ' as COLUMN_NAME, cast('
|| T.COLUMN_NAME
|| ' as
varchar2(100)) as SAMPLE_DATA from rpt.'
|| T.TABLE_NAME
|| ' where '
|| T.COLUMN_NAME
|| ' is not null and ROWNUM=1' into var1 , var2 ,var3 ....varn;
end if;
End Loop;
exception
when others then
dbms_output.put_lin(sqlcode ||'--'||sqlerrm);
End;

Comparing column by column between two rows in Oracle DB

I need to write a query to compare column by column (ie: find differences) between two rows in the database. For example:
row1: 10 40 sometext 24
row2: 10 25 sometext 24
After the query executed, it should shows only the fields that have difference (ie: the second field)
Here's what I have done so far:
select table1.column1, table1.column2, table1.column3, table1.column4
from table1
where somefield in (field1, field2);
The above query will show me two rows one above another like this:
10 40 sometext 24
10 25 sometext 24
Then I have to manually do the comparison and it takes a lot of time b/c the row contains a lot of column.
So again my question is: How can I write a query that will show me only the columns that have differences??
Thanks
Use UNPIVOT clause (see http://www.oracle-developer.net/display.php?id=506) to turn columns into rows, then filter out the same rows (using GROUP BY HAVING COUNT and finally use PIVOT to get rows with different columns only.
To do this easily you need to query the metadata for the table to get each row. You can use the following code as a script.
Replace the define table_name with your table name and define yes_drop_it = NO. Put your raw WHERE syntax into the where_clause. The comparison logic always compares the first two rows returned for the where clause.
whenever sqlerror exit failure rollback;
set linesize 150
define test_tab_name = tst_cf_cols
define yes_drop_it = YES
define order_by = 1, 2
define where_clause = 1 = 1
define tab_owner = user
<<clearfirst>> begin
for clearout in (
select 'drop table ' || table_name as cmd
from all_tables
where owner = &&tab_owner and table_name = upper('&&test_tab_name')
and '&&yes_drop_it' = 'YES'
) loop
execute immediate clearout.cmd;
execute immediate '
create table &&test_tab_name as
select 10 as column1, 40 as column2, ''sometext'' as column3, 24 as column4 from dual
union all
select 10 as column1, 25 as column2, ''sometext'' as column3, 24 as column4 from dual
';
end loop;
end;
/
column cfsynt format a4000 word_wrap new_value comparison_syntax
with parms as (select 'parmquery' as cte_name, 'row_a' as corr_name_1, 'row_b' as corr_name_2 from dual)
select
'select * from (select ' || LISTAGG(cfcol || ' AS cf_' || trim (to_char (column_id, '000')) || '_' || column_name
, chr(13) || ', ') WITHIN GROUP (order by column_id)
|| chr(13) || ' from (select * from parmquery where row_number = 1) ' || corr_name_1
|| chr(13) || ', (select * from parmquery where row_number = 2) ' || corr_name_2
|| chr(13) || ') where ''DIFFERENT'' IN (' || LISTAGG ('cf_' || trim (to_char (column_id, '000')) || '_' || column_name, chr(13) || ', ') within group (order by column_id) || ')'
as cfsynt
from parms, (
select
'decode (' || corr_name_1 || '.' || column_name || ', ' || corr_name_2
|| '.' || column_name || ', ''SAME'', ''DIFFERENT'')'
as cfcol,
column_name,
column_id
from
parms,
all_tab_columns
where
owner = &&tab_owner and table_name = upper ('&&test_tab_name')
);
with parmquery as (select rownum as row_number, vals.* from (
select * from &&test_tab_name
where &&where_clause
order by &&order_by
) vals
) &&comparison_syntax
;