Hemos encontrado un problema interesante con SQL Server. Considere el siguiente ejemplo de repro:
CREATE TABLE #test (s_guid uniqueidentifier PRIMARY KEY);
INSERT INTO #test (s_guid) VALUES ('7E28EFF8-A80A-45E4-BFE0-C13989D69618');
SELECT s_guid FROM #test
WHERE s_guid = '7E28EFF8-A80A-45E4-BFE0-C13989D69618'
AND s_guid <> NEWID();
DROP TABLE #test;
Por favor, olvide por un momento que la s_guid <> NEWID()
condición parece completamente inútil, este es solo un ejemplo mínimo de repro. Dado que la probabilidad de NEWID()
igualar un valor constante dado es extremadamente pequeña, debe evaluar VERDADERO cada vez.
Pero no lo hace. La ejecución de esta consulta generalmente devuelve 1 fila, pero a veces (con bastante frecuencia, más de 1 de cada 10 veces) devuelve 0 filas. Lo he reproducido con SQL Server 2008 en mi sistema, y puede reproducirlo en línea con el violín vinculado anteriormente (SQL Server 2014).
Al observar el plan de ejecución, se revela que el analizador de consultas aparentemente divide la condición en s_guid < NEWID() OR s_guid > NEWID()
:
... lo que explica completamente por qué falla a veces (si la primera ID generada es más pequeña y la segunda más grande que la ID dada).
¿Se permite que SQL Server evalúe A <> B
como A < B OR A > B
, incluso si una de las expresiones no es determinista? En caso afirmativo, ¿dónde está documentado? ¿O encontramos un error?
Curiosamente, AND NOT (s_guid = NEWID())
produce el mismo plan de ejecución (y el mismo resultado aleatorio).
Encontramos este problema cuando un desarrollador quería excluir opcionalmente una fila en particular y usaba:
s_guid <> ISNULL(@someParameter, NEWID())
como un "atajo" para:
(@someParameter IS NULL OR s_guid <> @someParameter)
Estoy buscando documentación y / o confirmación de un error. El código no es tan relevante, por lo que no se requieren soluciones alternativas.
fuente
Respuestas:
Este es un punto algo controvertido, y la respuesta es un "sí" calificado.
La mejor discusión que conozco se dio en respuesta al informe de error Connect de Itzik Ben-Gan Error con NEWID y Expresiones de tabla , que se cerró porque no se solucionará. Connect se ha retirado desde entonces, por lo que el enlace está a un archivo web. Lamentablemente, la desaparición de Connect perdió mucho material útil (o lo hizo más difícil de encontrar). De todos modos, las citas más útiles de Jim Hogg de Microsoft son:
Un ejemplo del cambio de comportamiento a este respecto a lo largo del tiempo es que NULLIF funciona incorrectamente con funciones no deterministas como RAND () . También hay otros casos similares que se usan, por ejemplo,
COALESCE
con una subconsulta que puede producir resultados inesperados y que también se están abordando gradualmente.Jim continúa:
Esto es una consecuencia de la normalización, que ocurre muy temprano durante la compilación de consultas. Ambas expresiones se compilan exactamente en la misma forma normalizada, por lo que se produce el mismo plan de ejecución.
fuente
Esto está documentado (más o menos) aquí:
Funciones definidas por el usuario
Este no es el único formulario de consulta donde el plan de consulta ejecutará NEWID () varias veces y cambiará el resultado. Esto es confuso, pero en realidad es crítico para que NEWID () sea útil para la generación de claves y la ordenación aleatoria.
Lo más confuso es que no todas las funciones no deterministas se comportan realmente de esta manera. Por ejemplo, RAND () y GETDATE () se ejecutarán solo una vez por consulta.
fuente
=
,<
y>
puede ser evaluado eficientemente contra un BTree.Para lo que vale, si observa este antiguo documento estándar SQL 92 , los requisitos sobre desigualdad se describen en la sección "
8.2 <comparison predicate>
" de la siguiente manera:Nota: Incluí 7b y 7h para completar, ya que hablan de
<>
comparación: no creo que la comparación de constructores de valores de fila con múltiples valores se implemente en T-SQL, a menos que solo esté malentendiendo enormemente lo que esto dice, lo cual es bastante posibleEste es un montón de basura confusa. Pero si quieres seguir buceando en el basurero ...
Yo creo que 1.ii es el elemento que se aplica en este escenario, ya que estamos comparando los valores de "elementos de valor de fila de constructores."
Básicamente dice que
X <> Y
es cierto si los valores representados por X e Y no son iguales. Dado queX < Y OR X > Y
es una reescritura lógicamente equivalente de ese predicado, es totalmente genial que el optimizador lo use.El estándar no impone restricciones en esta definición relacionadas con la determinismo (o lo que sea, lo obtienes) de los elementos del constructor de valores de fila a cada lado del
<>
operador de comparación. Es responsabilidad del código de usuario lidiar con el hecho de que una expresión de valor en un lado podría no ser determinista.fuente