How to print first 5 rows of all the tables in an oracle database (schema)? - sql

I want to open a database (schema) which holds about 100 tables,
Once it's opened I would like to print the top 5 rows of all the tables along with their column names.
My first step was to try and get the schema and table names in the database.
so this is what I used for that.
SELECT owner, table_name
FROM all_tables
The expected result will contain the table name, column names and top 5 rows of the table.
I am using Oracle SQL Developer ( Oracle database)

You can try this if you're running Oracle 12c and above.
SET SERVEROUTPUT ON
SET FEEDBACK OFF
DECLARE
x SYS_REFCURSOR;
BEGIN
FOR tabs IN ( SELECT rownum as rn,owner,table_name
FROM all_tables where owner='HR'
and ROWNUM < 10
) LOOP
OPEN x FOR 'SELECT '''|| tabs.owner||'.'|| tabs.table_name||'''
as "table" FROM DUAL';
dbms_sql.return_result(x);
OPEN x FOR 'select * from '
|| tabs.owner
||'.'
|| tabs.table_name
|| ' FETCH FIRST 5 ROWS ONLY ';
dbms_sql.return_result(x);
END LOOP;
END;
/
Output
table
----------
HR.REGIONS
REGION_ID REGION_NAME
---------- -------------------------
1 Europe
2 Americas
3 Asia
4 Middle East and Africa
table
------------
HR.LOCATIONS
LOCATION_ID STREET_ADDRESS POSTAL_CODE CITY STATE_PROVINCE CO
----------- ---------------------------------------- ------------ ------------------------------ ------------------------- --
1000 1297 Via Cola di Rie 00989 Roma IT
1100 93091 Calle della Testa 10934 Venice IT
1200 2017 Shinjuku-ku 1689 Tokyo Tokyo Prefecture JP
1300 9450 Kamiya-cho 6823 Hiroshima JP
1400 2014 Jabberwocky Rd 26192 Southlake Texas US
table
--------------
HR.DEPARTMENTS
DEPARTMENT_ID DEPARTMENT_NAME MANAGER_ID LOCATION_ID
------------- ------------------------------ ---------- -----------
10 Administration 200 1700
20 Marketing 201 1800
30 Purchasing 114 1700
40 Human Resources 203 2400
50 Shipping 121 1500

Normally we would use dynamic SQL for this sort of thing. But it's messy to handle the output of 100 different tables in PL/SQL. So, you need to generate a script.
Step 1: Run this in SQL Developer.
select 'prompt ' || owner ||'.' || table_name ||chr(10)
||'select * from ' || owner ||'.' || table_name
|| ' where rownum <=5;'
from all_tables
order by owner, table_name;
Step 2: Cut'n'paste the output into a SQL Worksheet. Unless you're luckier than I am you'll need to edit the annoying double quotes at the start and end of each line (a SQL Developer feature caused by the line break, I think).
Step 3: Run the (edited) output as a script and the Script Output pane will give you the results you want.
Is it possible for me to feed the schema(database) name
Add where owner = '&schema_name' after from all_tables. Or just use USER_TABLES if you're interested in the schema to which you're connected.

Following is the code which you are looking for. I have used USER_TAB_COLUMNS view so that only tables owned by your schema in which you are executing the code is considered.
Feel free to format the output according to your need.
SET SERVEROUT ON
DECLARE
LV_QUERY VARCHAR2(32767);
LV_RESULT VARCHAR2(32767);
BEGIN
DBMS_OUTPUT.PUT_LINE('---------------------------------------');
DBMS_OUTPUT.PUT_LINE('---------------------------------------');
FOR I IN (
SELECT
C.TABLE_NAME,
LISTAGG(C.COLUMN_NAME, ' || ''****'' || ') WITHIN GROUP(
ORDER BY
C.COLUMN_ID
) COLS
FROM
USER_TAB_COLUMNS C
GROUP BY
C.TABLE_NAME
ORDER BY
1
) LOOP
LV_QUERY := 'SELECT '
|| q'#RTRIM(XMLAGG(XMLELEMENT(E,VALS,CHR(10)).EXTRACT('//text()') ORDER BY 1).GetClobVal(),CHR(10)) FROM#'
|| '(SELECT '
|| I.COLS
|| ' AS VALS FROM '
|| I.TABLE_NAME
|| ' FETCH FIRST 5 ROWS ONLY)';
BEGIN
-- DBMS_OUTPUT.PUT_LINE(lv_query);
EXECUTE IMMEDIATE LV_QUERY
INTO LV_RESULT;
DBMS_OUTPUT.PUT_LINE(CHR(10));
DBMS_OUTPUT.PUT_LINE('TABLE NAME : ' || I.TABLE_NAME);
DBMS_OUTPUT.PUT_LINE('COLUMN NAMES : '
|| REPLACE(REPLACE(REPLACE(I.COLS, '*', ''), '||', ''), '''', '**'));
DBMS_OUTPUT.PUT_LINE(LV_RESULT);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('NO DATA IN TABLE : ' || I.TABLE_NAME);
END;
DBMS_OUTPUT.PUT_LINE(CHR(10));
DBMS_OUTPUT.PUT_LINE('---------------------------------------');
DBMS_OUTPUT.PUT_LINE('---------------------------------------');
END LOOP;
END;
Hope this is what you are looking for.

Related

How to read the same column from every table in a database?

I have a huge database with 400+ tables. Each table has the same column id for the Primary key and "timestamp_modify" in which the last change of the table is done.
So what I want are 2 things:
Now I want a list of all changes by ID and table name like:
Table | id | timestamp_modiy
Kid | 1 | 24.10.2021 00:01
Parent | 1000 | 24.10.2021 00:02
The only, very bad way I could come up with, is that I make a view in which I include every damn table by hand and read out the values...
Is there a better way?
How about a pipelined function?
Just setting datetime format (you don't have to do that):
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi:ss';
Session altered.
Types:
SQL> create or replace type t_row as object
2 (table_name varchar2(30),
3 id number,
4 timestamp_modify date)
5 /
Type created.
SQL> create or replace type t_tab is table of t_row;
2 /
Type created.
Function: querying user_tab_columns, its cursor FOR loop fetches tables that contain both ID and TIMESTAMP_MODIFY columns, dynamically creates select statement to return the last (MAX function, to avoid too_many_rows) columns' values for the last TIMESTAMP_MODIFY value (returned by the subquery).
SQL> create or replace function f_test
2 return t_tab pipelined
3 as
4 l_str varchar2(500);
5 l_id number;
6 l_timestamp_modify date;
7 begin
8 for cur_r in (select table_name from user_tab_columns
9 where column_name = 'ID'
10 intersect
11 select table_name from user_tab_columns
12 where column_name = 'TIMESTAMP_MODIFY'
13 )
14 loop
15 l_str := 'select max(a.id) id, max(a.timestamp_modify) timestamp_modify ' ||
16 'from ' || cur_r.table_name || ' a ' ||
17 'where a.timestamp_modify = ' ||
18 ' (select max(b.timestamp_modify) ' ||
19 ' from ' || cur_r.table_name || ' b ' ||
20 ' where b.id = a.id)';
21 execute immediate l_str into l_id, l_timestamp_modify;
22 pipe row(t_row(cur_r.table_name, l_id, l_timestamp_modify));
23 end loop;
24 end;
25 /
Function created.
Testing:
SQL> select * from table(f_test);
TABLE_NAME ID TIMESTAMP_MODIFY
------------------------------ ---------- -------------------
TABA 1 24.10.2021 14:59:29
TAB_1 1 24.10.2021 15:03:16
TAB_2 25 24.10.2021 15:03:36
TEST 5 24.10.2021 15:04:24
SQL>
Yes, the only way is to union all all tables, like:
select id, timestamp_modify
from kid
union all
select id, timestamp_modify
from parent
union all
...
The performance will be awful, since all the tables will be scanned every time :(
I think that you might reconsider you db design...
You can build a procedure for this, but even so it will have some impact in performance. Although there is a loop, with SQL Dynamic, you might only need 400 iterations, and in each one you will insert all the ids of that table.
I am taking some assumptions
You want all the IDs and their corresponding timestamp_modify per table
I create a table to store the results. If you use it with the same name always it will recycle the object. If you not, you can keep a history
I am assuming that only one timestamp_modify row is present per ID
I filter only the tables of your schema that contain both columns.
The table contains also the table_name that you can identify where the record is coming from.
One example
create or replace procedure pr_build_output ( p_tmp_table in varchar2 default 'TMP_RESULT' )
is
vcounter pls_integer;
vsql clob;
vtimestamp date; -- or timestamp
begin
-- create table to store results
select count(*) into vcounter from all_tables where table_name = upper(p_tmp_table) and owner = 'MY_SCHEMA';
if vcounter = 1
then
execute immediate ' drop table '||p_tmp_table||' purge ' ;
end if;
vsql := ' create table '||p_tmp_table||'
( table_name varchar2(128) ,
id number,
timestamp_modify date -- or timestamp
) ';
execute immediate vsql ;
-- Populate rows
for h in
( select a.table_name from all_tables a
where a.owner = 'MY_SCHEMA'
and a.table_name in ( select distinct b.table_name from all_tab_columns b where b.owner = 'MY_SCHEMA'
and b.column_name = 'ID' and b.column_name = 'TIMESTAMP_MODIFY'
)
)
loop
vsql := ' insert into '||p_tmp_table||' ( table_name , id, timestamp_modify )
select '''||h.table_name||''' as table_name , id , timestamp_modify
from my_schema.'||h.table_name||'
' ;
execute immediate vsql ;
commit ;
end loop;
exception when others then raise;
end;
/

Extract table_name, column_name, data_type, data_length and example of value in Oracle DB

I'm facing a strange request at my job regarding the extraction of the structure of a database. In this extraction the requirer needs the table name, the column name, the column type and the maximum length for VARCHAR columns. In addition it was asked if there is the possibility to provide also an example of value.
This is my query, at the moment
SELECT
table_name,
column_name,
data_type,
CASE WHEN (data_type = 'VARCHAR' OR data_type = 'VARCHAR2') THEN TO_CHAR(char_length) ELSE '-' END AS char_length
FROM all_tab_cols
WHERE owner = 'DB_OWNER'
ORDER BY table_name;
Is there a chance to make a similar query adding also an example of a value contained in each column?
Personally I think it is not possible unless using a very complex (and heavy) stored procedure, but probably someone more skilled than me in database administration has a better input.
Thanks in advance
Dynamic SQL is what you need. For example:
SQL> SET SERVEROUTPUT ON
SQL>
SQL> DECLARE
2 l_val VARCHAR2 (20);
3 BEGIN
4 FOR cur_r IN ( SELECT table_name,
5 column_name,
6 data_type,
7 CASE
8 WHEN ( data_type = 'VARCHAR'
9 OR data_type = 'VARCHAR2')
10 THEN
11 TO_CHAR (char_length)
12 ELSE
13 '-'
14 END AS char_length
15 FROM all_tab_cols
16 WHERE owner = 'SCOTT'
17 AND table_name IN ('DEPT', 'EMP')
18 ORDER BY table_name)
19 LOOP
20 EXECUTE IMMEDIATE 'select max('
21 || cur_r.column_name
22 || ') from '
23 || cur_r.table_name
24 INTO l_val;
25
26 DBMS_OUTPUT.put_line (
27 RPAD (cur_r.table_name || '.' || cur_r.column_name, 15, ' ')
28 || ': '
29 || l_val);
30 END LOOP;
31 END;
32 /
DEPT.DEPTNO : 40
DEPT.LOC : NEW YORK
DEPT.DNAME : test
EMP.MGR : 7902
EMP.HIREDATE : 12.01.1983 00:00
EMP.SAL : 5000
EMP.COMM : 1400
EMP.DEPTNO : 30
EMP.ENAME : WARD
EMP.EMPNO : 7934
EMP.JOB : SALESMAN
PL/SQL procedure successfully completed.
SQL>

Oracle:List all tables in the database with a column_value = '%value%' for a specific date range?

I need to list out all tables in the database where a particular employee made changes to records. I'm looking for a query in oracle to list out all tables where the employee_name column = 'person_name' and for date > 'sample_date'. is this possible ?
As you said - dynamic SQL helps. For example, based on Scott's sample schema, I'm searching for a table that contains both ENAME and HIREDATE columns with desired values (SCOTT and 09.12.1982 (dd.mm.yyyy)).
SQL> set serveroutput on
SQL>
SQL> DECLARE
2 l_str VARCHAR2(500);
3 l_cnt NUMBER := 0;
4 BEGIN
5 FOR cur_r IN (SELECT t.table_name, u1.column_name col1, u2.column_name col2
6 FROM user_tables t join user_tab_columns u1 on u1.table_name = t.table_name
7 join user_tab_columns u2 on u2.table_name = t.table_name
8 WHERE u1.column_name = 'ENAME'
9 AND u2.column_name = 'HIREDATE'
10 )
11 LOOP
12 l_str := 'SELECT COUNT(*) FROM ' || cur_r.table_name ||
13 ' WHERE ' || cur_r.col1 || ' = ''SCOTT''' ||
14 ' AND ' || cur_r.col2 || ' = date ''1982-12-09''';
15
16 EXECUTE IMMEDIATE (l_str) INTO l_cnt;
17
18 IF l_cnt > 0 THEN
19 dbms_output.put_line(l_cnt ||' row(s) in ' || cur_r.table_name);
20 END IF;
21 END LOOP;
22 END;
23 /
1 row(s) in EMP
PL/SQL procedure successfully completed.
SQL>

How to check if all the tables in database are modified after an Update activity is performed on columns of tables?

I have to update all the tables having column name like '%DIV%' with a value DD wherever it is MG , I have written the script for it , but I am not getting the idea of how to verify if columns of all the tables are updated to value DD after the activity is performed. I have written this query .
SELECT 'SELECT '||OWNER||'.'||TABLE_NAME||', '||COLUMN_NAME||' FROM '||OWNER||'.'||TABLE_NAME||' WHERE '||COLUMN_NAME||' = ''MG'' ;'
FROM RADHA.CHANGE_TABLE
WHERE VALID_FLAG='Y'
I was planning to make a table structure like
OWNER TABLE_NAME PREV_COUNT
The PREV_COUNT will hold the count of rows having Column Value as MG and after the activity is performed , I will verify with following query if the corresponding rows have been updated to DD .
SELECT 'SELECT '||OWNER||'.'||TABLE_NAME||', '||COLUMN_NAME||' FROM '||OWNER||'.'||TABLE_NAME||' WHERE '||COLUMN_NAME||' = ''DD'' ;' FROM RADHA.CHANGE_TABLE WHERE VALID_FLAG='Y'
And the output of this query would go into table
OWNER TABLE_NAME NEW_COUNT
But I am not able to get how to fetch records from the Select query as it is the string which is written inside the select query but I want the result set such that I can insert the records in my table mentioned above, please guide how to approach further
I don't have your tables, but - based on Scott's sample schema, here's a script which search through all its tables for a column named JOB (line #8) and checks how many of them have value that looks like (hint: like) CLERK in it (line #12).
See how it works, adjust it so that it works for you.
SQL> DECLARE
2 l_str VARCHAR2(500);
3 l_cnt NUMBER := 0;
4 BEGIN
5 FOR cur_r IN (SELECT u.table_name, u.column_name
6 FROM user_tab_columns u, user_tables t
7 WHERE u.table_name = t.table_name
8 AND u.column_name = 'JOB'
9 )
10 LOOP
11 l_str := 'SELECT COUNT(*) FROM ' || cur_r.table_name ||
12 ' WHERE ' || cur_r.column_name || ' like (''%CLERK%'')';
13
14 EXECUTE IMMEDIATE (l_str) INTO l_cnt;
15
16 IF l_cnt > 0 THEN
17 dbms_output.put_line(l_cnt ||' : ' || cur_r.table_name);
18 END IF;
19 END LOOP;
20 END;
21 /
4 : EMP --> there are 4 CLERKs in the EMP table
PL/SQL procedure successfully completed.
SQL>

How to select columns from a table which have non null values?

I have a table containing hundreds of columns many of which are null, and I would like have my select statement so that only those columns containing a value are returned. It would help me analyze data better. Something like:
Select (non null columns) from tablename;
I want to select all columns which have at least one non-null value.
Can this be done?
Have a look as statistics information, it may be useful for you:
SQL> exec dbms_stats.gather_table_stats('SCOTT','EMP');
PL/SQL procedure successfully completed.
SQL> select num_rows from all_tables where owner='SCOTT' and table_name='EMP';
NUM_ROWS
----------
14
SQL> select column_name,nullable,num_distinct,num_nulls from all_tab_columns
2 where owner='SCOTT' and table_name='EMP' order by column_id;
COLUMN_NAME N NUM_DISTINCT NUM_NULLS
------------------------------ - ------------ ----------
EMPNO N 14 0
ENAME Y 14 0
JOB Y 5 0
MGR Y 6 1
HIREDATE Y 13 0
SAL Y 12 0
COMM Y 4 10
DEPTNO Y 3 0
8 rows selected.
For example you can check if NUM_NULLS = NUM_ROWS to identify "empty" columns.
Reference: ALL_TAB_COLUMNS, ALL_TABLES.
Use the below:
SELECT *
FROM information_schema.columns
WHERE table_name = 'Table_Name' and is_nullable = 'NO'
Table_Name has to be replaced accordingly...
select column_name
from user_tab_columns
where table_name='Table_name' and num_nulls=0;
Here is simple code to get non null columns..
I don't think this can be done in a single query. You may need some plsql to first test what columns contain data and put together a statement based on that information. Of course, if the data in your table changes you have to recreate the statement.
declare
l_table varchar2(30) := 'YOUR_TABLE';
l_statement varchar2(32767);
l_test_statement varchar2(32767);
l_contains_value pls_integer;
-- select column_names from your table
cursor c is
select column_name
,nullable
from user_tab_columns
where table_name = l_table;
begin
l_statement := 'select ';
for r in c
loop
-- If column is not nullable it will always contain a value
if r.nullable = 'N'
then
-- add column to select list.
l_statement := l_statement || r.column_name || ',';
else
-- check if there is a row that has a value for this column
begin
l_test_statement := 'select 1 from dual where exists (select 1 from ' || l_table || ' where ' ||
r.column_name || ' is not null)';
dbms_output.put_line(l_test_statement);
execute immediate l_test_statement
into l_contains_value;
-- Yes, add column to select list
l_statement := l_statement || r.column_name || ',';
exception
when no_data_found then
null;
end;
end if;
end loop;
-- create a select statement
l_statement := substr(l_statement, 1, length(l_statement) - 1) || ' from ' || l_table;
end;
select rtrim (xmlagg (xmlelement (e, column_name || ',')).extract ('//text()'), ',') col
from (select column_name
from user_tab_columns
where table_name='<table_name>' and low_value is not null)
This block determines all columns in the table, loops through them in dynamic SQL and checks if they are null, then constructs a DBMS output query of the non-null query.
All you have to do is run the returned query.
I've included the exclusion of PKs and BLOB columns.
Obviously, this is quite slow as going through columns one by one, and it's not going to be great for very hot tables, as data may change too quickly, but this works for me as I control traffic in dev env.
DECLARE
l_table_name VARCHAR2(255) := 'XXXX';
l_counter NUMBER;
l_sql CLOB;
BEGIN
FOR r_col IN (SELECT *
FROM user_tab_columns tab_col
WHERE table_name = l_table_name
AND data_type NOT IN ('BLOB')
AND column_name NOT IN (SELECT column_name
FROM user_cons_columns con_col
JOIN user_constraints cons ON con_col.constraint_name = cons.constraint_name AND con_col.table_name = cons.table_name
WHERE con_col.table_name = tab_col.table_name
AND constraint_type = 'P')
ORDER BY column_id)
LOOP
EXECUTE IMMEDIATE 'SELECT COUNT(1) FROM '||l_table_name||' WHERE '||r_col.column_name||' IS NOT NULL'
INTO l_counter;
IF l_counter > 0 THEN
IF l_sql IS NULL THEN
l_sql := r_col.column_name;
ELSE
l_sql := l_sql||','||r_col.column_name;
END IF;
END IF;
END LOOP;
l_sql := 'SELECT '||l_sql||CHR(10)
||'FROM '||l_table_name;
----------
DBMS_OUTPUT.put_line(l_sql);
END;
What you're asking to do is establish a dependency on each row in the whole result. This is in fact not ever what you want. Just think of the ramifications if in one row every column had a value of '0' -- suddenly the schema of your result set grows to include all of those previously "empty" columns. You're effectively growing the badness of '*' exponentially, now your result set is not dependent on just the table's meta-data -- but your whole result set is dependent on the plain data.
What you want to do is just select the fields that have what you want, and not deviate from this simple plan.