In a legacy database infrastructure, how do I best find views that access a certain table or column? I'm currently refactoring certain tables (i.e. delete unused columns) and I want to find all views that still rely on those columns and will break, if I remove the columns.
Is there any tool/feature to search through all view definitions in Oracle SQL Developer?
You can use something like function dependent_views, code below. Example usage:
select dependent_views('CUSTOMER_NAME', 'CUSTOMERS') list from dual
Output:
LIST
-----------------
SCOTT.V_PERSONS
Function searches dependendent views in ALL_DEPENDENCIES, next searches TEXT column from ALL_VIEWS for occurence of column_name.
Note: Because all_dependences may not contain full data of dependent objects (for instance when view was created by execute immediate) - my function may not find this object.
Also if column_name is substring of other column - function may return to many views.
create or replace function dependent_views
(i_column varchar2, i_table varchar2, i_owner varchar2 default USER)
return varchar2 is
o_ret varchar2(4000) := '';
v_text long := '';
begin
for o in (
select * from all_dependencies
where referenced_name = upper(i_table)
and referenced_owner = upper(i_owner)
and type = 'VIEW')
loop
begin
select text into v_text from all_views
where view_name = o.name and owner = o.owner;
exception when no_data_found then
null;
end;
if upper(v_text) like '%'||upper(i_column)||'%' then
o_ret := o_ret||o.owner||'.'||o.name||' ';
end if;
end loop;
return o_ret;
end dependent_views;
how do I best find views that access a certain table
You could query the [USER|ALL|DBA]_DEPENDENCIES view.
SELECT name ,
type ,
referenced_name ,
referenced_type
FROM user_dependencies
WHERE TYPE = 'VIEW'
AND NAME = '<VIEW_NAME>'
AND referenced_type = '<TABLE_NAME'>;
To get the result for all the views at once, remove the filter NAME = '<VIEW_NAME>'.
For example,
SQL> column name format a15
SQL> column type format a15
SQL> column referenced_name format a15
SQL> column referenced_type format a15
SQL> SELECT name ,
2 type ,
3 referenced_name ,
4 referenced_type
5 FROM user_dependencies
6 WHERE TYPE = 'VIEW';
NAME TYPE REFERENCED_NAME REFERENCED_TYPE
--------------- --------------- --------------- ---------------
EMP_CUSTOM_VIEW VIEW EMP TABLE
EMP_VIEW VIEW EMP TABLE
SQL>
Cause you search for all views that access certain table, this might help:
select
name,
type,
referenced_name,
referenced_type
from user_dependencies
where type = 'VIEW'
and referenced_type = 'TABLE'
Related
Table A(Target) Name Varchar2(40)
Table B(source) Name Varchar2(60)
I need a script where A and B table should be compared and if the Datatype size dosen't match, then the datatype size in the A table should be altered to that of Datatype size of Table B.
Result
Table A(Target) Name Varchar2(60)
Table B(source) Name Varchar2(60)
It is the ALTER TABLE you'd use.
Sample table:
SQL> create table table_a (name varchar2(40));
Table created.
SQL> insert into table_a values ('Littlefoot');
1 row created.
How to modify column's size:
SQL> alter table table_a modify name varchar2(60);
Table altered.
No problem with making it larger, but - you might have problems if you'd want to make it smaller and length of some data in that column is longer than desired target size. In this example, 'Littlefoot' has 10 letters so making the column smaller than that raises an error:
SQL> alter table table_a modify name varchar2(5);
alter table table_a modify name varchar2(5)
*
ERROR at line 1:
ORA-01441: cannot decrease column length because some value is too big
SQL>
If you want to write some code which will do that job for you, you'll have to query user_tab_columns and use dynamic SQL (i.e. execute immediate). For example:
SQL> create table table_a (id number, name varchar2(40));
Table created.
SQL> create table table_b (id number, name varchar2(60));
Table created.
SQL> begin
2 for cur_r in (select a.column_name, a.data_length a_len,
3 b.data_length b_len
4 from user_tab_columns a join user_tab_columns b on a.column_name = b.column_name
5 where a.table_name = 'TABLE_A'
6 and b.table_name = 'TABLE_B'
7 and a.data_type = 'VARCHAR2'
8 )
9 loop
10 if cur_r.a_len < cur_r.b_len then
11 execute immediate 'alter table table_a modify ' || cur_r.column_name || ' varchar2(' ||
12 cur_r.b_len ||')';
13 end if;
14 end loop;
15 end;
16 /
PL/SQL procedure successfully completed.
What's the outcome?
SQL> desc table_a;
Name Null? Type
----------------------------------------- -------- ----------------------------
ID NUMBER
NAME VARCHAR2(60) --> this
SQL> desc table_b;
Name Null? Type
----------------------------------------- -------- ----------------------------
ID NUMBER
NAME VARCHAR2(60) --> this
SQL>
Columns' sizes match.
Of course, that simple code might require additional settings (if you'd want to affect other datatypes), but that should give you initial idea.
In Oracle there is table named customers. If we want to know what views depend on the customer table how can we find that out?
You could query the sys.all_dependancies object.
This will output the referencing object which is the name of the view using the table specified
SELECT referenced_owner || '.' || referenced_name as table_name,
referenced_type as type,
owner || '.' || name as referencing_object,
type as referencing_type
FROM sys.all_dependencies
WHERE referenced_type = 'VIEW'
AND referenced_name = 'customers' -- put your table/view name here
I have used following query to get the procedure list but I am getting just the schema name and package name. If the TYPE Column returns PACKAGE BODY, then how to get the procedure name within the package accessing the table EXCEPTIONAL_INFO, please help.
SELECT * FROM All_DEPENDENCIES WHERE REFERENCED_NAME = 'EXCEPTIONAL_INFO';
You can scan the source texts of the packages. I've used the view DBA_SOURCE because I believe ALL_SOURCE shows only the code of procedures I can execute.
CREATE TABLE exceptional_info (i INT);
CREATE OR REPLACE PACKAGE p IS
PROCEDURE a;
PROCEDURE b(table_name VARCHAR2);
END p;
/
CREATE OR REPLACE PACKAGE BODY p IS
PROCEDURE a AS
n NUMBER;
BEGIN
SELECT count(*) INTO n FROM exceptional_info;
END a;
PROCEDURE b(table_name VARCHAR2) AS
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE '||table_name;
END b;
END;
/
You can find the packages with DBA_DEPENDENCIES and scan the source text of those dependencies:
SELECT owner, type, name, s.line, s.text
FROM dba_dependencies d
LEFT JOIN dba_source s USING (owner, name, type)
WHERE d.referenced_name = 'EXCEPTIONAL_INFO'
AND upper(s.text) like '%EXCEPTIONAL_INFO%';
OWNER TYPE NAME LINE TEXT
SO PACKAGE BODY P 10 SELECT count(*) INTO n FROM exceptional_info;
You need to go up from line 10 to find the name of the procedure in the source text. I'm too lazy to code that now.
However, this method will find only static text. It cannot find PROCEDURE b in the example, which also can modify table exceptional_info, without having the name hard coded. Do you need to catch those usages, too?
you can try this query:
select owner, object_name, procedure_name
from all_procedures
where object_name =
(
SELECT name FROM All_DEPENDENCIES WHERE REFERENCED_NAME = 'EXCEPTIONAL_INFO'
and type='PACKAGE BODY' and rownum=1
)
;
I like to find all columns in my Oracle database schema that only contain numeric data but having a non-numeric type. (So basically column-candidates with probably wrong chosen data types.)
I have a query for all varchar2-columns:
SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE
FROM user_tab_cols
WHERE DATA_TYPE = 'VARCHAR2';
Furthermore I have a query to check for any non-numeric data inside a table myTable and a column myColumn:
SELECT 1
FROM myTable
WHERE NOT REGEXP_LIKE(myColumn, '^[[:digit:]]+$');
I like to combine both queries in that way that the first query only returns the rows where not exists the second.
The main problem here is that the first query is on meta layer of the data dictionary where TABLE_NAME and COLUMN_NAME comes as data and I need that data as identifiers (and not as data) in the second query.
In pseudo-SQL I have something like that in mind:
SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE
FROM user_tab_cols
WHERE DATA_TYPE = 'VARCHAR2'
AND NOT EXISTS
(SELECT 1 from asIdentifier(TABLE_NAME)
WHERE NOT REGEXP_LIKE(asIdentifier(COLUMN_NAME), '^[[:digit:]]+$'));
Create a function as this:
create or replace function isNumeric(val in VARCHAR2) return INTEGER AS
res NUMBER;
begin
res := TO_NUMBER(val);
RETURN 1;
EXCEPTION
WHEN OTHERS THEN
RETURN 0;
END;
Then you can use it like this:
DECLARE
r integer;
BEGIN
For aCol in (SELECT TABLE_NAME, COLUMN_NAME FROM user_tab_cols WHERE DATA_TYPE = 'VARCHAR2') LOOP
-- What about CHAR and CLOB data types?
execute immediate 'select count(*) from '||aCol.TABLE_NAME||' WHERE isNumeric('||aCol.COLUMN_NAME||') = 0' into r;
if r = 0 then
DBMS_OUTPUT.put_line(aCol.TABLE_NAME ||' '||aCol.COLUMN_NAME ||' contains numeric values only');
end if;
end loop;
end;
Note, the performance of this PL/SQL block will be poor. Hopefully this is a one-time-job only.
There are two possible approaches: dynamic SQL (DSQL) and XML.
First one was already demonstrated in another reply and it's faster.
XML approach just for fun
create or replace function to_number_udf(p in varchar2) return number
deterministic is
pragma udf;
begin
return p * 0;
exception when invalid_number or value_error then return 1;
end to_number_udf;
/
create table t_chk(str1, str2) as
select '1', '2' from dual union all
select '0001.1000', 'helloworld' from dual;
SQL> column owner format a20
SQL> column table_name format a20
SQL> column column_name format a20
SQL> with tabs_to_check as
2 (
3 select 'collection("oradb:/'||owner||'/'||table_name||'")/ROW/'||column_name||'/text()' x,
4 atc.*
5 from all_tab_columns atc
6 where table_name = 'T_CHK'
7 and data_type = 'VARCHAR2'
8 and owner = user
9 )
10 select --+ no_query_transformation
11 owner, table_name, column_name
12 from tabs_to_check ttc, xmltable(x columns "." varchar2(4000)) x
13 group by owner, table_name, column_name
14 having max(to_number_udf(".")) = 0;
OWNER TABLE_NAME COLUMN_NAME
-------------------- -------------------- --------------------
TEST T_CHK STR1
PS. On Oracle 12.2 you can use to_number(... default ... on conversion error) instead of UDF.
The faster way to check if a string is all digits vs. contains at least one non-digit character is to use the translate function. Alas, due to the non-SQL Standard way Oracle handles empty strings, the form of the function we must use is a little complicated:
translate(input_string, 'z0123456789', 'z')
(z can be any non-digit character; we need it so that the third argument is not null). This works by translating z to itself and 0, etc. to nothing. So if the input string was null or all-digits, and ONLY in that case, the value returned by the function is null.
In addition: to make the process faster, you can test each column with an EXISTS condition. If a column is not meant to be numeric, then in most cases the EXISTS condition will become true very quickly, so you will have to inspect a very small number of values from such columns.
As I tried to make this work, I ran into numerous side issues. Presumably you want to look in all schemas (except SYS and perhaps SYSTEM). So you need to run the procedure (anonymous block) from an account with SYSDBA privileges. Then - I ran into issues with non-standard table and column names (names starting with an underscore and such); which brought to mind identifiers defined in double-quotes - a terrible practice.
For illustration, I will use the HR schema - on which the approach worked. You may need to tweak this further; I wasn't able to make it work by changing the line
and owner = 'HR'
to
and owner != 'SYS'
So - with this long intro - here is what I did.
First, in a "normal" user account (my own, named INTRO - I run a very small database, with only one "normal" user, plus the Oracle "standard" users like SCOTT, HR etc.) - so, in schema INTRO, I created a table to receive the owner name, table name and column name for all columns of data type VARCHAR2 and which contain only "numeric" values or null (numeric defined the way you did.) NOTE HERE: If you then want to really check for all numeric values, you will indeed need a regular expression, or something like what Wernfried has shown; I would still, otherwise, use an EXISTS condition rather than a COUNT in the anonymous procedure.
Then I created an anonymous block to find the needed columns. NOTE: You will not have a schema INTRO - so change it everywhere in my code (both in creating the table and in the anonymous block). If the procedure completes successfully, you should be able to query the table. I show that at the end too.
While logged in as SYS (or another user with SYSDBA powers):
create table intro.cols_with_numbers (
owner_name varchar2(128),
table_name varchar2(128),
column_name varchar2(128)
);
declare x number;
begin
execute immediate 'truncate table intro.cols_with_numbers';
for t in ( select owner, table_name, column_name
from dba_tab_columns
where data_type like 'VARCHAR2%'
and owner = 'HR'
)
loop
execute immediate 'select case when exists (
select *
from ' || t.owner || '.' || t.table_name ||
' where translate(' || t.column_name || ',
''z0123456789'', ''z'') is not null
) then 1 end
from dual'
into x;
if x is null then
insert into intro.cols_with_numbers (owner_name, table_name, column_name)
values(t.owner, t.table_name, t.column_name);
end if;
end loop;
end;
/
Run this procedure and then query the table:
select * from intro.cols_with_numbers;
no rows selected
(which means there were no numeric columns in tables in the HR schema, in the wrong data type VARCHAR2 - or at least, no such columns that had only non-negative integer values.) You can test further, by intentionally creating a table with such a column and testing to see it is "caught" by the procedure.
ADDED - Here is what happens when I change the owner from 'HR' to 'SCOTT':
PL/SQL procedure successfully completed.
OWNER_NAME TABLE_NAME COLUMN_NAME
-------------------- -------------------- --------------------
SCOTT BONUS JOB
SCOTT BONUS ENAME
so it seems to work fine (although on other schemas I sometimes run into an error... I'll see if I can figure out what that is).
In this case the table is empty (no rows!) - this is one example of a "false positive" you may find. (More generally, you will get a false positive if everything in a VARCHAR2 column is null - in all rows of the table.)
NOTE also that a column may have only numeric values and still the best data type would be VARCHAR2. This is the case when the values are simply identifiers and are not meant as "numbers" (which we can compare to each other or to fixed values, and/or with which we can do arithmetic). Example - a SSN (Social Security Number) or the equivalent in other countries; the SSN is each person's "official" identifier for doing business with the government. The SSN is numeric (actually, perhaps to accentuate the fact it is NOT supposed to be a "number" despite the name, it is often written with a couple of dashes...)
I have this giant query which uses some text in it which look like variables but I have no idea what they are and I really can't figure it out. They aren't global or defined anywhere else in the oracle package. In particular below the variable(or whatever it is) called "has_value" is so confusing because it's used in multiple cases for queries across a LOT of tables.
PROCEDURE assemble_default_where(
i_search_id IN search_table.search_id%TYPE,
o_where_clause OUT VARCHAR2,
o_from_clause OUT VARCHAR2,
o_error_number OUT error_message.error_number%TYPE) IS
l_base VARCHAR2(30) := 'd';
CURSOR c_where_clause IS
SELECT DECODE
(sl.parameter_name,
'track Location', join_operator || ' ' || open_brackets || ' ' || not_operator || ' EXISTS(SELECT 1 FROM track_locations loc WHERE ' || l_base
|| '.plan_number = loc.plan_number AND ' || l_base || '.order_id = loc.order_id AND loc.t_id = NVL('''
|| track_location_rsect_id(has_value) || ''', loc.t_id) AND loc.tstatus = NVL(''' || track_tstatus_id(has_value)
FROM search_lines sl
WHERE search_id = i_search_id
ORDER BY line_no;
I have left out a bit of the query because it's not relevant to my question.
I want to know where join_operator, has_value and open_brackets come from and what they are???
There are several options:
Variable - defined in outer block.
Variable - defined in package body.
Variable - defined in package specification.
Column - the same column names could be in many tables.
Function - in the invoker's or definer's schema.
Library - in the invoker's or definer's schema.
Operator - in the invoker's or definer's schema.
Synonym - to a function, operator, or library.
In practice you probably would have already noticed if it was #1, #2, #3, or #4. And #6 and #7 are very rare. I would guess it is a function or a synonym to a function.
To rule out variables, search through the code either in your IDE or with this SQL:
select * from dba_source where lower(text) like '%join_operator%';
To rule out objects, search though all the objects with this SQL:
select * from dba_objects where object_name = 'JOIN_OPERATOR';
UPDATE
PL/SQL and DBA_DEPENDENCIES can also help identify objects.
Sample schema
alter session set plscope_settings = 'IDENTIFIERS:ALL';
create table search_lines(
search_id number, line_no number, parameter_name varchar2(100));
create or replace function join_operator_function return varchar2 is
begin
return 'asdf';
end;
/
create synonym join_operator for join_operator_function;
create or replace PROCEDURE assemble_default_where
is
CURSOR c_where_clause IS
SELECT DECODE(sl.parameter_name, 'track Location', join_operator)
FROM search_lines sl
ORDER BY line_no;
begin
null;
end;
/
PL/SCOPE example
WITH v AS (
SELECT Line,
Col,
INITCAP(NAME) Name,
LOWER(TYPE) Type,
LOWER(USAGE) Usage,
USAGE_ID,
USAGE_CONTEXT_ID
FROM USER_IDENTIFIERS
WHERE Object_Name = 'ASSEMBLE_DEFAULT_WHERE'
AND Object_Type = 'PROCEDURE'
)
SELECT RPAD(LPAD(' ', 2*(Level-1)) ||
Name, 30, '.')||' '||
RPAD(Type, 30)||
RPAD(Usage, 30)
IDENTIFIER_USAGE_CONTEXTS
FROM v
START WITH USAGE_CONTEXT_ID = 0
CONNECT BY PRIOR USAGE_ID = USAGE_CONTEXT_ID
ORDER SIBLINGS BY Line, Col
/
Assemble_Default_Where........ procedure declaration
Assemble_Default_Where...... procedure definition
C_Where_Clause............ cursor declaration
Join_Operator_Function.. function call
DBA_DEPENDENCIES example
select referenced_owner, referenced_name, referenced_type
from dba_dependencies
where owner = user
and name = 'ASSEMBLE_DEFAULT_WHERE';
REFERENCED_OWNER REFERENCED_NAME REFERENCED_TYPE
---------------- --------------- ---------------
SYS STANDARD PACKAGE
SYS SYS_STUB_FOR_PURITY_ANALYSIS PACKAGE
JHELLER_DBA JOIN_OPERATOR SYNONYM
JHELLER_DBA JOIN_OPERATOR_FUNCTION FUNCTION
JHELLER_DBA SEARCH_LINES TABLE