Estou consultando dados de um servidor vinculado por meio de uma exibição no servidor de origem. A visualização deve incluir algumas colunas padronizadas, como Created
, Modified
e Deleted
, mas, neste caso, a tabela no servidor de origem não possui nenhuma informação adequada. As colunas são, portanto, explicitamente convertidas para seus respectivos tipos. Atualizei a visualização, alterando uma coluna de
NULL AS Modified
para
CAST(NULL as DateTime) as Modified
No entanto, após realizar esta atualização, a visualização está acionando a seguinte mensagem de erro:
Msg 7341, Nível 16, Estado 2, Linha 3 Não é possível obter o valor da linha atual da coluna "(expressão gerada pelo usuário).Expr1002" do provedor OLE DB "SQLNCLI11" para o servidor vinculado "".
Fizemos essa mudança de "conversão explícita" geralmente no servidor de origem sem preocupações e suspeito que o problema possa estar relacionado à versão dos servidores envolvidos. Não precisamos realmente aplicar esse elenco, mas parece mais limpo. No momento, estou apenas curioso para saber por que isso está acontecendo.
Versão do servidor (origem):
Microsoft SQL Server 2012 - 11.0.5058.0 (X64) 14 de maio de 2014 18:34:29 Copyright (c) Microsoft Corporation Enterprise Edition (64 bits) no Windows NT 6.1 (Build 7601: Service Pack 1) (Hypervisor)
Versão do servidor (vinculado):
Microsoft SQL Server 2008 R2 (SP1) - 10.50.2500.0 (X64) 17 de junho de 2011 00:54:03 Copyright (c) Microsoft Corporation Enterprise Edition (64 bits) no Windows NT 6.1 (Build 7601: Service Pack 1) (Hypervisor )
Editar
Acabei de perceber que cometi um erro ao não postar todas as colunas em questão e devo me desculpar por deixar de fora um detalhe importante. Não sei como não percebi isso antes. A questão ainda permanece, no entanto.
A conversão errada não acontece com a conversão para DateTime, mas com uma coluna sendo convertida para UniqueIdentifier.
Este é o culpado:
CAST(NULL AS UniqueIdentifier) AS [GUID]
UniqueIdentifiers são suportados no SQL Server 2008 R2 e, conforme mencionado nos comentários, a consulta executada pela exibição é executada corretamente no servidor vinculado.
Então, consegui reproduzir o erro ao perceber que
CAST
estava sendo feito localmente, não na instância remota. Eu já havia recomendado mudar para o SP3 na esperança de corrigir isso (em parte devido a não ser capaz de reproduzir o erro no SP3 e em parte por ser uma boa ideia independentemente). No entanto, agora que posso reproduzir o erro, está claro que mudar para o SP3, embora provavelmente seja uma boa ideia, não vai consertar isso. E também reproduzi o erro no SQL Server 2008 R2 RTM e 2014 SP1 (usando um servidor vinculado local "loop-back" nos três casos).Parece que esse problema tem a ver com o local em que a consulta está sendo executada ou, pelo menos, onde parte (s) dela está sendo executada. Digo isso porque consegui fazer a
CAST
operação funcionar, mas apenas incluindo uma referência a um objeto de banco de dados local:Isso realmente funciona. Mas o seguinte obtém o erro original:
Suponho que, quando não há referências locais, toda a consulta é enviada para o sistema remoto para ser executada e, por algum motivo,
NULL
s não pode ser convertida emUNIQUEIDENTIFIER
, ou talvezNULL
esteja sendo traduzida incorretamente pelo driver OLE DB.Com base nos testes que fiz, isso parece ser um bug, mas não tenho certeza se o bug está no SQL Server ou no driver SQL Server Native Client/OLEDB. No entanto, o erro de conversão ocorre dentro do driver OLEDB e, portanto, não é necessariamente um problema de conversão de
INT
paraUNIQUEIDENTIFIER
(uma conversão que não é permitida no SQL Server), pois o driver não está usando o SQL Server para fazer conversões (o SQL Server também não permitem a conversãoINT
paraDATE
, mas o driver OLEDB lida com isso com sucesso, conforme mostrado em um dos testes).Fiz três testes. Para os dois que obtiveram sucesso, observei os planos de execução XML que mostram a consulta que está sendo executada remotamente. Para todos os três, capturei quaisquer exceções ou eventos OLEDB por meio do SQL Profiler:
Eventos:
Filtros de coluna:
OS TESTES
Teste 1
CAST(NULL AS UNIQUEIDENTIFIER)
isso funcionaParte relevante do plano de execução XML:
Teste 2
CAST(NULL AS UNIQUEIDENTIFIER)
que falha(nota: mantive a subconsulta lá, comentada, para que fosse uma diferença a menos quando comparasse os arquivos de rastreamento XML)
Teste 3
CAST(NULL AS DATE)
isso funciona(nota: mantive a subconsulta lá, comentada, para que fosse uma diferença a menos quando comparasse os arquivos de rastreamento XML)
Parte relevante do plano de execução XML:
Se você observar o Teste nº 3, verá que está fazendo um
SELECT TOP (2) NULL
no sistema "remoto". O rastreamento do SQL Profiler mostra que o tipo de dados desse campo remoto é de fatoINT
. O rastreamento também mostra que o campo no lado do cliente (ou seja, de onde estou executando a consulta) éDATE
, conforme o esperado. A conversão deINT
paraDATE
, algo que causará um erro no SQL Server, funciona perfeitamente no driver OLEDB. O valor remoto éNULL
, por isso é retornado diretamente, portanto, o<ColumnReference Column="Expr1002" />
.Se você observar o Teste nº 1, ele está fazendo um
SELECT 1
no sistema "remoto". O rastreamento do SQL Profiler mostra que o tipo de dados desse campo remoto é de fatoINT
. O rastreamento também mostra que o campo no lado do cliente (ou seja, de onde estou executando a consulta) éGUID
, conforme o esperado. A conversão deINT
paraGUID
(lembre-se, isso é feito dentro do driver e o OLEDB chama de "GUID"), algo que causará um erro no SQL Server, funciona muito bem no driver OLEDB. O valor remoto nãoNULL
é , então ele é substituído por um literalNULL
, daí o<Const ConstValue="NULL" />
.O teste nº 2 falha, portanto, não há plano de execução. No entanto, ele consulta o sistema "remoto" com sucesso, mas simplesmente não pode passar de volta o conjunto de resultados. A consulta que o SQL Profiler capturou é:
Essa é exatamente a mesma consulta que está sendo feita no Teste nº 1, mas aqui está falhando. Existem outras pequenas diferenças, mas não consigo interpretar completamente a comunicação OLEDB. No entanto, o campo remoto ainda está sendo exibido como
INT
(wType = 3 = adInteger / inteiro com sinal de quatro bytes / DBTYPE_I4) enquanto o campo "client" ainda está sendo exibido comoGUID
(wType = 72 = adGUID / identificador exclusivo global / DBTYPE_GUID). A documentação do OLE DB não ajuda muito, pois GUID Data Type Conversions , DBDATE Data Type Conversions e I4 Data Type Conversions mostram que a conversão de I4 para GUID ou DBDATE não é suportada, mas aDATE
consulta funciona.Os arquivos Trace XML para os três testes estão localizados em PasteBin. Se você quiser ver os detalhes de onde cada teste difere dos outros, você pode salvá-los localmente e depois fazer um "diff" neles. Os arquivos são:
ERGO?
O que fazer sobre isso? Provavelmente apenas a solução alternativa que observei na seção superior, visto que o SQL Native Client --
SQLNCLI11
-- está obsoleto a partir do SQL Server 2012. A maioria das páginas do MSDN sobre o tópico do SQL Server Native Client tem o seguinte aviso no topo:Para mais informações, consulte:
ODBC??
Eu configurei um servidor vinculado ODBC via:
E então tentou:
e recebi o seguinte erro:
PS
No que se refere ao transporte de GUIDs entre servidores remotos e locais, os valores não NULL são manipulados por meio de uma sintaxe especial. Observei as seguintes informações do evento OLE DB no rastreamento do SQL Profiler quando executei
CAST(0x00 AS UNIQUEIDENTIFIER)
:PPS
Eu também testei via
OPENQUERY
com a seguinte consulta:e teve sucesso, mesmo sem a referência de objeto local. O arquivo XML de rastreamento do SQL Profiler foi postado no PasteBin em:
NullGuidSuccessOPENQUERY.xml
O plano de execução XML mostra isso usando uma
NULL
constante, igual ao Teste #1.Existe apenas uma solução feia - use alguma constante de data como
'1900-01-01'
em vez denull
.Após a importação, você pode atualizar as colunas de
1900-01-01
volta para nulo.Este é um tipo de recurso/bug do SQL 2012 conforme aqui .
Editar: substituído
1900-00-00
por uma data válida1900-01-01
de acordo com o comentário @a_horse_with_no_name abaixo.O problema está relacionado a conversões de tipo de dados (conforme abordado nos comentários).
Considere o seguinte:
Observe que o
NullColumn
é do tipoint
. O SQL Server não gosta de converterint
valores em arquivosuniqueidentifier
. EstaSELECT
instrução falhará em uma conversão de tipo de dados:Embora esse valor específico (NULL) possa ser convertido em um GUID, o SQL Server lança o erro com base na conversão do tipo de dados, antes mesmo de examinar os valores específicos. Em vez disso, você precisará executar uma
CAST
operação de várias etapas para alterar o implícitoint
para um tipo de dados que pode ser convertido de forma limpa emuniqueidentifer
--o que significa converter primeiro paravarchar
depois parauniqueidentifier
:O OP pode decidir se esta é uma resposta apropriada.
Não tenho nenhuma prova 'absoluta', mas 'suspeito' que o problema decorre do fato de que um UniqueIdentifer é dependente do servidor e talvez o provedor esteja tendo dificuldade em descobrir de qual servidor (local ou remoto) obter esse identificador exclusivo, mesmo que seja nulo. É por isso que você provavelmente pode lançar qualquer outro tipo de dados com sucesso neste cenário, mas não o identificador único. Os tipos de dados que são dependentes do 'servidor', como UNIQUEIDENTIFIERS e DATETIMEOFFSET, fornecerão o erro que você está encontrando.
Usar OPENQUERY em vez de nome de 4 partes funciona.
Workaround: The accepted answer seems to indicate that the conversion needs to happen locally because the OLEDB driver doesn't support it.
So I think a simple workaround (at least in the case of my query which is selecting a null
uniqueidentifier
in the base case of a recursive CTE) is to declare a null variable: