Ответ: можно, но это не правильно, т.к. недокументировано.
Согласно документации предложение order by запрещено во вью (если не указан оператор top, но и тогда порядок не гарантируется) и если вам нужно получить отсортированный результат из вью — то предложение order by необходимо указывать в запросе к этому вью.
Тем не менее, отсортированные данные получить все-таки можно, т.к. «как правило» (чисто эмпирически) top совместно с order by приводит к сортировке в плане (однако т.к. это не документировано само собой нет гарантии, что так будет всегда).
Рассмотрим некоторые из способов создания такой сортировки.
Все что здесь написано относится к версиям выше 2000.
Прежде создадим неупорядоченную таблицу.
use tempdb; if object_id('dbo.NotOrderedTable') is not null drop table dbo.NotOrderedTable go -- создадим таблицу и заполним ее случайными значениями select top 100000 val = convert(int,rand(checksum(newid()))*100000) into dbo.NotOrderedTable from [master]..spt_values m1 cross join [master]..spt_values m2 cross join [master]..spt_values m3 go -- создадим вью ( в документации принят термин "представление", но уж больно долго печатать=) ) if object_id('dbo.OrderedView') is not null drop view dbo.OrderedView go create view dbo.OrderedView as select val from dbo.NotOrderedTable go -- результат простого запроса из вью (результаты неупорядочены) select * from dbo.OrderedView
Теперь помотрим способы получения из вью упорядоченных результатов.
1. Рекомендованный документацией способ
select * from dbo.OrderedView order by val
Попробуем создать сортировку прямо во вью, но т.к. создание вью без предложения топ у нас закончится неудачей еще на этапе компиляции
Msg 1033, Level 15, State 1, Procedure OrderedView, Line 3 The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified.
, то единственным способом обмануть компилятор будет включение в результат предложения top. Отсюда логичным образом вытекает следующий способ.
2. Задать в top очень большое число которое будет заведомо больше (как вы предполагаете) того количества строк которое вернет вью.
if object_id('dbo.OrderedView') is not null drop view dbo.OrderedView go create view dbo.OrderedView as -- например так (можно и больше, например максимальный bigint) select top 1000000000000000000 val from dbo.NotOrderedTable order by val go select * from dbo.OrderedView go
Но этот способ мне кажется, не очень красивым и напоминает, по идее реализации, создание «заведомо большего массива», вместо связанного списка для хранения заранее неизвестного числа элементов (да-да встречал я и такой код, C#: int[] a = new int[100] — и мотивация, «ну их же точно не будет больше ста!» =).
По этому, хотя, скорее всего такого большого кол-ва строк никогда не будет, но все же, поищем другой способ.
Вспомним, что top позволяет указывать не только кол-во строк, но и процент. Попробуем указать 100%.
if object_id('dbo.OrderedView') is not null drop view dbo.OrderedView go create view dbo.OrderedView as select top 100 percent val from dbo.NotOrderedTable order by val go select * from dbo.OrderedView go select @@version
Если у вас sql server 2000, то результат будет упорядоченным и на этом можно остановиться. Если у вас версия выше 2000, то, скорее всего результаты будут не отсортированы.
Дело в том, что оптимизатор «распознал» нашу хитрость и исключил сортировку из плана выполнения.
MS выпустила патч по этому поводу, он находится здесь, но чтобы это исправление возымело эффект, необходимо иметь уровень совместимости БД 8.0, что безусловно является шагом назад.
Продолжим изыскания.
Логичным будет попробовать «обмануть» оптимизатор, чтобы он не мог на этапе компиляции заранее вычислить конструкцию «select top 100 percent» и исключить ее из плана вместе с сортировкой.
Так мы приходим к способу, который не отличается изяществом, но, тем не менее, работает.
3. Задать вычисляемое выражение в top (о, мсье знает толк в извращениях)
if object_id('dbo.OrderedView') is not null drop view dbo.OrderedView go create view dbo.OrderedView as select top (100 + 0*convert(int,getdate())) percent val from dbo.NotOrderedTable order by val go select * from dbo.OrderedView go
Способ работает, но уж больно не красивое выражение в top, на самом деле можно написать проще и короче. Таким образом, приходим к способу 4. Идея такая же как и у предыдущего способа.
4. Задать более короткое вычисляемое выражение в top
if object_id('dbo.OrderedView') is not null drop view dbo.OrderedView go create view dbo.OrderedView as select top (select 100) percent val from dbo.NotOrderedTable order by val go select * from dbo.OrderedView go
Результаты отсортированы, способ тоже работает.
Ну, вот пожалуй и все.
Удалим тестовые данные.
drop view dbo.OrderedView drop table dbo.NotOrderedTable
Замечание.
Единственное, что я хочу заметить, это то, что все кроме первого способа — недокументированны и использовать их нужно на свой страх и риск.
Не исключена ситуация, когда МС «без предупреждения и объявления» изменит логику оптимизатора, как это случилось при переходе с 2000 на 2005, так что используйте осторожно и помните о том, что сказано в BOL:
ПримечаниеПри использовании предложения ORDER BY в определении представления, встроенной функции, производной таблице или вложенном запросе, предложение используется только для определения строк, возвращаемых предложением TOP. Предложение ORDER BY не гарантирует упорядочивания результатов при запросе этих конструкций, если оно не указано в самом запросе.
Скажу, что лично мне, не приходилось в работе сталкиваться с острой необходимостью, когда нужно отсортировать данные именно во вью, так что я не использую ни один из перечисленных способов и заметка написана чисто из спортивного интереса и «passion for tsql». =)