I have several t-sql procedures with name like Test;1, Test;2, Test;3 etc.
CREATE PROCEDURE [dbo].[Test] ; 1
AS
BEGIN
SELECT
OBJECT_SCHEMA_NAME(##PROCID) + '.' + OBJECT_NAME(##PROCID)
SET NOCOUNT ON;
END
GO
CREATE PROCEDURE [dbo].[Test] ; 2
AS
BEGIN
SELECT
OBJECT_SCHEMA_NAME(##PROCID) + '.' + OBJECT_NAME(##PROCID)
SET NOCOUNT ON;
END
GO
Is that any way to get full procedure name with it version?
You can retrieve that information from sys.numbered_procedures. You can filter based on the object_id and it returns the procedure number. To get the procedure name, you use the same functions you're already using. That also returns the T-SQL code if you want it.
You'll also want to use sys.numbered_procedures_parameters in order to get the information about parameters for these types of procedures. This is necessary since they can also have different parameters.
By the way, numbered procedures is deprecated functionality. As such, it's not recommended for use. Further, it won't work with newer functionality such as CREATE OR ALTER statements. I would strongly caution against using numbered procedures.
Related
I need to pass linked server name as variable to stored procedure right now after testing and research they all suggest to using dynamic sql and open query which I am using now. however I am not comfortable using it(sql injection) plus I need to call other user defined function to the query. I am looking for a more secure and direct call. Here is my SP
ALTER PROCEDURE [dbo].[GetBackUpStatus]
-- Add the parameters for the stored procedure here
#linkedServerName AS VARCHAR(100),
#exemptDB as VARCHAR(100)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
DECLARE #Sql varchar(8000)
SET NOCOUNT ON;
set #Sql = 'select * from openquery (' + #linkedServerName + ' , ''SELECT SERVERPROPERTY(''''SERVERNAME'''') AS "SERVERNAME",
T1.NAME AS DatabaseName,
MAX(T2.backup_finish_date) AS last_db_backup_date,
CAST(COALESCE(DATEDIFF(second, MAX(T2.backup_finish_date) , CURRENT_TIMESTAMP)/ 3600.0, 9999.0) as NUMERIC(6,2)) AS [Hours Since Backup]
FROM master.sys.databases T1
LEFT OUTER JOIN msdb.dbo.backupset T2 ON T2.database_name = T1.NAME
WHERE T1.NAME NOT IN (''''tempdb'''')
GROUP BY T1.NAME
ORDER BY T1.NAME'')'
Exec (#Sql)
END
the purpose of this query is to get the server status and its database, I don't like this because of that confusing single quotes, this query will eventually grow as I develop and add function calls.
I tried this and something like this is what I wanted, since it is direct query and cleaner without those quotes. That's how I typically use linked server.
Select * from [' + #linkedservername + '].[schema].table
thanks
Solution for a large scale data model with hundreds of tables / objects.
Dynamic modification and cloning of a stored procedure for every linked-server.
It is kinda hiding any dynamic SQL under the hood.
How to
Create a stored procedure which interacts with an existing linked-server.
During a database deployment process:
Obtain the source code of the stored procedure.
Replace the name of the linked-server in the code.
If you want to create a new stored procedure (cloned), replace the name of the initial stored procedure in the code.
Create a cloned stored procedure or modify the current.
Repeat all steps for each required linked-server.
There are another variations for it.
Now, any external logic may decide which procedure to use.
You can check the existence of a linked-server or its related stored procedure.
For modifications and cloning, it is possible to use SQL Server or external tools, such as C#, CMD, etc.
For creation under SQL Server.
I have several stored procedures in my database, structured like this:
CREATE PROCEDURE MyProcedure (.....)
AS
DECLARE #myvar NVARCHAR(100);
SET #myvar = (SELECT .... FROM my_table WHERE ....)
GO
I was asked to replace the table my_table in the FROM clause with another one in every procedure that has it.
I went through a lot of researches, but I should create a script that works by itself, and I haven't found anything suitable. For example I found the sp_helpTetx that shows the source code of a stored procedure, but is there a way to put it into a variable in order to edit it?
You can use tool like REDGATE SqlRefactor that works perfectly or you can script all the stored procedures, replace CREATE command with ALTER and then apply the other REPLACE in text you need...
I do it lot of time, you have to pay attention but it works...
Find all stored procedures with a reference to that table (you can either use the dependencies stuff built into SQL Server or run a query looking for that table name see Search text in stored procedure in SQL Server)
Script them out with an "ALTER" instead of "CREATE" Press CTRL-H (find and replace)
Execute the script.
Here is an article outlining how to handle this using a cursor, and the sp_HelpText as mentioned above (including set as also mentioned).
http://www.ideosity.com/ourblog/post/ideosphere-blog/2013/06/14/how-to-find-and-replace-text-in-all-stored-procedures
-- set "Result to Text" mode by pressing Ctrl+T
SET NOCOUNT ON
DECLARE #sqlToRun VARCHAR(1000), #searchFor VARCHAR(100), #replaceWith VARCHAR(100)
-- text to search for
SET #searchFor = '[MY-SERVER]'
-- text to replace with
SET #replaceWith = '[MY-SERVER2]'
-- this will hold stored procedures text
DECLARE #temp TABLE (spText VARCHAR(MAX))
DECLARE curHelp CURSOR FAST_FORWARD
FOR
-- get text of all stored procedures that contain search string
-- I am using custom escape character here since i need to espape [ and ] in search string
SELECT DISTINCT 'sp_helptext '''+OBJECT_SCHEMA_NAME(id)+'.'+OBJECT_NAME(id)+''' '
FROM syscomments WHERE TEXT LIKE '%' + REPLACE(REPLACE(#searchFor,']','\]'),'[','\[') + '%' ESCAPE '\'
ORDER BY 'sp_helptext '''+OBJECT_SCHEMA_NAME(id)+'.'+OBJECT_NAME(id)+''' '
OPEN curHelp
FETCH next FROM curHelp INTO #sqlToRun
WHILE ##FETCH_STATUS = 0
BEGIN
--insert stored procedure text into a temporary table
INSERT INTO #temp
EXEC (#sqlToRun)
-- add GO after each stored procedure
INSERT INTO #temp
VALUES ('GO')
FETCH next FROM curHelp INTO #sqlToRun
END
CLOSE curHelp
DEALLOCATE curHelp
-- find and replace search string in stored procedures
-- also replace CREATE PROCEDURE with ALTER PROCEDURE
UPDATE #temp
SET spText = REPLACE(REPLACE(spText,'CREATE PROCEDURE', 'ALTER PROCEDURE'),#searchFor,#replaceWith)
SELECT spText FROM #temp
-- now copy and paste result into new window
-- then make sure everything looks good and run
GO
If sp_HelpText returns a table, why not you use a cursor to loop over the results and join the resulting strings together? It's nasty, but would do the trick.
I have two different SQL Server databases (on the same server - if it helps) that need to share the same stored procedure logic. The solution I'm trying to achieve looks like this:
Database1
Table: TestTable
Synonym: sp_MyProc pointing at SharedDatabase.dbo.sp_MyProc
Database2
Table: TestTable
Synonym: sp_MyProc pointing at SharedDatabase.dbo.sp_MyProc
SharedDatabase
Proc: sp_MyProc which runs queries against TestTable
My hope was to use the synonyms so that if I execute sp_MyProc while in the context of Database1, it would use Database2.TestTable. And if I execute sp_MyProc while in the context of Database2, it would go against Database2.TestTable. However, when I execute sp_MyProc through either of the synonyms, it ignores the context of the synonym and executes looking for a local copy of TestTable, which is not found.
Is there a way to implement a shared stored procedure that executes against different copies of tables in different databases, either through synonyms or some other mechanism?
Edit
I should mention that in my case I am looking to do this with a large set of existing tables and procs, so any solution that requires modifying the procs or tables themselves are not ideal.
Something like this would work for the definition of the procedure. Be sure to guard against SQL injection since this is built dynamically.
CREATE PROCEDURE [dbo].dosomething
#databaseName sysname,
#schema sysname,
#tableName sysname
as
declare #cmd as nvarchar(max)
set #cmd = N'select * from ' + quotename(#schema) + N'.' + quotename(#tableName)
exec sp_executesql #cmd
Then use it like this:
dosomething 'SampleDb', 'dbo', 'sampleTable'
If the stored proc is in the SharedDatabase, then it will always run in context of SharedDatabase. To accomplish what you are trying to do to centralize code, I would maybe pass in a parameter to designate which server it is coming from, so then you can execute the query against that specific TestTable. Basically, you will need to refer to each table using their fully qualified name - i.e. Database1.dbo.TestTable
USE SharedDatabase
CREATE PROCEDURE [dbo].sp_MyProc
#dbsource varchar(50)
as
if(#dbsource == 'DB1')
begin
select * from Database1.dbo.TestTable
end
else
begin
select * from Database2.dbo.TestTable
end
GO
The other alternative is to make a view in SharedDatabase, which will be called TestTableComposite, with an extra column to identify where the source data is. And then pass that in as the parameter, and your SP on SharedDatabase will always be in context of that DB.
Is there any chance to create temporary stored procedure or function on MS SQL 2005? I would like to use this stored procedure only in my query so after execution it will be gone.
I have a query I would like to EXEC against some data. But for every table I will process this command, I need to change some parts of it. So I thought I would create temporary SP that would return for me a query from arguments I provide (like table name and so on) and than execute this query by EXEC.
And this stored procedure will be not useful for me later so I would like to have it temporary so that when I end executing my query - it will disappear.
This question is a bit old, but the other answers failed to provide the syntax for creating temporary procedures. The syntax is the same as for temporary tables: #name for local temporary objects, ##name for global temporary objects.
CREATE PROCEDURE #uspMyTempProcedure AS
BEGIN
print 'This is a temporary procedure'
END
This is described in the "Procedure Name" section of the official documentation. http://technet.microsoft.com/en-us/library/ms187926%28v=sql.90%29.aspx
I'm using this technique to deduplicate the code for my primitive T-SQL unit tests. A real unit testing framework would be better, but this is better than nothing and "garbage collects" after itself.
Re your edit - it sounds like you should be using sp_ExecuteSQL against a (parameterized) nvarchar that contains TSQL.
Search on sp_ExecuteSQL; a simple example:
DECLARE #SQL nvarchar(4000),
#Table varchar(20) = 'ORDERS',
#IDColumn varchar(20) = 'OrderID',
#ID int = 10248
SET #SQL = 'SELECT * FROM [' + #Table + '] WHERE ['
+ #IDColumn + '] = #Key'
EXEC sp_executesql #SQL, N'#Key int', #ID
Note that table and column names must be concatenated into the query, but values (such as #Key) can be parameterized.
There is a temporary stored procedure - but it is per connection, not per sp.
However, you might want to look at Common Table Expressions - they may be what you are after (although you can only read from them once).
Maybe if you can clarify what you are trying to do?
Just use the SQL of the stored proc inside your query. No need to create a stored procedure inside the DB, it won't give you any advantage over a normal query inside your query.
As part of my integration strategy, I have a few SQL scripts that run in order to update the database. The first thing all of these scripts do is check to see if they need to run, e.g.:
if #version <> #expects
begin
declare #error varchar(100);
set #error = 'Invalid version. Your version is ' + convert(varchar, #version) + '. This script expects version ' + convert(varchar, #expects) + '.';
raiserror(#error, 10, 1);
end
else
begin
...sql statements here...
end
Works great! Except if I need to add a stored procedure. The "create proc" command must be the only command in a batch of sql commands. Putting a "create proc" in my IF statement causes this error:
'CREATE/ALTER PROCEDURE' must be the first statement in a query batch.
Ouch! How do I put the CREATE PROC command in my script, and have it only execute if it needs to?
Here's what I came up with:
Wrap it in an EXEC(), like so:
if #version <> #expects
begin
...snip...
end
else
begin
exec('CREATE PROC MyProc AS SELECT ''Victory!''');
end
Works like a charm!
SET NOEXEC ON is good way to switch off some part of code
IF NOT EXISTS (SELECT * FROM sys.assemblies WHERE name = 'SQL_CLR_Functions')
SET NOEXEC ON
GO
CREATE FUNCTION dbo.CLR_CharList_Split(#list nvarchar(MAX), #delim nchar(1) = N',')
RETURNS TABLE (str nvarchar(4000)) AS EXTERNAL NAME SQL_CLR_Functions.[Granite.SQL.CLR.Functions].CLR_CharList_Split
GO
SET NOEXEC OFF
Found here:
https://codereview.stackexchange.com/questions/10490/conditional-create-must-be-the-only-statement-in-the-batch
P.S. Another way is SET PARSEONLY { ON | OFF }.
But watch out for single quotes within your Stored Procedure - they need to be "escaped" by adding a second one. The first answer has done this, but just in case you missed it. A trap for young players.
Versioning your database is the way to go, but... Why conditionally create stored procedures. For Views, stored procedures, functions, just conditionally drop them and re-create them every time. If you conditionally create, then you will not clean-up databases that have a problem or a hack that got put in 2 years ago by another developer (you or I would never do this) who was sure he would remember to remove the one time emergency update.
Problem with dropping and creating is you lose any security grants that had previously been applied to the object being dropped.
This is an old thread, but Jobo is incorrect: Create Procedure must be the first statement in a batch. Therefore, you can't use Exists to test for existence and then use either Create or Alter. Pity.
It is much better to alter an existing stored proc because of the potential for properties and permissions that have been added AND which will be lost if the stored proc is dropped.
So, test to see if it NOT EXISTS, if it does not then create a dummy proc. Then after that use an alter statement.
IF NOT EXISTS(SELECT * FROM sysobjects WHERE Name = 'YOUR_STORED_PROC_NAME' AND xtype='P')
EXECUTE('CREATE PROC [dbo].[YOUR_STORED_PROC_NAME] as BEGIN select 0 END')
GO
ALTER PROC [dbo].[YOUR_STORED_PROC_NAME]
....
I must admit, I would normally agree with #Peter - I conditionally drop and then unconditionally recreate every time. I've been caught out too many times in the past when trying to second-guess the schema differences between databases, with or without any form of version control.
Having said that, your own suggestion #Josh is pretty cool. Certainly interesting. :-)
My solution is to check if the proc exists, if so then drop it, and then create the proc (same answer as #robsoft but with an example...)
IF EXISTS(SELECT * FROM sysobjects WHERE Name = 'PROC_NAME' AND xtype='P')
BEGIN
DROP PROCEDURE PROC_NAME
END
GO
CREATE PROCEDURE PROC_NAME
#value int
AS
BEGIN
UPDATE SomeTable
SET SomeColumn = 1
WHERE Value = #value
END
GO
use the 'Exists' command in T-SQL to see if the stored proc exists. If it does, use 'Alter', else use 'Create'
IF NOT EXISTS(SELECT * FROM sys.procedures WHERE name = 'pr_MyStoredProc')
BEGIN
CREATE PROCEDURE pr_MyStoredProc AS .....
SET NOCOUNT ON
END
ALTER PROC pr_MyStoredProc
AS
SELECT * FROM tb_MyTable