Tenho uma tabela que possui uma coluna NVARCHAR que contém dados que não podem ser convertidos para BIGINT. Estou bem ciente disso e filtrei usando ISNUMERIC(BB.NVARCHARCOL) = 1
. Apesar disso, ainda recebo um erro ao tentar consultar os dados informando queError converting data type nvarchar to bigint.
O seguinte funciona bem (sem erros relatados pelo SQL):
SELECT *
FROM myNormalTable AA INNER JOIN myBadTable BB ON BB.NVARCHARCOL = AA.MYBIGINTCOL
WHERE ISNUMERIC(BB.NVARCHARCOL) = 1
O seguinte lança o erro:
SELECT *
FROM (
SELECT *
FROM myNormalTable AA INNER JOIN myBadTable BB ON BB.NVARCHARCOL = AA.MYBIGINTCOL
WHERE ISNUMERIC(BB.NVARCHARCOL) = 1
) ZZ
WHERE ZZ.MYBIGINTCOL = 1234
Essa variação também lança o erro:
SELECT *
FROM (
SELECT *
FROM myNormalTable AA INNER JOIN
(SELECT CAST(NVARCHARCOL AS BIGINT) NVARCHARCOL FROM myBadTable WHERE ISNUMERIC(NVARCHARCOL) = 1) BB
ON BB.NVARCHARCOL = AA.MYBIGINTCOL
) ZZ
WHERE ZZ.MYBIGINTCOL = 1234
Tenha em mente que isso não dá erro e retorna todos os registros com sucesso...
SELECT CAST(NVARCHARCOL AS BIGINT) NVARCHARCOL FROM myBadTable WHERE ISNUMERIC(NVARCHARCOL) = 1
Consegui encontrar uma solução, que era converter meu BIGINT para NVARCHAR na subconsulta:
SELECT *
FROM (
SELECT *
FROM myNormalTable AA INNER JOIN myBadTable BB ON BB.NVARCHARCOL = CAST(AA.MYBIGINTCOL AS NVARCHAR)
WHERE ISNUMERIC(BB.NVARCHARCOL) = 1
) ZZ
WHERE ZZ.MYBIGINTCOL = 1234
Se eu inserir os registros em uma tabela temporária:
SELECT CAST(NVARCHARCOL AS BIGINT) NVARCHARCOL INTO #TEMP FROM myBadTable WHERE ISNUMERIC(NVARCHARCOL) = 1
Eu posso usar essa tabela temporária em uma subconsulta com sucesso:
SELECT *
FROM (
SELECT *
FROM myNormalTable AA INNER JOIN #TEMP BB ON BB.NVARCHARCOL = AA.MYBIGINTCOL
WHERE ISNUMERIC(BB.NVARCHARCOL) = 1
) ZZ
WHERE ZZ.MYBIGINTCOL = 1234
O que anda acontecendo no mundo? Parece que o SQL se recusa a usar a subconsulta para obter um conjunto de resultados menor antes de executar a consulta externa, ele quer usar a tabela inteira, independentemente do que eu fizer. SQL Server 2012 Developer Edition
Use
try_cast()
em vez disso.No Sql Server 2012 e superior: cada um deles retornará
null
quando a conversão falhar em vez de um erro.try_convert(datatype,val)
try_cast(val as datatype)
try_parse(val as datatype [using culture])
O SqlZim já te deu um bom método para evitar o erro na resposta dele . No entanto, na pergunta e nos comentários, você parece curioso para saber por que uma consulta gera um erro e a outra não. Consegui reproduzir seu problema:
Esta consulta funciona bem:
Esta consulta gera um erro:
O otimizador de consulta do SQL Server pode reordenar os elementos de uma consulta como achar melhor para tentar encontrar um plano de consulta com um custo estimado suficientemente bom, desde que as alterações não afetem os resultados finais da consulta. Para ilustrar o conceito, vamos percorrer uma possível maneira pela qual a segunda consulta pode ser refatorada. Para ser claro, este não é o processo passo a passo real pelo qual o otimizador de consulta passa para este exemplo. Comece com a consulta:
Empurre o predicado:
A tabela derivada não é mais necessária, então livre-se dela:
Sabemos disso
BI.ID = NV.ID_NV
para que possamos aplicar o filtro tambémZ.ID
aNV.ID_NV
:A junção não precisa mais ser implementada
INNER JOIN
porque estamos filtrando para um único valor para ambas as colunas de junção. Podemos reescrever comoCROSS JOIN
:Se observarmos o plano de consulta para a segunda consulta, podemos dizer que o resultado final é muito semelhante à consulta transformada final:
Aqui está o texto do predicado de filtro para referência:
Se o SQL Server avaliar a
CONVERT_IMPLICIT
parte do predicado antes daisnumeric
parte, obteremos um erro.Como regra geral, evite confiar na ordem implícita das operações ao escrever consultas SQL. Você pode ter uma consulta que funciona bem hoje, mas começa a gerar erros se os dados forem adicionados à tabela ou se um plano de consulta diferente for escolhido. Existem, é claro, exceções (mais ou menos). Na prática, você normalmente verá as diferentes partes de uma
CASE
declaração para avaliar na ordem em que as escreveu, mas mesmo assim é possível obter erros que você não esperava . Você também pode adicionar um supérfluoTOP
a partes de sua consulta para incentivar uma determinada ordem de operações. Considere a seguinte consulta:Você e eu sabemos que
TOP
não alterará os resultados da consulta, no entanto, não há garantia para o otimizador de que a tabela derivada não retornará mais de 9223372036854775807 linhas, portanto, ele deve avaliar o arquivoTOP
. Tecnicamente, nessa consulta, solicitamos as primeiras 9223372036854775807 linhas e, em seguida, queremos filtrar as linhas com um valorID
diferente de 500. Empurrar oID = 500
predicado para a tabela derivada pode alterar os resultados, de modo que o SQL Server não fará isso. Neste exemplo, a consulta é executada sem erro e a filtragem é feita no final: