SQL: использование Top 1 в запросе UNION с Order By - PullRequest
18 голосов
/ 09 мая 2009

У меня есть таблица, как показано ниже

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

Я должен найти все ставки, действующие на текущую дату и после нее. Таким образом, чтобы получить текущий эффективный курс, я использую

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

для ставок после текущей даты запрос

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

Чтобы объединить эти два результата, я использую объединение как

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'

Ожидаемый результат:

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

Но я получаю фактический результат как

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

Понятия не имею, почему это происходит? Есть предложения?

Ответы [ 4 ]

26 голосов
/ 09 мая 2009

Работает так:

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'
8 голосов
/ 09 мая 2009

Порядок By в операторе выбора, который является частью объединения, игнорируется. Следовательно, ваш TOP 1 выбирает некоторую произвольную запись (вероятно, первую запись по кластерному ключу для таблицы).

3 голосов
/ 09 мая 2009

Order By недействителен при использовании с Union ...

Я разработал быстрые и грязные штучки, используя Common Table Expression с некоторыми хитростями операторов Rank и Case, чтобы получить результаты, которые вы искали.

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

Чтобы объяснить, что происходит ...

CTE определяет скорость, дату, текущие и флаги сортировки, возвращаемые из запроса ...

CASE разделяет результаты на результаты, предшествующие дате поиска, и результаты, предшествующие дате поиска. Мы используем результаты из регистра case (Cur) в нашем объединении, чтобы извлечь результаты из секционированного списка. .

Функция Rank () затем сортирует список, создавая раздел по тем же критериям, которые оператор CASE использует для разделения списка. Затем мы упорядочиваем по дате вступления в силу по убыванию. Это возьмет «прошлый» список и сделает его текущее «прошлое» входным рангом 1 ..

Затем в объединенной части запроса ..

В верхней части мы получаем ранг и дату из «прошлого» списка (cur = 0) и первой записи в «прошлом» списке .. (sort = 1) .., которая вернет 1 запись (или 0, если нет записей, предшествующих дате поиска) ..

Затем мы объединяем это со всеми записями из «текущего» списка (cur = 1)

Тогда, наконец ... мы берем РЕЗУЛЬТАТЫ СОЮЗА ... и упорядочиваем это к дате вступления в силу, давая нам все текущие записи и "самую последнюю" предыдущую запись.

1 голос
/ 09 мая 2009

Я полагаю, что указанные выше запросы исключают 05/01/2009 с использованием <и> вместо <= и> =.

...