Get max(length(column)) for all columns in an Oracle table - sql

I need to get the maximum length of data per each column in a bunch of tables. I'm okay with doing each table individually but I'm looking for a way to loop through all the columns in a table at least.
I'm currently using the below query to get max of each column-
select max(length(exampleColumnName))
from exampleSchema.exampleTableName;
I'm basically replacing the exampleColumnName with each column in a table.
I've already went through 3-4 threads but none of them were working for me either because they weren't for Oracle or they had more details that I required (and I couldn't pick the part I needed).
I'd prefer to have it in SQL than in PLSQL as I don't have any create privileges and won't be able to create any PLSQL objects.

Got the below query to work -
DECLARE
max_length INTEGER; --Declare a variable to store max length in.
v_owner VARCHAR2(255) :='exampleSchema'; -- Type the owner of the tables you are looking at
BEGIN
-- loop through column names in all_tab_columns for a given table
FOR t IN (SELECT table_name, column_name FROM all_tab_cols where owner=v_owner and table_name = 'exampleTableName') LOOP
EXECUTE IMMEDIATE
-- store maximum length of each looped column in max_length variable
'select nvl(max(length('||t.column_name||')),0) FROM '||t.table_name
INTO max_length;
IF max_length >= 0 THEN -- this isn't really necessary but just to ignore empty columns. nvl might work as well
dbms_output.put_line( t.table_name ||' '||t.column_name||' '||max_length ); --print the tableName, columnName and max length
END IF;
END LOOP;
END;
Do let me know if the comments explain it sufficiently, else I'll try to do better. Removing table_name = 'exampleTableName' might loop for all tables as well, but this is okay for me right now.

You can try this; although it uses PL/SQL it will work from within SQL-Plus. It doesn't loop. Hopefully you don't have so many columns that the SELECT query can't fit in 32,767 characters!
SET SERVEROUTPUT ON
DECLARE
v_sql VARCHAR2(32767);
v_result NUMBER;
BEGIN
SELECT 'SELECT GREATEST(' || column_list || ') FROM ' || table_name
INTO v_sql
FROM (
SELECT table_name, LISTAGG('MAX(LENGTH(' || column_name || '))', ',') WITHIN GROUP (ORDER BY NULL) AS column_list
FROM all_tab_columns
WHERE owner = 'EXAMPLE_SCHEMA'
AND table_name = 'EXAMPLE_TABLE'
GROUP BY table_name
);
EXECUTE IMMEDIATE v_sql INTO v_result;
DBMS_OUTPUT.PUT_LINE(v_result);
END;
/

Related

ORACLE SQL: Looping over table

I have a table that contains a list of table names.
I would like to search each of these tables one by one to see if they contain a particular element (the primary key, specified at the start of the script).
I would like to return a list of all of the tables that this element is present in (ideally distinct).
I'm fairly new to this PL/SQL "not just a query" stuff. so i apologise in advance for the attrocious attempt you are about to see, but hopefully it illustrates what i'm going for:
PROCEDURE CHECK_FOR_ELEMENTS
BEGIN
DECLARE
ELEMENT_KEY varchar(5):=X78ehryfk;
RNUM_MAX int :=167;
----create output table for script
create or replace table ALL_TABLES CONTAINING_&ELEMENT_KEY
(ELEMENT_KEY VARCHAR(255),
TABLE_NAME varchar(255))
/
commit;
---begin loop over rnum;
FOR rnum_counter in 1..&RNUM_MAX
LOOP
--define this statement as variable TABLE_NAME_VAR
select table_name from (select * from (select table_name, rownum as rnum
from all_tables
where owner = 'RMS'
and table_name like 'ABC%'
and table_name not like '%STG'
and table_name not like '%BKP'
and num_rows>0
order by num_rows desc)
where rnum = rnum_counter
)INTO TABLE_NAME_VAR
;
----run below to collect row, if it exists, from table being searched
SQL_STMT:='INSERT INTO ALL_TABLES CONTAINING_&ELEMENT_KEY
SELECT distinct key,'||TABLE_NAME_VAR||' as UMF from
'||TABLE_NAME_VAR||
' where key like 'ELEMENT_KEY-%'
execute immediate SQL_STMT;
commit;
---insert row into table created for output
END LOOP
---loop over all tables
END;
The main error message i get is that TABLE_NAME_VAR is not a valid table name within the dynamic SQL statement. I've googled a bit and i now understand you can't use variables to input table names in this way.
Any help is greatly appreciated!
Thankyou!
Here, I tried to clean it up for you. Let me know if you still get errors.
create or replace PROCEDURE CHECK_FOR_ELEMENTS is
ELEMENT_KEY varchar2(14):='X78ehryfk';
RNUM_MAX int :=167;
TABLE_NAME_VAR varchar2(30);
SQL_STMT varchar2(4000);
BEGIN
----create output table for script
begin
execute immediate 'drop table ALL_TABLES_WITH_' || element_key;
exception when others then null;
end;
execute immediate 'create table ALL_TABLES_WITH_' || element_key || '
(ELEMENT_KEY VARCHAR2(255), -- does this need to be 255 characters?
TABLE_NAME varchar2(30))';
--- implicit cursor loop
FOR rnum_row in (select table_name, rownum as rnum
from all_tables
where owner = 'RMS'
and table_name like 'ABC%'
and table_name not like '%STG'
and table_name not like '%BKP'
and num_rows>0
order by num_rows desc)
LOOP
if rnum_row.rnum > RNUM_MAX
then exit;
end if;
TABLE_NAME_VAR := rnum_row.table_name;
----run below to collect row, if it exists, from table being searched
SQL_STMT:='INSERT INTO ALL_TABLES_WITH_' || element_key || '
(ELEMENT_KEY, TABLE_NAME)
SELECT distinct key, :1 as UMF from
'||TABLE_NAME_VAR||
' where key like :2';
execute immediate SQL_STMT using TABLE_NAME_VAR, element_key || '-%';
---insert row into table created for output
END LOOP;
commit;
---loop over all tables
END CHECK_FOR_ELEMENTS;
/

Get all ids in a database

I want to get all the ids in a database from all columns that have an ID field.
My script so far is:
BEGIN
FOR tname IN (select table_name from all_tab_columns where column_name = 'ID' and owner='PACC_USER') LOOP
EXECUTE IMMEDIATE
'select unique id from ' || tname;
END LOOP;
End;
I get the error PLS-00306: wrong number or types of arguments in call to '||'. What is the problem exactly? Any help is welcomed :)
In your code tname is a record for referencing the cursor result set i.e. a namespace not an attribute. Fix it like this:
BEGIN
FOR tname IN (select table_name
from all_tab_columns
where column_name = 'ID' and owner='PACC_USER')
LOOP
EXECUTE IMMEDIATE
'select unique id from ' || tname.table_name ;
END LOOP;
End;
One would hope that a column called ID would return unique rows without needing the unique keyword but we live in troubled times.
Your code needs to select results into something: PL/SQL is not T-SQL, it requires target variables. So let's improve your code a bit more.
declare
ids_nt sys.dbms-debug_vc2coll;
BEGIN
FOR tname IN (select table_name
from all_tab_columns
where column_name = 'ID' and owner='PACC_USER')
LOOP
EXECUTE IMMEDIATE
'select unique id from ' || tname.table_name
bulk collect into ids_nt;
dbms_output.put_line('IDS for table '|| tname.table_name);
for idx in ids_nt.first() .. ids_nt.last loop
dbms_output.put_line(ids_nt(idx));
end loop;
END LOOP;
End;
Maybe this isn't the kind of thing you want to do with the IDs. If so please edit your question to clarify your intent.
Select column_name,table name from all_tab_column where upper(column_name)=upper('Id') ;

sql query to fetch all tables in a schema that was updated on sysdate [duplicate]

This question already has answers here:
How to find out when an Oracle table was updated the last time
(11 answers)
Closed 9 years ago.
need to write a sql query to fetch all tables in a schema
that was updated on sysdate.
select distinct(table_name)
from All_Tab_Columns
where owner = 'DBO'
and last_analyzed = sysdate;
It doesn't seem to work properly.
You need to apply TRUNC function on last_analyzed and sysdate and then it will work
select distinct(table_name)
from All_Tab_Columns
where owner = 'DBO'
and trunc(last_analyzed) = trunc(sysdate);
As mentioned in answers to the question I linked to, you can use the ORA_ROWSCN pseudo-column to get an idea of when the table was last updated. This will example all tables in your schema and list those which were modified on the specified date, according to the ORA_ROWSCN. This may take a while to run, of course.
set serveroutput on
declare
last_update varchar2(10);
bad_scn exception;
no_scn exception;
pragma exception_init(bad_scn, -8181);
pragma exception_init(no_scn, -1405);
begin
for r in (select table_name from all_tables where owner = 'DBO') loop
begin
execute immediate 'select to_char(scn_to_timestamp(max(ora_rowscn)), '
|| '''YYYY-MM-DD'') from DBO.' || r.table_name
into last_update;
if last_update = '2014-02-21' then
dbms_output.put_line(r.table_name || ' last updated on ' || last_update);
end if;
exception
when bad_scn then
dbms_output.put_line(r.table_name || ' - bad scn');
when no_scn then
dbms_output.put_line(r.table_name || ' - no scn');
end;
end loop;
end;
/
The exception handlers are covering views (which are listed but have no SCN), and where there is an invalid SCN for some reason; you may want to ignore those rather than displaying them.
If you are only looking for today, not a specific date, then this might be faster:
declare
start_scn number;
changed_rows number;
changed_tables number := 0;
begin
start_scn := timestamp_to_scn(trunc(systimestamp));
for r in (select table_name from all_tables where owner = 'BDO'
order by table_name) loop
execute immediate 'select count(*) from ('
|| 'select ora_rowscn from BDO.' || r.table_name
|| ') where ora_rowscn >= :1 and rownum < 2'
into changed_rows
using start_scn;
if changed_rows > 0 then
dbms_output.put_line(r.table_name || ' updated');
changed_tables := changed_tables + 1;
end if;
end loop;
dbms_output.put_line(changed_tables || ' tables updated today');
end;
/
You could do the same thing for any date really but you'd need to find the earliest and latest SCN for that day (which is more complicated for the current date). Also note that this may only work within your flashback window - if you go back to far you won't be able to translate an SCN to a timestamp anyway.
There is no easy way to do that. You have to operate table by table. Then execute this query on each table:
select max(SCN_TO_TIMESTAMP(ORA_ROWSCN)) from <table_name>;
ORA_ROWSCN is Oracle virtual pseudo-column, it is stored on block level. It contains "a sequence number" of the last transaction, which modified the database block.
The function SCN_TO_TIMESTAMP converts it into human readable date datatype.
You can use DMV (works on HEAPs as well - i.e. tables with no indexes) - you can expand to join on schemas
SELECT
OBJECT_NAME(OBJECT_ID) AS TableName, last_user_update AS UpdateDateTime
FROM
sys.dm_db_index_usage_stats
WHERE
database_id = DB_ID('PUT_DB_NAME')
AND last_user_update = 'EnterDateTimeHereToFilterOn'

Retrieving Column Names and Data types in PLSQL

I am writing a PL SQL block that retrieves all the columns and the data types of the tables in the database. I am able to get the columns , but not the datatypes. Looking for suggestions for a good approach. Any help would be appreciated. My code is as follows
ACCEPT p_1 PROMPT 'Please enter the Table Name'
DECLARE
v_table_name VARCHAR2(40) :='&p_1';
-- First cursor
CURSOR get_tables IS
SELECT DISTINCT table_name
FROM user_tables
WHERE UPPER(table_name) = UPPER(v_table_name);
--Second cursor
CURSOR get_columns IS
SELECT DISTINCT column_name
FROM user_tab_columns
WHERE table_name = v_table_name;
v_column_name VARCHAR2(100);
-- Third Cursor
CURSOR get_types IS
SELECT data_type
FROM user_tab_columns
WHERE table_name = v_table_name;
v_data_type user_tab_columns.data_type%type;
BEGIN
-- Open first cursor
OPEN get_tables;
FETCH get_tables INTO v_table_name;
DBMS_OUTPUT.PUT_LINE(' ');
DBMS_OUTPUT.PUT_LINE('Table = ' || v_table_name );
DBMS_OUTPUT.PUT_LINE('=========================');
CLOSE get_tables;
-- Open second cursor
OPEN get_columns;
FETCH get_columns INTO v_column_name;
WHILE get_columns%FOUND LOOP
DBMS_OUTPUT.PUT_LINE(' ' || v_column_name);
FETCH get_columns INTO v_column_name;
END LOOP;
CLOSE get_columns;
--Open Third Cursor
OPEN get_types;
FETCH get_types into v_data_type;
WHILE get_types%FOUND LOOP
DBMS_OUTPUT.PUT_LINE(' ' || v_data_type );
FETCH get_types into v_data_type;
END LOOP;
CLOSE get_types;
END;
My error states PLS-00371: at most one declaration for 'V_DATA_TYPE' is permitted
Not a PLSQL guru but here's my grain.
Select data_type from user_tab_columns where TABLE_NAME = 'YourTableName'
Props to Eric, check this thread and his answer.
Remember you can use DESC command to describe an Oracle Table, View, Synonym, package or Function. It will give you name, data_type and lengh.
And if this actually works for you, you should be able to get the data for all of your tables, although I'm not a huge fan of cursors, you should do fine.
Try this:
-- Open second cursor
OPEN get_columns;
LOOP
FETCH get_columns INTO v_column_name, v_data_type;
EXIT WHEN get_columns%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(' ' || v_column_name);
END LOOP;
CLOSE get_columns;
END LOOP;
But be careful on the datatype you've chosen for v_data_type variable.
Good effort, but too much code. you need a short vacation :)
SELECT table_name,
column_name,
data_type,
data_length,
nullable
FROM cols
WHERE table_name='&YOUR_TABLE'
ORDER BY column_id
Your block has way too much code. This is all you need:
begin
for r in ( select column_name, data_type
from user_tab_columns
where table_name = upper('&&p_1')
order by column_id )
loop
dbms_output.put_line(r.column_name ||' is '|| r.data_type );
end loop;
end;
I encountered a similar problem. It can be viewed here: Retrieving Table Structure with Dynamic SQL.
Of things to note, I made sure that if data_scale = 0 I indicated it was to be a Integer, and if it was >0, it was a Double.

How to find Number of null column in table using PL/SQL

A database has a lot of columns (more than 100). Some of these columns have null entries. How can I find out how many columns have null entries in at least one row, without manually testing each and every column?
Try:
declare
l_count integer;
begin
for col in (select table_name, column_name
from user_tab_columns where table_name='EMP')
loop
execute immediate 'select count(*) from '||col.table_name
||' where '||col.column_name
||' is not null and rownum=1'
into l_count;
if l_count = 0 then
dbms_output.put_line ('Column '||col.column_name||' contains only nulls');
end if;
end loop;
end;
Try analyzing your table (compute statistics, don't estimate) and then (immediately) do:
select column_name, num_nulls
from all_tab_columns
where table_name = 'SOME_TABLENAME'
and owner = 'SOME_OWNER';
Of course as data later changes, this will become slightly more incorrect. If you need to get more fancy and do a field population count (fieldpop), then you'll need to loop through all rows and check for nulls explicitly (and exclude any other values you deem "not populated", perhaps a default of 0 for a number field for example).
I can give you the direction in which to research:
Check "user_tab_columns" through which you can get information related to columns in a table.
E.g.
select count(*) from user_tab_columns where table_name = 'YOURTABLENAME'
This gives you the number of columns in that table.
Together with this you would need to use a cursor, i think, to check each column for null values rather than adding a null check in WHERE clause for each column.
This will give you the number of NULL column values per row of data:
declare
TYPE refc IS REF CURSOR;
col_cv refc;
l_query varchar(3999);
v_rownum number;
v_count number;
begin
l_query := 'select rownum, ';
for col in (select table_name, column_name
from user_tab_columns where table_name='EMP')
loop
l_query := l_query ||'DECODE('||col.column_name||',NULL,1,0)+';
end loop;
l_query := l_query||'+0 as no_of_null_values from EMP';
DBMS_OUTPUT.PUT_LINE(l_query);
OPEN col_cv FOR l_query;
LOOP
FETCH col_cv into v_rownum, v_count;
EXIT WHEN col_cv%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_rownum || ' ' || v_count);
END LOOP;
CLOSE col_cv;
end;
I feel dirty even writing it! (It won't work when the number of columns in the table is very large and l_query overflows).
You just need to change the table name (EMP above).