How to find the columns used in a stored procedure - oracle

Is it possible to get a list of used columns in a particular procedure using PL/SQL? I have to check over 500 procedures in my Oracle DB and print out all the columns used in them.

Surprisingly, Oracle does make the calculated table dependencies in PL/SQL available for viewing:
select * from user_dependencies -- can also use all_ or dba_dependencies
where type = 'PROCEDURE' and REFERENCED_TYPE = 'TABLE';
But if you really need column-level dependencies, I think it'll be much more difficult. Here's a very simple answer that'll illustrate some of the problems with the question.
select name, type, line, text, table_name, column_name
from user_source
join user_tab_cols
on text like '%'|| column_name || '%';
So you can search through all your procedures for any strings that match an existing column name. I can think of dozens of problems with this approach; the commenters above mentioned some of them, like how deep into the call stack you want to look. Or depending on how your columns are named, ambiguous names (SID, MODIFIED_DATE, etc) will produce duplicate entries. And there's no way to distinguish between actual references to column names and local variables which happen to have the same spelling. Or comments. And views or synonyms can screw up your searching. It seems like a really tough question. I hope the tables from user_dependencies will be enough for your needs.

Related

How to know if a db-link is used somewhere in a DB

I would like to know if it exists an Oracle command to know if a DB-LINK (name: myBDLink) is used somewhere in a DB and how to display the objects (views,materialized views, procedures, functions, ...) which use it.
Could you please help me with that ?
Thanks for your help
Well, you could try to query various system views and see whether any of them contains a string you're looking for. As you want to check the whole database, you'll probably connect as a privileged user and check dba_ views; otherwise, all_ or user_ would do.
For example, to check procedures, functions, packages, ...:
select owner, name, type, line
from dba_source
where owner not in ('SYS', 'SYSTEM')
and lower(text) like '%mydblink%';
To check views, you'll need a function which will search through its LONG datatype column (as you can't use it directly in SQL):
create or replace function f_long(par_view in varchar2, par_String in varchar2)
return varchar2
is
l_text varchar2(32000);
begin
select text
into l_text
from dba_views
where owner not in ('SYS', 'SYSTEM')
and view_name = par_view;
return case when instr(lower(l_text), lower(par_string)) > 0 then 1
else 0
end;
end;
/
and then
select owner, view_name
from dba_views
where f_long(view_name, 'mydblink') = 1;
I excluded SYS and SYSTEM as they should not contain anything of users' stuff. Perhaps you'd want to exclude some more users.
To see some more (re)sources, query the dictionary, e.g.
select table_name, comments
from dictionary;
TABLE_NAME COMMENTS
------------------------------ --------------------------------------------------
USER_CONS_COLUMNS Information about accessible columns in constraint
definitions
ALL_CONS_COLUMNS Information about accessible columns in constraint
definitions
DBA_CONS_COLUMNS Information about accessible columns in constraint
definitions
<snip>
There is no complete answer. How would the database know of code that is outside of the database? It can't. So if you have a sql script, or some application that does not rely on stored procedures to do everything, the database will not know of them.
That said, for dependencies that are in stored procedures in the database, you can try this:
select *
from dba_dependencies
where referenced_link_name is not null
;
To add to the other (correct) answers that have been posted by #Littlefoot and #EdStevens, a quick-and-dirty analysis can also be made against the Automated Workload Repository (AWR).
The benefit of this approach is it will find usages of remote objects from SQL submitted to the database whether that SQL is in DBA_SOURCE or not (e.g., is embedded in an external application).
-- Find any objects referenced across a database link (sort of)
select object_node, object_name, count(distinct sql_id) sql_id_count
from dba_hist_sql_plan
where object_type = 'REMOTE'
group by object_node, object_name
order by object_node, object_name
;
The problem is that AWR data isn't 100% complete. First of all, it's not kept around forever, so a database link last used more than a month (or two months or however long your DBAs keep AWR data for) wouldn't be seen. Second of all, AWR only takes snapshots periodically, say every hour. So it's theoretically possible for a SQL to use a database link and then get aged out of the library cache before the next AWR snapshot.
I think the chance of missing something due to that last bit is small on the systems I work with. But, if you have poorly written applications (i.e., no bind variables) and limited shared pool space, it's something to worry about.

How can I alter a table in Oracle while adding a column, which is of the same type as a column from a different table

I have two tables : table1 and table2.
There is a customerId field in table2 of data type Varchar2(30), how can I alter table1 to add the customerId field of the same data type as table2 using %type.
I tried the below code but no luck.
alter table table2
add customer_id table1.CUSTOMER_ID%type;
is it possible to alter using %type? Will this work. Please advise.
If it does not work, shall I do it manually by stating
alter table table2
add customer_id varchar2(30);
alter table table2
add customer_id table1.CUSTOMER_ID%type;
%type is a PL/SQL construct. We use it to define local variables in a program which are based on table columns. It does not work in SQL.
"if the data type of customerId changes, we have to manually change everywhere, instead if it is a copy, we just need to change at one place, "
This is not how Oracle (and most other if not all) databases work. They are engines for storing and retrieving data. They make this easy by enforcing strong data-typing and by making it hard to lose data carelessly. The rigour of the data dictionary is there to protect us from our lazy selves.
As a thought experiment, consider the impact on table2.customer_id if we did any one of the following:
alter table table1 modify customer_id not null;
alter table table1 modify customer_id number(6,0); -- from number(9,0)
alter table table1 modify customer_id number(6,0); -- from varchar2(6)
alter table table1 drop column customer_id;
All of these are possible real-life cases. For any of them ,the state of data in table2.customer_id could cause the statement to fail on table2 (even though they would succeed on table1). Is that desirable? Almost certainly yes. But it now means we cannot change table1, which greatly reduces the utility of having a template column.
"i thought is more of a good practice."
The best practice is to get it right first time. Obviously that's not always possible, because circumstances evolve over time. We need to accept there will be change, and the good practice for handling change is to run an impact assessment: if we change table1.customer_id what else might be affected? What else will need to change after that? What about all the program code which uses these columns?
Data management is hard, but it's hard for a good reason. Unlike source code, databases have state. Changing state is expensive, and reverting to a previous state even more so. Changing the datatype of a column means changing the state of all the data in that column. This is not something which should be done lightly.
So. Do proper analysis. Have a decent data model. Understand your data structures. These are good practices.
This is not an answer (mathguy told it to you already), but a comment is a little bit "short" for what I'd like to say.
While attending HrOUG conference, I saw a man wearing a T-shirt saying
Thank you for spending months in coding & saving us days in planing
In other words, carefully choose CUSTOMERID data type. If you are selling products to 13 customers today, don't set it to NUMBER(2) because (if your company develops and becomes prosperous), you'll soon be selling products to thousands of customers. Will you first alter it (and its all dependant column data types, as well as all its appearances in your application(s)) to NUMBER(3), and then to NUMBER(4), etc.? Think about the future!
Similarly, at the same conference, there were guys who said that they have tables with 570 columns. Gosh! 5-7-0! What are they doing with such a tables? Their answer was: "We pay Oracle a lot of money. It allows us to create tables with 1000 columns, and we are going to use every single one of them." The audience was kind of puzzled (hint: normalization?), but hey - it's their choice.
Yes, I noticed that you chose a VARCHAR2 data type for that ID column. (I'm not saying that it is wrong, but I, somehow, prefer numbers over strings for such purposes.) So, what do you think? Will 30 characters be enough? How much would it cost if you set it to 50 characters? Or 100? They won't take any additional space on a disk. If there is 'A234' in your VARCHAR2(100 BYTE) column, it'll take only 4 bytes on a disk. Memory is a different story, as Oracle will pre-allocate space when you use such a variable in your PL/SQL code, so you might end up in wasting space unnecessarily. Adding more RAM? Sure, it is an option, but it costs money.
Therefore, once again - design your data model carefully and you should be OK, following the supported ALTER TABLE syntax.
Note : Use this with caution.
Use this only after you have read all other answers and still think you want it that way.
To use a DDL Trigger. The below is just a sample which considers customer_id as NUMBER type.For VARCHAR2, DATE etc, you need a generic way to construct the DDL. Refer Issue in dynamic table creation
CREATE OR REPLACE TRIGGER trg_alter_table1
AFTER ALTER
ON SCHEMA
WHEN (ORA_DICT_OBJ_TYPE = 'TABLE' AND ORA_DICT_OBJ_NAME = 'TABLE1')
DECLARE
v_ddl VARCHAR2 (200);
BEGIN
SELECT 'ALTER TABLE TABLE2 MODIFY '
|| column_name
|| ' '
|| data_type
|| '('
|| data_precision
|| ')'
INTO v_ddl
FROM user_tab_columns
WHERE table_name = 'TABLE1' AND column_name = 'CUSTOMER_ID';
EXECUTE IMMEDIATE v_ddl;
END;
/

How to identify all stored procedures refering a particular table in Oracle

I am working with Oracle 12c and need to find all references where a specific table or view is being used in Stored Procedure/Function and packages.
I have found a this answer about MS SQL Server, but it's not related to Oracle, besides sp_help and sp_depends sometimes return inaccurate results.
I know to search in column text of table all_source, for example, this code (search only standard user defined package names, not system package):
SELECT type, name, line, text
FROM all_source
WHERE type = 'PACKAGE BODY'
AND name like 'P%'
AND UPPER(text) like '%' || p_table_or_view_name || '%'
ORDER BY name, line;
but I'm looking if there's a more elegant and/or standard solution in Oracle.
I'm also checking if this answer can help me in any way.
I will appreciate any assistance.
Use the ALL_DEPENDENCIES dictionary table:
SELECT *
FROM ALL_DEPENDENCIES
WHERE referenced_name = 'YOUR_TABLE_NAME'
AND owner = 'YOUR_USER';

Oracle - select statement alias one column and wildcard to get all remaining columns

New to SQL. Pardon me if this question is a basic one. Is there a way for me to do this below
SELECT COLUMN1 as CUSTOM_NAME, <wildcard for remaining columns as is> from TABLE;
I only want COLUMN1 appear once in the final result
There is no way to make that kind of dynamic SELECT list with regular SQL*.
This is a good thing. Programming gets more difficult the more dynamic it is. Even the simple * syntax, while useful in many contexts, causes problems in production code. The Oracle SQL grammar is already more complicated than most traditional programming languages, adding a little meta language to describe what the queries return could be a nightmare.
*Well, you could create something using Oracle data cartridge, or DBMS_XMLGEN, or a trick with the PIVOT clause. But each of those solutions would be incredibly complicated and certainly not as simple as just typing the columns.
This is about as close as you will get.
It is very handy for putting the important columns up front,
while being able to scroll to the others if needed. COLUMN1 will end up being there twice.
SELECT COLUMN1 as CUSTOM_NAME,
aliasName.*
FROM TABLE aliasName;
In case you have many columns it might be worth to generate a full column list automatically instead of relying on the * selector.
So a two step approach would be to generate the column list with custom first N columns and unspecified order of the other columns, then use this generated list in your actual select statement.
-- select comma separated column names from table with the first columns being in specified order
select
LISTAGG(column_name, ', ') WITHIN GROUP (
ORDER BY decode(column_name,
'FIRST_COLUMN_NAME', 1,
'SECOND_COLUMN_NAME', 2) asc) "Columns"
from user_tab_columns
where table_name = 'TABLE_NAME';
Replace TABLE_NAME, FIRST_COLUMN_NAME and SECOND_COLUMN_NAME by your actual names, adjust the list of explicit columns as needed.
Then execute the query and use the result, which should look like
FIRST_COLUMN_NAME, SECOND_COLUMN_NAME, OTHER_COLUMN_NAMES
Ofcourse this is overhead for 5-ish columns, but if you ever run into a company database with 3 digit number of columns, this can be interesting.

Process SQL result set entirely

I need to work with a SQL result set in order to do some processing for each column (medians, standard deviations, several control statements included)
The SQL is dynamic so I don't know the number of columns, rows.
First I tried to use temporary tables, views, etc to store the results, however I did not manage to overcome the 30 character limit of Oracle columns when using the below sql:
create table (or view or global temporary table) as select * from (
SELECT
DMTTBF_MAT_MATURATO_BILL_POS.MAT_V_COD_ANNOMESE,
SUM(DMTTBF_MAT_MATURATO_BILL_POS.MAT_N_NUM_EVENTI_CHZ +DMTTBF_MAT_MATURATO_BILL_POS. MAT_N_NUM_EVENTI) <-- exceeds the 30 character limit
FROM DMTTBF_MAT_MATURATO_BILL_POS
WHERE DMTTBF_MAT_MATURATO_BILL_POS.MAT_V_COD_ANNOMESE >= '201301'
GROUP BY DMTTBF_MAT_MATURATO_BILL_POS.MAT_V_COD_ANNOMESE
)
Second choice was to use some PL/SQL types to store the entire table information, so I could call it like in other programming languages (e.g. a matrix result[i][j]) but I could not find anything similar.
Third variant, using files for reading and writing: i did not try it yet; i'm still expecting a more elegant pl/sql solution
It's possible that I have the wrong approach here so any advice is more than welcome.
UPDATE: Modifying the input SQL is not an option. The program has to accept any select statement.
Note that you can alias both tables and fields. Using a table alias keeps references to it from producing walls of text in the query. Using one for a field gives it a new name in the output.
SELECT A.LONG_FIELD_NAME_HERE AS SHORTNAME
FROM REALLY_LONG_TABLE_NAME_HERE A
The auto naming adds _1 and _2 etc to differentiate the same column name coming from different table references. This often puts a field already borderline over the limit. Giving the fields names yourself bypasses this.
You can put the alias also in dynamic SQL:
sqlstr := 'create table (or view or global temporary table) as select * from (
SELECT
DMTTBF_MAT_MATURATO_BILL_POS.MAT_V_COD_ANNOMESE,
SUM(DMTTBF_MAT_MATURATO_BILL_POS.MAT_N_NUM_EVENTI_CHZ + DMTTBF_MAT_MATURATO_BILL_POS.MAT_N_NUM_EVENTI) AS '||SUBSTR('SUM(DMTTBF_MAT_MATURATO_BILL_POS.MAT_N_NUM_EVENTI_CHZ +DMTTBF_MAT_MATURATO_BILL_POS.MAT_N_NUM_EVENTI)', 1, 30)
||' FROM DMTTBF_MAT_MATURATO_BILL_POS
WHERE DMTTBF_MAT_MATURATO_BILL_POS.MAT_V_COD_ANNOMESE >= ''201301''
GROUP BY DMTTBF_MAT_MATURATO_BILL_POS.MAT_V_COD_ANNOMESE
)'

Resources