Testing linked server conccetion inside trigger or procedure - sql-server

I wrote a trigger that updates local table and similar table on linked server.
CREATE TRIGGER myTtableUpdate ON myTable
AFTER UPDATE
AS
IF (COLUMNS_UPDATED() > 0)
BEGIN
DECLARE #retval int;
BEGIN TRY
EXEC #retval = sys.sp_testlinkedserver N'my_linked_server';
END TRY
BEGIN CATCH
SET #retval = sign(##error);
END CATCH;
IF (#retval = 0)
BEGIN
UPDATE remoteTable SET remoteTable.datafield = i.datafield
FROM my_linked_server.remote_database.dbo.myTable remoteTable
INNER JOIN inserted i ON (remoteTable.id = i.id)
END
END -- end of trigger
Unfortunately when connection is down I get error message
'Msg 3616, Level 16, State 1, Line 2'
'Transaction doomed in trigger. Batch has been aborted'
and locally made update is rolled back.
Is there a way to maintain this error and keep local updates?
Note that I'm using SQL Server 2005 Express Edition on both PCs running Windows XP Pro.
edit1: SQL server is Express Edition
edit2: Both PCs run Windows XP Pro so these aren't servers

don't write to the remote server in the trigger.
create a local table to store rows that need to be pushed to the remote server
insert into this new local table in the trigger
create a job that runs every N minutes to insert from this local table into remote server.
this job can run a procedure that can test for the connection, and when it is back up, it will handle all rows in the new local table. It can process the rows in the local table this way:
declare #OutputTable table (RowID int not null)
insert into my_linked_server.remote_database.dbo.myTable remoteTable(...columns...)
OUTPUT INSERTED.RowID
INTO #OutputTable
SELECT ...columns...
from NewLocalTable
delete NewLocalTable
from NewLocalTable n
inner join #OutputTable o ON n.RowID=o.RowID
EDIT based OP comment
after inserting into this new local table start the job from the trigger (sp_start_job), it will run in its own scope. If you can't use sql server jobs, use xp_cmdshell to execute the stored procedure (lookup SQLCMD or ISQL or OSQL, I'm not sure what you have). still schedule the job every N minutes, so it will eventually run when the connection comes up.

Is at least one of the servers Workgroup edition or higher? You can use Service Broker to ship your records instead of linked servers, but it will not work between to Express editions due to licensing restrictions. Is a solution relying exclusively on SQL, offers reliability in case of incidents (one of the servers is unavailable) and your updates will propagate in real time (as soon as they are committed). My site has many examples on how to do this, you can start with this article here on how to achieve high message throughput.

Related

Creating a Stored Procedure despite errors (to transfer to different server)

I've got a database that is replicated on two servers, a live server and a test server, so that whenever it's needed the 'test' database gets overwritten by the 'live' database (so that I can reset everything if I've made a mess.)
I want an Stored Procedure in the 'test' database, that will only run in the 'test' database, but to do this I need to have it in the 'live' database as well, so that it can be copied over when 'test' is overwritten.
The procedure starts:
if ##SERVERNAME<>'TEST'
begin
raiserror ('NOT ON TEST! This SP must only be run on TEST.',16,1)
return
end
So that if it runs in live, it immediately exits.
Unfortunately the "Live" database server uses an older version of SQL, and doesn't seem to understand the lead/lag/over statements in the script, and refuses to create the procedure because of these "Incorrect syntax" errors.
The SP definitely works in the test server.
Is there a way to disregard the error messages when creating a stored procedure?
I've found a prior question that explained how to make a stored procedure with the same name, but I need the stored procedure to contain the script that the server thinks is incorrect.
The only way to not get the stored-procedure validated when created, is to run a dynamic-sql query within.
Example:
CREATE PROCEDURE dbo.YourStoredProcedure AS
BEGIN
IF ##SERVERNAME<>'TEST'
BEGIN
RAISERROR ('NOT ON TEST! This SP must only be run on TEST.',16,1)
RETURN
END
DECLARE #SQL NVARCHAR = N'
SELECT rowOne
, rowTwo
, valueOne
, LEAD(valueOne) OVER (PARTITION BY rowOne ORDER BY rowTwo DESC) AS preValue
FROM dbo.YourTable
'
EXEC(#SQL)
END
Notes:
On the long term, try to find a better strategy than DB replication for different systems. Check Continuous Deployment
Make sure to check concatenated dynamic-sql for potential issues (sql injection). Check QUOTENAME()

How to Automate Query Results in Microsoft SQL Management Studio 2012 and send the query results to WCF service as Instant Notifications?

I am new to SQL Server. I need track the Data Changes in Database Table inside the Microsoft SQL Server Management Studio.If any Data insertion,Deletion and modification done..in Table ,the WCF service should receive notifications from SQL Server 2012 Database.
Is this possible to send instant notifications of Data Table from SQL server to WCF Service?
Done's and Studied:-
I am already refer some websites. I have an Idea about Change Data Capture and Change Tracking procedure in SQL Server.Then i were followed the Change Data Capture Procedure to Track the Data in SQL Server.Then i look over some information about Jobs in SQL Server Data Agent.With the help of jobs,possible to automate a query execution in sql server with a time interval?
Then i am created a Database in SQL server using DML and enabled the Change Data Capture Feature for Database and Table Level,specified number of Columns!
My New Database:-
CREATE DATABASE Testcdc_feature
GO
Use Testcdc_feature;
GO
CREATE TABLE dbo.one(
Name varchar(100),
Id int,
Designation varchar(100),
Salary int,
);
Enabled Change Data Capture Feature for Database:-
use Testcdc_feature;
GO
-- enable CDC on the database
EXEC sys.sp_cdc_enable_db;
GO
Enabled CDC Feature for The Table and Specified Columns:-
USE Testcdc_feature;
GO
EXEC sys.sp_cdc_enable_table
#source_schema = N'dbo',
#source_name = N'one',
#role_name = NULL, -- Role gating not enabled
#filegroup_name = N'PRIMARY', -- should consider writing audit date to separate filegroup
#captured_column_list = N'Name,Id,Designation,Salary';
GO
Inserted Values to the Table Columns:-
USE Testcdc_feature;
GO
SET NOCOUNT ON;
INSERT INTO dbo.one VALUES(
'thirunavukkarasu',2345,'TeamLeader',12000000
);
INSERT INTO dbo.one VALUES(
'Selva',3456,'Developer',30000);
INSERT INTO dbo.one VALUES(
'John',9876,'Application Tester',45000
);
INSERT INTO dbo.one VALUES(
'Anand',56789,'Developer',56000
);
INSERT INTO dbo.one VALUES(
'Priya',6709,'Developer',78000
);
INSERT INTO dbo.one VALUES(
'Swetha',8907,'Developer',100000
);
For Check the Data Changes the query is written as below(DEMO):-
USE Testcdc_feature;
GO
DECLARE #from_lsn binary(10), #to_lsn binary(10)
SET #from_lsn = sys.fn_cdc_get_min_lsn('dbo_one')
SET #to_lsn = sys.fn_cdc_get_max_lsn()
SELECT
CT.__$start_lsn,
CT.__$operation,
CASE CT.__$operation
WHEN 1 THEN 'Delete'
WHEN 2 THEN 'Insert'
WHEN 3 THEN 'Update - Pre'
WHEN 4 THEN 'Update - Post'
END AS Operation,
CT.*,
LSN.tran_begin_time,
LSN.tran_end_time,
LSN.tran_id
FROM
cdc.fn_cdc_get_all_changes_dbo_one
(#from_lsn, #to_lsn, N'all update old') AS CT INNER JOIN
cdc.lsn_time_mapping AS LSN ON CT.__$start_lsn = LSN.start_lsn
GO
When Executing the Last Query i got output as below
I need to automate the last Query (Demo) for a time interval and send it results as instant notifications of Data modifications from SQL server to WCF Service?
The WCF Should Receive the Insertion,Modification and Deletion of Data From SQL server.is this possible?
My Question:-
1.How to automate a query execution with a time interval in MS SQL Server 2012?
2.How to send the automated query results into wcf service?
How do i achieve this? Could anyone provide me a idea or solution?
It Might me more Helpful!
From what I understand.
Your needs are to communicate to a WCF Service any transactions done to a table with a time interval.
What I recommand you is to create a trigger on your table that will insert in a specific table for transaction informations that you need (columns changed? is it INSERT/UPDATE/DELETE? previous and new values? a status flag?).
After that, you set up a new SQL job with your time constraints that will raise your WCF Service. Finally, your WCF Service will read any news data in your new transaction table.
Here is a link to setting up a Job.
EDIT : You will probably need indexes on your transaction tables particularly on the previous tables key and on your status flag. What a mean by a status flag is information related to your WCF Service (did it already read this line? If you implements a re-processing in case of error, which step this line is on your WCF Service?)
When your WCF Service will read your transaction table, to do it properly you may use transactions in order to re-process from the start. Here is a link to use transactions. I don't recommand this since your transaction table will be highly accessed and using a transaction will put locks on your table if not use properly.
Or you may Update your status flag and retrieve the data with an OUTPUT clause in order to re-process step-by-step. Here is a link to use output clause on update. (The example you are looking for is C.)
By what I am understanding, all you want is an event triggering when you
"If any Data insertion,Deletion and modification done..in Table"
what you can do is create SQL triggers, you can set them to trigger(throw an event) when you delete from, update or insert into the table.

Query multiple SQL Servers in One query

I am trying to set up a query that will grab the Windows version of each SQL Server I have and throw it into a table. I have the query that grabs the version but I think there is a better way to get the information needed than connecting to each indiviual server one by one to run the query. I am not opposed to using XP_cmdshell I am just wondering if there is a way to run one query that will grab the version of each Windows OS I have on the sql servers. Also I do have a list of servers to use.
EDIT: I know I wil have to in some way touch each server. I would just like a way to get around having the RDP to each server and open SQL server and query it or haveing to connect to each server within sql server and running the query one by one.
All I have right now code wise is a simple INSERT STATEMENT I get here and I draw a blank on where to go next of even hoe to tackle the problem. The table below has two columns ServerName and Win_Ver ServerName is already populated with all the servers I have.
INSERT INTO mtTable
(Win_Ver)
SELECT ##Version
Given that:
there are "roughly 112 servers"
the servers being a "mixture between 2008 - 2012"
"There is table we are keeping with all of our DB server Statistics."
and "We periodically get asked to produce these statistics"
one option is to cycle through that table of servers using a cursor, and for each one, execute xp_cmdshell to call SQLCMD to run the query. You would use a table variable to capture the result set from SQLCMD as returned by xp_cmdshell. Something like:
DECLARE #ServerName sysname,
#Command NVARCHAR(4000),
#CommandTemplate NVARCHAR(4000);
DECLARE #Results TABLE ([ResultID] INT IDENTITY(1, 1) NOT NULL, [Result] NVARCHAR(4000));
SET #CommandTemplate = N'SQLCMD -S {{SERVER_NAME}} -E -h-1 -Q "PRINT ##VERSION;"';
DECLARE srvrs CURSOR LOCAL READ_ONLY FAST_FORWARD
FOR SELECT [ServerName]
FROM ServerStats;
OPEN srvrs;
FETCH NEXT
FROM srvrs
INTO #ServerName;
WHILE (##FETCH_STATUS = 0)
BEGIN
SET #Command = REPLACE(#CommandTemplate, N'{{SERVER_NAME}}', #ServerName);
INSERT INTO #Results ([Result])
EXEC xp_cmdshell #Command;
-- Get results via SELECT [Result] FROM #Results ORDER BY [ResultID];
-- Do something with the data in #Results
DELETE FROM #Results;
FETCH NEXT
FROM srvrs
INTO #ServerName;
END;
CLOSE srvrs;
DEALLOCATE srvrs;
And it wouldn't hurt to throw in a TRY / CATCH in there :-).
Even if not the most ideal of solutions, it is at least doesn't require adding 112 Linked Servers, and is dynamic and will adjust to servers being added and removed.
In SQL Server you are able to create a Linked Server that you can query from another server.
On the server you wish to write the query in:
Open the Object Explorer
Go to Server Objects
Right Click Linked Servers and add a New Linked Server
Add the Name of your networked server, select SQL server and make sure to define security roles.

How to store the results of a stored procedure without requiring a distributed transaction?

I have a remote stored procedure that i am running:
EXECUTE Contoso.Frob.dbo.Grobber #StartDate='20140513', #EndDate='20140518'
and this remote stored procedure returns a rowset:
EmployeeID EmployeeName StartDateTime EndDateTime
---------- -------------- ------------- -----------------------
619 Guyer, Kirsten 2014-05-13 19:00:00.000 2014-05-13 19:00:00.000
...
Excellent. Perfect. Good. Sweet.
Now that i have these results, i need to store them in a table. Any kind of table. I don't care what kind of table:
physical table
temporary table
global temporary table
table variable
I just need them stored so that i can process them. The problem is that when i try to insert the results into a table, whether it be:
a physical table
INSERT INTO EmployeeSchedule
EXECUTE Contoso.Frob.dbo.Grobber #StartDate='20140513', #EndDate='20140518'
temporary table
INSERT INTO #EmployeeSchedule
EXECUTE Contoso.Frob.dbo.Grobber #StartDate='20140513', #EndDate='20140518'
a global temporary table
INSERT INTO ##EmployeeSchedule
EXECUTE Contoso.Frob.dbo.Grobber #StartDate='20140513', #EndDate='20140518'
a table variable
INSERT INTO #EmployeeSchedule
EXECUTE Contoso.Frob.dbo.Grobber #StartDate='20140513', #EndDate='20140518'
SQL Server insists (nay, demands) that it begin a distributed transaction:
OLE DB provider "SQLNCLI10" for linked server "Contoso" returned message "The partner transaction manager has disabled its support for remote/network transactions.".
Msg 7391, Level 16, State 2, Line 41
The operation could not be performed because OLE DB provider "SQLNCLI10" for linked server "Contoso" was unable to begin a distributed transaction.
Why not just...
Now, making changes to the Contoso server is not an option. Why? Doesn't matter. Pretend that Jack Bauer will make an appearance and Guantanamo anyone who tries to modify Contoso. This means i cannot enable or reconfigure MSDTC on \\Contoso.
Did you try using READ UNCOMMITTED?
Yes.
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
INSERT INTO #EmployeeSchedule
EXECUTE wclnightdb.NGDemo.dbo.tbtGetSchedule #StartDate, #EndDate
The partner transaction manager has disabled its support for remote/network transactions.
And:
INSERT INTO #EmployeeSchedule
WITH (NOLOCK)
EXECUTE wclnightdb.NGDemo.dbo.tbtGetSchedule #StartDate, #EndDate
Sorry. No nolock. Nolock is a no no:
Msg 1065, Level 15, State 1, Line 15
The NOLOCK and READUNCOMMITTED lock hints are not allowed for target tables of INSERT, UPDATE, DELETE or MERGE statements.
I always could give up on SQL Server
If i were doing this in a programming environment, it would be fairly easy to fix:
using (IDataReader rdr = ADOHelper.Execute(conn, "EXECUTE Contoso.Frob.dbo.Grobber #StartDate='20140513', #EndDate='20140518'")
{
while (rdr.Read())
{
InsertRowIntoTable(conn, rdr);
}
}
Although that would require me to create a binary, ship it, and schedule it. I'm looking for the option that works with SQL Server (so SQL Agent can schedule the job).
Bonus Reading
SET REMOTE_PROC_TRANSACTIONS (Transact-SQL)
How do I use the results of a stored procedure from within another?
How can one iterate over stored procedure results from within another stored procedure....without cursors?
SQL Server insists (nay, demands) that it begin a distributed
transaction:
If you can't configure your servers to use distributed transactions for whatever reason, you can tell it not to.
USE [master]
GO
EXEC master.dbo.sp_serveroption
#server = N'Contoso',
#optname = N'remote proc transaction promotion',
#optvalue = N'false'
GO
Or in SSMS GUI:
I don't know all implications of turning off this option, but at least now my INSERT ... EXEC [LinkedServer]... works.
Two options to try would be:
Since you already have a Linked Server set up, use it with OPENQUERY, as in:
SELECT column1, column2 FROM OPENQUERY(Contoso, 'EXECUTE Frob.dbo.Grobber #StartDate=''20140513'', #EndDate=''20140518''')
If the returned columns will remain consistent, create a SQLCLR Table-Valued Function. This assumes that the remote proc is Read-Only (i.e. SELECT-only). But unlike T-SQL functions, SQLCLR functions can execute Stored Procedures using the connection string "Context Connection = True;" as long as the Stored Procedure is SELECT-only (i.e. does not change the state of the DB through DML, DDL, etc).
How about this:
-- Either create a job that runs your remote sql via a SQLSMD command, or just run something like this:
EXEC master..xp_cmdshell 'SQLCMD -S Server\SQLSERVERDEV2005 -i"c:\DML.sql"'
(It might be easier with a job because you can modify the job step easily via sp_update_jobstep to get the right values in for your parameters)
-- Output the result of the sqlcmd into a file
-- Load the file into a table via bulk import.

Errors: "INSERT EXEC statement cannot be nested." and "Cannot use the ROLLBACK statement within an INSERT-EXEC statement." How to solve this?

I have three stored procedures Sp1, Sp2 and Sp3.
The first one (Sp1) will execute the second one (Sp2) and save returned data into #tempTB1 and the second one will execute the third one (Sp3) and save data into #tempTB2.
If I execute the Sp2 it will work and it will return me all my data from the Sp3, but the problem is in the Sp1, when I execute it it will display this error:
INSERT EXEC statement cannot be nested
I tried to change the place of execute Sp2 and it display me another error:
Cannot use the ROLLBACK statement
within an INSERT-EXEC statement.
This is a common issue when attempting to 'bubble' up data from a chain of stored procedures. A restriction in SQL Server is you can only have one INSERT-EXEC active at a time. I recommend looking at How to Share Data Between Stored Procedures which is a very thorough article on patterns to work around this type of problem.
For example a work around could be to turn Sp3 into a Table-valued function.
This is the only "simple" way to do this in SQL Server without some giant convoluted created function or executed sql string call, both of which are terrible solutions:
create a temp table
openrowset your stored procedure data into it
EXAMPLE:
INSERT INTO #YOUR_TEMP_TABLE
SELECT * FROM OPENROWSET ('SQLOLEDB','Server=(local);TRUSTED_CONNECTION=YES;','set fmtonly off EXEC [ServerName].dbo.[StoredProcedureName] 1,2,3')
Note: You MUST use 'set fmtonly off', AND you CANNOT add dynamic sql to this either inside the openrowset call, either for the string containing your stored procedure parameters or for the table name. Thats why you have to use a temp table rather than table variables, which would have been better, as it out performs temp table in most cases.
OK, encouraged by jimhark here is an example of the old single hash table approach: -
CREATE PROCEDURE SP3 as
BEGIN
SELECT 1, 'Data1'
UNION ALL
SELECT 2, 'Data2'
END
go
CREATE PROCEDURE SP2 as
BEGIN
if exists (select * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..#tmp1'))
INSERT INTO #tmp1
EXEC SP3
else
EXEC SP3
END
go
CREATE PROCEDURE SP1 as
BEGIN
EXEC SP2
END
GO
/*
--I want some data back from SP3
-- Just run the SP1
EXEC SP1
*/
/*
--I want some data back from SP3 into a table to do something useful
--Try run this - get an error - can't nest Execs
if exists (select * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..#tmp1'))
DROP TABLE #tmp1
CREATE TABLE #tmp1 (ID INT, Data VARCHAR(20))
INSERT INTO #tmp1
EXEC SP1
*/
/*
--I want some data back from SP3 into a table to do something useful
--However, if we run this single hash temp table it is in scope anyway so
--no need for the exec insert
if exists (select * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..#tmp1'))
DROP TABLE #tmp1
CREATE TABLE #tmp1 (ID INT, Data VARCHAR(20))
EXEC SP1
SELECT * FROM #tmp1
*/
My work around for this problem has always been to use the principle that single hash temp tables are in scope to any called procs. So, I have an option switch in the proc parameters (default set to off). If this is switched on, the called proc will insert the results into the temp table created in the calling proc. I think in the past I have taken it a step further and put some code in the called proc to check if the single hash table exists in scope, if it does then insert the code, otherwise return the result set. Seems to work well - best way of passing large data sets between procs.
This trick works for me.
You don't have this problem on remote server, because on remote server, the last insert command waits for the result of previous command to execute. It's not the case on same server.
Profit that situation for a workaround.
If you have the right permission to create a Linked Server, do it.
Create the same server as linked server.
in SSMS, log into your server
go to "Server Object
Right Click on "Linked Servers", then "New Linked Server"
on the dialog, give any name of your linked server : eg: THISSERVER
server type is "Other data source"
Provider : Microsoft OLE DB Provider for SQL server
Data source: your IP, it can be also just a dot (.), because it's localhost
Go to the tab "Security" and choose the 3rd one "Be made using the login's current security context"
You can edit the server options (3rd tab) if you want
Press OK, your linked server is created
now your Sql command in the SP1 is
insert into #myTempTable
exec THISSERVER.MY_DATABASE_NAME.MY_SCHEMA.SP2
Believe me, it works even you have dynamic insert in SP2
I found a work around is to convert one of the prods into a table valued function. I realize that is not always possible, and introduces its own limitations. However, I have been able to always find at least one of the procedures a good candidate for this. I like this solution, because it doesn't introduce any "hacks" to the solution.
I encountered this issue when trying to import the results of a Stored Proc into a temp table, and that Stored Proc inserted into a temp table as part of its own operation. The issue being that SQL Server does not allow the same process to write to two different temp tables at the same time.
The accepted OPENROWSET answer works fine, but I needed to avoid using any Dynamic SQL or an external OLE provider in my process, so I went a different route.
One easy workaround I found was to change the temporary table in my stored procedure to a table variable. It works exactly the same as it did with a temp table, but no longer conflicts with my other temp table insert.
Just to head off the comment I know that a few of you are about to write, warning me off Table Variables as performance killers... All I can say to you is that in 2020 it pays dividends not to be afraid of Table Variables. If this was 2008 and my Database was hosted on a server with 16GB RAM and running off 5400RPM HDDs, I might agree with you. But it's 2020 and I have an SSD array as my primary storage and hundreds of gigs of RAM. I could load my entire company's database to a table variable and still have plenty of RAM to spare.
Table Variables are back on the menu!
I recommend to read this entire article. Below is the most relevant section of that article that addresses your question:
Rollback and Error Handling is Difficult
In my articles on Error and Transaction Handling in SQL Server, I suggest that you should always have an error handler like
BEGIN CATCH
IF ##trancount > 0 ROLLBACK TRANSACTION
EXEC error_handler_sp
RETURN 55555
END CATCH
The idea is that even if you do not start a transaction in the procedure, you should always include a ROLLBACK, because if you were not able to fulfil your contract, the transaction is not valid.
Unfortunately, this does not work well with INSERT-EXEC. If the called procedure executes a ROLLBACK statement, this happens:
Msg 3915, Level 16, State 0, Procedure SalesByStore, Line 9 Cannot use the ROLLBACK statement within an INSERT-EXEC statement.
The execution of the stored procedure is aborted. If there is no CATCH handler anywhere, the entire batch is aborted, and the transaction is rolled back. If the INSERT-EXEC is inside TRY-CATCH, that CATCH handler will fire, but the transaction is doomed, that is, you must roll it back. The net effect is that the rollback is achieved as requested, but the original error message that triggered the rollback is lost. That may seem like a small thing, but it makes troubleshooting much more difficult, because when you see this error, all you know is that something went wrong, but you don't know what.
I had the same issue and concern over duplicate code in two or more sprocs. I ended up adding an additional attribute for "mode". This allowed common code to exist inside one sproc and the mode directed flow and result set of the sproc.
what about just store the output to the static table ? Like
-- SubProcedure: subProcedureName
---------------------------------
-- Save the value
DELETE lastValue_subProcedureName
INSERT INTO lastValue_subProcedureName (Value)
SELECT #Value
-- Return the value
SELECT #Value
-- Procedure
--------------------------------------------
-- get last value of subProcedureName
SELECT Value FROM lastValue_subProcedureName
its not ideal, but its so simple and you don't need to rewrite everything.
UPDATE:
the previous solution does not work well with parallel queries (async and multiuser accessing) therefore now Iam using temp tables
-- A local temporary table created in a stored procedure is dropped automatically when the stored procedure is finished.
-- The table can be referenced by any nested stored procedures executed by the stored procedure that created the table.
-- The table cannot be referenced by the process that called the stored procedure that created the table.
IF OBJECT_ID('tempdb..#lastValue_spGetData') IS NULL
CREATE TABLE #lastValue_spGetData (Value INT)
-- trigger stored procedure with special silent parameter
EXEC dbo.spGetData 1 --silent mode parameter
nested spGetData stored procedure content
-- Save the output if temporary table exists.
IF OBJECT_ID('tempdb..#lastValue_spGetData') IS NOT NULL
BEGIN
DELETE #lastValue_spGetData
INSERT INTO #lastValue_spGetData(Value)
SELECT Col1 FROM dbo.Table1
END
-- stored procedure return
IF #silentMode = 0
SELECT Col1 FROM dbo.Table1
Declare an output cursor variable to the inner sp :
#c CURSOR VARYING OUTPUT
Then declare a cursor c to the select you want to return.
Then open the cursor.
Then set the reference:
DECLARE c CURSOR LOCAL FAST_FORWARD READ_ONLY FOR
SELECT ...
OPEN c
SET #c = c
DO NOT close or reallocate.
Now call the inner sp from the outer one supplying a cursor parameter like:
exec sp_abc a,b,c,, #cOUT OUTPUT
Once the inner sp executes, your #cOUT is ready to fetch. Loop and then close and deallocate.
If you are able to use other associated technologies such as C#, I suggest using the built in SQL command with Transaction parameter.
var sqlCommand = new SqlCommand(commandText, null, transaction);
I've created a simple Console App that demonstrates this ability which can be found here:
https://github.com/hecked12/SQL-Transaction-Using-C-Sharp
In short, C# allows you to overcome this limitation where you can inspect the output of each stored procedure and use that output however you like, for example you can feed it to another stored procedure. If the output is ok, you can commit the transaction, otherwise, you can revert the changes using rollback.
On SQL Server 2008 R2, I had a mismatch in table columns that caused the Rollback error. It went away when I fixed my sqlcmd table variable populated by the insert-exec statement to match that returned by the stored proc. It was missing org_code. In a windows cmd file, it loads result of stored procedure and selects it.
set SQLTXT= declare #resets as table (org_id nvarchar(9), org_code char(4), ^
tin(char9), old_strt_dt char(10), strt_dt char(10)); ^
insert #resets exec rsp_reset; ^
select * from #resets;
sqlcmd -U user -P pass -d database -S server -Q "%SQLTXT%" -o "OrgReport.txt"

Resources