Temporary stored procedure scope - sql

SQL Server supports temporary tables(local and global).
Using dynamic-SQL (EXEC or dbo.sp_executesql) we could create new context and local temporary table is visible only in dynamic-SQL block but not outside.
-- Normal table
EXEC ('CREATE TABLE tab(i INT); INSERT INTO tab(i) VALUES (1)');
SELECT * FROM tab;
-- Global temporary table
EXEC ('CREATE TABLE ##tab(i INT); INSERT INTO ##tab(i) VALUES (2)');
SELECT * FROM ##tab;
-- Local temporary table
EXEC ('CREATE TABLE #tab(i INT); INSERT INTO #tab(i) VALUES (3)');
SELECT * FROM #tab;
-- Invalid object name '#tab'.
LiveDemo
Now let's try the same with stored procedures:
-- Normal procedure
EXEC ('CREATE PROCEDURE my_proc AS SELECT 1 AS col;');
EXEC my_proc;
-- Global temporary procedure
EXEC ('CREATE PROCEDURE ##my_proc AS SELECT 2 AS col;');
EXEC ##my_proc;
-- Local temporary procedure
EXEC ('CREATE PROCEDURE #my_proc AS SELECT 3 AS col;');
EXEC #my_proc;
LiveDemo2
The question is why local temporary procedure behaves differently and it is visible outside EXEC?

The reason it keeps it around outside the exec because SQL Server can reuse the proc's query plan.
The proc disappears as soon as the session where it was defined (via the exec) is closed.
MS is very careful indicate that temp 'tables' (one with one # sign) are local to the context and not visible to calling program.
See:
Any tables you create are visible to the EXEC() context in
https://technet.microsoft.com/en-us/library/aa175921%28v=sql.80%29.aspx

Related

Inserting into a user-defined table type via a dynamic query in SQL Server?

I have a user-defined table type tyAnalysisNumbers. I need to populate my user defined data type within a stored procedure with a SELECT statement and I am struggling to get that working within my stored procedure.
The following ways I have tried do not work
DECLARE #MyTable tyAnalysisNumbers;
INSERT INTO #MyTable
EXEC ('SELECT * FROM ' + #someTable);
I get this error:
An INSERT EXEC statement cannot be nested
I am unsure how to insert into my custom table via a select statement.
Can anyone help me accomplish this?
An INSERT EXEC statement cannot be nested
Above error is self explanatory. Please look at below scenario:
For example, we have one procedure which inserts data in table type and return result.
CREATE PROCEDURE uspInsertData1
AS
BEGIN
DECLARE #MyTable tyAnalysisNumbers;
INSERT INTO #MyTable
EXEC ('SELECT * FROM someTable');
select * from #MyTable
END
Now, let's say we have another procedure which will call above procedure and again insert data in another table.
CREATE PROCEDURE uspInsertData2
AS
BEGIN
DECLARE #MyTable tyAnalysisNumbers;
INSERT INTO sometable
EXEC uspInsertData1
END
Now, if you execute 1st procedure it will work fine but if you execute second procedure you will get this error.
An INSERT EXEC statement cannot be nested.
Because now you have nested EXEC statements.
I suggest to finish your work in single stored procedure if possible.
Try it like this:
DECLARE #MyTable tyAnalysisNumbers;
SELECT * INTO #Temp FROM #MyTable;
DECLARE #tblName AS SYSNAME = (SELECT name FROM sys.tables WHERE name = #someTable);
EXEC ('INSERT INTO #Temp SELECT * FROM ' + #tblName);
This also addresses the SQL Injection problem.

SQL Server Alert on Missing Stored Procedure

I’m cleaning out a large database and am going to be removing a lot of stored procedures. I can be pretty certain but not 100% that the SP isn’t being used. How can I be alerted that something is calling a missing stored procedure, and throwing the error:
Could not find stored procedure ‘X’
You could create synonyms for each deleted procedure and substitute a different procedure (with matching parameters). In the example below the procedure doesn't take any parameters.
The code:
create "replacement" proc called 'dbo.test_replacement_proc'
exec 'dbo.test_replacement_proc'
create "test" proc called 'dbo.test_proc'
exec 'dbo.test_proc'
drop 'dbo.test_proc'
create synonym 'dbo.test_proc' for 'dbo.test_replacement_proc'
exec 'dbo.test_proc'
When #4 executes it's the original procedure. When #7 executes it's the replacement procedure.
drop proc if exists dbo.test_replacement_proc;
go
create proc dbo.test_replacement_proc
as
set nocount on;
select 'Could not find stored procedure' replacement_message
go
-- run the proc
exec dbo.test_replacement_proc;
-- Create proc
drop synonym if exists dbo.test_proc;
drop proc if exists dbo.test_proc;
go
create proc dbo.test_proc
as
set nocount on;
select 'This is the text' procedure_message
go
-- run the proc
exec dbo.test_proc;
-- drop procedure
drop proc if exists dbo.test_proc;
-- Create a synonym for the deleted procedure
create synonym dbo.test_proc
for dbo.test_replacement_proc;
go
-- run the proc (which is now a synonym for the replacement proc)
exec dbo.test_proc;
Output #2
replacement_message
Could not find stored procedure
Output #4
procedure_message
This is the text
output #7
replacement_message
Could not find stored procedure
Use SQL Server Agent to create an alert for error #2812.
You need to create an operator also, and then you can setup a notification for the alert.
See MSSQLTips on how to do it all.

How to store results of a Dynamic Query in a temp table without creating a table?

We are writing a stored procedure responsible for getting a stored procedure name and returning a result containing the stored procedure columns and their data types.
However, we bumped into a problem executing a dynamic query to return the results of stored procedure, but we can't store it in a temp table!
You can see our query below:
DECLARE #ProcName VARCHAR(100)='spGetOraganizationsList',
#ParamName VARCHAR(100),#DataType VARCHAR(20),
#Query NVARCHAR(MAX)='EXEC '+'spGetOraganizationsList '
SELECT PARAMETER_NAME,DATA_TYPE
INTO #Tmp
FROM information_schema.PARAMETERS
WHERE SPECIFIC_NAME=#ProcName
DECLARE ParamCursor CURSOR
FOR SELECT * FROM #Tmp
OPEN ParamCursor
FETCH NEXT FROM ParamCursor
INTO #ParamName,#DataType
WHILE ##FETCH_STATUS = 0
BEGIN
SET #Query=#Query+#ParamName+'=Null,'
FETCH NEXT FROM ParamCursor INTO #ParamName,#DataType
END
CLOSE ParamCursor
DEALLOCATE ParamCursor
DROP TABLE #Tmp
EXEC sp_executesql #Query
The thing is I can't store the results of it in a temp table,
and OPENROWSET does not accept variables.
I think it comes from sql concept that it doesn't trust in result of stored procedures and because of that we cannot select on it or store it in a table by 'making in query table' method.
Unless you create a table and define it's columns and sql trust to you and you insert result of it into this table for example take below situation
Create table test (name varchar(10),family varchar(20))
Insert into test
Exec sp-testResult
Now if you define wrong column for your table you will receive query runtime error .actually sql doesn't predict result of sp and leaves it to you to define result of your stored procedure.
You can certainly INSERT the results of a stored procedure into a TEMP table:
CREATE PROCEDURE PurgeMe
AS
SELECT convert(int, 1) AS DaData
UNION
SELECT convert(int, 2)
GO
CREATE TABLE #Doodles (AnInteger int)
INSERT #Doodles EXECUTE PurgeMe
SELECT * FROM #Doodles
Questions arise about the SCOPE of TEMP tables, however. You might find that in your calling routine you will not be able to see a TEMP table created within your routine.
The solution to the SCOPE problem is to do the following:
Create a minimal TEMP table (say, with one column)
Use ALTER TABLE on the TEMP table within your routine to make its schema match
your needs (this can be tricky, but it can be done)
Put data into the TEMP table
return from your routine - the calling routine will now be able to access the temp
table
If this is of interest I can make a longer post with a stored procedure to do the above. It was written to facilitate dynamic SQL
Write select query as you want in the stored procedure. You will get the result without creating temp table.
Use global temp table and dynamic OPENROWSET
DROP TABLE ##Tmp;
GO
DECLARE #ProcName VARCHAR(100)='spGetOraganizationsList',
#ParamName VARCHAR(100), #DataType VARCHAR(20),
-- Mind to specify database and schema of the SP
#Query NVARCHAR(MAX)=' EXEC [mydb].[dbo].spGetOraganizationsList ';
SELECT PARAMETER_NAME,DATA_TYPE
INTO #Tmp
FROM information_schema.PARAMETERS
WHERE SPECIFIC_NAME=#ProcName;
-- Build SP exec
DECLARE ParamCursor CURSOR
FOR SELECT * FROM #Tmp
OPEN ParamCursor
FETCH NEXT FROM ParamCursor
INTO #ParamName,#DataType
WHILE ##FETCH_STATUS = 0
BEGIN
SET #Query=#Query+#ParamName+'=Null,'
FETCH NEXT FROM ParamCursor INTO #ParamName,#DataType
END
CLOSE ParamCursor
DEALLOCATE ParamCursor
SET #Query = left(#Query, len(#Query) - 1);
-- Build ad hoc distributed query which creates ##Tmp from SP exec.
SET #Query = 'SELECT * INTO ##Tmp FROM OPENROWSET(''SQLNCLI'', ''Server=localhost;Trusted_Connection=yes;'',''' + #Query + ''')';
EXEC (#Query);
-- Created by dynamic sql `##Tmp` is availabe in the current context.
SELECT *
FROM ##Tmp;
Don't forget to enable ad hoc distributed queries first.
sp_configure 'Show Advanced Options', 1
GO
RECONFIGURE
GO
sp_configure 'Ad Hoc Distributed Queries', 1
GO
RECONFIGURE
GO
EDIT
My answer solves only one problem, storing the result of a dynamic proc call in a temp table. And there are more problems.
First, #p=null just will not compile if the type of #p is user-defined table type. You need kind of declare #t myType;
exec mySp ... ,#p=#t ....
Next is the 'cannot retrieve matadata for sp because contain dynamic query' error you commented on. Looks like you need an application, SqlClr or standalone, which would be capable to read and parse Datasets returned by procs.
Finally, if an SP contains conditional sql which can return a result set of different schema depending on parameter values, the result of all those efforts is still questionable.
In C#, you can use an SqlDataReader or a DataTable to get the results from a stored procedure without knowing the schema beforehand. If you then want to write that data to a temporary table, I think you can do that from C# (though I've never tried to do it).

How to execute a stored procedure after it is created?

I'm trying to execute a stored procedure directly after its creation however it is not getting called. It looks like the stored procedure is not yet created during the execution call.
Here is how the script looks like:
CREATE PROCEDURE sp_Transfer_RegionData
AS
BEGIN
INSERT INTO Region (regionName)
SELECT column1
FROM openquery(ITDB, 'select * from db.table1')
END
EXEC sp_Transfer_RegionData
The script runs fine however the needed table is not populated. After replacing the execution part with:
IF OBJECT_ID('sp_Transfer_RegionData') IS NOT NULL
begin
exec [dbo].[sp_Transfer_RegionData]
print 'tada'
end
I could see that the stored procedure does not exist when it has to be executed. Couldn't find a solution for this in the internet...
So how to make the SQL script run sync so that the stored procedure would already exist during the execution part?
You need a GO after you created the SP otherwise you have created a recursive SP that calls itself "indefinitely" which is 32 times in SQL Server.
Maximum stored procedure, function, trigger, or view nesting level
exceeded (limit 32).
Try this:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE sp_Transfer_RegionData
AS
BEGIN
INSERT INTO Region (regionName)
SELECT column1
FROM openquery(ITDB, 'select * from db.table1')
END
GO
EXEC sp_Transfer_RegionData

How to check if a stored procedure exists before creating it

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