Eu tenho uma configuração de servidor vinculada do SQL Server Ent 2012 para PostgreSQL 9.3 e um procedimento armazenado puxando 3 tabelas filtradas dele e armazenando cada uma em uma tabela temporária.
Há uma consulta final no final do procedimento armazenado com muitas junções à esquerda, do banco de dados SQL local e inclui essas 3 tabelas temporárias.
Desativei "Habilitar promoção de transações distribuídas para RPC". Cada 1/6 ou mais tentativas de usar este procedimento armazenado falham, com erro no servidor web como:
Unknown Error Detected
System.Data.SqlClient.SqlException (0x80131904): The operation could not be performed because OLE DB provider "MSDASQL" for linked server "POSTGRESQL" was unable to begin a distributed transaction.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection
No entanto, sem erros no SQL Server.
Definição de servidor vinculado:
EXEC master.dbo.sp_addlinkedserver @server = N'POSTGRESQL', @srvproduct=N'PostgreSQL', @provider=N'MSDASQL', @datasrc=N'PostgreSQL'
EXEC master.dbo.sp_addlinkedsrvlogin @rmtsrvname=N'POSTGRESQL',@useself=N'False',@locallogin=NULL,@rmtuser=N'xxxxxx',@rmtpassword='xxxxx'
EXEC master.dbo.sp_serveroption @server=N'POSTGRESQL', @optname=N'collation compatible', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'POSTGRESQL', @optname=N'data access', @optvalue=N'true'
EXEC master.dbo.sp_serveroption @server=N'POSTGRESQL', @optname=N'dist', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'POSTGRESQL', @optname=N'pub', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'POSTGRESQL', @optname=N'rpc', @optvalue=N'true'
EXEC master.dbo.sp_serveroption @server=N'POSTGRESQL', @optname=N'rpc out', @optvalue=N'true'
EXEC master.dbo.sp_serveroption @server=N'POSTGRESQL', @optname=N'sub', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'POSTGRESQL', @optname=N'connect timeout', @optvalue=N'0'
EXEC master.dbo.sp_serveroption @server=N'POSTGRESQL', @optname=N'collation name', @optvalue=null
EXEC master.dbo.sp_serveroption @server=N'POSTGRESQL', @optname=N'lazy schema validation', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'POSTGRESQL', @optname=N'query timeout', @optvalue=N'0'
EXEC master.dbo.sp_serveroption @server=N'POSTGRESQL', @optname=N'use remote collation', @optvalue=N'true'
EXEC master.dbo.sp_serveroption @server=N'POSTGRESQL', @optname=N'remote proc transaction promotion', @optvalue=N'false'
Procedimento armazenado/consulta (desculpe muito simplificado)
ALTER PROCEDURE [dbo].[SP_Name]
@DateFrom DATETIME,
@DateTo DATETIME
AS
DECLARE @variableB int
SET @variableB= {some criteria}
IF (@variableB < 1000)
BEGIN
-- Passthrough to PG for a filtered table, instead of entire table grabbed via open query
IF OBJECT_ID('tempdb.dbo.#temp_table1', 'U') IS NOT NULL
DROP TABLE #temp_table1;
create table #temp_table1 (column1 int, column2 varchar(60))
DECLARE @TSQL varchar(max)
SET @TSQL = 'select c1, c2 from OpenQuery([POSTGRESQL],''
select c1, c2 from table1 t where
t.date>=''''' +CONVERT(VARCHAR(15),@DateFrom,102)+ '''''' + 't.date <=''''' +CONVERT(VARCHAR(15),@DateTo,102) + '''''' +'order by c1'')'
insert into #temp_table1
EXEC (@TSQL)
create clustered index temp_index1 on #temp_table1
(column1 asc)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
-- 2nd table passthrough to PG for a filtered table, instead of entire table grabbed via open query
IF OBJECT_ID('tempdb.dbo.#temp_table2', 'U') IS NOT NULL
DROP TABLE temp_table2;
create table #temp_table2 (column1 int, column2 varchar(60), column3 int)
SET @TSQL = 'select a, b, c from OpenQuery([POSTGRESQL],''
select a, b, c from table2 t where
t.date>=''''' +CONVERT(VARCHAR(15),@DateFrom,102)+ '''''' + 't.date <=''''' +CONVERT(VARCHAR(15),@DateTo,102) + '''''' +'order by a'')'
insert into #temp_table2
EXEC (@TSQL)
-- 3rd table passthrough to PG for a filtered table, instead of entire table grabbed via open query
IF OBJECT_ID('tempdb.dbo.#temp_table3', 'U') IS NOT NULL
DROP TABLE temp_table3;
create table #temp_table3 (column1 int, column2 varchar(60))
SET @TSQL = 'select a1, a2, a3 from OpenQuery([POSTGRESQL],''
select a1, a2, a3
from
table3 t
where
t.date>=''''' +CONVERT(VARCHAR(15),@DateFrom,102)+ '''''' + 't.date <=''''' +CONVERT(VARCHAR(15),@DateTo,102) + ''''''
{plus other filtering criteria} +'order by a1'')'
insert into #temp_table2
EXEC (@TSQL)
IF (@variableB < 1000)
BEGIN
select {many columns}
from {various local tables with left joins}
left join #temp_table1 on {conditions}
left join #temp_table2 on {conditions}
left join #temp_table3 on {conditions}
END
Propriedades DTC locais
- O que eu acho que está acontecendo é que as 3 consultas abertas em tabelas temporárias estão sendo distribuídas e, de alguma forma, isso simplesmente não funciona. Confesso que não entendo muito bem isso - está além da minha cabeça. Também sei que ativar "Ativar promoção de transações distribuídas para RPC" faz com que as falhas ocorram com mais frequência.
- Isso novamente funciona 5/6 vezes e falha cerca de 1/6 vezes. Portanto, não tenho certeza do que está acontecendo.
- Esta é uma transação somente leitura no PostgreSQL - não sei por que a transação distribuída está sendo invocada.
Editar , vejo esta pergunta aqui e pensando que não deveria estar tendo esse problema.
Editar 2 Posso ter encontrado uma pergunta duplicada aqui .
Envolver as 3 inserções de tabela temporária em uma transação com ISOLATION LEVEL READ UNCOMMITTED parece ter interrompido a promoção para uma transação distribuída e resolvido meu problema intermitente.
Esta foi uma pergunta duplicada e, finalmente, me deu a solução. Talvez o PostgreSQL ODBC não suporte transações distribuídas - e desativá-las nas propriedades do servidor vinculado não é suficiente ao tentar usar tabelas temporárias ou tentar conectar-se a um banco de dados remoto 3 vezes - devido à funcionalidade ACID.