Obtenir SELECT pour renvoyer une valeur constante même si zéro ligne correspond


15

Considérez cette instruction select:

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

Il renvoie la colonne query_idavec la valeur 1ainsi que les autres colonnes d'un joueur.

Comment peut -on faire le retour SQL ci - dessus au moins query_idde 1même si les trouvailles sélectionnez aucune ligne que match?

BTW, c'est PostgreSQL 8.4.

Réponses:


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');

Ou comme alternative ( peut être plus rapide car aucune seconde sous-sélection n'est requise):

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

Vous pouvez réécrire ce qui précède dans une représentation plus "compacte":

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

Mais je pense que le CTE explicite ( with...) est plus lisible (bien que ce soit toujours aux yeux du spectateur).


1
En essayant le premier exemple, il semble que le mot clé ALL ne soit pas nécessaire?
Nathanael Weiss

2
@NatWeiss: si vous avez besoin d'une commande spécifique, vous devez fournir un order by. Le second "crée" une table virtuelle avec exactement une ligne et une colonne et fait une jointure externe (sans aucune condition de jointure "réelle"), ainsi vous récupérez toujours au moins cette ligne. L'utilisation select *dans le code de production est un mauvais style. Ne le fais pas. Énumérez toujours les colonnes dont vous avez besoin. select *devrait uniquement être utilisée dans des requêtes ad hoc.
a_horse_with_no_name

2
@NatWeiss: à quelle «syntaxe alternative» pour «autres jointures» faites-vous référence. Et pourquoi pensez-vous que ce left joinn'est pas lisible?
a_horse_with_no_name

2
@NatWeiss: la jointure implicite dans la clause where est un mauvais style de codage et doit être évitée. Cela peut entraîner des jointures cartésiennes indésirables sans vous donner d'erreur. Et il sépare clairement les deux concepts (relationnels) de jointure et de filtrage
a_horse_with_no_name

4
re: le modificateur "all" de la clause "union" n'est pas nécessaire: UNION ALLpeut parfois être plus efficace que UNION, car vous dites explicitement au planificateur de requête que vous vous attendez à ce qu'il n'y ait pas de lignes en double sortant des UNIONrequêtes ed ou si vous voulez qu'ils soient sortis. Sans le ALLmodificateur, il suppose que vous souhaitez supprimer les doublons (un seul de chaque retourné), tout comme avec le DISTINCTmot - clé, et garantir qu'il peut avoir besoin de recourir + de ré-analyser les résultats une fois de plus. Utilisez donc ALLavec UNIONsauf si vous avez spécifiquement besoin d'une déduplication des lignes de sortie.
David Spillett

7

Si vous ne vous attendez qu'à une ou zéro ligne, cela fonctionnera également:

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

Cela retournera une ligne avec toutes les valeurs ayant null sauf query_id si aucune ligne n'est trouvée.


2
Joli tour. Le seul inconvénient est que les valeurs de col1 et col2 peuvent ne pas appartenir à la même ligne, s'il y en a plusieurs correspondant à la conditionusername = 'foobar'
a_horse_with_no_name

1
Coalesce () pourrait-il également être utilisé de cette façon?
Nathanael Weiss

1
Coalesce ne générerait pas de ligne où aucune n'est projetée à partir de la table.
David Aldridge

1
@a_horse_with_no_name oui, bien que les noms de table et de colonne suggèrent que le prédicat se trouve sur une clé candidate pour la table, donc zéro ou une ligne serait projetée.
David Aldridge

3

Sonner de manière tardive ici, mais voici une syntaxe qui fonctionne (au moins dans 9.2, je n'ai pas essayé les versions antérieures).

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;

Renvoie uniquement la ligne "vide" si le contenu entier de "a" est nul.

Prendre plaisir. / bithead


En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.