 |
|
 |
Thanks for the article, it was a very handy pointer. I've just put this together in pure SQL, it doesn't include checks for ANSI nulls and quoted identifiers but they could be added quite easily.
DECLARE @Schema NVARCHAR(100),
@Name NVARCHAR(100),
@Type NVARCHAR(100),
@Definition NVARCHAR(MAX),
@CheckSQL NVARCHAR(MAX)
DECLARE crRoutines CURSOR
FOR SELECT OBJECT_SCHEMA_NAME(sm.OBJECT_ID) AS schema_name,
OBJECT_NAME(sm.object_id) AS object_name,
o.type_desc,
CASE WHEN o.type_desc = 'SQL_STORED_PROCEDURE' THEN STUFF(sm.definition, CHARINDEX('CREATE PROC', sm.definition), 11, 'ALTER PROC')
WHEN o.type_desc lIKE '%FUNCTION%' THEN STUFF(sm.definition, CHARINDEX('CREATE FUNC', sm.definition), 11, 'ALTER FUNC')
WHEN o.TYPE = 'VIEW' THEN STUFF(sm.definition, CHARINDEX('CREATE VIEW', sm.definition), 11, 'ALTER VIEW')
WHEN o.TYPE = 'SQL_TRIGGER' THEN STUFF(sm.definition, CHARINDEX('CREATE TRIG', sm.definition), 11, 'ALTER TRIG')
END
FROM sys.sql_modules AS sm
JOIN sys.objects AS o ON sm.object_id = o.OBJECT_ID
ORDER BY 1, 2 ;
OPEN crRoutines
FETCH NEXT FROM crRoutines INTO @Schema, @Name, @Type, @Definition
WHILE @@FETCH_STATUS = 0
BEGIN
IF LEN(@Definition) > 0
BEGIN
BEGIN TRY
SET PARSEONLY ON ;
EXEC ( @Definition ) ;
SET PARSEONLY OFF ;
END TRY
BEGIN CATCH
PRINT @Type + ': '+ @Schema + '.' + @Name
PRINT ERROR_MESSAGE()
END CATCH
END
FETCH NEXT FROM crRoutines INTO @Schema, @Name, @Type, @Definition
END
CLOSE crRoutines
DEALLOCATE crRoutines
modified on Saturday, January 16, 2010 1:00 PM
|
|
|
|
 |
|
 |
Thanks for converting it - works a treat and useful on customer site where 'installing' software is often too hard.
Ian
(Its not that the glass is half full or half empty, it was the wrong size to begin with.)
|
|
|
|
 |
|
 |
Thanks, mate, just what I needed, gonna save me lots of time!!! Cheers. Jim
|
|
|
|
 |
|
 |
Wonderful - thank you.
On SQL Server Express 2008 I find that two changes are needed to get it to check triggers and views:
WHEN o.TYPE = 'VIEW'
should be WHEN.o.type_desc = 'VIEW'
and
WHEN o.TYPE = 'SQL_TRIGGER' should be WHEN o.type_desc = 'SQL_TRIGGER'
|
|
|
|
 |
|
 |
You can compile check a few more objects.
Also in sql05 you can use the sys.views which are documented.
Also in 05 you can choose not to check some of MS built in and hidden sprocs.
if (SqlServerMajorVersion(conn) < 9)
{
return "select name, OBJECTPROPERTY(id, 'ExecIsQuotedIdentOn') as quoted_ident_on, OBJECTPROPERTY(id, 'ExecIsAnsiNullsOn') as ansi_nulls_on, user_name(o.uid) [schema], [type], [type] type_desc from sysobjects o where type in ('P', 'V', 'FN') and category = 0 order by Type, Name";
}
else
{
return "select name, OBJECTPROPERTY(object_id, 'ExecIsQuotedIdentOn') as quoted_ident_on, OBJECTPROPERTY(object_id, 'ExecIsAnsiNullsOn') as ansi_nulls_on, object_schema_name(o.object_id) [schema], [type], [type_desc] from sys.objects o where type in ('P', 'V', 'FN', 'IF' ,'TF', 'TR') AND object_id NOT IN (SELECT major_id FROM sys.extended_properties WHERE name = 'microsoft_database_tools_support') order by Type, Name";
}
//from Books Online
//C = CHECK constraint
//D = Default or DEFAULT constraint
//F = FOREIGN KEY constraint
//L = Log
//FN = Scalar function
//IF = Inlined table-function
//P = Stored procedure
//PK = PRIMARY KEY constraint (type is K)
//RF = Replication filter stored procedure
//S = System table
//TF = Table function
//TR = Trigger
//U = User table
//UQ = UNIQUE constraint (type is K)
//V = View
//X = Extended stored procedure
|
|
|
|
 |
|
 |
So little code, so useful. I love it.
Thanks for sharing.
|
|
|
|
 |
|
 |
Just stumbled upon this today.
Brilliant piece of software!
|
|
|
|
 |
|
 |
This utility worked well for me except in one fairly large database (14,500 rows in sys.sql_modules, with largest object definition > 500K), for which the program terminated with this exception:
Unhandled Exception: System.InvalidOperationException: ExecuteNonQuery: CommandText property has not been initialized
at System.Data.SqlClient.SqlCommand.ValidateCommand(String method, Boolean async)
at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult result, String methodName, Boolean sendToPipe)
at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
at DbValidator.Main1.Main(String[] args) in c:\documents and settings\coxi\my documents\visual studio projects\dbvalidator\sourcedistribution\main1.cs:line 88
|
|
|
|
 |
|
 |
Thanks bud. Works like a charm.
|
|
|
|
 |
|
 |
I made a couple tweaks to the output by changing the exception handlers to output tab delimited strings without the stack trace (I also removed the extra line between failures). This let me copy/paste the output into Excel (and Google Spreadsheets) for easy viewing. Here's the two blocks:
First catch statement
string errText = string.Format("{0}\tUNREADABLE\t{1}", objectName, ex.Message).Replace("\r", " ").Replace("\n", " ");;
Console.WriteLine(errText);
Debug.WriteLine(errText);
Second catch statement
string errText = string.Format("{0}\tFAILED\t{1}", objectName, ex.Message).Replace("\r", " ").Replace("\n", " ");
Console.WriteLine(errText);
Debug.WriteLine(errText);
continue;
You also need to be familiar with the data application blocks to know how to adjust the connection string. Would be nice to have a command line interface that allows you to just provide a connection string at runtime. If I get a chance over the next couple days, I'll make the mod and post again.
|
|
|
|
 |
|
 |
I have posted an updated version of the code[^] with the changes above as well as a few other tweaks. The most significant change is probably support for SQL 2005.
|
|
|
|
 |
|
|
 |
|
 |
This is going to save me a lot of time in the next few days.
thank you very much
---Guy H (  ---
|
|
|
|
 |
|
 |
After repeating the process of building a replication snapshot a dozen times, identifying the broken procs one at a time and building it again, this was just brilliant.
Cheers
Nick Hoggard
|
|
|
|
 |
|
 |
Glad it was useful for you
Cheers
Ian
|
|
|
|
 |
|
|
 |
|
|
 |
|
 |
Thanks for the idea to use Profiler to see what Enterprise Manager/Query Analyzer etc are actually doing behind the scenes when you use their interfaces. That's very useful.
|
|
|
|
 |
|
 |
I hadn't really thought about it too much, but you are right it is generally useful. Handy for understanding how things are working certainly.
|
|
|
|
 |
|
 |
I use the Generate scripts function and include stored procedures, functions, and views. Then copy and paste into word or some other 'word like' tool. Now I search for the removed column.
|
|
|
|
 |
|
 |
That is one way of doing it, although a bit slow when you have 100s or stored procs. They way I used to do it was by searching through syscomments, which is equivalent to your method but a bit nicer. I still use this method for some situations (e.g. ones where the test compile would be no good like deleting tables)
e.g.
DECLARE @SearchStr VARCHAR(255)
SET @SearchStr = 'SearchString'
SELECT DISTINCT USER_NAME(o.uid) + '.' + OBJECT_NAME(c.id) AS 'Object name',
CASE
WHEN OBJECTPROPERTY(c.id, 'IsReplProc') = 1
THEN 'Replication stored procedure'
WHEN OBJECTPROPERTY(c.id, 'IsExtendedProc') = 1
THEN 'Extended stored procedure'
WHEN OBJECTPROPERTY(c.id, 'IsProcedure') = 1
THEN 'Stored Procedure'
WHEN OBJECTPROPERTY(c.id, 'IsTrigger') = 1
THEN 'Trigger'
WHEN OBJECTPROPERTY(c.id, 'IsTableFunction') = 1
THEN 'Table-valued function'
WHEN OBJECTPROPERTY(c.id, 'IsScalarFunction') = 1
THEN 'Scalar-valued function'
WHEN OBJECTPROPERTY(c.id, 'IsInlineFunction') = 1
THEN 'Inline function'
WHEN OBJECTPROPERTY(c.id, 'IsView') = 1
THEN 'View'
END AS 'Object type',
'EXEC sp_helptext ''' + USER_NAME(o.uid) + '.' + OBJECT_NAME(c.id) + '''' AS 'Run this command to see the object text'
FROM syscomments c
INNER JOIN
sysobjects o
ON c.id = o.id
WHERE c.text LIKE '%' + @SearchStr + '%' AND
encrypted = 0 AND
(
OBJECTPROPERTY(c.id, 'IsReplProc') = 1 OR
OBJECTPROPERTY(c.id, 'IsExtendedProc') = 1 OR
OBJECTPROPERTY(c.id, 'IsProcedure') = 1 OR
OBJECTPROPERTY(c.id, 'IsTrigger') = 1 OR
OBJECTPROPERTY(c.id, 'IsTableFunction') = 1 OR
OBJECTPROPERTY(c.id, 'IsScalarFunction') = 1 OR
OBJECTPROPERTY(c.id, 'IsInlineFunction') = 1 OR
OBJECTPROPERTY(c.id, 'IsView') = 1
)
ORDER BY 'Object type', 'Object name'
PRINT @@ROWCOUNT
|
|
|
|
 |
|
 |
One thing with current versions of MS SQL Server, though: a "test compile" like this will only catch certain problems. It won't catch all or even most obvious problems because, from SQL Server v7 and later (SQL Server 2000/2005), executable objects like stored procedures and user-defined functions are late-bound: the execution plan isn't bound until run time.
As an example, SQL Server will happily compile this stored procedure with nary a care in the world:
create procedure foobar
as
select * from nonexistent_table
go
Actually, execution of it, on the other hand, is a different story:
exec foobar
go
raises this SQL error at runtime:
Server: Msg 208, Level 16, State 1, Procedure foobar, Line 4
Invalid object name 'nonexistent_table'. Determination of whether or not SQL Server's late-binding is a feature, is of course, left as an exercise for the reader.
But it sure would be nice to be able turn it off.
|
|
|
|
 |
|
 |
Good point.
This tool isn't intended to be a panacea, but to assist in certain areas. It was born when I was deleting unused columns from a database, and was to help find references to those columns - rather than just searching syscomments for a string. For this situation I think it works.
You are correct though, and you wouldn't want to use this if you were deleting tables or wanting to check if new code is ok. It is certainly not a substitute for testing.
|
|
|
|
 |
|
 |
After reading this message thread, it made me think.. what happens to the sp_depends stored proc when there is an invalid table reference in a created procedure. It seems to be that it doesn't return rows, but rather returns a message "Object does not reference any object, and no objects reference it."(even if there are other "valid" tables in the procedure definition).
So maybe, the addition of a call to sp_depends after the "create" checking will get it to validate both columns and tables.
Mike
|
|
|
|
 |
|
 |
That's an intersting thought Mike. I will definately look into it and get back to you
|
|
|
|
 |