I am using tSQLt (through Red Gate's SQL Test version 1.0.0.455). tSQLt is installed on database A. I am trying to do a tSQLt.FakeTable on a table in database B on the same SQL server instance through a synonym on database A.
Code:
ALTER PROCEDURE [ErrorType109NonTankHasSizeOrVolume].[test AliasTest]
AS
BEGIN
Exec tSQLt.FakeTable 'dbo.Bygning';
Insert Into dbo.Bygning (ObjStatus) Values (1);
EXEC tSQLt.AssertEquals 1, 1
END;
Where dbo.Bygning is a synonym in database A referring to a table in database B and ObjStatus is a column in dbo.Bygning
Error message:
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 0, current count = 1.
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 0, current count = 1.
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 0, current count = 1.
[ErrorType109NonTankHasSizeOrVolume].[test AliasTest] failed: An invalid parameter or option was specified for procedure 'sp_addextendedproperty'.{sp_addextendedproperty,37}
Is there any way to tSQLt.FakeTable synonym tables?
Clarification:
The error message comes when running the test.
tSQLt does not support faking on synonyms at the moment. However, I think it may be easy to add support for this. I quickly prototyped the following fix and hope it may solve your issue. Can you please try it and confirm? If it works out for you, I'll make sure it gets into the next release.
ALTER PROCEDURE tSQLt.Private_MarkFakeTable
#SchemaName NVARCHAR(MAX),
#TableName NVARCHAR(MAX),
#NewNameOfOriginalTable NVARCHAR(4000)
AS
BEGIN
DECLARE #UnquotedSchemaName NVARCHAR(MAX);SET #UnquotedSchemaName = OBJECT_SCHEMA_NAME(OBJECT_ID(#SchemaName+'.'+#TableName));
DECLARE #UnquotedTableName NVARCHAR(MAX);SET #UnquotedTableName = OBJECT_NAME(OBJECT_ID(#SchemaName+'.'+#TableName));
DECLARE #Level1Type NVARCHAR(MAX);
SELECT #Level1Type =
CASE type
WHEN 'SN' THEN 'SYNONYM'
ELSE 'TABLE'
END
FROM sys.objects
WHERE object_id = OBJECT_ID(#SchemaName+'.'+#TableName);
EXEC sys.sp_addextendedproperty
#name = N'tSQLt.FakeTable_OrgTableName',
#value = #NewNameOfOriginalTable,
#level0type = N'SCHEMA', #level0name = #UnquotedSchemaName,
#level1type = N'TABLE', #level1name = #UnquotedTableName;
END;
GO
I ran into this today and devised the following solution.
In the Assemble section:
CREATE TABLE #mock
(
id_item VARCHAR(15),
descr_1 VARCHAR(50)
)
INSERT INTO #mock
( id_item, descr_1 )
VALUES ('123456-01', 'Great description here'),
('123456-02', 'Blue, gnarly, cloud')
EXEC sp_rename 'syn_name', 'syn_name_orig'
CREATE SYNONYM syn_name FOR #mock
Then clean up at the end of the act section:
DROP SYNONYM syn_name
EXEC sp_rename 'syn_name_orig', 'syn_name'
It's working for me. Might have side-effects for concurrent processes trying to use that synonym, but I only run my tests in a development environment, so I am not worried about it.
The workaround I've used is to change the production code to use a view, which then uses the synonym. I can then mock the view using tSQLt.FakeTable.
I have also faced similar issue.
I have 2 databases i.e. config and main db
I have a table 'tableA' in config db. and its synonym is created in main db with same name i.e. dbo.tableA. But while mocking that synonym it gave me same error. So here is my solution which worked for me.
First, I have created synonym of a tSQLt.FakeTable sp from config db into my main db
for e.g.
CREATE SYNONYM tSQLt.tSqltFakeTable_config FOR [Config_db].tSQLt.faketable
Now in test case, I used new synonym tSQLt.tSqltFakeTable_config as below to fake a synonym of 'tableA'
CREATE PROC [testclass].[test faking synonym which points to another db]
AS
BEGIN
--arrange
exec [tSQLt].tSqltFakeTable_config 'dbo.tableA';
--act
--assert
END
But I felt there would be only 1 issue that if you uninstall tSQLt from config db. You have to keep track of all such new synonyms and remove them first then you can uninstall tSQLt from Config db.
Suggestions are welcome.
Related
I want to run SQL queries which retrieve from a database according to user input.
Lets assume there is a table named Queries and the fields of the table are Index, Query, Name.
query - select * from Student
name - GetStudents
Index - 1
When user clicks a button an index will be passed into the server and a query match with that index will be triggered. Assume there are no inputs into queries.
Lets say there are 5 rows in the table and when user pass 3,the third query will be run. When user pass 4 the fourth query will be run.
I think I can simply do this by storing the query as a string on table retrieving the query and run. But I'm not sure whether this workaround is efficient.
Please help me with these points.
Is this approach is okay or is there any better workaround that I can follow.
Is it okay to store query as a string in a table.
Is there any workaround that I can create Stored Procedures pragmatically using asp.net in SQL server management studio.
I'm using ASP.Net and SQL server.
Note that here I can't use Stored Procedures to do this task. Because there is another front-end where user can insert queries into table that I have mentioned above. User has no access to use SQL server management studio.
In theory, yes you certainly could store the query string and then use sp_executesql to run that particular query string.
However, CAUTION. If you have a front end that allows a user to write and submit a query then how are you sanitizing that input? Is there anything to prevent the user submitting 'DROP DATABASE' as the query or event introducing other SQL injection attacks?
A better approach would be to create the procedures (assuming that the activities are all standard tasks) and allowing the user to select which procedure to execute.
You could check the integrity of the dynamic select statement by executing it under the most restrictive security context (a dbuser that has readonly permissions). To take it a bit further, you could also wrap the dynamic select statement into an ever changing dynamic container/string (ever changing part + dynamic query + ever changing part) and suppress any errors that happen during the validation/integrity check.
You cannot rely solely on sanitization because you'll end up in a never-ending catching-up struggle.
use mydbxyz;
go
--create a readonly dbuser
create user readonlydbuser without login;
alter role db_datareader add member readonlydbuser;
alter role db_denydatawriter add member readonlydbuser;
go
--procedure to execute dynamic select (no cte, no variables, just selects)
create or alter procedure execute_simpleselect #sqlinput nvarchar(max) = null
with execute as owner
as
begin
set nocount on;
if nullif(#sqlinput, '') is null
begin
--nothing to execute
return;
end
--check if sql input is a valid/simple select query
declare #foocontrol tinyint;
declare #tblalias sysname = quotename(newid());
declare #sqlcheck nvarchar(max) = N'
select #var = 1;
begin transaction;
begin try
select top (0) #var = '+ #tblalias + '.mycol
from
(
select 1 as mycol
where exists
(
'
+ #sqlinput +
'
)
) as '+ #tblalias + N'
end try
begin catch
select #var = 2;
end catch
rollback transaction;
';
/*
create user readonlydbuser without login;
alter role db_datareader add member readonlydbuser;
alter role db_denydatawriter add member readonlydbuser;
*/
--catch errors
begin try
--change context to a readonlyuser
execute as user='readonlydbuser'; --if this dbuser does not exist, nothing executes
exec sys.sp_executesql #stmt = #sqlcheck, #params = N'#var tinyint output', #var = #foocontrol output;
end try
begin catch
--do nothing, suppress errors
end catch
revert;
--if #foocontrol is not 1, the query cannnot be executed (syntactically incorrect, violation under minimal permissions etc)
if isnull(#foocontrol, 2) = 2
begin
raiserror('what are you trying to do?', 16, 1);
return;
end
--change to the callers security context
exec as caller;
exec sys.sp_executesql #stmt = #sqlinput;
end
--test
exec execute_simpleselect #sqlinput = 'select * from sys.objects';
exec execute_simpleselect #sqlinput = 'create table dbo.testtbl(id int)';
exec execute_simpleselect #sqlinput = 'drop table dbo.tablexzy';
exec execute_simpleselect #sqlinput = 'select user_name()';
I need to write into a log table from a stored procedure.
Now this log info has to survive a rollback offcourse.
I know this question has been asked before, but my situation is different and I cannot find an answer to my problem in these questions.
When there is no error in the stored procedure things are simple, the entry in the logtable will just be there.
When there is an error than things are complicated.
Inside the procedure I can do rollback in the catch and then insert the data into the log table, I know that and I am already doing that.
But the problem is when the stored procedure is called like this :
begin transaction
exec myStoredProcedure
rollback transaction
select * from myLogTable
I know this code makes not much sense, I kept it mimimal to demonstrate my problem.
If the caller of the stored procedure does the commit/rollback then it does not matters what I do in the stored procedure. My logentry will always be rolled back.
I also cannot use the temporary table trick, which is to return the data I want to log and let the caller use that data to insert it into the logtable after it has done the rollback, because the caller is an external application that I do not have the source from.
The logging is done in a seperate procedure that only has one line of code, the insert into the logtable.
What I need is a way to commit the insert in this procedure, outside the current transaction so it survives any rollback.
Is there a way to do this ?
The Solution:
I used lad2025 answer and thus far it is working without problems or performance issues.
But this procedure will only be called about 1000 times each day which is not that much so I guess I don't have to expect any problems either.
It is quite interesting topic so let's check how MS approaches it.
First documentation: Migrating-Oracle-to-SQL-Server-2014-and-Azure-SQL-DB.pdf
Page 152.
Simulating Oracle Autonomous Transactions
This section describes how SSMA for Oracle V6.0 handles autonomous transactions
(PRAGMA AUTONOMOUS_TRANSACTION). These autonomous transactions do not
have direct equivalents in Microsoft SQL Server 2014.
When you define a PL/SQL block (anonymous block, procedure, function, packaged
procedure, packaged function, database trigger) as an autonomous transaction, you
isolate the DML in that block from the caller's transaction context. The block becomes
an independent transaction started by another transaction, referred to as the main
transaction.
To mark a PL/SQL block as an autonomous transaction, you simply include the
following statement in your declaration section:
PRAGMA AUTONOMOUS_TRANSACTION;
SQL Server 2014 does not support autonomous transactions. The only way to isolate a
Transact-SQL block from a transaction context is to open a new connection.
Use the xp_ora2ms_exec2 extended procedure and its extended version
xp_ora2ms_exec2_ex, bundled with the SSMA 6.0 Extension Pack, to open new
transactions. The procedure's purpose is to invoke any stored procedure in a new
connection and help invoke a stored procedure within a function body. The
xp_ora2ms_exec2 procedure has the following syntax:
xp_ora2ms_exec2
<active_spid> int,
<login_time> datetime,
<ms_db_name> varchar,
<ms_schema_name> varchar,
<ms_procedure_name> varchar,
<bind_to_transaction_flag> varchar,
[optional_parameters_for_procedure];
Then you need to install on your server stored procedures and other scripts:
SSMA for Oracle Extension Pack (only SSMA for Oracle Extension Pack.7.5.0.msi).
Your stored procedure will become:
CREATE TABLE myLogTable(i INT IDENTITY(1,1),
d DATETIME DEFAULT GETDATE(),
t NVARCHAR(1000));
GO
CREATE OR ALTER PROCEDURE my_logging
#t NVARCHAR(MAX)
AS
BEGIN
INSERT INTO myLogTable(t) VALUES (#t);
END;
GO
CREATE OR ALTER PROCEDURE myStoredProcedure
AS
BEGIN
-- some work
SELECT 1;
INSERT INTO myLogTable(t)
VALUES ('Standard logging that will perish after rollback');
DECLARE #login_time DATETIME = GETDATE();
DECLARE #custom_text_to_log NVARCHAR(100);
SET #custom_text_to_log=N'some custom loging that should survive rollback';
DECLARE #database_name SYSNAME = DB_NAME();
EXEC master.dbo.xp_ora2ms_exec2_ex
##spid,
#login_time,
#database_name,
'dbo',
'my_logging',
'N',
#custom_text_to_log;
END;
And final call:
begin transaction
exec myStoredProcedure
rollback transaction
select * from myLogTable;
OUTPUT:
i d t
2 2017-08-21 some custom loging that should survive rollback
So you really search for some sort of Autonomous transaction (like in Oracle).
One ugly way to simulate it is to use loopback linked server.
Warning: This is PoC (I would think twice before I would use it in PROD) and do a lot of testing.
DECLARE #servername SYSNAME;
SET #servername = CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName'));
EXECUTE sys.sp_addlinkedserver
#server = N'loopback',
#srvproduct = N'',
#provider = N'SQLNCLI',
#datasrc = #servername;
EXECUTE sys.sp_serveroption
#server = N'loopback',
#optname = 'RPC OUT',
#optvalue = 'ON';
EXECUTE sys.sp_serveroption
#server = N'loopback',
#optname = 'remote proc transaction promotion',
#optvalue = 'OFF';
And code:
DROP TABLE IF EXISTS myLogTable;
CREATE TABLE myLogTable(i INT IDENTITY(1,1),
d DATETIME DEFAULT GETDATE(),
t NVARCHAR(1000));
GO
CREATE OR ALTER PROCEDURE my_logging
#t NVARCHAR(MAX)
AS
BEGIN
INSERT INTO myLogTable(t) VALUES (#t);
END;
GO
CREATE OR ALTER PROCEDURE myStoredProcedure
AS
BEGIN
-- some work
SELECT 1;
INSERT INTO myLogTable(t)
VALUES ('Standard logging that will perish after rollback');
EXEC loopback.T1.dbo.my_logging
#t = N'some custom loging that should survive rollback';
END;
Final call:
begin transaction
exec myStoredProcedure
rollback transaction
select * from myLogTable
Output:
i d t
2 2017-08-17 some custom loging that should survive rollback
We're upgrading from SQL Server 2005 to 2008. Almost every database in the 2005 instance is set to 2000 compatibility mode, but we're jumping to 2008. Our testing is complete, but what we've learned is that we need to get faster at it.
I've discovered some stored procedures that either SELECT data from missing tables or try to ORDER BY columns that don't exist.
Wrapping the SQL to create the procedures in SET PARSEONLY ON and trapping errors in a try/catch only catches the invalid columns in the ORDER BYs. It does not find the error with the procedure selecting data from the missing table. SSMS 2008's intellisense, however, DOES find the issue, but I can still go ahead and successfully run the ALTER script for the procedure without it complaining.
So, why can I even get away with creating a procedure that fails when it runs? Are there any tools out there that can do better than what I've tried?
The first tool I found wasn't very useful: DbValidator from CodeProject, but it finds fewer problems than this script I found on SqlServerCentral, which found the invalid column references.
-------------------------------------------------------------------------
-- Check Syntax of Database Objects
-- Copyrighted work. Free to use as a tool to check your own code or in
-- any software not sold. All other uses require written permission.
-------------------------------------------------------------------------
-- Turn on ParseOnly so that we don't actually execute anything.
SET PARSEONLY ON
GO
-- Create a table to iterate through
declare #ObjectList table (ID_NUM int NOT NULL IDENTITY (1, 1), OBJ_NAME varchar(255), OBJ_TYPE char(2))
-- Get a list of most of the scriptable objects in the DB.
insert into #ObjectList (OBJ_NAME, OBJ_TYPE)
SELECT name, type
FROM sysobjects WHERE type in ('P', 'FN', 'IF', 'TF', 'TR', 'V')
order by type, name
-- Var to hold the SQL that we will be syntax checking
declare #SQLToCheckSyntaxFor varchar(max)
-- Var to hold the name of the object we are currently checking
declare #ObjectName varchar(255)
-- Var to hold the type of the object we are currently checking
declare #ObjectType char(2)
-- Var to indicate our current location in iterating through the list of objects
declare #IDNum int
-- Var to indicate the max number of objects we need to iterate through
declare #MaxIDNum int
-- Set the inital value and max value
select #IDNum = Min(ID_NUM), #MaxIDNum = Max(ID_NUM)
from #ObjectList
-- Begin iteration
while #IDNum <= #MaxIDNum
begin
-- Load per iteration values here
select #ObjectName = OBJ_NAME, #ObjectType = OBJ_TYPE
from #ObjectList
where ID_NUM = #IDNum
-- Get the text of the db Object (ie create script for the sproc)
SELECT #SQLToCheckSyntaxFor = OBJECT_DEFINITION(OBJECT_ID(#ObjectName, #ObjectType))
begin try
-- Run the create script (remember that PARSEONLY has been turned on)
EXECUTE(#SQLToCheckSyntaxFor)
end try
begin catch
-- See if the object name is the same in the script and the catalog (kind of a special error)
if (ERROR_PROCEDURE() <> #ObjectName)
begin
print 'Error in ' + #ObjectName
print ' The Name in the script is ' + ERROR_PROCEDURE()+ '. (They don''t match)'
end
-- If the error is just that this already exists then we don't want to report that.
else if (ERROR_MESSAGE() <> 'There is already an object named ''' + ERROR_PROCEDURE() + ''' in the database.')
begin
-- Report the error that we got.
print 'Error in ' + ERROR_PROCEDURE()
print ' ERROR TEXT: ' + ERROR_MESSAGE()
end
end catch
-- Setup to iterate to the next item in the table
select #IDNum = case
when Min(ID_NUM) is NULL then #IDNum + 1
else Min(ID_NUM)
end
from #ObjectList
where ID_NUM > #IDNum
end
-- Turn the ParseOnly back off.
SET PARSEONLY OFF
GO
You can choose different ways. First of all SQL SERVER 2008 supports dependencies which exist in DB inclusive dependencies of STORED PROCEDURE (see http://msdn.microsoft.com/en-us/library/bb677214%28v=SQL.100%29.aspx, http://msdn.microsoft.com/en-us/library/ms345449.aspx and http://msdn.microsoft.com/en-us/library/cc879246.aspx). You can use sys.sql_expression_dependencies and sys.dm_sql_referenced_entities to see and verify there.
But the most simple way to do verification of all STORED PROCEDURE is following:
export all STORED PROCEDURE
drop old existing STORED PROCEDURE
import just exported STORED PROCEDURE.
If you upgrade DB the existing Stored Procedure will be not verified, but if you create a new one, the procedure will be verified. So after exporting and exporting of all Stored Procedure you receive all existing error reported.
You can also see and export the code of a Stored Procedure with a code like following
SELECT definition
FROM sys.sql_modules
WHERE object_id = (OBJECT_ID(N'spMyStoredProcedure'))
UPDATED: To see objects (like tables and views) referenced by Stored Procedure spMyStoredProcedure you can use following:
SELECT OBJECT_NAME(referencing_id) AS referencing_entity_name
,referenced_server_name AS server_name
,referenced_database_name AS database_name
,referenced_schema_name AS schema_name
, referenced_entity_name
FROM sys.sql_expression_dependencies
WHERE referencing_id = OBJECT_ID(N'spMyStoredProcedure');
UPDATED 2: In the comment to my answer Martin Smith suggested to use sys.sp_refreshsqlmodule instead of recreating a Stored Procedure. So with the code
SELECT 'EXEC sys.sp_refreshsqlmodule ''' + OBJECT_SCHEMA_NAME(object_id) +
'.' + name + '''' FROM sys.objects WHERE type in (N'P', N'PC')
one receive a script, which can be used for verifying of Stored Procedure dependencies. The output will look like following (example with AdventureWorks2008):
EXEC sys.sp_refreshsqlmodule 'dbo.uspGetManagerEmployees'
EXEC sys.sp_refreshsqlmodule 'dbo.uspGetWhereUsedProductID'
EXEC sys.sp_refreshsqlmodule 'dbo.uspPrintError'
EXEC sys.sp_refreshsqlmodule 'HumanResources.uspUpdateEmployeeHireInfo'
EXEC sys.sp_refreshsqlmodule 'dbo.uspLogError'
EXEC sys.sp_refreshsqlmodule 'HumanResources.uspUpdateEmployeeLogin'
EXEC sys.sp_refreshsqlmodule 'HumanResources.uspUpdateEmployeePersonalInfo'
EXEC sys.sp_refreshsqlmodule 'dbo.uspSearchCandidateResumes'
EXEC sys.sp_refreshsqlmodule 'dbo.uspGetBillOfMaterials'
EXEC sys.sp_refreshsqlmodule 'dbo.uspGetEmployeeManagers'
Here is what worked for me:
-- Based on comment from http://blogs.msdn.com/b/askjay/archive/2012/07/22/finding-missing-dependencies.aspx
-- Check also http://technet.microsoft.com/en-us/library/bb677315(v=sql.110).aspx
select o.type, o.name, ed.referenced_entity_name, ed.is_caller_dependent
from sys.sql_expression_dependencies ed
join sys.objects o on ed.referencing_id = o.object_id
where ed.referenced_id is null
You should get all missing dependencies for your SPs, solving problems with late binding.
Exception: is_caller_dependent = 1 does not necessarily mean a broken dependency. It just means that the dependency is resolved on runtime because the schema of the referenced object is not specified. You can avoid it specifying the schema of the referenced object (another SP for example).
Credits to Jay's blog and the anonymous commenter...
I am fond of using Display Estimated Execution Plan. It highlights many errors reasonably without ever having to really run the proc.
I had the same problem in a previous project and wrote an TSQL checker on SQL2005 and later a Windows program implementing the same functionality.
When I came across this question I was interested in finding a safe, non-invasive, and fast technique for validating syntax and object (table, column) references.
While I agree that actually executing each stored procedure will likely turn up more issues than just compiling them, one must exercise caution with the former approach. That is, you need to know that it is, in fact, safe to execute each and every stored procedure (i.e. does it erase some tables, for example?). This safety issue can be addressed by wrapping the execution in a transaction and rolling it back so no changes are permanent, as suggested in devio's answer. Still, this approach could potentially take quite a long time depending on how much data you are manipulating.
The code in the question, and the first portion of Oleg's answer, both suggest re-instantiating each stored procedure, as that action recompiles the procedure and does just such syntactic validation. But this approach is invasive--it's fine for a private test system, but could disrupt the work of other develoeprs on a heavily used test system.
I came across the article Check Validity of SQL Server Stored Procedures, Views and Functions, which presents a .NET solution, but it is the follow-up post at the bottom by "ddblue" that intrigued me more. This approach obtains the text of each stored procedure, converts the create keyword to alter so that it can be compiled, then compiles the proc. And that accurately reports any bad table and column references. The code runs, but I quickly ran into some issues because of the create/alter conversion step.
The conversion from "create" to "alter" looks for "CREATE" and "PROC" separated by a single space. In the real-world, there could spaces or tabs, and there could be one or more than one. I added a nested "replace" sequence (thanks, to this article by Jeff Moden!) to convert all such occurrences to a single space, allowing the conversion to proceed as originally designed. Then, since that needed to be used wherever the original "sm.definition" expression was used, I added a common table expression to avoid massive, unsightly code duplication. So here is my updated version of the code:
DECLARE #Schema NVARCHAR(100),
#Name NVARCHAR(100),
#Type NVARCHAR(100),
#Definition NVARCHAR(MAX),
#CheckSQL NVARCHAR(MAX)
DECLARE crRoutines CURSOR FOR
WITH System_CTE ( schema_name, object_name, type_desc, type, definition, orig_definition)
AS -- Define the CTE query.
( SELECT OBJECT_SCHEMA_NAME(sm.object_id) ,
OBJECT_NAME(sm.object_id) ,
o.type_desc ,
o.type,
REPLACE(REPLACE(REPLACE(LTRIM(RTRIM(REPLACE(sm.definition, char(9), ' '))), ' ', ' ' + CHAR(7)), CHAR(7) + ' ', ''), CHAR(7), '') [definition],
sm.definition [orig_definition]
FROM sys.sql_modules (NOLOCK) AS sm
JOIN sys.objects (NOLOCK) AS o ON sm.object_id = o.object_id
-- add a WHERE clause here as indicated if you want to test on a subset before running the whole list.
--WHERE OBJECT_NAME(sm.object_id) LIKE 'xyz%'
)
-- Define the outer query referencing the CTE name.
SELECT schema_name ,
object_name ,
type_desc ,
CASE WHEN type_desc = 'SQL_STORED_PROCEDURE'
THEN STUFF(definition, CHARINDEX('CREATE PROC', definition), 11, 'ALTER PROC')
WHEN type_desc LIKE '%FUNCTION%'
THEN STUFF(definition, CHARINDEX('CREATE FUNC', definition), 11, 'ALTER FUNC')
WHEN type = 'VIEW'
THEN STUFF(definition, CHARINDEX('CREATE VIEW', definition), 11, 'ALTER VIEW')
WHEN type = 'SQL_TRIGGER'
THEN STUFF(definition, CHARINDEX('CREATE TRIG', definition), 11, 'ALTER TRIG')
END
FROM System_CTE
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
-- Uncomment to see every object checked.
-- RAISERROR ('Checking %s...', 0, 1, #Name) WITH NOWAIT
BEGIN TRY
SET PARSEONLY ON ;
EXEC ( #Definition ) ;
SET PARSEONLY OFF ;
END TRY
BEGIN CATCH
PRINT #Type + ': ' + #Schema + '.' + #Name
PRINT ERROR_MESSAGE()
END CATCH
END
ELSE
BEGIN
RAISERROR ('Skipping %s...', 0, 1, #Name) WITH NOWAIT
END
FETCH NEXT FROM crRoutines INTO #Schema, #Name, #Type, #Definition
END
CLOSE crRoutines
DEALLOCATE crRoutines
Nine years after I first posed this question, and I've just discovered an amazing tool built by Microsoft themselves that not only can reliably verify stored procedure compatibility between SQL Server versions, but all other internal aspects as well. It's been renamed a few times, but they currently call it:
Microsoft® Data Migration Assistant v5.4*
* Version as of 6/17/2021
https://www.microsoft.com/en-us/download/details.aspx?id=53595
Data Migration Assistant (DMA) enables you to upgrade to a modern data platform by detecting compatibility issues that can impact database functionality on your new version of SQL Server. It recommends performance and reliability improvements for your target environment. It allows you to not only move your schema and data, but also uncontained objects from your source server to your target server.
The answers above that use EXEC sys.sp_refreshsqlmodule were a great start, but we ran into one MAJOR problem running it on 2008 R2: any stored procedure or function that was renamed (using sp_rename, and not a DROP/CREATE pattern) REVERTED to its prior definition after running the refresh procedure, because the internal metadata isn't refreshed under the new name. It's a known bug that was fixed in SQL Server 2012, but we had a fun day of recovery afterwards. (One workaround, future readers, is to issue a ROLLBACK if the refresh throws an error.)
Anyway, times have changed, new tools are available -- and good ones at that -- thus the late addition of this answer.
I have a SQL script that has to be run every time a client executes the "database management" functionality. The script includes creating stored procedures on the client database. Some of these clients might already have the stored procedure upon running the script, and some may not. I need to have the missing stored procedures added to the client database, but it doesn't matter how much I try to bend T-SQL syntax, I get
CREATE/ALTER PROCEDURE' must be the first statement in a query batch
I've read that dropping before creating works, but I don't like doing it that way.
IF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'MyProc')
DROP PROCEDURE MyProc
GO
CREATE PROCEDURE MyProc
...
How can I add check for the existence of a stored procedure and create it if it doesn't exist but alter it if it does exist?
I realize this has already been marked as answered, but we used to do it like this:
IF NOT EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND OBJECT_ID = OBJECT_ID('dbo.MyProc'))
exec('CREATE PROCEDURE [dbo].[MyProc] AS BEGIN SET NOCOUNT ON; END')
GO
ALTER PROCEDURE [dbo].[MyProc]
AS
....
Just to avoid dropping the procedure.
You can run procedural code anywhere you are able to run a query.
Just copy everything after AS:
BEGIN
DECLARE #myvar INT
SELECT *
FROM mytable
WHERE #myvar ...
END
This code does exactly same things a stored proc would do, but is not stored on the database side.
That's much like what is called anonymous procedure in PL/SQL.
Update:
Your question title is a little bit confusing.
If you only need to create a procedure if it not exists, then your code is just fine.
Here's what SSMS outputs in the create script:
IF EXISTS ( SELECT *
FROM sys.objects
WHERE object_id = OBJECT_ID(N'myproc')
AND type IN ( N'P', N'PC' ) )
DROP …
CREATE …
Update:
Example of how to do it when including the schema:
IF EXISTS ( SELECT *
FROM sysobjects
WHERE id = object_id(N'[dbo].[MyProc]')
and OBJECTPROPERTY(id, N'IsProcedure') = 1 )
BEGIN
DROP PROCEDURE [dbo].[MyProc]
END
In the example above, dbo is the schema.
Update:
In SQL Server 2016+, you can just do
CREATE OR ALTER PROCEDURE dbo.MyProc
If you're looking for the simplest way to check for a database object's existence before removing it, here's one way (example uses a SPROC, just like your example above but could be modified for tables, indexes, etc...):
IF (OBJECT_ID('MyProcedure') IS NOT NULL)
DROP PROCEDURE MyProcedure
GO
This is quick and elegant, but you need to make sure you have unique object names across all object types since it does not take that into account.
I know you want to "ALTER a procedure if it exists and create it if it does not exist", but I believe it is simpler to:
Drop the procedure (if it already exists) and then
Re-create it.
Like this:
IF OBJECT_ID('MyProcedure', 'P') IS NOT NULL
DROP PROCEDURE MyProcedure
GO
CREATE PROCEDURE MyProcedure AS
BEGIN
/* ..... */
END
GO
The second parameter tells OBJECT_ID to only look for objects with object_type = 'P', which are stored procedures:
AF = Aggregate function (CLR)
C = CHECK constraint
D = DEFAULT (constraint or stand-alone)
F = FOREIGN KEY constraint
FN = SQL scalar function
FS = Assembly (CLR) scalar-function
FT = Assembly (CLR) table-valued function
IF = SQL inline table-valued function
IT = Internal table
P = SQL Stored Procedure
PC = Assembly (CLR) stored-procedure
PG = Plan guide
PK = PRIMARY KEY constraint
R = Rule (old-style, stand-alone)
RF = Replication-filter-procedure
S = System base table
SN = Synonym
SO = Sequence object
TF = SQL table-valued-function
TR = Trigger
You can get the full list of options via:
SELECT name
FROM master..spt_values
WHERE type = 'O9T'
As of SQL SERVER 2016 you can use the new DROP PROCEDURE IF EXISTS.
DROP { PROC | PROCEDURE } [ IF EXISTS ] { [ schema_name. ] procedure } [ ,...n ]
Reference :
https://msdn.microsoft.com/en-us/library/ms174969.aspx
I know it is a very old post, but since this appears in the top search results hence adding the latest update for those using SQL Server 2016 SP1 -
create or alter procedure procTest
as
begin
print (1)
end;
go
This creates a Stored Procedure if does not already exist, but alters it if exists.
Reference
DROP IF EXISTS
is a new feature of SQL Server 2016
https://blogs.msdn.microsoft.com/sqlserverstorageengine/2015/11/03/drop-if-exists-new-thing-in-sql-server-2016/
DROP PROCEDURE IF EXISTS dbo.[procname]
I had the same error. I know this thread is pretty much dead already but I want to set another option besides "anonymous procedure".
I solved it like this:
Check if the stored procedure exist:
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='my_procedure') BEGIN
print 'exists' -- or watever you want
END ELSE BEGIN
print 'doesn''texists' -- or watever you want
END
However the "CREATE/ALTER PROCEDURE' must be the first statement in a query batch" is still there. I solved it like this:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE -- view procedure function or anything you want ...
I end up with this code:
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID('my_procedure'))
BEGIN
DROP PROCEDURE my_procedure
END
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].my_procedure ...
Here's a method and some reasoning behind using it this way. It isn't as pretty to edit the stored proc but there are pros and cons...
UPDATE: You can also wrap this entire call in a TRANSACTION. Including many stored procedures in a single transaction which can all commit or all rollback. Another advantage of wrapping in a transaction is the stored procedure always exists for other SQL connections as long as they do not use the READ UNCOMMITTED transaction isolation level!
1) To avoid alters just as a process decision. Our processes are to always IF EXISTS DROP THEN CREATE. If you do the same pattern of assuming the new PROC is the desired proc, catering for alters is a bit harder because you would have an IF EXISTS ALTER ELSE CREATE.
2) You have to put CREATE/ALTER as the first call in a batch so you can't wrap a sequence of procedure updates in a transaction outside dynamic SQL. Basically if you want to run a whole stack of procedure updates or roll them all back without restoring a DB backup, this is a way to do everything in a single batch.
IF NOT EXISTS (select ss.name as SchemaName, sp.name as StoredProc
from sys.procedures sp
join sys.schemas ss on sp.schema_id = ss.schema_id
where ss.name = 'dbo' and sp.name = 'MyStoredProc')
BEGIN
DECLARE #sql NVARCHAR(MAX)
-- Not so aesthetically pleasing part. The actual proc definition is stored
-- in our variable and then executed.
SELECT #sql = 'CREATE PROCEDURE [dbo].[MyStoredProc]
(
#MyParam int
)
AS
SELECT #MyParam'
EXEC sp_executesql #sql
END
In Sql server 2008 onwards, you can use "INFORMATION_SCHEMA.ROUTINES"
IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_NAME = 'MySP'
AND ROUTINE_TYPE = 'PROCEDURE')
**The simplest way to drop and recreate a stored proc in T-Sql is **
Use DatabaseName
go
If Object_Id('schema.storedprocname') is not null
begin
drop procedure schema.storedprocname
end
go
create procedure schema.storedprocname
as
begin
end
Here is the script that I use. With it, I avoid unnecessarily dropping and recreating the stored procs.
IF NOT EXISTS (
SELECT *
FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[uspMyProcedure]')
)
BEGIN
EXEC sp_executesql N'CREATE PROCEDURE [dbo].[uspMyProcedure] AS select 1'
END
GO
ALTER PROCEDURE [dbo].[uspMyProcedure]
#variable1 INTEGER
AS
BEGIN
-- Stored procedure logic
END
why don't you go the simple way like
IF EXISTS(SELECT * FROM sys.procedures WHERE NAME LIKE 'uspBlackListGetAll')
BEGIN
DROP PROCEDURE uspBlackListGetAll
END
GO
CREATE Procedure uspBlackListGetAll
..........
In addition to the answer from #Geoff I've created a simple tool which generates a SQL-file which statements for Stored Procedures, Views, Functions and Triggers.
See MyDbUtils # CodePlex.
I wonder! Why i don't write the whole query like
GO
create procedure [dbo].[spAddNewClass] #ClassName varchar(20),#ClassFee int
as
begin
insert into tblClass values (#ClassName,#ClassFee)
end
GO
create procedure [dbo].[spAddNewSection] #SectionName varchar(20),#ClassID int
as
begin
insert into tblSection values(#SectionName,#ClassID)
end
Go
create procedure test
as
begin
select * from tblstudent
end
i already know that first two procedures are already exist sql will run the query will give the error of first two procedures but still it will create the last procedure
SQl is itself taking care of what is already exist this is what i always do to all my clients!
CREATE Procedure IF NOT EXISTS 'Your proc-name' () BEGIN ... END
I'm not talking about doing a "SET NOCOUNT OFF". But I have a stored procedure which I use to insert some data into some tables. This procedure creates a xml response string, well let me give you an example:
CREATE PROCEDURE [dbo].[insertSomeData] (#myParam int) AS
DECLARE #reply varchar(2048)
... Do a bunch of inserts/updates...
SET #reply = '<xml><big /><outputs /></xml>'
SELECT #reply
GO
So I put together a script which uses this SP a bunch of times, and the xml "output" is getting to be too much (it's crashed my box once already).
Is there a way to suppress or redirect the output generated from this stored procedure? I don't think that modifying this stored procedure is an option.
thanks.
I guess i should clarify. This SP above is being called by a T-SQL Update script that i wrote, to be run through enterprise studio manager, etc.
And it's not the most elegant SQL i've ever written either (some psuedo-sql):
WHILE unprocessedRecordsLeft
BEGIN
SELECT top 1 record from updateTable where Processed = 0
EXEC insertSomeData #param = record_From_UpdateTable
END
So lets say the UpdateTable has some 50k records in it. That SP gets called 50k times, writing 50k xml strings to the output window. It didn't bring the sql server to a stop, just my client app (sql server management studio).
The answer you're looking for is found in a similar SO question by Josh Burke:
-- Assume this table matches the output of your procedure
DECLARE #tmpNewValue TABLE ([Id] int, [Name] varchar(50))
INSERT INTO #tmpNewValue
EXEC [ProcedureB]
-- SELECT [Id], [Name] FROM #tmpNewValue
I think I found a solution.
So what i can do now in my SQL script is something like this (sql-psuedo code):
create table #tmp(xmlReply varchar(2048))
while not_done
begin
select top 1 record from updateTable where processed = 0
insert into #tmp exec insertSomeData #param=record
end
drop table #tmp
Now if there was a even more efficient way to do this. Does SQL Server have something similar to /dev/null? A null table or something?
Answering the question, "How do I suppress stored procedure output?" really depends on what you are trying to accomplish. So I want to contribute what I encountered:
I needed to supress the stored procedure (USP) output because I just wanted the row count (##ROWCOUNT) from the output. What I did, and this may not work for everyone, is since my query was already going to be dynamic sql I added a parameter called #silentExecution to the USP in question. This is a bit parameter which I defaulted to zero (0).
Next if #silentExecution was set to one (1) I would insert the table contents into a temporary table, which is what would supress the output and then execute ##ROWCOUNT with no problem.
USP Example:
CREATE PROCEDURE usp_SilentExecutionProc
#silentExecution bit = 0
AS
BEGIN
SET NOCOUNT ON;
DECLARE #strSQL VARCHAR(MAX);
SET #strSQL = '';
SET #strSQL = 'SELECT TOP 10 * ';
IF #silentExecution = 1
SET #strSQL = #strSQL + 'INTO #tmpDevNull ';
SET #strSQL = #strSQL +
'FROM dbo.SomeTable ';
EXEC(#strSQL);
END
GO
Then you can execute the whole thing like so:
EXEC dbo.usp_SilentExecutionProc #silentExecution = 1;
SELECT ##ROWCOUNT;
The purpose behind doing it like this is if you need the USP to be able to return a result set in other uses or cases, but still utilize it for just the rows.
Just wanted to share my solution.
I have recently come across with a similar issue while writing a migration script and since the issue was resolved in a different way, I want to record it.
I have nearly killed my SSMS Client by running a simple while loop for 3000 times and calling a procedure.
DECLARE #counter INT
SET #counter = 10
WHILE #counter > 0
BEGIN
-- call a procedure which returns some resultset
SELECT #counter-- (simulating the effect of stored proc returning some resultset)
SET #counter = #counter - 1
END
The script result was executed using SSMS and default option on query window is set to show “Results to Grid”[Ctrl+d shortcut].
Easy Solution:
Try setting the results to file to avoid the grid to be built and painted on the SSMS client. [CTRL+SHIFT+F keyboard shortcut to set the query results to file].
This issue is related to : stackoverflow query
Man, this is seriously a case of a computer doing what you told it to do instead of what you wanted it to do.
If you don't want it to return results, then don't ask it to return results. Refactor that stored procedure into two:
CREATE PROCEDURE [dbo].[insertSomeData] (#myParam int) AS
BEGIN
DECLARE #reply varchar(2048)
--... Do a bunch of inserts/updates...
EXEC SelectOutput
END
GO
CREATE PROCEDURE SelectOutput AS
BEGIN
SET #reply = '<xml><big /><outputs /></xml>'
SELECT #reply
END
From which client are you calling the stored procedure? Say it was from C#, and you're calling it like:
var com = myConnection.CreateCommand();
com.CommandText = "exec insertSomeData 1";
var read = com.ExecuteReader();
This will not yet retrieve the result from the server; you have to call Read() for that:
read.Read();
var myBigString = read[0].ToString();
So if you don't call Read, the XML won't leave the Sql Server. You can even call the procedure with ExecuteNonQuery:
var com = myConnection.CreateCommand();
com.CommandText = "exec insertSomeData 1";
com.ExecuteNonQuery();
Here the client won't even ask for the result of the select.
You could create a SQL CLR stored procedure that execs this. Should be pretty easy.
I don't know if SQL Server has an option to suppress output (I don't think it does), but the SQL Query Analyzer has an option (under results tab) to "Discard Results".
Are you running this through isql?
You said your server is crashing. What is crashing the application that consumes the output of this SQL or SQL Server itself (assuming SQL Server).
If you are using .Net Framework application to call the stored procedure then take a look at SQLCommand.ExecuteNonQuery. This just executes stored procedure with no results returned. If problem is at SQL Server level then you are going to have to do something different (i.e. change the stored procedure).
You can include in the SP a parameter to indicate if you want it to do the select or not, but of course, you need to have access and reprogram the SP.
CREATE PROCEDURE [dbo].[insertSomeData] (#myParam int, #doSelect bit=1) AS
DECLARE #reply varchar(2048)
... Do a bunch of inserts/updates...
SET #reply = '<xml><big /><outputs /></xml>'
if #doSelect = 1
SELECT #reply
GO
ever tried SET NOCOUNT ON; as an option?