2009-05-09 7 views
15

J'ai une table comme ci-dessousSQL: Utilisation de Top 1 dans la requête UNION avec Trier par

Rate Effective_Date 
---- -------------- 
5.6 02/02/2009 
5.8 05/01/2009 
5.4 06/01/2009 
5.8 12/01/2009 
6.0 03/15/2009 

je suis censé trouver les tous les tarifs qui sont efficaces pour date et après. Donc, pour obtenir le taux effectif actuel, j'utilise

SELECT TOP 1 * from table 
where effective_date < '05/05/2009' 
order by effective date desc 

pour les taux après la date de la requête est

SELECT * from table 
where effective_date > '05/05/2009' 

Pour combiner ces deux résultat j'utilise un syndicat comme

SELECT TOP 1 * from table 
where effective_date < '05/05/2009' 
order by effective date desc 

UNION 

SELECT * from table 
where effective_date > '05/05/2009' 

Le résultat attendu est

Rate Effective Date 
---- -------------- 
5.8 05/01/2009 
5.4 06/01/2009 
5.8 12/01/2009 
6.0 03/15/2009 

Mais j'obtenir le résultat réel

Rate Effective Date 
---- -------------- 
5.6 02/02/2009 
5.4 06/01/2009 
5.8 12/01/2009 
6.0 03/15/2009 

Je n'ai pas la moindre idée de pourquoi cela se produit? Aucune suggestion?

+1

Je ne suis pas sûr que vos "résultats attendus" sont en fait les résultats que vous attendez .. 15/03/2009 est le "dernier avant dernier" record "précédent" avant votre date de recherche de '5/5/2009 '.. vous devriez seulement avoir 3 enregistrements dans votre jeu de résultats attendu. – datacop

Répondre

24

Il fonctionne de cette façon:

select * 
from (
    select top 1 * 
    from table 
    where effective_date <= '05/05/2009' 
    order by effective_date desc 
) as current_rate 

union all 

select * 
from table 
where effective_date > '05/05/2009' 
+0

Est-ce que le problème du 'top x' n'est pas respecté dans une requête de l'Union? (Je suppose que c'est le cas mais que je n'ai pas joué avec ça dans MSSS) – Karl

+0

Oui, ça fonctionne. Je l'ai testé avec SQL Server 2005. – splattne

+0

Oui, la version éditée de votre réponse fonctionne (encapsulation de l'ordre par requête dans une requête non ordonnée par l'union). Comme pour les chats, il y a une douzaine de façons de les peler. J'ai vérifié votre requête pour retourner les mêmes résultats que le mien. Le vôtre est génial pour les situations "gimmies les données maintenant" rapides et sales ... J'aime la méthode CTE que j'ai décrite car une fois écrite, vous pouvez faire tout ce que vous voulez avec les données. (ie, donnez-moi le deuxième taux le plus précédent [mais pas le plus précédent] et tous les taux «courants») – datacop

1

Je crois que les requêtes ci-dessus excluent 05/01/2009 en utilisant < et> au lieu de < = et> =.

7

L'ordre par une instruction select qui fait partie d'un syndicat est ignoré. Par conséquent, votre TOP 1 sélectionne un enregistrement arbitraire (probablement le premier enregistrement de la clé en cluster pour la table).

+0

Exactement et bien dit. J'ai déjà rencontré ce problème. –

2

Trier par est invalide lorsqu'il est utilisé avec une Union ...

J'ai travaillé un quickie et sale en utilisant thingy expression de table commune avec un certain rang et la ruse de déclaration de cas pour obtenir les résultats que vous recherchez ..

WITH CTE_RATES (RATE, EFFECTIVE_DATE, CUR, SORT) 
AS (
    SELECT 
     Rate, 
     Effective_date, 
     CASE WHEN Effective_date > '5/5/2009' THEN 1 
      ELSE 0 
     END, 
     RANK() OVER (PARTITION BY 
         CASE WHEN EFFECTIVE_DATE > '5/5/2009' THEN 1 
           ELSE 0 
         END 
        ORDER BY EFFECTIVE_DATE DESC) 
    FROM TestTable 
) 

SELECT RATE, EFFECTIVE_DATE 
FROM (
    SELECT RATE, EFFECTIVE_DATE 
    FROM CTE_RATES 
    WHERE CUR = 0 AND SORT = 1 

    UNION ALL 

    SELECT RATE, EFFECTIVE_DATE 
    FROM CTE_RATES 
    WHERE CUR = 1 
    ) AS QRY 
ORDER BY EFFECTIVE_DATE 

Pour expliquer ce qui se passe ...

le CTE définit le taux, la date, les drapeaux en cours et le tri retour de la requête ...

Le CASE sépare les résultats en ceux qui sont avant la date de recherche, et ceux qui sont après la date de recherche .. Nous utilisons les résultats de l'affaire (Cur) dans notre union pour tirer les résultats de la liste partitionnée ..

La fonction Rank() trie ensuite la liste en créant une partition selon les mêmes critères que l'instruction CASE pour séparer la liste. Ensuite, nous la classons par ordre décroissant. Cela prendra la liste « passé » et rendre son rang d'entrée « passé » le plus courant 1 ..

Ensuite, dans la partie syndicale de la requête ..

Dans la partie supérieure, nous obtenons le rang et date de la liste "passé" (cur = 0) et la première entrée dans la liste "passé" .. (sort = 1) .. qui retournera 1 enregistrement (ou 0 s'il n'y a pas d'enregistrements qui sont avant la date de recherche) ..

Ensuite, nous syndicat avec tous les enregistrement de la liste « courant » (= 1 cabot)

Puis enfin .. nous prenons les résultats de l'UNION .. et l'ordre que par la date d'effet de nous donner tous les enregistrements en cours et l'enregistrement précédent "le plus récent".