OLe DB provider "SQLNCLI" for linked server was unable to begin a distributed transaction - sql

I am trying to call a stored procedure in SQL Server 2008 and store the fetched data into a local temp table.
When I try to run it, I receive the following error:
The operation could not be completed because OLe DB provider "SQLNCLI"
for linked server was unable to begin a distributed transaction
My code is as follows :
create table #temp(
col1 as int,
col2 as varchar(50)
)
insert into #temp
exec [192.168.0.9].[db1].[dbo].[tablename] #usr_id=3

You can prevent using distributed transactions for the linked server by setting server option 'remote proc transaction promotion' to 'false':
EXEC sp_serveroption 'servername', 'remote proc transaction promotion', 'false'
Here's the same issue

linked server was unable to begin a distributed transaction errors are because of issues in MSDTC (MS distributed transaction coordiinator). Issues can arise from a number of problems. Including MSDTC not running, being blocked by firewall, and others.
If you require transactions, you have to debug the problem pretty much yourself since it is environmental. If you can rewrite to avoid requiring a transaction your life will be simpler. Just to make sure it is a MSDTC issue, write a simple query that will not depend on MSDTC. e.g.
create table #temp( col1 as int, col2 as varchar(50) )
insert into #temp
select col1, col2 from [192.168.0.9].[db1].[dbo].[tablename] where usr_id=3
If this works, its definitely MSDTC (and perhaps an avoidable problem)
-- Added this. Spent a little while looking for MSDTC debugging. http://www.sqlwebpedia.com/content/msdtc-troubleshooting was pretty good as was http://www.mssqltips.com/sqlservertip/2083/troubleshooting-sql-server-distributed-transactions-part-1-of-2/ togehter they cover just about every thing I can recall having to debug for MSDTC problems (and some others too).

Related

Stored procedure with multiple 'INSERT INTO Table_Variable EXECUTE stored_procedure' statements [duplicate]

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"

creating a trigger that updates row in a linked mysql server

i created a linked Mysql server on SQL server 2008 r2. i'm trying to create a trigger on sql table that automatically updates a field in the linked server table, i have a table called "QFORCHOICE" in sql that has fields "Prodcode,prodname and avqty" and a table "que_for_choie" in mysql that has fields "procode,proname and avqty"
i want the trigger to update the value of "procode" in the linked server if the value of "prodcode" in sql server changes. this is what i have so far but it has errors,
create trigger [QFORCHOICE]
ON dbo.QFORCHOICE
FOR INSERT
AS
DECLARE #prodcode numeric(18,0)
DECLARE #prodname varchar(50)
DECLARE #avqty numeric(18,0)
BEGIN
SELECT
#procode = procode,
#proname = proname,
#avqty = avqty
FROM inserted
update [LINKED_MYSQL].[que_for_choice]
SET prodname=#prodname,avqty=#avqty
WHERE prodcode = #prodcode
end
can anybody please help.
thanks in advance
1- From within a trigger, you shouldn't attempt to access anything external to the current database. It will severely slow down any insert activity, and if there are any networking issues or the remote server is down for any reason, you'll then cause the original transaction to roll back. This is rarely the right thing to do
2- you're making the reliability of your system dependent on the reliability of two servers rather than one (say they both have 99% reliability - your system that ties them together with a trigger now has 98% overall reliability).

Can I sacrifice consistency to allow concurrent 'insert or update' and read access to a single table with SQL Server?

I have a table that contains two columns: a resource key, and (very roughly) when it was last accessed.
I have a number of servers that are periodically dumping data about resource accesses to the table. They should either update the access time for a resource key if it already exists, or insert it if it doesn't.
Another server will very rarely generate a report from this table.
I don't require this table to be consistent. I'm okay with the reporting server reading the table in the middle of a dump. If two writing servers try to update the same row, I don't care which gets it's data in.
There are two major questions:
Is what I'm looking for even possible with SQL Server?
If it is possible, I'm potentially going to have multiple servers racing on their 'insert or update' and resulting in primary key constraint violations. Is there any way to resolve this problem?
I'm okay with the reporting server reading the table in the middle of a dump.
Look into the READ COMMITTED SNAPSHOT ISOLATION option. This was introduced in SQL Server 2005 and appears to be available across all editions. It is typically better than using the WITH (NOLOCK) table hint. For more info, check out:
Snapshot Isolation in SQL Server
Understanding Row Versioning-Based Isolation Levels
If two writing servers try to update the same row, I don't care which gets it's data in.
It is not possible for two operations to write the same row at the same time. One will wait.
Regarding two trying to INSERT the same value at the same time, since you don't care which one "wins", just trap and discard the error ;-).
Maybe something along the lines of:
BEGIN TRY
UPDATE tbl
SET tbl.AccessTime = GETDATE()
FROM SchemaName.TableName tbl
WHERE tbl.ResourceKey = #ResourceKey;
IF (##ROWCOUNT = 0)
BEGIN
INSERT INTO SchemaName.TableName (ResourceKey, AccessTime)
VALUES (#ResourceKey, GETDATE());
END;
END TRY
BEGIN CATCH
IF (ERROR_NUMBER() <> 2627) -- 2627 = Violation of PRIMARY KEY constraint
BEGIN
;THROW;
END;
END CATCH;
If you are on SQL Server 2014 (or newer, whenever that happens), then you can look into using:
the WITH DELAYED_DURABILITY = ON option for COMMIT TRAN. Look here for more info: Control Transaction Durability
In-Memory OLTP (64 bit, Enterprise Edition only)

Unable to execute remote stored procs from TST (SQL automation tool)

I am currently investigating TST (SQL automation test tool) which will faciliate database regression testing. I've been able to create a basic test case, which is below:
/*
Verification of stored proc - FOH_Status
Returns online availabity status for specified date
*/
/****** Object: StoredProcedure [dbo].[SQLTest_Status] Script Date: 11/23/2011 16:56:00 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE dbo.SQLTest_Status
AS
BEGIN
---CREATE A TEMP TABLE
CREATE TABLE #ControlValue (
ControlMode TINYINT
)
INSERT INTO #ControlValue EXEC dbo.Status 17254, 3 ,'2011-11-20 00:00:00'
DECLARE #CONTROL TINYINT
SET #CONTROL = (SELECT TOP 1* FROM #ControlValue)
EXEC TST.Assert.Equals 'ControlMode enabled for specified date', 1, #CONTRO
END
GO
I then proceed to link to our QA SQL server, which was successful. Then modified the stored procedure to call a stored procedure on the QA SQL server:
INSERT INTO #ControlValue EXEC [X.X.X.X].databasename.dbo.Status 17254, 3 ,'2011-11-20 00:00:00'
Upon running the test from TST runner will fail with DTC transaction fail message:
OLE DB provider "SQLNCLI10" for linked server "X.X.X.X" returned message "The transaction manager has disabled its support for remote/network transactions.".
Error: 7391, The operation could not be performed because OLE DB provider "SQLNCLI10" for linked server "X.X.X.X" was unable to begin a distributed transaction.
I've already confirmed that MS DTC will allow remote connections, inbound and outbound calls on the QA SQL server.
In addition, run the stored procedure in query window and returns the correct result set
EXEC [X.X.X.X].databasename.dbo.Status 17254, 3 ,'2011-11-20 00:00:00'
Any ideas as to how rectify this issue?
Should I be modifying the querying to include transaction, since the returned error includes the 'was unable to begin a distributed transaction'
Have you tried every step described in Distributed Transaction Issue for Linked Server in SQL Server 2008?
I'm interested that you're using a SQL Server unit testing framework. Here at Red Gate we're doing some research into the use of such tools and we hope to release a graphical unit test runner in SSMS fairly soon. See www.sql-test.com . This tool builds on the tSQLt open source framework. Is this one that you've come across?
Might you be interested in helping us by giving us feedback on your unit testing requirements?

Is it possible to create a temp table on a linked server?

I'm doing some fairly complex queries against a remote linked server, and it would be useful to be able to store some information in temp tables and then perform joins against it - all with the remote data. Creating the temp tables locally and joining against them over the wire is prohibitively slow.
Is it possible to force the temp table to be created on the remote server? Assume I don't have sufficient privileges to create my own real (permanent) tables.
This works from SQL 2005 SP3 linked to SQL 2005 SP3 in my environment. However if you inspect the tempdb you will find that the table is actually on the local instance and not the remote instance. I have seen this as a resolution on other forums and wanted to steer you away from this.
create table SecondServer.#doll
(
name varchar(128)
)
GO
insert SecondServer.#Doll
select name from sys.objects where type = 'u'
select * from SecondServer.#Doll
I am 2 years late to the party but you can accomplish this using sp_executeSQL and feeding it a dynamic query to create the table remotely.
Exec RemoteServer.RemoteDatabase.RemoteSchema.SP_ExecuteSQL N'Create Table here'
This will execute the temp table creation at the remote location..
It's not possible to directly create temporary tables on a linked remote server. In fact you can't use any DDL against a linked server.
For more info on the guidelines and limitations of using linked servers see:
Guidelines for Using Distributed Queries (SQL 2008 Books Online)
One work around (and off the top of my head, and this would only work if you had permissions on the remote server) you could:
on the remote server have a stored procedure that would create a persistent table, with a name based on an IN parameter
the remote stored procedure would run a query then insert the results into this table
You then query locally against that table perform any joins to any local tables required
Call another stored procedure on the remote server to drop the remote table when you're done
Not ideal, but a possible work around.
Yes you can but it only lasts for the duration of the connection.
You need to use the EXECUTE AT syntax;
EXECUTE('SELECT * INTO ##example FROM sys.objects; WAITFOR DELAY ''00:01:00''') AT [SERVER2]
On SERVER2 the following will work (for 1 minute);
SELECT * FROM ##example
but it will not work on the local server.
Incidently if you open a transaction on the second server that uses ##example the object remains until the transaction is closed. It also stops the creating statement on the first server from completing. i.e. on server2 run and the transaction on server1 will continue indefinately.
BEGIN TRAN
SELECT * FROM ##example WITH (TABLOCKX)
This is more accademic than of practical use!
If memory is not much of an issue, you could also use table variables as an alternative to temporary tables. This worked for me when running a stored procedure with need of temporary data storage against a Linked Server.
More info: eg this comparison of table variables and temporary tables, including drawbacks of using table variables.