How to find Number of null column in table using PL/SQL - 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).

Related

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') ;

Call stored procedure for each Row with/without using a cursor

I want to a run a stored procedure for almost 1000 records (P_SHIPMENT_GID) in one go and below is the pseudo code.
DECLARE
P_SHIPMENT_GID VARCHAR2(200);
BEGIN
P_SHIPMENT_GID := NULL;
ULE_PKG_UNPLANNED_ICT_CALC.UNPLANNED_ICT_CALC(
P_SHIPMENT_GID => P_SHIPMENT_GID
);
END;
How can I achieve this with or without using cursors?
It is not all that clear what you want to do (where are the 1000 records from?) but here is a "pattern" I am pretty sure you can use :
BEGIN
FOR i IN (SELECT table_name, status FROM user_tables) LOOP
dbms_output.put_line('name : ' || i.table_name ||
' status : ' || i.status);
END LOOP;
END;
This creates a loop on an implicit cursor and allows you to use the returned rows/column in a readable way.
You can write this anonymous block for your requirement. Although its not clear from where you are storing your SHIPMENT_GID values which you wanted to pass to your procedure/pkg.
BEGIN
FOR rec IN ( --Assuming your shipmentid are stored in a table
SELECT SHIPMENT_GID
FROM Your_TABLE)
LOOP
ULE_PKG_UNPLANNED_ICT_CALC.UNPLANNED_ICT_CALC (
P_SHIPMENT_GID => rec.SHIPMENT_GID);
END LOOP;
END;

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

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

how to compare values in two columns of two different tables using cursor

declare
v_column varchar2(100);
v_incident_id number;
v_check_incident varchar2(100);
v_check_audit varchar2(100);
query_str varchar2(1000);
cursor c1 is
select COLUMN_NAME
from ALL_TAB_COLUMNS
where upper(TABLE_NAME)=upper('Incidents');
begin
open c1;
loop
fetch c1 into v_column;
exit when c1%notfound;
select incident_id into v_incident_id from incident_audit;
DBMS_OUTPUT.PUT_LINE('incident column: '||v_column);
select v_column
into v_check_incident
from incidents
where incident_id= v_incident_id;
select to_char(v_column)
into v_check_audit
from incident_audit
where incident_id=v_incident_id;
DBMS_OUTPUT.PUT_LINE('incident column: '||v_check_incident);
DBMS_OUTPUT.PUT_LINE('audit column : '||v_check_audit);
if(v_check_incident != v_check_audit)
then
DBMS_OUTPUT.PUT_LINE('inside insert');
insert into PHIIT_AUD(AUDIT_ID ,INCIDENT_ID,OLD_VALUE,NEW_VALUE,UPDATED_BY,UPDATED_ON,COLUMN_NAME)
values(audit_id_seq.nextval,v_incident_id,v_check_audit,v_check_incident,NVL(v('APP_USER'),USER),sysdate,v_column);
end if;
end loop;
DBMS_OUTPUT.PUT_LINE('Total number of rows : '||c1%ROWCOUNT);
close c1;
end;
Problem: Getting Column names from ALL_TAB_COLUMNS in cursor and using it to compare with the other column of other table
output : incident column: INCIDENT_ID
incident column: INCIDENT_ID
audit column : INCIDENT_ID
incident column: BUSINESS_UNIT_ID
incident column: BUSINESS_UNIT_ID
audit column : BUSINESS_UNIT_ID
The cursor is returning value as column_name instead of its value in order to compare.
"The cursor is returning value as column_name "
Of course it is, that's what you're telling it to do. select v_column from incidents means select the value in the variable.
What you need to do is use Dynamic SQL to create a select statement which substitutes the value in the variable for a column name: 'select '||v_column||' from incidents' .
Here is the sort of thing you need to do.
declare
v_column varchar2(100);
v_incident_id number;
v_check_incident varchar2(100);
v_check_audit varchar2(100);
query_str varchar2(1000);
cursor c1 is
select COLUMN_NAME
from ALL_TAB_COLUMNS
where upper(TABLE_NAME)=upper('Incidents');
rc sys_refcursor;
begin
open c1;
loop
fetch c1 into v_column;
exit when c1%notfound;
select incident_id into v_incident_id from incident_audit;
DBMS_OUTPUT.PUT_LINE('incident column: '||v_column);
-- dynamic SQL
open rc for
'select'|| v_column ||
' from incidents where incident_id= :1'
using v_incident_id;
fetch rc into v_check_incident;
close rc;
open rc for
'select'|| v_column ||
' from incident_audit where incident_id= :1'
using v_incident_id;
fetch rc into v_check_audit;
close rc;
DBMS_OUTPUT.PUT_LINE('incident column: '||v_check_incident);
DBMS_OUTPUT.PUT_LINE('audit column : '||v_check_audit);
if(v_check_incident != v_check_audit)
then
DBMS_OUTPUT.PUT_LINE('inside insert');
insert into PHIIT_AUD(AUDIT_ID ,INCIDENT_ID,OLD_VALUE,NEW_VALUE,UPDATED_BY,UPDATED_ON,COLUMN_NAME)
values(audit_id_seq.nextval,v_incident_id,v_check_audit,v_check_incident,NVL(v('APP_USER'),USER),sysdate,v_column);
end if;
end loop;
DBMS_OUTPUT.PUT_LINE('Total number of rows : '||c1%ROWCOUNT);
close c1;
end;
I have kept it close to your original implementation so you can understand the principle. However, I think this is a poor implementation which will be very inefficient if you have lots of audits to verify. But addressing that is a different matter.

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.