Obtener SELECT para devolver un valor constante incluso si las filas cero coinciden

15

Considere esta declaración de selección:

SELECT *, 
       1 AS query_id 
FROM players 
WHERE username='foobar';

Devuelve la columna query_idcon valor 1junto con las otras columnas de un jugador.

¿Cómo podría uno hacer que el SQL anterior devuelva al menos el query_idde 1incluso si la selección no encuentra filas que coincidan?

Por cierto, es PostgreSQL 8.4.

Nathanael Weiss
fuente

Respuestas:

22
SELECT col1, 
       col2, 
       col3, 
       1 AS query_id 
FROM players 
WHERE username='foobar'
union all 
select null,
       null,
       null,
       1
where not exists (select 1 from players where username = 'foobar');

O como alternativa ( podría ser más rápido ya que no se requiere una segunda subselección):

with qid (query_id) as (
   values (1)
) 
select p.*, 
       qid.query_id
from qid 
  left join players as p on (p.useranme = 'foobar');

Puede volver a escribir lo anterior en una representación más "compacta":

select p.*, 
       qid.query_id
from (values (1)) as qid (query_id)
  left join players as p on (p.useranme = 'foobar');

Pero creo que el CTE explícito ( with...) es más legible (aunque eso siempre está a los ojos del espectador).

un caballo sin nombre
fuente
1
Al probar el primer ejemplo, ¿parece que la palabra clave ALL no es necesaria?
Nathanael Weiss
2
@NatWeiss: si necesita un orden específico, que tienen que suministrar una order by. La segunda "crea" una tabla virtual con exactamente una fila y una columna y realiza una unión externa (sin ninguna condición de unión "real"), por lo que siempre obtiene al menos esa fila. Usar select *en el código de producción es un mal estilo. No lo hagas Enumere siempre las columnas que necesita. select *debe solamente ser utilizados en las consultas ad-hoc.
a_horse_with_no_name
2
@NatWeiss: a qué "sintaxis alternativa" para "otras uniones" se refiere. ¿Y por qué crees que left joinno es legible?
a_horse_with_no_name
2
@NatWeiss: la unión implícita en la cláusula where es un mal estilo de codificación y debe evitarse. Puede conducir a uniones cartesianas no deseadas sin darle un error. Y separa claramente los dos conceptos (relacionales) de unión y filtrado
a_horse_with_no_name
44
re: el modificador "todo" de la cláusula "union" no se necesita: a UNION ALLveces puede ser más eficiente que UNION, ya que explícitamente le está diciendo al planificador de consultas que espera que no haya filas duplicadas que salgan de las UNIONconsultas ed o si hay que quieres que salgan. Sin el ALLmodificador, supone que desea eliminar las filas duplicadas (solo una de cada una devuelta) de forma muy similar a la DISTINCTpalabra clave, y para garantizar que pueda necesitar recurrir + volver a escanear los resultados un tiempo extra. Por lo tanto, use ALLcon a UNIONmenos que necesite específicamente la desduplicación de la fila de salida.
David Spillett
7

Si solo espera una o cero filas hacia atrás, esto también funcionaría:

SELECT
  max(col1) col1,
  max(col2) col2, 
  1 AS query_id 
FROM
  players 
WHERE
  username='foobar';

Esto devolverá una fila con todos los valores nulos, excepto query_id si no se encuentra ninguna fila.

David Aldridge
fuente
2
Buen truco. El único inconveniente es que los valores para col1 y col2 podrían no pertenecer a la misma fila, si hay más de uno que coincida con la condiciónusername = 'foobar'
a_horse_with_no_name
1
¿Podría coalesce () también usarse de esta manera?
Nathanael Weiss
1
La fusión no generaría una fila donde no se proyecte ninguno de la tabla.
David Aldridge
1
@a_horse_with_no_name sí, aunque los nombres de tabla y columna sugieren que el predicado está en una clave candidata para la tabla, por lo que se proyectaría cero o una fila.
David Aldridge
3

Entrando tarde aquí, pero aquí hay una sintaxis que funciona (al menos en 9.2, no he probado versiones anteriores).

SELECT (COALESCE(a.*,b.*::players)).*
FROM ( SELECT col1,  col2,  col3, 1 AS query_id 
       FROM players WHERE username='foobar' ) a
RIGHT JOIN (select null col1, null col2, null col3, 1 col4) b
ON a.query_id = b.col4;

Solo devolverá la fila "en blanco" si todo el contenido de "a" es nulo.

Disfrutar. / cabeza de bit

Kirk Roybal
fuente
-1
select isnull(column,1) from table
usuario86795
fuente
2
Esto no generará una fila adicional cuando la consulta devuelva 0 filas.
ypercubeᵀᴹ