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
Related
I have a stored procedure in a SQL Server 2019 database that does various checks for me when I run. This stored procedure contains several SELECT type statements - if the condition is true it will send an email.
This all works great when I run the whole stored procedure. As none of the statements are linked, I thought it would be more efficient if I ran each statement then had a GO command to release any memory or resources from previous SELECT.
But I can't do this as the GO does not work when I run me ALTER script on the stored procedure. It cuts off any lines after the GO. Is there any alternative T-SQL command that I can use similar to GO to free up resources?
ALTER PROCEDURE [dbo].[uspDataChecker]
AS
SET NOCOUNT OFF;
IF ((SELECT COUNT(DISTINCT GroupName) FROM tblSurveys) <> 5)
BEGIN
EXEC uspWebmasterSendEmail 'FAILED GroupName Check', 'GroupName does not contain 5 rows as expected'
END
-- I would like to put a "GO" statement here but when I
-- execute my ALTER script it chops off the below check
IF ((SELECT COUNT(*) FROM tblAccounts) <> 0)
BEGIN
EXEC uspWebmasterSendEmail 'FAILED Accounts Check', 'tblAccounts has rwos'
END
I have a stored procedure, usp_region and it has a select statement with 50 columns as the result set. This procedure is called by multiple other stored procedures in our application.
Most of the stored procedure pass a parameter to this procedure and display the result set that it returns. I have one stored procedure, usp_calculatedDisplay, that gets the columns from this stored procedure and inserts the values into a temp table and does some more calculations on the columns.
Here's a part of the code in usp_calculatedDisplay.
Begin Procedure
/* some sql statements */
Declare #tmptable
(
-- all the 50 columns that are returned from the usp_region procedure
)
Insert Into #tmptable
exec usp_region #regionId = #id
Select t.*, /* a few calculated columns here */
From #tmptable t
End of procedure
Every time I add a column to the usp_region procedure, I'll also have to make sure I have to add it to this procedure. Otherwise it breaks. It has become difficult to maintain it since it is highly possible for someone to miss adding a column to the usp_calculatedDisplay procedure when the column is added to the usp_region.
In order to overcome this problem, I decided to do this:
Select *
Into #tmptable
From OPENROWSET('SQLNCLI',
'Server=localhost;Trusted_Connection=yes;',
'EXEC [dbo].[usp_region]')
The problem is 'Ad Hoc Distributed Queries' component is turned off. So I can't use this approach to overcome this issue. I was wondering if there are any other ways of overcoming this problem. I would really appreciate any help. Thank you!
Every time I add a column to the usp_region procedure
SQL Server is a structured database and it does not meant to solve such cases that you need to change your structure every day.
If you add/remove columns so often then you probably did not choose the right type of database, and you better re-design your system.
It has become difficult to maintain it since it is highly possible for someone to miss adding a column to the usp_calculatedDisplay procedure when the column is added to the usp_region.
There are two simple solutions for this (1) using DDL Triggers - very bad idea but simple to implement and working. (2) Using my trick to select from stored procedure
Option 1: using DDL trigger
You can automate the entire procedure and ALTER the stored procedure usp_calculatedDisplay every time that the stored procedure usp_region is changed
https://learn.microsoft.com/en-us/sql/relational-databases/triggers/ddl-triggers
The basic approach is
CREATE OR ALTER TRIGGER NotGoodSolutionTrig ON DATABASE FOR ALTER_PROCEDURE AS BEGIN
DECLARE #var_xml XML = EVENTDATA();
IF(
#var_xml.value('(EVENT_INSTANCE/DatabaseName)[1]', 'sysname') = 'tempdb'
and
#var_xml.value('(EVENT_INSTANCE/SchemaName)[1]', 'sysname') = 'dbo'
and
#var_xml.value('(EVENT_INSTANCE/ObjectName)[1]', 'sysname') = 'usp_region'
)
BEGIN
-- Here you can parse the text of the stored procedure
-- and execute ALTER on the first SP
-- To make it simpler, you can design the procedure usp_region so the columns names will be in specific row or between to comment which will help us to find it
-- The code of the Stored Procedure which you need to parse is in the value of:
-- #var_xml.value('(EVENT_INSTANCE/TSQLCommand/CommandText)[1]', 'NVARCHAR(MAX)'))
-- For example we can print it
DECLARE #SP_Code NVARCHAR(MAX)
SET #SP_Code = CONVERT(NVARCHAR(MAX), #var_xml.value('(EVENT_INSTANCE/TSQLCommand/CommandText)[1]', 'NVARCHAR(MAX)'))
PRINT #SP_Code
-- In your case, you need to execute ALTER on the usp_calculatedDisplay procedure using the text from usp_region
END
END
Option 2: trick to select from stored procedure using sys.dm_exec_describe_first_result_set
This is simple and direct way to get what you need.
CREATE OR ALTER PROCEDURE usp_calculatedDisplay AS
-- Option: using simple table, so it will exists outsie the scope of the dynamic query
DROP TABLE IF EXISTS MyTable;
DECLARE #sqlCommand NVARCHAR(MAX)
select #sqlCommand = 'CREATE TABLE MyTable(' + STRING_AGG ([name] + ' ' + system_type_name, ',') + ');'
from sys.dm_exec_describe_first_result_set (N'EXEC usp_region', null,0)
PRINT #sqlCommand
EXECUTE sp_executesql #sqlCommand
INSERT MyTable EXECUTE usp_region;
SELECT * FROM MyTable;
GO
Note!!! Both solutions are not recommended in production. My advice is to avoid such needs by redesign your system. If you need to re-write 20 SP so do it and don't be lazy! Your goal should be what best for the database usage.
I have a stored procedure, let's call it stored procedure 'B'. Stored procedure 'B' calls stored procedure 'A' which returns a resultset that needs to be inserted into a temp table within stored procedure 'B', in order to do further mutations. Because of nested inserts, I have used OPENROWSET (and tried OPENQUERY too).
Now, it seems to work great! However, next to returning a resultset, stored procedure 'A' also does INSERTS in a table. The weird thing is, when stored procedure 'A' is executed from within stored procedure 'B', stored procedure 'A' only returns the resultset, and does NO insert at all. It just seems to skip the entire INSERT INTO statement. I have tried putting dummy SELECT 'test' breakpoints before and after the INSERT, and they are executed fine! How is this possible?
This query looks like this (I changed data and columns up a bit):
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = 'INSERT INTO #Temp (1,2,3)
SELECT * FROM OPENROWSET (
''SQLOLEDB'',
''Server=(local);TRUSTED_CONNECTION=yes;'',
''SET FMTONLY OFF EXECUTE StoredProcedureA
#Parameter1 = '''''+#InputValue1+'''''
,#Parameter_2 = '''''+#InputValue2+'''''
''
)'
EXEC(#SQL)
No errors are returned. The resultset (SELECT statement from procedure A) is correctly loaded into #Temp within procedure B. But the INSERT that is done within procedure A is not executed.
Does openquery/openrowset not allow INSERTS and only execute SELECT outputs? I thought, maybe its a security/rights issue? Is there any other way to workaround this issue?
Thanks in advance guys!
It is because you are using a temporary table denoted by #.
The scope of this table is ends when your nested stored procedure ends and the temporary table is dropped.
So the insert happens, the table just doesn't exist anymore.
If you create the table before starting your nested procedure you can solve this problem. You can just drop the table in you Procedure B if you really want it gone.
I have a trigger for executing two procedures.
ALTER TRIGGER [dbo].[TRG_SP_SYNCH_CAB]
ON [VTBO_INTERFACE].[dbo].[T_TRIGGER_TABLE_FOR_SYNCH]
INSTEAD OF INSERT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for trigger here
INSERT INTO T_TRIGGER_TABLE_FOR_SYNCH (DT)
VALUES (GETDATE());
exec PUMPOMAT_HO.DBO.SP_CM_TransferCAB
exec PUMPOMAT_HO.DBO.SP_CM_UpdateCAB
END
Execution time for two procedures is 5 mins. When I try to insert a value to T_TRIGGER_TABLE_FOR_SYNCH table, my other tables which are used in stored procedures are locked along 5 mins. But when I try to execute two procedures directly like
exec SP_CM_TransferCAB
exec SP_CM_UpdateCAB
No lock happens. What should I write in trigger to avoid of table locks.
Thanks.
Try by calling the second procedure inside(end of) the first procedure since I see no parameters are given.
Is this table [VTBO_INTERFACE].[dbo].[T_TRIGGER_TABLE_FOR_SYNCH] used in any of the procedure?
You should try to change the design/data flow to mimic this procedure call.
This question already has answers here:
How do I conditionally create a stored procedure in SQL Server?
(11 answers)
Closed 7 years ago.
I'm writing a db update script which basically retrieve the current version number from the database then create a number of stored procedure if the version is valid. If the current version does not match the expected version then it should skip executing the code.
However I run into a problem when I write the script because CREATE PROCEDURE has to be the first statement in a batch, so it's not possible for me to insert if .. else statement before the create procedure statement.
I've also tried using GOTO but to no avail because GOTO doesn't span across multiple batches. Same thing applies to RETURN and RAISEERROR - the rest of the code will still execute.
Sample script:
IF #Version = '1.0' --doesn't work
BEGIN
CREATE PROCEDURE dbo.uspCreateAccount
AS BEGIN
--The rest of the code goes here
END
END
Can anyone provide some insight on this?
You can accomplish this using the exec functionality.
IF #Version = '1.0'
BEGIN
--Drop it if it already exists
IF OBJECT_ID('uspCreateAccount', 'P') IS NOT NULL
EXEC ('DROP PROCEDURE uspCreateAccount')
--Recreate it.
EXEC('
CREATE PROCEDURE uspCreateAccount
AS BEGIN
--The rest of the code goes here
END
')
END
You can use exec to run a SQL command in a new scope. The following snippet will run even when dbo.YourProc already exists, because the SQL command inside exec() will never be parsed in that case.
if not exists (select * from sys.procedures where name = 'YourProc')
exec ('create procedure dbo.YourProc as select 1 as a')
go
alter procedure dbo.YourProc
as
select ...
This construct creates an empty stub procedure if the procedure does not exist. If the procedure exists, it runs alter. So it preserves rights that have been granted on the procedure.