web-dev-qa-db-fra.com

Ajouter une ligne vide aux résultats de la requête si aucun résultat n'a été trouvé

J'écris des procs stockés qui sont appelés par un système hérité. Une des contraintes du système hérité est qu’il doit y avoir au moins une ligne dans le seul jeu de résultats renvoyé par le proc stocké. La norme est de renvoyer un zéro dans la première colonne (oui, je sais!).

Le moyen évident d'y parvenir est de créer une table temporaire, d'y insérer les résultats, de tester toutes les lignes de la table temporaire et de renvoyer les résultats de la table temporaire ou du résultat unique vide.

Une autre solution consiste à créer une clause EXISTS par rapport à la même clause where qui se trouve dans la requête principale avant que la requête principale ne soit exécutée.

Ni l'un ni l'autre ne sont très satisfaisants. Quelqu'un peut-il penser à un meilleur moyen. Je pensais comme UNION comme ceci (je sais que cela ne marche pas):

--create table #test
--(
--  id int identity,
--  category varchar(10)
--)
--go
--insert #test values ('A')
--insert #test values ('B')
--insert #test values ('C')

declare @category varchar(10)

set @category = 'D'

select
    id, category
from #test
where category = @category
union
select
    0, ''
from #test
where @@rowcount = 0
20
Chris Simpson

C'est une vieille question, mais j'avais le même problème ... La solution est vraiment simple, SANS double-sélectionner:

select top(1) WITH TIES * FROM (
select
id, category, 1 as orderdummy
from #test
where category = @category
union select 0, '', 2) ORDER BY orderdummy

par "WITH TIES", vous obtenez TOUTES les lignes (toutes ont un 1 comme "orderdummy", donc toutes sont égales), ou s'il n'y a aucun résultat, vous obtenez votre defaultrow.

10
swe

Très peu d'options j'ai peur.

Vous devez toujours toucher la table deux fois, que ce soit COUNT, EXISTS before, EXISTs in UNION, clause TOP, etc.

select
    id, category
from mytable
where category = @category
union all --edit, of course it's quicker
select
    0, ''
where NOT EXISTS (SELECT * FROM mytable where category = @category)

Une solution EXISTS est préférable à COUNT car elle s’arrêtera lorsqu’elle trouvera une ligne. COUNT traversera toutes les lignes pour les compter

28
gbn

C'est la réponse de @ swe, seulement mieux formaté.

CREATE FUNCTION [mail].[f_GetRecipients]
(
    @MailContentCode VARCHAR(50)
)
RETURNS TABLE
AS
RETURN
(
    SELECT TOP 1 WITH TIES -- returns all rows having highest priority found
        [To],
        CC,
        BCC
    FROM (
        SELECT
            [To],
            CC,
            BCC,
            1 AS Priority -- if no rows, priority 2 under UNION will get returned
        FROM mail.Recipients
        WHERE 1 = 1
            AND IsActive = 1
            AND MailContentCode = @MailContentCode

        UNION ALL

        SELECT
            *
        FROM (VALUES
            (N'[email protected]', NULL, NULL, 2),
            (N'[email protected]', NULL, NULL, 2)
        ) defaults([To], CC, BCC, Priority)
    ) emails
    ORDER BY Priority
)
2
ensisNoctis

Vous pouvez utiliser une jointure externe complète. Quelque chose à l'effet de ...

declare @category varchar(10)

set @category = 'D'

select #test.id, ISNULL(#test.category, @category) as category from (
    select
        id, category
    from #test
    where category = @category
)  
FULL OUTER JOIN (Select @category as CategoryHelper ) as EmptyHelper on 1=1   

Actuellement, je teste moi-même les performances de ce scénario, donc je ne suis pas sûr du type d'impact que cela aurait, mais cela vous donnera une ligne vide avec une catégorie renseignée. 

2
Andrew Jansen

Pour éviter de dupliquer la requête de sélection, pourquoi pas une table temporaire pour stocker le résultat de la requête en premier? Et sur la base de la table temporaire, retourne la ligne par défaut si la table temporaire est vide ou renvoie la température temporaire quand il en résulte?

0
JieLi

Je suppose que vous pourriez essayer:

Declare @count int
set @count = 0

Begin
Select @count = Count([Column])
From //Your query

if(@Count = 0) 
   select 0
else //run your query

L'inconvénient est que vous exécutez effectivement votre requête deux fois, mais que vous sautez la table temporaire.

0
AllenG