Oracle Table Usage Across Stored Procedures - sql

I have a 11G database. I need to examine a number of stored procedures to see if they use a particular table. (Both front end and back end sps) I have full access to the database, and I also have a copy of all the individual sps for the project which are stored on a TFS.
I would like a way to generate a list of all the sps that interact with this particular table. I'm unfamiliar with how to go about searching for these. Can anyone advise the most logical way of obtaining this data?
Thanks.

If I understand this correctly, you're trying to search for occurrence of a table in all stored procs. In that case, you can use this query:
When searching for occurrences of SP in your schema
SELECT * FROM user_source WHERE text LIKE '%tab_name%';
When searching for occurrences of SP in all schemas
SELECT * FROM all_source WHERE text LIKE '%tab_name%';

Two things, in PL/SQL there are some changes which will require the recompilation of pl/sql object, other don't.
To see the first one, you have the ALL_DEPENDENCIES view. Or DBA_ if you prefer.
If you just want to see where the table name appears in all the pl/sql code, whether a change to the table will require recompilation or not, you can use ALL_SOURCE using a upper and %, but it might take some time.

I use PLSQL Developer, in which you can browse to a table (or other object), and view 'Referenced by', to see all objects that refer to the table. That's about as easy as it gets.
I can imagine other tools have similar features.
I don't know if this pre-parsed information is readily available in Oracle, but I can imagine so, since those tools seem to work pretty fast.
This information is available in the viewAll_DEPENDENCIES, which these tools probably use.
The source of stored procedures can be found in the USER_SOURCE (or ALL_SOURCE) view, in which the structure of the entire database is stored. Nevertheless, fetching and parsing the code from there would be quite cumbersome.

Here is snippet I wrote to perform impact analysis (ONLY MERGE, INSERT and UPDATE) for a given #schema (upper case only) and #table (upper case only). It will return all the procedure name, procedure code, from line number and to line number along with other details. It can be easily used to include functions objects as well instead of package. am working on a utility that can run across all schema or selected schema (that will include SELECT rows as well). Though this will be good enough for you to start working.
I know you can use the dependency and references available in Oracle to perform similarly. But for package level impact this is good addition. We can also use regex for more complex searches. But like operator is simple and efficient for my needs.
Note, this doesn't work on any dynamic code that may be working in your environment. This is just a appropriate starting point for quick one on impact with static PL/SQL code in your packages.
WITH TableDep as
-- This table returns references where the table is used within the code for UPDATE OR INSERT
(
SELECT
owner as schemaname,
name as packagename,
type as typename,
TEXT as refcodeline,
CASE WHEN upper(text) LIKE '%INSERT%' THEN 'INSERT'
WHEN upper(text) LIKE '%UPDATE%' THEN 'UPDATE'
WHEN upper(text) LIKE '%MERGE%' THEN 'MERGE'
END AS opr,
:Tablename AS Tablename,
line refline
FROM dba_source WHERE upper(owner) = upper(:OWNER)
AND type = 'PACKAGE BODY'
AND (
upper(text) LIKE ('%INSERT INTO '||:Tablename||'%')
OR
upper(text) LIKE ('%UPDATE%'||:Tablename||' %')
OR
upper(text) LIKE ('%MERGE%'||:Tablename||' %')
)
),
ProcedureDetails as
-- This code build all procedures within the package for references that is found in above query
(
SELECT
owner as schemaname,
name as packagename,
type as typename,
TEXT,
trim(REGEXP_SUBSTR(TEXT, '(PROCEDURE [[:print:]]+)\(',1,1,null,1)) as procedure_name,
line startline,
LEAD(line, 1) OVER (partition by name order by line)-1 as endline
FROM dba_source
WHERE owner = upper(:OWNER)
AND type = 'PACKAGE BODY'
AND upper(text) LIKE '%PROCEDURE%(%'
and exists (SELECt 1 FROM TableDep WHERE TableDep.packagename=name)
)
,ProcCode as
-- This code builds procedures into one cell per program for a given package. Later to find the effected procedures
(
SELECT
ProcTag.packagename ,
ProcTag.schemaname,
ProcTag.typename,
ProcTag.PROCEDURE_NAME,
ProcTag.startline,
ProcTag.endline,
TO_CLOB(rtrim(xmlagg(xmlelement(e,codeline.text).extract('//text()') order by line).GetClobVal(),',')) as Procedure_Code
FROM
ProcedureDetails ProcTag
INNER JOIN dba_source codeline ON ProcTag.packagename=codeline.name
AND ProcTag.schemaname=codeline.owner
and ProcTag.typename=codeline.type
and codeline.line between ProcTag.startline and ProcTag.endline
--WHERE PROCEDURE_NAME='PROCEDURE TRANS_KAT_INSO'
group by
ProcTag.packagename ,
ProcTag.schemaname,
ProcTag.typename,
ProcTag.PROCEDURE_NAME,
ProcTag.startline,
ProcTag.endline
)
-- extract all the reference code for the given table selected with it complete procedure code.
SELECT
ProcHeader.Packagename, ProcHeader.schemaname, ProcHeader.typename, ProcHeader.procedure_name, ProcHeader.Procedure_Code ,ProcHeader.startline,ProcHeader.endline,ProcReference.Tablename, ProcReference.opr
FROM
ProcCode ProcHeader
INNER JOIN
(
SELECT DISTINCT ProcCode.Packagename, ProcCode.schemaname, ProcCode.typename, ProcCode.procedure_name , TableDep.Tablename, TableDep.opr
FROM ProcCode
INNER JOIN TableDep ON ProcCode.packagename=TableDep.packagename
AND ProcCode.schemaname=TableDep.schemaname
and ProcCode.typename=TableDep.typename
and TableDep.refline between ProcCode.startline and ProcCode.endline
) ProcReference
ON ProcHeader.Packagename=ProcReference.Packagename
AND ProcHeader.schemaname=ProcReference.schemaname
AND ProcHeader.typename=ProcReference.typename
AND ProcHeader.procedure_name=ProcReference.procedure_name
;

This question already have an accepted answer but anyhow the query used inside the accepted answer will pick all the user sources which uses the particular table.
Because the question is specific about Procedures you can go for the below query to get the result
SELECT * FROM user_source WHERE text LIKE '%YourTableName%' and TYPE='PROCEDURE';

Related

Find out all useful columns in a table in sql server

I have a table which has 50+ columns but only few columns are getting used. that means when any stored procedure uses that table it only refers 4-5 columns in select/where statements . rest of columns are not getting used . i just want to list down those columns that are actually getting used. one way is finding out the dependencies of a table and then go through every SP and find out which columns are getting used . but in that case i have around 30+ Sp. is there any efficient way to do it.
To use multiple columns in a procedure, you can use a code like below
create procedure sp_sample
#column_names varchar(200)
as
if #column_names='' or #column_nams is null
set #column_names='*'
exec ('select '+#column_name +' from table')
Here are some examples :
exec sp_sample #columnname='id,name'
or
exec sp_sample #columnname='id,name,telphone'
Try this:
select name from syscomments c
join sysobjects o on c.id = o.id
where TEXT like '%table_name%' and TEXT like '%column_name%'
In table_name give you table name, in column_name give the column for which you want to chck the procedure dependencies.You will get the stored procedure names as output
If you import your database as a database project using the SQL Server Data Tools, you will be able to find all references to a table or column using the "Find All References" context command. What makes this particularly useful is the accuracy: it will even find instances of SELECT * that don't mention the column explicitly, but implicitly refer to it anyway. It will also not be confused by tables or columns with similar names (finding particular instances of ID is otherwise rather problematic).
If all you want to know if a column is referenced at all, you can simply delete it and see if any "unresolved reference" errors appear in the error list -- if yes, then the column is used somewhere.

Query across multiple databases on same server

I am looking for a way of dealing with the following situation:
We have a database server with multiple databases on it (all have the same schema, different data).
We are looking for a way to query across all the databases (and for it to be easy to configure, as more databases may be added at any time). This data access must be realtime.
Say, as an example, you have an application that inserts orders - each application has its own DB etc. What we are then looking for is an efficient way for a single application to then access the order information in all the other databases in order to query it and subsequently action it.
My searches to date have not revealed very much, however I think I may just be missing the appropriate keywords in order to find the correct info...
You must specify the database name before any database object.
Single database:
SELECT * FROM [dbo].[myTable]
Multiple dabases:
SELECT * FROM [DB01].[dbo].[myTable]
UNION ALL
SELECT * FROM [DB02].[dbo].[myTable]
UNION ALL
SELECT * FROM [DB03].[dbo].[myTable]
It's not going to be the cleanest solution ever, but you could define a view on a "Master database" (if your individual databases are not going to stay constant) that includes the data from the individual databases, and allows you to execute queries on a single source.
For example...
CREATE VIEW vCombinedRecords AS
SELECT * FROM DB1.dbo.MyTable
UNION ALL
SELECT * FROM DB2.dbo.MyTable
Which allows you to do...
SELECT * FROM vCombinedRecords WHERE....
When your databases change, you just update the view definition to include the new tables.
You can build the union dynamically:
select name from sys.databases
and then check if the database has the table:
select name from [dbname_from_above].sys.tables where name = 'YourTable'
That gives you all databases for the union. You can build the query client side or in dynamic SQL.
Note - My answer below received a couple down votes, but only one comment giving any reason why it might be down-voted. The comment was that this answer is very similar to the accepted answer, but even less performant. I disagree with this opinion and I reproduce my response here - in the actual answer - so that anyone else reading my answer might have a better chance at seeing why this is not the same as the accepted answer at all, and in fact better addresses the original question.
My response to the suggestion this is similar to the accepted answer:
on the contrary - the original question notes that new databases are added regularly. The accepted solution will require maintenance each time a new database is added. The solution here will work regardless of whether any new databases are added (in line with the original question that states they all have the same schema). Further, the accepted answer requires you to duplicate the query once per database queried. If the query is complex, that gets ugly fast. The proposal here ensures a single source of truth for the logic being used in the query.
And the answer itself:
Shooting from the hip here.
use master;
go
create table #Temp (sourceDBName varchar(128), colA varchar(128), colB varchar(128));
exec sp_MSforeachDB ' USE [?];
insert into #Temp
SELECT DISTINCT
''?'',
tableA.colA,
tableB.colB
FROM tableA JOIN tableB on some_conditions
WHERE someCol LIKE ''%some_term%''
'
select sourceDBName, colA, colB from #Temp order by 1, 2, 3;
drop table #Temp;
This logic should allow you to apply a single query to all databases. To use it though, you will want to add logic to filter out system databases, or explicitly include only the databases you specify. To achieve that, you might like to put this logic into a stored procedure which then returns a result set, so in the end, your call to this logic is a select statement that returns a rowset you can join, filter, etc.
Check out https://www.mssqltips.com/sqlservertip/2855/sql-server-multi-database-query-with-registered-servers/
SELECT * FROM (
SELECT
##SERVERNAME as [ServerName],
##version [Version],
Format(##CONNECTIONS,'N0') [Conections],
Format(##CPU_BUSY ,'N0')[CPUBusy]
) SQLInfo
LEFT JOIN (
SELECT
##SERVERNAME AS [ServerName],
SERVERPROPERTY('ProductVersion') [Version Build],
SERVERPROPERTY ('Edition') AS [Edition],
SERVERPROPERTY('ProductLevel') AS [Service Pack],
CASE SERVERPROPERTY('IsIntegratedSecurityOnly')
WHEN 0 THEN 'SQL Server and Windows Authentication mode'
WHEN 1 THEN 'Windows Authentication mode'
END AS [Server Authentication],
CASE SERVERPROPERTY('IsClustered')
WHEN 0 THEN 'False'
WHEN 1 THEN 'True'
END AS [Is Clustered?],
SERVERPROPERTY('ComputerNamePhysicalNetBIOS') AS [Current Node Name],
SERVERPROPERTY('Collation') AS [ SQL Collation],
[cpu_count] AS [CPUs],
[physical_memory_kb]*0.00000095367432 AS [RAM (GB)]
FROM [sys].[dm_os_sys_info]
) SQLInfo2 on SQLInfo.[ServerName]=SQLInfo2.[ServerName]
LEFT JOIN (
SELECT
##SERVERNAME as [ServerName],
NodeName,
Status_Description,
is_Current_Owner
FROM [MASTER].[sys].[fn_virtualservernodes]()
)Clusterinfo on SQLInfo.[ServerName]=Clusterinfo.[ServerName]

query with CONTAINS on table without full text index

Without using dynamic SQL, and other than an If ELSE is there any way to test if a table has a full text index without causing additional locking.
I'm looking for some way that I can still execute:
SELECT * FROM Cars WHERE <not has full text index> OR (<has full text index> AND CONTAINS(*, 'fast'))
There is no full-text index on this the table Cars ideally I would like for it to just return all rows when we try and specify the contains string.
What I have currently from SQL is:
SELECT * FROM Cars
WHERE NOT EXISTS (SELECT 1 FROM sys.columns WHERE object_id = object_id('Cars') AND COLUMNPROPERTY(object_id, name, 'IsFulltextIndexed') = 1)
OR (EXISTS (SELECT 1 FROM sys.columns WHERE object_id = object_id('Cars') AND COLUMNPROPERTY(object_id, name, 'IsFulltextIndexed') = 1)
AND CONTAINS(*, 'fast'))
Which parses fine but fails because:
Msg 7601, Level 16, State 1, Line 3 Cannot use a CONTAINS or FREETEXT
predicate on table or indexed view 'Cars' because it is not full-text
indexed.
Is there a way to do this without dynamic SQL and without using an IF ELSE?
Bonus points if you can explain why the optimizer doesn't short-circuit the 1=0 and CONTAINS from SELECT * FROM Cars WHERE 1=1 OR (1=0 AND CONTAINS(*, 'fast')).
In practice I will likely create a new class attribute and then use that to ignore the full text indexes in my ORM, but I am interested to see if there is another option.
No.
All single queries are compiled into a single plan, and so all the individual components must always be valid. It is not possible to write a single statement where different components are valid in different circumstances.
To do so require more traditional programatic constructs. These are available in T-SQL, but not within a single SQL statement. As such, the solutions you have listed as innapropriate are actually the ways to resolve such issues.

Getting list of table comments in PostgreSQL

Postgresql allows adding comments to objects such as tables. For example I've added a comment to table "mytable" by using this SQL command:
COMMENT ON TABLE mytable IS 'This is my table.';
My question is:
If I want to use a SQL-command to get all tables along with their respective comment - how would I do this? What would be the appropriate query for this?
Thanks in advance!
Cheers!
All comments are stored in pg_description
To get the comments on a table, you need to join it to pg_class
As an alternative you can also use the function obj_description() to retrieve this information:
SELECT obj_description(oid)
FROM pg_class
WHERE relkind = 'r'
Edit
In psql you can simply use the \d+ command to show all tables including their comments. Or use the \dd command to show all comments in the system
The main problem with "show comments" is to remember the name of specific fucntions, catalog names, etc. to retrieve the comment... Or its pages on the Guide. At this answer we solve in 2 ways: by a summary of the ordinary way (the pg-way) to show comments; and by offering shortcut functions, to reduce the "remember problem".
The pg-way
The simplest, on psql, is to use \dt+ to show table comments and \d+ to show column comments. Some for function comments?
To get on SQL, and for people that remember all parameters, the pg-way is to use the obj_description() function (Guide) in conjunction with adequate reg-type:
Function: select obj_description('mySchema.myFunction'::regproc, 'pg_proc')
Table or View: ("... and most everything else that has columns or is otherwise similar to a table",guide) select obj_description('mySchema.myClass'::regclass, 'pg_class')
other generic: select obj_description('mySchema.myObject'::regName, pg_regName), where regName is 1 in 10 of datatype-oid references Guide, and pg_regName is the same replacing prefix reg by prefix pg_.
other specific: similar select obj_description('schema.myObject'::regName, catalog_name), where catalog_name is to be more specific about a (1 in 95) key-word at catalogs Guide. It can reduce some "namespace pollution". For example pg_proc for functions, pg_aggregate for aggregate functions.
to get comment for a shared database object, analog but using the function shobj_description() (same page Guide).
Column: select col_description('mySchema.myObject'::regClass, column_number), where column_number is the column's ordinal position (at the CREATE TABLE). No column-name... See col_description(table,column_name) complement bellow.
IMPORTANT: the use of same reg-type and _catalog_name_ (e. g. ::regclass and pg_class) seems redundant and sometimes obj_description('obj'::regObj) works fine, with only reg-type! ...But, as the Guide say:
it is deprecated since there is no guarantee that OIDs are unique across different system catalogs; therefore, the wrong comment might be returned.
Shortcut functions to get comments
if you are finding it difficult to remember all the type-casts and parameters, the best is to adopt a new and simplest function to retrieve comments.
CREATE FUNCTION rel_description(
p_relname text, p_schemaname text DEFAULT NULL
) RETURNS text AS $f$
SELECT obj_description((CASE
WHEN strpos($1, '.')>0 THEN $1
WHEN $2 IS NULL THEN 'public.'||$1
ELSE $2||'.'||$1
END)::regclass, 'pg_class');
$f$ LANGUAGE SQL;
-- EXAMPLES OF USE:
-- SELECT rel_description('mytable');
-- SELECT rel_description('public.mytable');
-- SELECT rel_description('otherschema.mytable');
-- SELECT rel_description('mytable', 'otherschema');
-- PS: rel_description('public.mytable', 'otherschema') is a syntax error,
-- but not generates exception: returns the same as ('public.mytable')
We need also something less ugly to show column comments. There are no kind of pg_get_serial_sequence() function to get ordinal position of a column from its name. The native col_description('mySchema.myObject'::regClass, column_number) needs a complement:
CREATE FUNCTION col_description(
p_relname text, -- table name or schema.table
p_colname text, -- table's column name
p_database text DEFAULT NULL -- NULL for current
) RETURNS text AS $f$
WITH r AS (
SELECT CASE WHEN array_length(x,1)=1 THEN array['public',x[1]] ELSE x END
FROM regexp_split_to_array(p_relname,'\.') t(x)
)
SELECT col_description(p_relname::regClass, ordinal_position)
FROM r, information_schema.columns i
WHERE i.table_catalog = CASE
WHEN $3 IS NULL THEN current_database() ELSE $3
END and i.table_schema = r.x[1]
and i.table_name = r.x[2]
and i.column_name = p_colname
$f$ LANGUAGE SQL;
-- SELECT col_description('tableName','colName');
-- SELECT col_description('schemaName.tableName','colName','databaseName);
NOTES:
As recommended by this answer: "If you want to know which queries does psql run when you do \dt+ or \d+ customers, just launche it with psql -E".
It is possible to express multiline comment, using any multiline string (with E\n or $$...$$)... But you can't apply trim() or use another dynamic aspect. Must use dynamic SQL on COMMENT clause for it.
No comments to see? PostgreSQL programmers not use COMMENT clause because it is ugly to use: there are no syntax to add comment on CREATE TABLE or on CREATE FUNCTION; and there are no good IDE to automatize it.
The modern http://postgREST.org/ interface show comments on the Web!
You can use pg_catalog.obj_description function and information_schema.tables schema view:
SELECT t.table_name, pg_catalog.obj_description(pgc.oid, 'pg_class')
FROM information_schema.tables t
INNER JOIN pg_catalog.pg_class pgc
ON t.table_name = pgc.relname
WHERE t.table_type='BASE TABLE'
AND t.table_schema='public';
FUNCTIONS-INFO-COMMENT-TABLE
INFORMATION_SCHEMA Support in MySQL, PostgreSQL
If you still have tables with mixed case names you can use the following to get a complete list of all tables in a database with their comment, as well as catalog, schema, table type, etc. (tested in PostGIS - pgAdmin 4):
select *,
obj_description((table_schema||'.'||quote_ident(table_name))::regclass)
from information_schema.tables where table_schema <> 'pg_catalog'

SQL/JDBC : select query on variable tablenames

I'm using Oracle DB and I would like to write a SQL query that I could then call with JDBC. I'm not very familiar with SQL so if someone can help me, that could be great ! Here is the problem. I have a table MY_TABLE wich contains a list of another tables, and I would like to keep only the nonempty tables and those that their names start by a particular string.
The query I wrote is the following :
select TABLE_NAME
from MY_TABLE
where TABLE_NAME like '%myString%'
and (select count(*) from TABLE_NAME where rownum=1)<>0
order by TABLE_NAME;`
The problem comes from the second SELECT, but I don't know how can I do to use the TABLE_NAME value.
Does someone have an idea ?
Thanks.
[Added from comments]
Actually, I need to test the V$ views contained in the ALL_CATALOG table. But if I can find another table where all these views are contained too and with a NUM_ROWS column too, it would be perfect !
Standard versions of SQL do not allow you to replace 'structural elements' of the query, such as table name or column name, with variable values or place-holders.
There are a few ways to approach this.
Generate a separate SQL statement for each table name listed in MY_TABLE, and execute each in turn. Brute force, but effective.
Interrogate the system catalog directly.
Investigate whether there are JDBC metadata operations that allow you to find out about the number of rows in a table without being tied to the system catalog of the specific DBMS you are using.
Can you use oracle view USER_TABLES? then query will be much easier
select TABLE_NAME
from USER_TABLES
where TABLE_NAME like '%myString%'
and Num_ROWS > 0
order by TABLE_NAME;`