¿Cuál es el resultado correcto para esta consulta?

20

Encontré este rompecabezas en los comentarios aquí

CREATE TABLE r (b INT);

SELECT 1 FROM r HAVING 1=1;

SQL Server y PostgreSQL devuelven 1 fila.

MySQL y Oracle devuelven cero filas.

¿Cual es correcta? ¿O son ambos igualmente válidos?

Martin Smith
fuente
Bonito rompecabezas Creo que lo correcto es devolver 1 fila. Sin embargo, SQL-Server se contradice porque SELECT COUNT(*) FROM r;devuelve 1 fila (con 0), mientras que SELECT COUNT(*) FROM r GROUP BY ();no devuelve ninguna fila.
ypercubeᵀᴹ
1
¿Quieren más? SELECT 1 WHERE 1=0 HAVING 1=1;. SQL Server y PostgreSQL aún devuelven una fila. Oracle quiere DE DUAL y no devuelve filas. MySQL no compila ni con FROM DUAL ni sin él .
Andriy M
1
@AndriyM Por alguna razón desconocida "dual" y "HAVING" no funcionan bien en MySQL. (Buen hallazgo). Pero el equivalente funciona: SELECT 1 AS t FROM (SELECT 1) tmp WHERE 1=0 HAVING 1=1; 1-row-no-dual y devuelve 0 filas.
ypercubeᵀᴹ
1
@SQLKiwi: ¿qué pasa con este pasaje de la especificación? "Si TE no contiene inmediatamente a <group by clause>, entonces “GROUP BY ()”está implícito". ¿No deberían ambas consultas devolver los mismos resultados entonces?
Martin Smith
1
Pero no estoy de acuerdo con esto (Oracle ejecuta las consultas de manera HAVINGdiferente): SQl-fiddle 2: HAVING hace las cosas diferentes
ypercubeᵀᴹ

Respuestas:

17

Por el estándar:

SELECT 1 FROM r HAVING 1=1

medio

SELECT 1 FROM r GROUP BY () HAVING 1=1

Cita ISO / IEC 9075-2: 2011 7.10 Regla de sintaxis 1 (Parte de la definición de la cláusula HAVING):

Deja HCser el <having clause>. Deja que TEsea ​​lo <table expression>que contiene de inmediato HC. Si TEno contiene inmediatamente a <group by clause>, entonces " GROUP BY ()" está implícito. Sea Tel descriptor de la tabla definida por el <group by clause> GBCcontenido inmediato TEy Rsea ​​el resultado de GBC.

Ok, eso está bastante claro.


Afirmación: 1=1es verdadera condición de búsqueda. No proporcionaré ninguna cita para esto.


Ahora

SELECT 1 FROM r GROUP BY () HAVING 1=1

es equivalente a

SELECT 1 FROM r GROUP BY ()

Cita ISO / IEC 9075-2: 2011 7.10 Regla general 1:

El <search condition>se evalúa para cada grupo de R. El resultado de la <having clause>es una tabla agrupada de aquellos grupos de R para los cuales el resultado de la <search condition>es True.

Lógica: dado que la condición de búsqueda siempre es verdadera, el resultado es R, que es el resultado del grupo por expresión.


Lo siguiente es un extracto de las Reglas Generales de 7.9 (la definición del GRUPO POR CLÁUSULA)

1) Si no <where clause>se especifica no, entonces Tsea ​​el resultado de lo anterior <from clause>; de lo contrario, Tsea ​​el resultado de lo anterior <where clause>.

2) Caso:

a) Si no hay columnas de agrupación, el resultado de la <group by clause>tabla agrupada consiste Ten su único grupo.

Así podemos concluir que

FROM r GROUP BY ()

da como resultado una tabla agrupada, que consta de un grupo, con cero filas (ya que R está vacío).


Un extracto de las Reglas generales de 7.12, que define una Especificación de consulta (también conocida como una instrucción SELECT):

1) Caso:

a) Si Tno es una tabla agrupada, entonces [...]

b) Si Tes una tabla agrupada, entonces

Caso:

i) Si Ttiene 0 (cero) grupos, entonces deje que TEMP sea una tabla vacía.

ii) Si Ttiene uno o más grupos, cada uno <value expression>se aplica a cada grupo para obtener Tuna tabla TEMPde Mfilas, donde Mestá el número de grupos T. La icolumna -th de TEMP contiene los valores derivados de la evaluación de i-th <value expression>. [...]

2) Caso:

a) Si <set quantifier> DISTINCTno se especifica, entonces el resultado de la <query specification>es TEMP.

Por lo tanto, dado que la tabla tiene un grupo, debe tener una fila de resultados.

Así

SELECT 1 FROM r HAVING 1=1

debería devolver un conjunto de resultados de 1 fila.

QED

Kevin Cathcart
fuente
+1 ¡Gracias por todos esos problemas! Como dice @ypercube, SQL Server parece contradecirse aquí como SELECT 1 FROM r GROUP BY (); devuelve cero filas, pero el pasaje que citó parece bastante claro en este punto.
Martin Smith
¿Puedo preguntar dónde encontraste el estándar? Si dices 'en mi estantería' estaré decepcionado :)
dezso
Técnicamente, utilicé el borrador del Estándar Internacional Final, en lugar del estándar en sí. Según las normas ISO / IEC, solo se permiten cambios editoriales (no técnicos) entre FDIS y el estándar final. El estándar se divide en varias partes. Parte 1 , Parte 2 , Parte 4 ...
Kevin Cathcart
Parte 11 y Parte 14 . Las partes 3,9,10 y 13 no se actualizaron en 2011, por lo que se aplican sus versiones anteriores. No hay parte 12. Del mismo modo, no hay partes 5-8. Consulte la página de Wikipedia para SQL: 2011 o la Parte 1 para obtener una explicación de lo que contiene cada parte.
Kevin Cathcart
7

Cuando hay una HAVINGcláusula, sin una WHEREcláusula:

SELECT 1 FROM r HAVING 1=1;

... entonces GROUP BY ()está implícito. Entonces, la consulta debe ser equivalente a:

SELECT 1 FROM r GROUP BY () HAVING 1=1;

... que debería agrupar todas las filas de la tabla en un grupo (incluso si la tabla no tiene filas en absoluto, sigue siendo un grupo de 0 filas) y devolver 1 fila. El HAVINGcon la Truecondición no debería tener ningún efecto después de eso.


Desde un ángulo diferente, ¿cuántas filas debería devolver una consulta como esta?

SELECT COUNT(*), MAX(b) FROM r;

¿Uno, cero o "cero o uno, dependiendo de si la tabla está vacía o no"?

Creo que una fila, no importa cuántas filas rtenga.

ypercubeᵀᴹ
fuente
Bueno, la cuestión clave es si es cierto que "incluso si la tabla no tiene filas, sigue siendo un grupo de 0 filas". Y el estándar resulta ser explícito sobre esto: "Si no hay columnas de agrupación, entonces ... es la tabla agrupada que consiste en T como su único grupo". (y eso se mantiene incluso si T está vacío, por lo que efectivamente hay un grupo). Más adelante, la cláusula have especifica que la condición se aplica a cada grupo (en el ejemplo, una vez). Probablemente lo definieron de esta manera para hacer que SUM y COUNT devuelvan una fila incluso para las T vacías.
Erwin Smout
+1 (¡antes!) Aunque su lógica es la misma que la de Kevin, he aceptado su respuesta debido a las citas de la especificación. ¡Gracias!
Martin Smith
@MartinSmith. Thnx. Que obtengo de ser perezoso :)
ypercubeᵀᴹ
@ypercube: +1 de mi parte también. Decidí tomarme el tiempo extra para extraer de la especificación para demostrar que no había palabras de comadreja ocultas en algún lugar que hicieran que su respuesta fuera incorrecta. Pero una vez que hice eso, podría publicarlo como respuesta completa. Así que lo hice.
Kevin Cathcart
3
@ErwinSmout: Por supuesto que no. Sin embargo, esto está dentro del uso justo bajo la ley de derechos de autor de los Estados Unidos. Porciones relativamente pequeñas, citadas en el contexto de análisis (es decir, crítica) del trabajo, con fines educativos, con un impacto insignificante en la capacidad del trabajo para ser vendido.
Kevin Cathcart
3

Por lo que veo, parece que SQLServer y PostgerSQL no se molestan en mirar la tabla en absoluto:

CREATE TABLE r (b INT);
insert into r(b) values (1);
insert into r(b) values (2);
SELECT 1 FROM r HAVING 1=1;

También devuelve solo una fila. Aunque los documentos de SQLServer dicen

Cuando GROUP BY no se usa, HAVING se comporta como una cláusula WHERE.

eso no es cierto en este caso, en WHERE 1=1lugar de HAVINGdevolver el número adecuado de filas. Yo diría que es un error del optimizador (o al menos un error de documentación) ... el plan SQLServer muestra 'Análisis constante' en caso de HAVING'análisis de tabla' para WHERE...

El comportamiento de Oracle y Mysql me parece más lógico y correcto ...

a1ex07
fuente
1
Tienes razón en que SQL Server no mira la tabla. El plan de ejecución solo tiene un escaneo constante y ni siquiera hace referencia a la tabla. Si solo fuera SQL Server, lo habría atribuido a un error, pero como no es solo SQL Server, me pregunto si hay alguna ambigüedad genuina aquí.
Martin Smith
PostgreSQL muestra los mismos resultados que SQLServer, y por lo que puedo decir de la salida de explain"Resultado (filas = 1) ..." para tener y "Seq Scan" para "DONDE" tampoco se ve en la tabla. .. Supongo que de alguna manera está relacionado con el hecho de que "FROM" no es obligatorio en TSQL y PostgreSQL. Sé que Mysql tampoco lo requiere, pero dado que son compatibles dual, probablemente analicen la consulta un poco diferente. Estoy de acuerdo, parece una especulación, pero espero que tenga sentido.
a1ex07