SQL: Utilisation de la requête Top 1 dans UNION avec Order By
-
20-08-2019 - |
Question
J'ai un tableau comme ci-dessous
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 tous les tarifs en vigueur à la date du jour et après celle-ci. 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 tarifs postérieurs à la date actuelle de la requête
SELECT * from table
where effective_date > '05/05/2009'
Pour combiner ces deux résultats, j'utilise une union 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 je reçois le résultat réel comme
Rate Effective Date
---- --------------
5.6 02/02/2009
5.4 06/01/2009
5.8 12/01/2009
6.0 03/15/2009
Je ne sais pas pourquoi cela se produit? Des suggestions?
La solution
Cela fonctionne comme suit:
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'
Autres conseils
La commande par dans une instruction select faisant partie d'une union est ignorée. Par conséquent, votre TOP 1 sélectionne un enregistrement arbitraire (probablement le premier enregistrement par la clé en cluster de la table).
Order By n'est pas valide lorsqu'il est utilisé avec une union ...
J'ai mis au point un concept rapide et coquin en utilisant Common Table Expression avec quelques astuces sur les déclarations de rang et de cas pour obtenir les résultats que vous recherchiez.
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 les indicateurs de taux, de date, de courant et de tri renvoyés par la requête ...
Le CASE sépare les résultats en ceux antérieurs à la date de recherche et en ceux postérieurs à la date de recherche. Nous utilisons les résultats du cas (Cur) dans notre syndicat pour extraire les résultats de la liste partitionnée. .
La fonction Rank () trie ensuite la liste en créant une partition sur les mêmes critères que ceux utilisés par l'instruction CASE pour séparer la liste. Nous classons ensuite par date d'effet de manière décroissante. Cela prendra le & Quot; passé & Quot; lister et mettre à jour " past " classement au rang 1 ..
Ensuite, dans la partie union de la requête.
Dans la partie supérieure, nous obtenons le rang et la date du & "passé &"; list (cur = 0) et la première entrée du & "passé &"; list .. (sort = 1) .. qui retournera 1 enregistrement (ou 0 s'il n'y a pas d'enregistrements antérieurs à la date de la recherche) ..
Ensuite, nous unissons cela à tous les enregistrements du " actuel " liste (cur = 1)
Ensuite, enfin… nous prenons les RÉSULTATS de l’UNION .. et les ordonnons d’ici à la date d’entrée en vigueur, nous donnant ainsi tous les enregistrements en cours, ainsi que le & «plus récent &»; enregistrement précédent.
Je crois que les requêtes ci-dessus excluent le 01/05/2009 en utilisant < et > au lieu de < = et > = =.