Используйте ORDER BY с UNION ALL

У меня есть таблица со столбцом даты события. Я хочу возвращать записи в том порядке, в котором сначала отображаются самые последние предстоящие записи, а когда все предстоящие записи закончены, прошлые записи должны отображаться с последними пройденными первыми.

Для этого я планировал сначала получить записи с event date > GETDATE(), отсортированные по возрастанию, и объединить их с остальными записями в порядке убывания даты события.

Поскольку указание 2 ORDER BY невозможно в UNION, какой подход можно использовать в этом случае.

Подводя итог, мой запрос требует таких результатов, как:

SELECT EventName, EventDate
FROM EventTable 
WHERE EventDate>GETDATE()
ORDER BY EventDate

UNION ALL

SELECT EventName, EventDate
FROM EventTable 
WHERE EventDate<=GETDATE()
ORDER BY EventDate DESC

person Saksham    schedule 06.12.2013    source источник


Ответы (4)


Вам нужно указать ORDER BY, который применяется ко всем результатам, и если вы хотите, чтобы результаты из первого SELECT отображались первыми, вы также должны указать это:

SELECT EventName,EventDate FROM (
  SELECT EventName, EventDate, 1 as ResultSet
  FROM EventTable 
  WHERE EventDate>GETDATE()

  UNION ALL

  SELECT EventName, EventDate, 2
  FROM EventTable 
  WHERE EventDate<=GETDATE()
) t
ORDER BY ResultSet,
         CASE WHEN ResultSet = 1 THEN EventDate END,
         CASE WHEN ResultSet = 2 THEN EventDate END DESC

Строго говоря, второе выражение CASE не требуется, но я включил его для симметрии.


Или, как предлагает Аллан, может быть, просто:

SELECT EventName, EventDate
FROM EventTable 
ORDER BY
     CASE WHEN EventDate > GETDATE() THEN 1 ELSE 2 END,
     CASE WHEN EventDate > GETDATE() THEN EventDate END,
     EventDate desc

(Где на этот раз я опустил последнее выражение CASE)

person Damien_The_Unbeliever    schedule 06.12.2013

Вы можете добавить ORDER BY только в окончательный запрос:

SELECT EventName, EventDate
FROM EventTable 
WHERE EventDate>GETDATE()

UNION ALL

SELECT EventName, EventDate
FROM EventTable 
WHERE EventDate<=GETDATE()
ORDER BY EventDate DESC

По сути, он берет первый запрос, объединяет его со вторым и упорядочивает. Если вам нужен другой порядок по , вам нужно ввести ключ сортировки в оба запроса, а затем отсортировать по нему.

Однако в вашем примере, я думаю, вам нужно переосмыслить запрос и, возможно, посмотреть на ROW_NUMBER или CASE в ORDER BY, поэтому вам не нужно объединяться.

person Allan S. Hansen    schedule 06.12.2013

Другой вариант, который, как и @Linger answer, использует CTE, но немного отличается, потому что он полностью отбрасывает UNION ALL и выполняет одиночное сканирование вместо двух сканирований (или двух сканирований, если EventDate имеет покрывающий индекс). У него тот же план, что и у второго запроса @Damien, но он должен сравниваться с GETDATE() только один раз (я просто нахожу, что это немного аккуратнее).

;WITH x AS 
(
  SELECT EventName, EventDate, s = CASE WHEN EventDate <= GETDATE() THEN 1 END
    FROM dbo.EventTable
)
SELECT EventName, EventDate FROM x
ORDER BY s, CASE s WHEN 1 THEN EventDate END DESC, EventDate;

всегда ссылайтесь на схему и постарайтесь привыкнуть завершать операторы с точкой с запятой.

person Aaron Bertrand    schedule 06.12.2013

person    schedule
comment
@Saksham, с Sql Server в качестве базы данных и всего 1000 записей не должно быть проблем с производительностью. Это небольшое изменение для SQL Server. - person Linger; 06.12.2013
comment
И я где-то читал, что не следует использовать SELECT * для предотвращения SQL Injection - person Saksham; 06.12.2013
comment
@Saksham Где ты это прочитал? Похоже, вы смешиваете истории - person Aaron Bertrand; 06.12.2013
comment
@Saksham, просто измените SELECT * на нужные поля. Я использовал SELECT * только для того, чтобы дать вам хороший ответ. Выбор полей не имел никакого отношения к вопросу. - person Linger; 06.12.2013