We are upgrading our ERP system and need a method to identify the procedures, views and functions that schema changes may have broken. We host all of our interface logic and supporting entities within a supplemental db within the same instance as the production db. I need a script that will collect all the tables within the new production db along with their fields, data type, and size and compare these results to all the procedures, views and functions within the supplemental db and return those dependencies that are now broken. Is there a way to do this?
In 2005, this is going to be pretty tough, since sysdepends / sys.sql_dependencies is thoroughly broken and better dependency features weren't added until SQL Server 2008.
I suppose you could ensure sysdepends was up-to-date by recompiling all of your views, procedures, functions and triggers twice (since on the first pass there still might be deferred name resolution issues etc):
DECLARE #sql NVARCHAR(MAX); SET #sql = N'';
SELECT #sql = #sql + N'
EXEC sp_refreshview N''' + s.name + '.' + v.name + ''';'
FROM sys.views AS v
INNER JOIN sys.schemas AS s
ON v.[schema_id] = s.[schema_id];
SELECT #sql = #sql + N'
EXEC sp_recompile N''' + s.name + '.'
+ OBJECT_NAME(m.[object_id]) + ''';'
FROM sys.sql_modules AS m
INNER JOIN sys.objects AS o
ON m.[object_id] = o.[object_id]
INNER JOIN sys.schemas AS s
ON o.[schema_id] = s.[schema_id]
AND o.[type] IN (N'TR', N'FN', N'IF', N'P', N'TF');
PRINT #sql;
--EXEC sp_executesql #sql;
Now you can have a little more faith in sysdepends / sys.sql_dependencies, but it might be even better to generate ALTER commands for them so they are actively recompiled instead of just marked for recompilation. This assumes that you haven't gotten cute with comments before the CREATE <object> command that themselves contain the text create. (Wouldn't a way to generate ALTER commands be nice? Or the ability to CREATE OR REPLACE?)
DECLARE #sql NVARCHAR(MAX); SET #sql = N'';
SELECT #sql = #sql + N'
GO
' + STUFF(definition, CHARINDEX('CREATE', UPPER(definition)), 6, 'ALTER')
FROM sys.sql_modules AS m
INNER JOIN sys.objects AS o
ON m.[object_id] = o.[object_id]
INNER JOIN sys.schemas AS s
ON o.[schema_id] = s.[schema_id]
AND o.[type] IN (N'TR', N'FN', N'IF', N'P', N'TF');
PRINT #sql;
-- EXEC sp_executesql #sql;
Once you've done that, you can look at sys.sql_dependencies (looks best in results to grid):
SELECT
s1.name + '.' + OBJECT_NAME(d.[object_id]),
' (' + o1.type_desc + ')',
' depends on => ',
s2.name + '.' + OBJECT_NAME(d.referenced_major_id),
' (' + o2.type_desc + ')'
FROM sys.sql_dependencies AS d
INNER JOIN sys.objects AS o1
ON d.[object_id] = o1.[object_id]
INNER JOIN sys.schemas AS s1
ON o1.[schema_id] = s1.[schema_id]
INNER JOIN sys.objects AS o2
ON d.[referenced_major_id] = o2.[object_id]
INNER JOIN sys.schemas AS s2
ON o2.[schema_id] = s2.[schema_id];
The joins to sys.schemas are necessary throughout because, while you could use OBJECT_SCHEMA_NAME(), that wasn't available in RTM, and I don't know what build you're on. If you have access to that function then you could simplify some of these joins.
Another idea
You could take a look at 3rd party tools, like Red-Gate's SQL Dependency Tracker. I've only looked at it peripherally and it was a long time ago, so I can't vouch for it, only that the company makes tools that just work.
Related
SELECT TOP 10
*
FROM
(SELECT
COLUMN_NAME, TABLE_NAME
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
COLUMN_NAME LIKE '%MAIL%')
I am trying to get the top 10 rows from each of the tables within my search. Any ideas? SQL Server 2008 R2
While nscheaffer's answer is valid, I feel obligated to tell you that you can get the exact same functionality without using a cursor, and also while using a query which might be a bit easier to implement.
Just concatenate all of the possible queries together based off the system tables and then execute them simultaneously, like this:
DECLARE #SQL NVARCHAR(MAX)
;
SELECT #SQL =
(
SELECT 'SELECT TOP 10 * FROM ' + OBJECT_NAME(C.Object_ID) + ';' + CHAR(10)
FROM sys.Columns C
INNER JOIN sys.Tables T
ON C.Object_ID = T.Object_ID
AND T.is_ms_shipped = 0
WHERE C.Name LIKE '%Mail%'
GROUP BY C.Object_ID
ORDER BY C.Object_ID
FOR XML PATH('')
)
;
EXEC sp_ExecuteSQL #SQL
;
If you want to check the SQL before it runs, just comment out the EXEC command and replace it with a SELECT, like this:
SELECT #SQL;
--EXEC sp_ExecuteSQL #SQL
;
I would use a cursor to create dynamic SQL and then execute that SQL. Does this work for you?
DECLARE #SQL NVARCHAR (4000) = ''
DECLARE #Table_Name NVARCHAR(50)
DECLARE TableCursor CURSOR FAST_FORWARD READ_ONLY FOR
SELECT DISTINCT TABLE_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME LIKE '%MAIL%'
OPEN TableCursor
FETCH NEXT FROM TableCursor INTO #Table_Name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = #SQL + '
SELECT TOP 10 * FROM ' + #Table_Name + '
GO
'
FETCH NEXT FROM TableCursor INTO #Table_Name
END
CLOSE TableCursor
DEALLOCATE TableCursor
EXECUTE sp_executeSQL #SQL
I am trying to write a query that for a single SQL Server instance, lists all columns in all tables in all databases on the server instance. I have found examples that list all columns in all tables but you have to know the database name. I have found examples that list databases on a SQL Server instance but not the tables in the databases. Now I am trying to find something that combines the two, but I am not having much luck.
Would anyone know if such a thing exists or is it a manual process to bridge the gap?
Thanks,
Tom
You can use dynamic query. I did not find any other way.
declare #str varchar(max) = ''
;with dbs as (
select *
from sys.databases
where dbs.name not in ('master', 'tempdb', 'model', 'msdb')
)
select #str = #str + 'select ''' + dbs.name + ''', tbl.name, col.name from ' +
dbs.name + '.sys.tables tbl inner join ' +
dbs.name + '.sys.columns col ON col.object_id = tbl.object_id; '
from dbs
print #str
exec(#str)
Please try this and feed back with comments.
The requirement was to find out one particular table from all the database. This was not possible by visual inspection as it might take lots of time and human error was possible. She was aware of the system view sys.tables.
SELECT *
FROM sys.Tables
WHERE name LIKE '%Address%'
The limitation of query mentioned above is that it only searches in one database and user has to keep on changing database manually and run the query again. I wrote down following quick script which looks into all the database on the server and provides the database name, schema name and table containing searched word in its name.
CREATE PROCEDURE usp_FindTableNameInAllDatabase
#TableName VARCHAR(256)
AS
DECLARE #DBName VARCHAR(256)
DECLARE #varSQL VARCHAR(512)
DECLARE #getDBName CURSOR
SET #getDBName = CURSOR FOR
SELECT name
FROM sys.databases
CREATE TABLE #TmpTable (DBName VARCHAR(256),
SchemaName VARCHAR(256),
TableName VARCHAR(256))
OPEN #getDBName
FETCH NEXT
FROM #getDBName INTO #DBName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #varSQL = 'USE ' + #DBName + ';
INSERT INTO #TmpTable
SELECT '''+ #DBName + ''' AS DBName,
SCHEMA_NAME(schema_id) AS SchemaName,
name AS TableName
FROM sys.tables
WHERE name LIKE ''%' + #TableName + '%'''
EXEC (#varSQL)
FETCH NEXT
FROM #getDBName INTO #DBName
END
CLOSE #getDBName
DEALLOCATE #getDBName
SELECT *
FROM #TmpTable
DROP TABLE #TmpTable
GO
EXEC usp_FindTableNameInAllDatabase 'Address'
GO
On this link
http://gallery.technet.microsoft.com/Compare-Data-0c5bfc87#content
we can find a stored procedure example which can compare two tables data. WHat I would like is to call this sp for each table in database. I have found next sp that will enumerate through all tables
http://weblogs.sqlteam.com/joew/archive/2007/10/23/60383.aspx
The problem is that I cannot get to pass properly parameters. Here is what I tried (I have placed both databases on local server):
Exec sp_MSforeachtable "EXEC sp_CompareTable dbName1, dbName2, NULL, PARSENAME('?', 1)"
and that fails with
Msg 102, Level 15, State 1, Line 1 Incorrect syntax near '[dbo].[Activities]'.
And the same message for every table. Can anyone help me with what I am doing wrong here?
I'll stick my neck out and post this as an answer because it's not formatted nicely as a comment.
Have you tried this:
sp_MSforeachtable "EXEC sp_CompareTable dbName1, dbName2, NULL, PARSENAME('[?]', 1)"
Update:
It looks like it doesn't like the PARSENAME. You could try this (I tried this on a version of sp_CompareTable with the EXEC changed to a PRINT).
Add this line to sp_CompareTable (before the EXEC):
SET #TableName = PARSENAME(#TableName,1)
Call it like this:
sp_MSforeachtable "EXEC sp_CompareTable dbName1, dbName2, dbo, '?'"
NB: This would be a quick fix for the case where you only have the "dbo" schema. It doesn't really answer the question of exactly why the original syntax doesn't work.
Update Again:
Here's a version of the Compare Table stored procedure which is tailored to run with sp_MSforeachtable
CREATE PROC [dbo].[uspCompareTable](#db1 varchar(250), #db2 sysname, #TableName sysname)
AS
declare #reason varchar(7)='Missing';
IF #TableName = '[dbo].[sysdiagrams]'
RETURN
IF CHARINDEX('.',#db1,1) <> 0
SET #db1=QUOTENAME(SUBSTRING(#db1,1, CHARINDEX('.',#db1,1)-1))+'.'+QUOTENAME(SUBSTRING(#db1, CHARINDEX('.',#db1,1)+1,DATALENGTH(#db1)-CHARINDEX('.',#db1,1)))
IF CHARINDEX('.',#db2,1) <> 0
SET #db2=QUOTENAME(SUBSTRING(#db2,1, CHARINDEX('.',#db2,1)-1))+'.'+QUOTENAME(SUBSTRING(#db2, CHARINDEX('.',#db2,1)+1,DATALENGTH(#db2)-CHARINDEX('.',#db2,1)))
EXEC ('
SELECT * FROM
(SELECT * FROM '+ #db1 + '.' + #TableName +'
EXCEPT
SELECT * FROM '+ #db2 + '.' + #TableName +') T
CROSS JOIN (SELECT '''+#reason +' in '+#db2 +'.' + #TableName+''' Reason) T2
UNION ALL
SELECT * FROM
(SELECT * FROM '+ #db2 + '.' + #TableName +'
EXCEPT
SELECT * FROM '+ #db1 + '.' + #TableName +' ) T
CROSS JOIN (SELECT ''' + #reason + ' in ' + #db1 + '.' + #TableName + ''' Reason) T2')
Here I'm assuming that schema will be part of the TableName (which it should be if you're calling from sp_MSforeachtable). Also a tweak to skip sysdiagrams which gets picked up on my system (SQL Server 2008 Express).
Usage would be
sp_MSforeachtable "EXEC uspCompareTable dbname1, dbname2, '?'"
I have the following SQL Server stored procedure:
CREATE PROCEDURE ispsDcOhcAgg #TmpTableName NVARCHAR(50), #ListItem NVARCHAR(50)
AS
IF EXISTS (SELECT name
FROM sys.tables
WHERE name = #TmpTableName)
DROP TABLE #TmpTableName; -- This will not work.
GO
This will clearly not work (see the comment in the above snippit). The only (and very ugly) way I have found to get around this problem is to do the following
CREATE PROCEDURE ispsDcOhcAgg #TmpTableName NVARCHAR(50), #ListItem NVARCHAR(50)
AS
DECLARE #SQL NVARCHAR(4000)
SET #SQL = N'IF EXISTS (SELECT name ' +
N'FROM sys.tables ' +
N'WHERE name = N' + N'''' + #TmpTableName + N''') ' +
N'DROP TABLE ' + #TmpTableName + N';'
EXEC sp_executesql #SQL;
GO
which truly stinks and for large stored procedures, it's horrendous!
Is there another way of doing this that I don't know about?
Thanks for your time.
No, if you want to use a table name dynamically like this, you need to use dynamic SQL.
So you should make sure you don't open yourself up to nasty SQL injection risks!
Try something like this:
SET #SQL = 'IF EXISTS (SELECT name ' +
N'FROM sys.tables ' +
N'WHERE name = #TableName) ' +
N'DROP TABLE ' + QUOTENAME(#TmpTableName) + ';'
EXEC sp_executesql #SQL, N'#TableName sysname', #TmpTableName;
No, if you want to determine the table to be dropped at runtime, there is no alternative to dynamic SQL.
There is a slightly less ugly way: you only use dynamic SQL for the command that needs to be dynamic (the DROP command):
DECLARE #SQL NVARCHAR(100)
IF EXISTS (SELECT name
FROM sys.tables
WHERE name = #TmpTableName)
BEGIN
SET #SQL = N'DROP TABLE ' + #TmpTableName + N';'
EXEC sp_executesql #SQL;
END
Running this code
DECLARE #SQL VARCHAR(2500) =
'''SELECT z.* from openrowset(''''SQLNCLI'''',''''Server=server;UID=user;PWD=pwd;'''',
''''SELECT distinct x.PackageName
FROM [dw].[dbo].[dex] x
JOIN [dw].dbo.log l on l.executionid = x.SSISExecutionGUID '''') z''' EXECUTE (#SQL)
Issues a syntax error Incorrect syntax near 'select z.*
from openrowset('SQLNCLI', 'Server=server;UID=user;PWD=pw;',
'SELECT distinct x.PackageN'.
if I remove the parens from the (#SQL) the error changes to this:
The name ''select z.*
from openrowset(''SQLNCLI'', ''Server=server;UID=user;PWD=pwd;'',
''SELECT distinct x.PackageName
FROM [dw].[dbo].[dex] x join [dw].dbo.log l on l.executionid = x.SSISExecutionGUID ''
) z'' is not a valid identifier.
and funniest of all, if i change Execute to Print, then manually take the printed result and wrap EXECUTE around it, it works as expected and gets me my results.
I'm thinking it's some crazy single quote issue, but I can't see it.
Anyone have any ideas?
I just took the output of the print and pasted that into the variable assignment instead and it appears to work (at least as far as I can tell this end - at least it doesn't give any syntax errors)
DECLARE #SQL VARCHAR(2500) = '
SELECT z.* from openrowset(''SQLNCLI'',''Server=server;UID=user;PWD=pwd;'',
''SELECT distinct x.PackageName FROM [dw].[dbo].[dex] x
JOIN [dw].dbo.log l on l.executionid = x.SSISExecutionGUID '') z'
EXECUTE (#SQL)