I have several queries that drop the proc if it exists, recreate it, and set permissions on it, similar to this:
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
if exists (select *
from dbo.sysobjects
where id = object_id(N'[dbo].[spMyStoredProcedureName]')
and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[spMyStoredProcedureName]
GO
CREATE PROCEDURE [dbo].[spMyStoredProcedureName]
AS
/* more proc stuff */
GRANT EXECUTE ON [dbo].[spMyStoredProcedureName]
TO Some_User_Group
My question is: is there some way to define a variable for [dbo].[spMyStoredProcedureName] so that I can declare it once and refer to the variable? I need to use the variable two ways - once as a string in the select statement and the rest of the time as a reference to the stored proc I'm creating/dropping.
I'm assuming your goal is to replace your "several scripts" with just one script that can be used by changing the value of the variable that holds the name of the stored procedure.
If so you could try a dynamic approach:
Declare #spMyStoredProcedureName nvarchar(255);
--either use a cursor or whatever means to populate the variable.
--for this example I will simply set it
SET #spMyStoredProcedureName = '[dbo].[spMyStoredProcedureName]';
DECLARE #sql nvarchar(max);
SET #sql='
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
if exists (select *
from dbo.sysobjects
where id = object_id(N''#spMyStoredProcedureName'')
and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure #spMyStoredProcedureName
GO
CREATE PROCEDURE #spMyStoredProcedureName
AS
/* more proc stuff */
GRANT EXECUTE ON #spMyStoredProcedureName
TO Some_User_Group
';
SET #sql = REPLACE(#sql, '#spMyStoredProcedureName', #spMyStoredProcedureName)
EXECUTE(#sql)
Note that you'll need to escape all the single quotes in your stored proc code when doing this. Also I'm not so sure you can use the GO command in dynamic sql. If not, you'd need to do two separate dynamic statements per stored proc.
Use dynamic sql for your intended ddl statement. A sample implementation below.
-- Declare variable to store the procedure name
Declare #proc varchar(100) = 'myProc';
-- Construct the ddl statement
DECLARE #script nvarChar(MAX);
SET #script =
'
if exists (select *
from dbo.sysobjects
where id = object_id(N'''+ #proc + ''')
and OBJECTPROPERTY(id, N''IsProcedure'') = 1)
drop procedure ' + #proc + ';
GO
CREATE PROCEDURE ' + #proc + '
AS
print ''Hello World!'';
/* more proc stuff */
GO
GRANT EXECUTE ON ' + #proc + '
TO Some_User_Group;
'
SET #script = 'EXEC (''' + REPLACE(REPLACE(#script, '''', ''''''),
'GO', '''); EXEC(''') + ''');'
-- Run the dynamic ddl
EXEC (#script);
-- Test
exec myproc;
Related
I want to pass 2 parameters to my stored procedure, one of which I wish to use as a part of a variable definition. I am using SQL Server Management Studio 2018. My stored procedure will take a consignment number as a parameter and append it to the variable where the definition will appear similar to this:
passed parameter = #ConsignmentNo
variable #TmpDictionary = 'tmp.' + #ConsignmentNo + '_Dictionary'
So that I may have a generated table name such as
tmp.Cons1234_Dictionary
SQL is throwing an error
Must declare the table variable "#TmpDictionary"
Please see my attempt here:
/****** Object: --------------- Script Date: 29/06/2022 16:17:32 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE InsertProcedureNameHere
#Source varchar (max),
#ConsignmentNo varchar(max)
AS
DECLARE
#sql varchar(MAX),
#loop int,
#max_loop int,
#TmpDictionary nvarchar(max) = 'tmp.' + #ConsignmentNo + '_Dictionary',
#TmpThesaurus nvarchar(max) = 'mail.' + #ConsignmentNo + '_Thesaurus'
--set #TmpLookup = 'tmp.' + #JobNumber + '_Mailing_Lookup'
--set #MailSelection = 'mail. + #JobNumber + '_Mailing_Selection'
SELECT #loop = MIN(ID) FROM #TmpDictionary
WHERE [Source] = #Source
SELECT #max_loop = MAX(ID) FROM #TmpDictionary
WHERE [Source] = #Source
--print #loop print #max_loop
WHILE #loop <= #max_loop
BEGIN
SELECT
---------
FROM #TmpDictionary t
WHERE ID = #loop
BEGIN
SET #sql = '
update t
-------------------
from #Thesaurus t
where 1=1
-------------------
PRINT (#sql)
-- EXEC (#sql)
END
SET #loop = #loop +1
END
Please accept the fact that anything with dashes is just a placeholder for parts of the query that I cannot share or are needed for the purpose of this question. In a nutshell, I need to be able to pass a variable to my stored procedure, and use it within a variable definition to be able to reference tables dynamically using dynamic queries.
I hope this question makes sense!
The following code executes successfully, unless I add create procedure dbo.proc_name ... as to it:
use db_name
go
declare #sp_date date;
select #sp_date = getdate();
if object_id('tempdb..##global_tmp_tbl') is not null drop table ##global_tmp_tbl;
begin transaction
set xact_abort on
declare #query varchar(250), #exec_stmnt varchar(500);
set #query = 'exec remote_db.dbo.remote_sp' + ' ''''' + cast(#sp_date as varchar(10)) + ''''' ';
set #query = '''' + #query + '''';
set #exec_stmnt = 'select * into ##global_tmp_tbl from openquery(LS_RMT,' + #query + ')';
exec (#exec_stmnt);
commit transaction
go
if object_id('tempdb..#local_tmp_tbl') is not null drop table #local_tmp_tbl;
select * into #local_tmp_tbl from ##global_tmp_tbl;
Here LS_RMT is a linked server, and remote_sp is a stored procedure on the database remote_db on that linked server.
When I try to put this code into a stored procedure, SQL Server complains that ##global_tmp_tbl is an invalid name when trying to read from it after executing the stored procedure on the linked server which loads it.
I'm guessing that the scope of the global temporary table changes once within the context of a stored procedure, but I can't find any documentation on why that might be the case so I'm not sure.
Is this a scope issue, or is it actually possible to use the global temporary table within a stored procedure after it has been created inside a transaction that loads it from an openquery statement and I am just doing it wrong?
Although I am not entirely sure why the code above works when outside the context of a stored procedure, I did determine that embedding all references to the global temporary table within the committed transaction allowed the stored procedure to work. So something like the following:
use db_name
go
create procedure dbo.proc_name
#sp_date date = NULL
as
if isnull(#sp_date,'') = ''
begin
select #sp_date = getdate();
end
if object_id('tempdb..##global_tmp_tbl') is not null drop table ##global_tmp_tbl;
begin transaction
set xact_abort on
declare #query varchar(250), #exec_stmnt varchar(500);
set #query = 'exec remote_db.dbo.remote_sp' + ' ''''' + cast(#sp_date as varchar(10)) + ''''' ';
set #query = '''' + #query + '''';
set #exec_stmnt = 'select * into ##global_tmp_tbl from openquery(LS_RMT,' + #query + ')';
exec (#exec_stmnt);
if object_id('tempdb..#local_tmp_tbl') is not null drop table #local_tmp_tbl;
select * into #local_tmp_tbl from ##global_tmp_tbl;
commit transaction
go
I want to create a stored procedure that will update a table. The procedure will join two tables and I want to pass the table name using a variable (#tablename).
This error is generated:
Must declare the table variable "#tablename".
My code:
Create Procedure dbo.SpUpdate (#TableName varchar(50))
as
begin
set #tablename='Customer'
Update a
Set AgentNumber = '5',
From dbo.CustomerList a
join #tablename b on a.customerid = b.customerid
end
You can use this script:
Create Procedure dbo.SpUpdate (#TableName varchar(50))
as
begin
DECLARE #SqlText NVARCHAR(MAX)
SET #SqlText =
'Update a
Set AgentNumber=''5'',
From dbo.CustomerList a
join ' + QUOTENAME(#tablename) + ' b
on a.customerid= b.customerid'
EXEC sp_executesql #SqlText
end
If I run the select statement below by itself (with the table name hard coded in) it runs fine and the temp table is created. If I run it as below it says 'Invalid object name '#TempCodes'' although when I print #Sql1 it looks exactly the same as when I run it by itself (with the table name hard coded in).
Any ideas on what's going on here will be appreciated.
DECLARE #TableName NVARCHAR(50)
SET #TableName = '[My Table Name]'
DECLARE #Sql1 NVARCHAR(MAX);
SET #Sql1 = N'SELECT AccountNumber,LTRIM(RTRIM(m.n.value(''.[1]'',''varchar(8000)''))) AS mdcodes INTO #TempCodes FROM (SELECT AccountNumber,CAST(''<XMLRoot><RowData>''
+ REPLACE(MD_Results,'','',''</RowData><RowData>'')
+ ''</RowData></XMLRoot>'' AS XML) AS x FROM ' + #TableName
+ N')t CROSS APPLY x.nodes(''/XMLRoot/RowData'')m(n)'
IF OBJECT_ID('tempdb.dbo.#TempCodes', 'U') IS NOT NULL
BEGIN
drop table #TempCodes
END
EXECUTE sp_executesql #Sql1
Select * from #TempCodes
I believe ## is a typo. Even if you fix it to # it will throw same error.
EXECUTE sp_executesql #Sql1
A local temporary table created in a stored procedure is dropped
automatically when the stored procedure is finished.
In your case sp_executesql is the stored procedure.
the table created inside the dynamic query will be dropped after the exec sp_executesql is completed. That's why you are getting that error.
You need to create table outside and use Insert into table..select syntax inside the dynamic query
IF OBJECT_ID('tempdb.dbo.#TempCodes', 'U') IS NOT NULL
drop table #TempCodes
create table #TempCodes(AccountNumber varchar(100),mdcodes varchar(100))
SET #Sql1 = N'Insert into #TempCodes
SELECT AccountNumber,LTRIM(RTRIM(m.n.value(''.
[1]'',''varchar(8000)''))) AS mdcodes FROM (SELECT
AccountNumber,CAST(''<XMLRoot><RowData>'' +
REPLACE(MD_Results,'','',''</RowData><RowData>'') + ''</RowData></XMLRoot>''
AS XML) AS x FROM '+#TableName+ N')t CROSS APPLY
x.nodes(''/XMLRoot/RowData'')m(n)'
Try using a global temp table ##TempCodes as local temporary tables are only visible in current session.
DECLARE #TableName NVARCHAR(50)
SET #TableName = '[My Table Name]'
DECLARE #Sql1 NVARCHAR(MAX);
SET #Sql1 = N'SELECT AccountNumber,LTRIM(RTRIM(m.n.value(''.
[1]'',''varchar(8000)''))) AS mdcodes INTO ##TempCodes FROM (SELECT
AccountNumber,CAST(''<XMLRoot><RowData>'' +
REPLACE(MD_Results,'','',''</RowData><RowData>'') + ''</RowData></XMLRoot>''
AS XML) AS x FROM '+#TableName+ N')t CROSS APPLY
x.nodes(''/XMLRoot/RowData'')m(n)'
IF OBJECT_ID('tempdb.dbo.##TempCodes', 'U') IS NOT NULL
BEGIN
drop table ##TempCodes
END
EXECUTE sp_executesql #Sql1
Select * from ##TempCodes
I've created a stored procedure where I want to pass the #DB through a USE command. I know I can define dynamically the correct path for the table with the name of the DB (e.g. ... '+ #DB +' .dbo.tAnalysisResult' ) but I have several inner joins with many tables on the same database so I'd like to define the DB only once at the beginning.
Any help will be highly appreciated. Below my query which currently doesn't work:
USE [DB_Research]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].sp_test1 #DB VARCHAR(300), #OUTCOME INT OUTPUT
AS
SET NOCOUNT ON
EXECUTE ('USE' + #DB)
BEGIN TRY
SELECT #OUTCOME = (select SUM(ResultSID) from tAnalysisResult)
END TRY BEGIN CATCH
-- If error encountered, display it
SELECT #OUTCOME = '0, line: ' + CAST(ERROR_LINE() AS VARCHAR(MAX)) + ', msg:' + ERROR_MESSAGE()
END CATCH
SET NOCOUNT OFF
-- Execute the stored procedure
declare #OUTCOME INT
exec sp_test1 'Loss_DB', #OUTCOME OUTPUT
Print(#OUTCOME)
Select * from dbname..schemaname.tablename