I have inherited a number of very large stored procedures with nested stored procedures inside. I wanted to create a stored procedure I could run to show me all the dependencies for a particular stored procedure - listing all the tables and nested stored procedures within it.
I am using the following code but this only gives me the top level dependencies and not anything from the nested stored procedures
CREATE TABLE #TEMP_ROADMAP
(
TBL_OBJECT_NAME VARCHAR(50),
TBL_TYPE VARCHAR(20),
TBL_PARENT VARCHAR(20),
PROCNAME VARCHAR(50)
)
DECLARE #PARENTPROC VARCHAR(100)
SET #PARENTPROC = '[DBO].[RULE_PROC_1]'
INSERT INTO #TEMP_ROADMAP (TBL_OBJECT_NAME,TBL_TYPE)
SELECT DISTINCT
[OBJECT_NAME] = SCHEMA_NAME(O.[SCHEMA_ID]) + '.' + O.NAME, O.TYPE_DESC
FROM
SYS.DM_SQL_REFERENCED_ENTITIES (#PARENTPROC, 'OBJECT') D
JOIN
SYS.OBJECTS O ON D.REFERENCED_ID = O.[OBJECT_ID]
WHERE
O.[TYPE] IN ('U', 'V', 'P', 'IF', 'FN')
UPDATE #TEMP_ROADMAP
SET TBL_PARENT = 'PARENT',
PROCNAME = #PARENTPROC
WHERE TBL_OBJECT_NAME IS NOT NULL
SELECT * FROM #TEMP_ROADMAP
DROP TABLE #TEMP_ROADMAP
Output:
TBL_OBJECT_NAME TBL_TYPE TBL_PARENT PROCNAME
dbo.RULE_PROC_2 SQL_STORED_PROCEDURE PARENT [DBO].[RULE_PROC_1]
dbo.TEST_TABLE_1 USER_TABLE PARENT [DBO].[RULE_PROC_1]
Can it done where I can get the tables and name of the other nested stored procedures in this parent procedure?
Sorry if I misunderstand you but I think you need this :-
EXECUTE SP_DEPENDS <PROCNAME>
Related
Following is the sample to search stored procedures which have the keyword Student in them:
SELECT TOP (1000) *
FROM [eUniversityManagement].[sys].[all_sql_modules]
WHERE definition LIKE '%Student%'
I just wondering can we search any stored procedure that has and UPDATE on Student.name for example:
UPDATE STUDENT
SET Name = #Name,
Address = #Address,
DOB = #DOB
WHERE id = #StudentID
Thanks.
You can use the sys.dm_sql_referenced_entities function for this. It's a little complicated, as it wants the procedure name which references the column, so you need to start off with all objects and apply the functions
SELECT o.name, schema_name = s.name
FROM sys.objects o
JOIN sys.schemas s ON s.schema_id = o.schema_id
CROSS APPLY sys.dm_sql_referenced_entities (QUOTENAME(s.name) + '.' + QUOTENAME(o.name), 'OBJECT') r
WHERE o.type IN ('P', 'TR')
AND r.referenced_entity_name = 'student'
AND r.referenced_minor_name = 'Name'
AND r.is_updated = 1;
I need help in getting list of all table used in any particular query in SQL Server whether its part of join or any subquery.
I am expecting output as below.
Lets say query is->
"select A.*, B.col1, B.col2 from table1 A inner join table2 B on A.abc=b.abc"
Expected output is list of table used in above query. which is table1, table2
Is there any way i can get the tables used for any random query?
-----------------------------------------------------------
Thankyou Everyone - I get it worked for SQL Server. I am looking for same thing for Netezza also.
Is there any equivalent for sys.dm_exec_describe_first_result_set in Netezza which can provide same results?
You can put your query into a stored-procedure (e.g. sp_Dummy), then you can get the dependencies with
SELECT DISTINCT
o.name
,o.type_desc
FROM sys.dm_sql_referenced_entities(QUOTENAME(N'dbo') + N'.' + QUOTENAME(N'sp_Dummy'), 'OBJECT') AS dm
INNER JOIN sys.objects AS o
ON o.object_id = dm.referenced_id
INNER JOIN sys.schemas AS sch
ON sch.schema_id = o.schema_id
Here a procedure shell
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_Dummy]') AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql #statement = N'CREATE PROCEDURE [dbo].[sp_Dummy] AS'
END
GO
-- =======================================================
-- Author: XXXX
-- Create date: 31.10.2019
-- Description: Dummy procedure to infer dependencies
-- =======================================================
ALTER PROCEDURE [dbo].[sp_Dummy]
#in_object_type AS varchar(2)
,#in_object_uid AS uniqueidentifier
,#in_report_date AS datetime2
-- your query's parameters go here...
AS
BEGIN
SET NOCOUNT ON;
-- your query goes here
END
GO
This won't get dynamic sql, though - but I think that's as good as it can get.
You can extract all paramters with:
System.Text.RegularExpressions.Regex.Matches(sql, #"\#\w+")
.Cast<Match>().Select(m => m.Value).ToList();
I need to check the library used by several stored procs that extract data from a remote server.
I have (with SO help, see SO 21708681) built the below code:
DECLARE #tProcs TABLE
(
procID int IDENTITY,
procObjectID nvarchar(100),
procName nvarchar(100)
);
insert into #tProcs
SELECT object_id, name
FROM sys.objects
WHERE type in (N'P', N'PC') and name like '%_Extract'
declare #countProcs int, #I int=0
select #countProcs=COUNT(*) from #tProcs
while #I<#countProcs
Begin
declare #source_code nvarchar(max)
declare #objectID nvarchar(50)
declare #proc_Name nvarchar(200)
select #objectID=procObjectID from #tProcs where procID=#I
select #proc_Name=procName from #tProcs where procID=#I
select #source_code = definition
from sys.sql_modules
where object_id = #objectID
SELECT PATINDEX('BOCTEST.%', #proc_Name) as Pos, #proc_Name
-- or SELECT charindex(#source_code, '%BOCTEST%')
set #I=#I+1
End
Inside each of the target stored procs there is a line like this:
DECLARE YP040P_cursor CURSOR FOR SELECT * FROM BOCTEST.S653C36C.LIVEBOC_A.YP040P
I need to know for each of the stored procs the part 'LIVEBOC_A' (which can either be 'LIVEBOC_A' or LIVEBOC_B)
I tried to use PATINDEX and CHARINDEX to get the location of the start opf that string in the definition from sysmodules but all I get back is either zero or an error that string or binary data would be truncated.
try
SELECT
name,
table_name = CASE WHEN OBJECT_DEFINITION(OBJECT_ID) LIKE '%BOCTEST.S653C36C.LIVEBOC_A.YP040P%' THEN 'LIVEBOC_A'
WHEN OBJECT_DEFINITION(OBJECT_ID) LIKE '%BOCTEST.S653C36C.LIVEBOC_B.YP040P%' THEN 'LIVEBOC_B' END
FROM sys.objects o
WHERE o.[type] IN ('P', 'PC')
AND name like '%_Extract'
You can do what you want with a query like this one:
select name = s.name + '.' + p.name ,
dt_created = p.create_date ,
dt_modified = p.modify_date ,
livboc_usage = case
when m.definition like '%declare%cursor%boctext.[^.].LIVEBOC_A.%' then 'A'
when m.definition like '%declare%cursor%boctext.[^.].LIVEBOC_B.%' then 'B'
else null
end
from sys.schemas s
join sys.procedures p on p.schema_id = s.schema_id
join sys.sql_modules m on m.object_id = p.object_id
But since what you're looking for are cross-server dependencies of a table, you should be able to get that simply by querying the system view sys.sql_expression_dependencies, which
Contains one row for each by-name dependency on a user-defined entity in the current
database. A dependency between two entities is created when one entity, called the
referenced entity, appears by name in a persisted SQL expression of another entity,
called the referencing entity. For example, when a table is referenced in the definition
of a view, the view, as the referencing entity, depends on the table, the referenced
entity. If the table is dropped, the view is unusable.
You can use this catalog view to report dependency information for the following
entities:
Schema-bound entities.
Non-schema-bound entities.
Cross-database and cross-server entities.
Entity names are reported; however, entity IDs are not resolved.
Column-level dependencies on schema-bound entities.
Column-level dependencies for non-schema-bound objects can be returned
by using sys.dm_sql_referenced_entities.
Server-level DDL triggers when in the context of the master database.
To that end, running a query like this in the database the referencing stored procedures live should do you:
select name = o.name ,
type = o.type_desc ,
liveboc_usage = case d.referenced_schema_name
when 'liveboc_a' then 'A'
when 'liveboc_b' then 'B'
else null
end ,
has_dependency_on = d.referenced_server_name
+ '.' + d.referenced_database_name
+ '.' + d.referenced_schema_name
+ '.' + d.referenced_entity_name
from sys.sql_expression_dependencies d
join sys.objects o on o.object_id = d.referenced_id
join sys.schemas s on s.schema_id = o.schema_id
where d.referenced_server_name = 'BOCTEST'
and d.referenced_database_name = 'S653C36C'
and d.referenced_schema_name like 'LIVEBOC_[AB]'
and d.referenced_entity_name = 'YP040P'
I have inherited a large database project with thousands of views.
Many of the views are invalid. They reference columns that no longer exist. Some of the views are very complex and reference many columns.
Is there an easy way to track down all the incorrect columns references?
This answer finds the underlying columns that were originally defined in the views by looking at sys.views, sys.columns and sys.depends (to get the underlying column if the column has been aliased). It then compares this with the data held in INFORMATION_Schema.VIEW_COLUMN_USAGE which appears to have the current column usage.
SELECT SCHEMA_NAME(v.schema_id) AS SchemaName,
OBJECT_NAME(v.object_id) AS ViewName,
COALESCE(alias.name, C.name) As MissingUnderlyingColumnName
FROM sys.views v
INNER JOIN sys.columns C
ON C.object_id = v.object_id
LEFT JOIN sys.sql_dependencies d
ON d.object_id = v.object_id
LEFT JOIN sys.columns alias
ON d.referenced_major_id = alias.object_id AND c.column_id= alias.column_id
WHERE NOT EXISTS
(
SELECT * FROM Information_Schema.VIEW_COLUMN_USAGE VC
WHERE VIEW_NAME = OBJECT_NAME(v.object_id)
AND VC.COLUMN_NAME = COALESCE(alias.name, C.name)
AND VC.TABLE_SCHEMA = SCHEMA_NAME(v.schema_id)
)
For the following view:
create table test
( column1 varchar(20), column2 varchar(30))
create view vwtest as select column1, column2 as column3 from test
alter table test drop column column1
The query returns:
SchemaName ViewName MissingUnderlyingColumnName
dbo vwtest column1
This was developed with the help of this Answer
UPDATED TO RETRIEVE ERROR DETAILS
So this answer gets you what you want but it isn't the greatest code.
A cursor is used (yes I know :)) to execute a SELECT from each view in a TRY block to find ones that fail. Note I wrap each statement with a SELECT * INTO #temp FROM view X WHERE 1 = 0 this is to stop the EXEC returning any results and the 1=0 is so that SQL Server can optimize the query so that it is in effect a NO-OP.
I then return a list of any views whose sql has failed.
I haven't performed lots of testing on this, but it appears to work. I would like to get rid of the execution of each SELECT from View.
So here it is:
DECLARE curView CURSOR FOR
SELECT v.name AS ViewName
FROM sys.views v
INNER JOIN sys.sql_modules m
on v.object_id = m.object_id
OPEN curView
DECLARE #viewName SYSNAME
DECLARE #failedViews TABLE
(
FailedViewName SYSNAME,
ErrorMessage VARCHAR(MAX)
)
FETCH NEXT FROM curView
INTO #ViewName
WHILE ##FETCH_STATUS = 0
BEGIN
BEGIN TRY
exec ('SELECT * INTO #temp FROM ' + #viewName + ' WHERE 1=0' )
END TRY
BEGIN CATCH
INSERT INTO #failedViews VALUES (#viewName, ERROR_MESSAGE())
END CATCH
FETCH NEXT FROM curView
INTO #ViewName
END
CLOSE curView
DEALLOCATE curView
SELECT *
FROM #failedViews
An example of an ERROR returned is:
FailedViewName ErrorMessage
--------------- -------------
vwtest Invalid column name 'column1'.
You could use system tables get information.
SELECT v.VIEW_NAME,v.TABLE_CATALOG,v.TABLE_SCHEMA,v.TABLE_NAME,v.COLUMN_NAME
from INFORMATION_SCHEMA.VIEW_COLUMN_USAGE v
left outer join INFORMATION_SCHEMA.COLUMNS c
ON v.TABLE_CATALOG=c.TABLE_CATALOG AND v.TABLE_SCHEMA=c.TABLE_SCHEMA AND v.TABLE_NAME=c.TABLE_NAME AND v.COLUMN_NAME=c.COLUMN_NAME
WHERE c.TABLE_NAME IS NULL
ORDER BY v.VIEW_NAME
I'm trying to create a simple script to dump the results of a complex view out into a table for reporting. I have used synonyms to simplify tweaking the view and table names.
The idea is that the user of the script can put the name of the view they want to use as the source, and the name of the target reporting table in at the start and away they go. If the table doesn't exist then the script should create it. If the table already exists then the script should only copy the records from the view which aren't already in the table over.
The script below covers all those requirements, but I can't find a nice way to check if the table behind the synonym already exists:
CREATE SYNONYM SourceView FOR my_view
CREATE SYNONYM TargetReportingTable FOR my_table
-- Here's where I'm having trouble, how do I check if the underlying table exists?
IF (SELECT COUNT(*) FROM information_schema.tables WHERE table_name = TargetReportingTable) = 0
BEGIN
-- Table does not exists, so insert into.
SELECT * INTO TargetReportingTable FROM SourceView
END
ELSE
BEGIN
-- Table already exists so work out the last record which was copied over
-- and insert only the newer records.
DECLARE #LastReportedRecordId INT;
SET #LastReportedRecordId = (SELECT MAX(RecordId) FROM TargetReportingTable)
INSERT INTO TargetReportingTable SELECT * FROM SourceView WHERE RecordId > #LastReportedRecordId
END
DROP SYNONYM SourceView
DROP SYNONYM TargetReportingTable
I know I could just get the user of the script to copy the table name into the 'information_schema' line as well as into the synonym at the top, but that leaves scope for error.
I also know I could do something filthy like put the table name into a variable and blat the SQL out as a string, but that makes me feel a bit sick!
Is there a nice elegant SQL way for me to check if the table behind the synonym exists? Or a totally different way to solve to problem?
Not the most elegant of solutions, but you could join the sys.synonyms table to the sys.tables table to check whether the table exists.
If the table does not exist, the join will fail and you will get 0 rows (hence IF EXISTS will be false). If the table does exist, the join will success and you will get 1 row (and true):
IF EXISTS( SELECT *
FROM sys.synonyms s
INNER JOIN sys.tables t ON REPLACE(REPLACE(s.base_object_name, '[', ''), ']', '') = t.name
WHERE s.name = 'TargetReportingTable')
BEGIN
-- Does exist
END
ELSE
BEGIN
-- Does not exist
END
Replace 'TargetReportingTable' with whichever synonym you wish to check.
The above solutions did not work for me if the synonym referenced another database. I recently discovered the function [fn_my_permissions] which is useful for showing permissions for a specific database object, so I figure this could be used as follows:
IF EXISTS
(
select *
from sys.synonyms sy
cross apply fn_my_permissions(sy.base_object_name, 'OBJECT')
WHERE sy.name = 'TargetReportingTable'
)
print 'yes - I exist!'
Late to the party, I have created a query to test out the existence of Synonyms and share with you.
DECLARE #Synonyms table
(
ID int identity(1,1),
SynonymsDatabaseName sysname,
SynonymsSchemaName sysname,
SynonymsName sysname,
DatabaseName nvarchar(128),
SchemaName nvarchar(128),
ObjectName nvarchar(128),
Remark nvarchar(max),
IsExists bit default(0)
)
INSERT #Synonyms (SynonymsDatabaseName, SynonymsSchemaName, SynonymsName, DatabaseName, SchemaName, ObjectName)
SELECT
DB_NAME() AS SynonymsDatabaseName,
SCHEMA_NAME(schema_id) AS SynonymsSchemaName,
name AS SynonymsName,
PARSENAME(base_object_name,3) AS DatabaseName,
PARSENAME(base_object_name,2) AS SchemaName,
PARSENAME(base_object_name,1) AS ObjectName
FROM sys.synonyms
SET NOCOUNT ON
DECLARE #ID int = 1, #Query nvarchar(max), #Remark nvarchar(max)
WHILE EXISTS(SELECT * FROM #Synonyms WHERE ID = #ID)
BEGIN
SELECT
#Query = 'SELECT #Remark = o.type_desc FROM [' + DatabaseName + '].sys.objects o INNER JOIN sys.schemas s ON o.schema_id = s.schema_id WHERE s.name = ''' + SchemaName + ''' AND o.name = ''' + ObjectName + ''''
FROM #Synonyms WHERE ID = #ID
EXEC sp_executesql #Query, N'#Remark nvarchar(max) OUTPUT', #Remark OUTPUT;
UPDATE #Synonyms SET IsExists = CASE WHEN #Remark IS NULL THEN 0 ELSE 1 END, Remark = #Remark WHERE ID = #ID
SELECT #ID += 1, #Remark = NULL
END
SELECT * FROM #Synonyms
You can do this with dynamic SQL:
-- create synonym a for information_schema.tables
create synonym a for b
declare #exists int = 1;
begin try
exec('select top 0 * from a');
end try
begin catch
set #exists = 0;
end catch
select #exists;
This doesn't work with non-dynamic SQL, because the synonym reference is caught at compile-time. That means that the code just fails with a message and is not caught by the try/catch block. With dynamic SQL, the block catches the error.
You can test if Synonym exists in your database using the Object_Id function avaliable in SQL Server
IF OBJECT_ID('YourDatabaseName..YourSynonymName') IS NOT NULL
PRINT 'Exist SYNONYM'
ELSE
PRINT 'Not Exist SYNONYM'
Another simpler solution:
IF (EXISTS (SELECT * FROM sys.synonyms WHERE NAME ='mySynonymName'))
BEGIN
UPDATE mySynonymName
SET [Win] = 1
END
In this case, I do database setup first. I drop all Synonyms in my database (database1) first, then run a SPROC to create synonyms for all tables in the destination database(database2).
Some SPROCS in database1 call on tables in DB2. If table doesnt exist in DB2 the SPROC fails. If table doesnt exist in DB2, the synonmy is not automatically created on database setup. So I just use the above to check if the Synonym exist, and skip that part of the SPROC if the Synonym is not present.