Данное описание основано на SQL Serve Books Online и книжки "Microsoft SQL Server 2000. Наиболее полное руководство" Е. Мамаев.
Содержание
Введение
Раздел SELECT
Раздел INTO
Раздел FROM
Раздел WHERE
Раздел GROUP BY
Раздел HAVING
Раздел UNION
Раздел ORDER BY
Раздел COMPUTE
Раздел FOR
Раздел OPTION

Введение

    SQL Server 2000 позволяет в одном запросе обращаться сразу к множеству разнообразных источников данных, возможно расположенных на разных серверах сети За счет использования технологии OLE DB пользователи могут получить доступ не только к реляционным источникам данных, как это было бы во время применения ODBC, но и к нереляционным, таким как текстовые файлы и электронные таблицы.
    Для выборки данных в Transact-SQL существует команда SELECT, которая позволяет как делать простую выборку всех данных из одной таблицы текущей базы данных, так и выполнять сложные запросы одновременно к множеству таблиц различных баз данных, расположенных на нескольких серверах сети В самом простейшем случае выборка данных производится с помощью команды:
SELECT  *  FROM table_name
   
    Эта команда выводит данные из всех столбцов для всех строк таблицы, т е. в результате выполнения запроса возвращается вся информация, содержащаяся в таблице. Однако в большинстве случаев применяются более сложные конструкции, использующие группировку, агрегирование, подзапросы, условия и другие дополнительные механизмы управления запросом
    Полный синтаксис команды SELECT следующий:

SELECT select_list
[INTO new_table_] FROM table_source
[WHERE search_condition]
[GROUP BY group_by_expression]
[HAVING search_condition]
[UNION]
[ORDER BY order_expression   [ASC   |   DESC]]
[COMPUTE compure_expression]
[FOR]    [OPTION <query_hint>]
   
    Как видно, синтаксис команды SELECT может быть очень сложный. Пожалуй, команда SELECT является самой сложной и многофункциональной командой Transact-SQL. Эта команда является хорошо структурированной командой, разбитой на отдельные разделы. Каждый из разделов выполняет узкоспециализированную функцию и является практически независимым от других разделов. Пользователи могут указывать только те разделы, которые им действительно необходимы, и не вникать в логику работы остальных разделов. Хотя количество разделов довольно велико, на практике обычно применяется "облегченный" вариант команды SELECT:

SELECT select_list
[INTO new_table_] FROM table_source
[WHERE search_condition]
[GROUP BY group_by_expression]
[ORDER BY order_expression   [ASC   |   DESC]]
   
    Вряд ли можно перебрать все варианты использования команды SELECT. Нередко к одному и тому же результату можно прийти различными путями. Кроме того, оптимизатор запросов (query optimizer) при необходимости может изменить вид запроса для повышения производительности. Эти действия прозрачны для пользователя и приводят к требуемому результату.

Раздел SELECT

    С помощью этого раздела указывается список столбцов, которые будут включены в результат выборки. Кроме того, в этом разделе можно управлять количеством и качеством строк, входящих в результат выборки. Структура раздела SELECT такова:

SELECT [ALL | DISTINCT] [TOP n [PERCENT] [ WITH TIES]] <select_list>

    Рассмотрим использование параметров раздела.

ALL

    Это ключевое слово указывает, что в результат выборки должны быть включены все строки, возвращаемые запросом. То есть результат выборки может содержать повторяющиеся строки. Параметр ALL используется по умолчанию и его указание необязательно.

DISTINCT

    Применение этого параметра позволяет исключить из возвращаемого результата повторяющиеся строки. Тем самым можно обеспечить уникальность каждой строки, возвращаемой запросом. Значения NULL считаются эквивалентными и включаются в выборку. Если DISTINCT не указывается, то будет использоваться параметр ALL.

ТОР n [PERCENT]  [WITH TIES]

    С помощью этой конструкции можно ограничить количество строк, которые будут включены в результат выборки. После ключевого слова ТОР с помощью параметра n задается максимальное количество строк, которое может содержать результат. Если указывается ключевое слово PERCENT, то параметр n означает количество строк в процентах от общего числа строк, возвращаемых запросом. Например, если таблица содержит 50 строк, а запрос должен возвратить всего 30 строк, то при указании ТОР 50 PERCENT будет возвращено 15 строк.
Если в запросе используется раздел ORDER BY, определяющий порядок сортировки, то возможна ситуация, что вследствие ограничения количества возвращаемых строк будет разорвана цепочка однотипных строк. Указание параметра WITH TIES предписывает включить в результат выборки дополнительные строки, имеющие то же значение в столбцах, указанных в разделе ORDER BY, что и последняя строка. Например, если сортировка выполняется по названию штата и количество строк ограничивается с помощью ТОР, то возможна ситуация, что для одного из штатов будет выведена только часть строк. Чтобы вывести все строки, относящиеся к штатам, фигурирующим в выборке, достаточно использовать параметр WITH TIES.  Применение WITH TIES допускается только совместно с разделом ORDER BY.

<select_list>
    С помощью этой конструкции формируется собственно список столбцов, которые будут включены в результат выборки, а также значения для этих столбцов. Структура этой конструкции такова:

<select_list>   ::=  {   *
|   {table_name   |   view_name   |   table_alias}.*
|   {column_name   |   expression   |   IDENTITYCOL   |   ROWGUIDCOL}
[[AS]   column_alias]   |   column_alias=expression}   [,...n]

Рассмотрим подробно назначение каждого из параметров:
  • *. Указание этого символа повлечет включение в результат выборки всех столбцов всех таблиц и представлений, участвующих в запросе и указанных в разделе FROM. Однако следует быть внимательным при выборке одноименных столбцов из разных таблиц. Обращение к таким столбцам будет весьма затруднено при дальнейшем использовании результата выборки, т. к. сервер вряд ли сможет определить, к какому конкретно столбцу необходимо обратиться. Порядок перечисления столбцов в результате выборки соответствует физическому порядку столбцов в таблице.  Кроме того, сначала перечисляются все столбцы первой таблицы, указанной в разделе FROM, затем второй таблицы и т. д., пока не будут выведены столбцы всех таблиц. Если не используется конструкция WHERE, то для каждой строки одной таблицы будет выводиться полный набор комбинаций строк других таблиц. Например, если выборка производится из двух таблиц с количеством строк 23 и 13, то общее количество возвращенных строк будет 299 (23x13) (Декартово произведение).
  • {table_name | view_name | table_alias}. *. Позволяет ограничить количество столбцов включением только всех столбцов одной таблицы или представления Как видно из синтаксиса, сначала указывается имя объекта, из которого будет производиться выборка, потом точка, и в конце символ *. Параметры table_name и view_name говорят о том, что можно ссылаться на конкретную таблицу или представление соответственно Параметр table_alias позволяет ссылаться на данные через псевдоним таблицы. При этом под псевдонимом можно обратиться как к реальной физической таблице, так и к результату выборки, возвращаемому подзапросом (динамической таблице). В любом случае, имя объекта должно быть упомянуто в разделе FROM.
  • column_name. Подразумевает указание имени столбца, который должен быть включен в результат выборки. Столбец должен принадлежать таблице или представлению, указанному в разделе FROM. Если в таблицах и представлениях, используемых в запросе, содержится более одного столбца с одинаковым именем и этот столбец должен быть включен в результат выборки, то помимо имени самого столбца следует указать имя таблицы, к которой принадлежит столбец То есть необходимо задать полное имя столбца в формате table_name.column_name В противном случае нельзя будет определить, какой именно столбец должен быть включен в выборку.
  • expression. Этот параметр подразумевает указание выражения, на основе которого будет формироваться содержимое столбца. Имя столбца является частным случаем выражения. В выражении допускается использование констант, переменных, функций, имен столбцов, а также любых разрешенных операций над ними. Следует учитывать, что по умолчанию столбец, содержимое которого формируется на основе вычисления выражения, не имеет никакого имени. Если требуется присвоить столбцу конкретное имя, то необходимо указать псевдоним столбца Псевдонимы столбцов будут описаны далее.
  • identitycol. Указание этого параметра включает в результат выборки столбец-счетчик (с установленным свойством IDENTITY). Аналогичного результата можно добиться, явно указав имя столбца с помощью параметра column_name Использование параметра IDENTITYCOL позволяет гарантированно включить в результат выборки столбец счетчика, даже не зная его имени. Если в запросе участвует более одной таблицы, в которой имеется столбец счетчика, то дополнительно необходимо указать имя таблицы в формате table_name.IDENTITYCOL. В выборке вместо IDENTITYCOL будет подставлено имя столбца, присвоенное ему при создании. Таким образом можно легко включить в выборку один и тот же столбец дважды. Если в таблице не определен столбец-счетчик, то при попытке использования параметра IDENTITYCOL будет выдано сообщение об ошибке, говорящее об указании неверного имени столбца.
  • rowguidcol. Использование этого параметра похоже на применение предыдущего параметра. Наличие параметра ROWGUIDCOL позволяет включить в результат выборки столбец с установленным свойством ROWGUIDCOL. Принципы работы и использования этого параметра полностью соответствуют аналогичным характеристикам параметра IDENTITYCOL.
  • [as] column_alias. С помощью этого параметра можно определять псевдонимы (alias) для столбцов. Не нужно путать псевдонимы столбцов с псевдонимами таблиц или представлений. Использование псевдонимов позволяет изменять имена столбцов, под которыми они будут выведены в результат выборки, по сравнению с их первоначальными именами. SQL Server 2000 позволяет включать в результат выборки столбцы с одинаковыми именами или вообще без имен. С помощью псевдонимов можно лишить столбцы имен или сделать их все одинаковыми. Тем не менее, обычно псевдонимы служат для формирования в результате выборки набора столбцов с разными именами или для присваивания столбцам более понятных названий, возможно на национальном языке. Также псевдонимы позволяют присваивать имена столбцам, формируемым на основе вычисления выражений. По умолчанию таким столбцам не присвоено никакого имени. Применение псевдонимов неизбежно при работе с подзапросами. Для ссылки на столбцы подзапроса каждый из столбцов обязан иметь конкретное уникальное имя. Если в подзапросе используются одноименные столбцы или столбцы на основе выражения, то им необходимо присвоить уникальные имена с помощью псевдонимов.
  • column_alias=expression. Этот параметр является вторым способом задания псевдонимов для столбцов. В этом варианте сначала указывается имя, которое будет присвоено столбцу в результате выборки, а после знака равенства — выражение, на основе которого будет формироваться столбец. В простейшем случае выражение допускает использование имени отдельного столбца, что позволяет просто назначить столбцу новое имя. В более сложных случаях применяются константы, переменные, функции, имена столбцов, над которыми производятся различные действия. Возможности, предоставляемые параметрами [AS] column_alias и column_alias=expression, практически эквивалентны.

Следует отметить, что имя псевдонима может быть задано непосредственно, в квадратных скобках, в одинарных или двойных кавычках: table_name.column_name as column_aliastable_name.column_name as [column_alias], 'column_alias'=table_name.column_nametable_name.column_name "column_alias".

Раздел INTO

   Раздел INTO предназначен для помещения результата выборки в отдельную таблицу, которая создается как часть выполнения запроса.

Раздел FROM

   С помощью этого раздела указываются таблицы и представления, из которых будет производиться выборка данных При этом все таблицы и представления, участвующие в выборке данных, должны быть указаны в разделе FROM. Кроме того, даже если ни один из столбцов таблицы не включен в результат выборки, но используется в разделе WHERE, ORDER BY, GROUP BY или других, та имя этой таблицы также должно быть указано в разделе FROM
Замечание
Выборка данных из таблиц и представлений не имеет принципиальных различий, поэтому далее в основном будет рассматриваться только работа с таблицами Однако если не будет отмечено отдельно, то сказанное о таблицах может быть применено и к представлениям.
Раздел FROM имеет следующий синтаксис:

FROM {<table_source>} [,...n]
<table_source> ::= table_name [[AS] table_alias]
[WITH (<table_hint> [,...n])] | view_name [[AS] table_alias]
| rowset_function [[AS] table_alias]
| derived_table [AS] table_alias [(coluim_alias [,...n])]
| <joined_table>

Рассмотрим подробно использование каждого из параметров:
  • table_name
    Имя таблицы, из которой будет производиться выборка. Любая таблица, из которой предполагается произвести выборку, должна быть указана в разделе FROM. Кроме того, даже если данные из таблицы непосредственно не выводятся в результат выборки, но участвуют в подготовке результата, то имя этой таблицы также должно быть приведено. Допускается применение таблиц, расположенных вне текущей базы данных, а также на других серверах сети. Для ссылки на таблицу базы данных локального сервера необходимо указать имя таблицы в формате database_name.table_name. Если же таблица находится на другом сервере, то дополнительно нужно указать и имя сервера. Чтобы иметь возможность работы с удаленными серверами, необходимо предварительно сконфигурировать связанные (linked) серверы на локальном SQL Server 2000
  • [AS]   table_alias
    С помощью этого параметра можно присвоить таблице псевдоним, под которым на нее можно будет ссылаться в запросе. Часто псевдонимы служат для упрощения вида запроса при работе с длинными именами таблиц. Например, если в запросе часто упоминается таблица pubs.titieauthor, то ей можно присвоить псевдоним, например, ta. Это позволит не писать каждый раз полное имя таблицы, а приводить короткое имя. Кроме того, псевдонимы таблиц активно используются при работе с подзапросами. Если в подзапросе будет выполняться обращение к той же таблице, что и в основном запросе, возможна ситуация, что подзапрос обращается к столбцу, а сервер не может определить, какое значение нужно вернуть — текущее значение основного запроса или значение, обрабатываемое в подзапросе. Применение псевдонима позволяет однозначно идентифицировать нужные данные.
  • WITH   (<table_hint>   [,...n])
    С помощью этой конструкции можно установить конкретный режим блокирования по усмотрению пользователя. Если конструкция WITH не указана, сервер сам выбирает наиболее оптимальный режим блокирования. Режим блокирования устанавливается с помощью конструкции <table_hint>, имеющей следующую структуру:
    <table_hint> : := {INDEX (index_val [,...n])
    | FASTFIRSTROW | HOLDLOCK | NOLOCK | PAGLOCK | READCOMMITTED | READPAST | READUNCOMMITTED | REPEATABLEREAD | ROWLOCK | SERIALIZABLE | TABLOCK | TABLOCKX | UPDLOCK}

    Замечание
    Использование хинтов и описание уровней блокирования было рассмотрено в главе 19.
  • view_name   [[AS]   table_alias]
    С помощью этого параметра в запросе разрешается использование представлений, что позволяет производить из них выборку данных и применять в различных разделах. Работа с представлениями практически ничем не отличается от работы с обычными таблицами. Так же, как и для таблиц, для представлений можно использовать псевдонимы.
  • rowset_function   [[AS]   table_alias]
    Параметр rowset_function подразумевает указание команд подготовки набора данных — OPENQUERY, OPENROWSET, FREETEXTTABLE или CONTAINSTABLE. Эти команды позволяют использовать в запросе динамические таблицы, построенные на основе данных, полученных в результате выполнения запросов к удаленным источникам данных OLE DB В качестве таких источников данных могут выступать серверы Oracle, SQL Server, текстовые файлы, файлы DBF и XLS, базы данных MS Access и т. д. Подготовленному набору данных может быть присвоен псевдоним, что позволяет работать с данными как с обычной таблицей.
  • derived_table [AS] table_alias [(column_alias [,...n])]
    Эта конструкция позволяет использовать в запросе динамические таблицы. Динамические таблицы представляют собой набор данных, формируемых в момент выполнения запроса на основе информации, возвращаемой подзапросом. Динамические таблицы не существуют физически в базе данных, и поэтому у них нет постоянного имени. Однако чтобы иметь возможность ссылаться на данные динамической таблицы, ей должно быть присвоено какое-то имя. Для этого используются псевдонимы, которые указываются с помощью параметра table_alias. В отличие от представлений и стандартных таблиц, для динамических таблиц указание псевдонима обязательно. Дополнительно с помощью параметра column_alias можно задать псевдонимы для столбцов динамической таблицы, т. е. переименовать их.
  • <joined_table>
    Эта конструкция предназначена для выборки данных из связанных таблиц. В SQL Server 2000 нет механизмов автоматического отслеживания взаимосвязи данных с установленными ограничениями целостности foreign key. Для обработки таких данных может с успехом выступать конструкция <joined_table>. Помимо этого, конструкция <joined_table> используется и при связывании таблиц, между которыми не установлены ограничения целостности. Область применения конструкции <joined_table> станет более понятна    при    рассмотрении   типов    связывания.    Синтаксис    конструкции <joined_table> таков:

    <joined_table>   ::=
    <table_source> <join_type> <table_source> ON <search_condition> |   <table_source> CROSS JOIN <table_source> 
     |   <joined_table>

    Рассмотрим назначение и использование каждого из аргументов:
    • <table_source>. С помощью этого параметра указывается источник, из которого будут браться данные. В качестве источника может выступать стандартная статическая таблица, представление, функция подготовки набора данных (rowset function) или динамическая таблица. Весь текущий раздел    посвящен    практически    только    рассмотрению    конструкции <table_source>. Даже конструкция <joined_table> является частью конструкции <table_source>. Таким образом, получается рекурсивная зависимость  конструкций <table_source>   и   <joined_table>  друг  от друга, что позволяет создавать многоуровневые зависимости между таблицами.
    • <join_type>. Этот параметр определяет метод связывания данных двух таблиц между собой. От выбранного метода связывания зависит, какие конкретно строки каждой из двух связываемых таблиц будут рассматриваться  при  выполнении  запроса.   Структура  конструкции  <join_type> следующая:
      <join_type> ::= [INNER | {{LEFT | RIGHT | FULL} [OUTER]}] [<join_hint>] JOIN
      Рассмотрим назначение параметров и их использование:
      • inner. При задании этого типа связывания каждая из двух участвующих в связывании таблиц будет включать только те строки, для которых есть соответствие во второй таблице. Данный тип связывания используется по умолчанию. Соответствие определяется условием связывания, которое задается с помощью параметра <search_condition>. Например, если в качестве условия связывания таблиц titles и titleauthor рассматривать       столбец       title_id,       то       оно       будет       выглядеть       как: titles.title_id=titleauthor.title_id.   При   использовании   связывания INNER в каждую таблицу будут включены только те строки, для которых значения title_id имеются как в левой (titles), так и в правой (titleauthor) таблицах. То есть в таблице titles будут доступны только те строки, у которых значение в столбце titie_id соответствует значению столбца title_id хоть одной строки таблицы titleauthor. Аналогично, в таблице titleauthor будут доступны только те строки, у которых  значение   в   столбце   title_id   соответствует  значению   столбца title_id хоть одной строки таблицы titles.
      • LEFT   [OUTER]. При использовании этого типа связывания в левой таблице будут оставлены все строки независимо от того, есть ли для них соответствие в правой таблице. Применительно к предыдущему примеру это означает, что если в таблице titles имеются книги, которые не упомянуты в таблице titleauthor, то строки, соответствующие этим книгам, все равно окажутся доступными. В правой же таблице будут доступны только те строки, для которых имеется соответствие в левой таблице. В столбцах, соответствующих отсутствующим данным правой таблицы, будут выведены значения NULL.
      • RIGHT [OUTER]. Действие этого параметра обратно предыдущему. При использовании указанного типа связывания в правой таблице будут оставлены все строки независимо от того, есть ли для них соответствие в левой таблице. В результате выборки столбцы, отображающие данные левой таблицы, будут содержать значения NULL во всех строках, для которых нет соответствия.
      • FULL [OUTER] . Тип связывания разрешает использование всех строк связываемых таблиц. Можно представить этот тип связывания как одновременное использование LEFT И RIGHT (что не разрешено). При выборке в столбцах с отсутствующими данными будет выведено значение NULL.
       
      Замечание
      Тип связывания INNER можно легко заменить использованием оператора = в разделе WHERE. С помощью специальных операторов можно заменить типы связывания *= - LEFT и =* - RIGHT.


    • <join_hint>. С помощью этой конструкции задается алгоритм связывания данных. Обычно сервер сам выбирает наиболее оптимальный режим связывания таблиц. Однако пользователь имеет возможность явно указать алгоритм связывания. Мы не будем рассматривать поведение сервера при использовании того или иного алгоритма, ограничившись приведением синтаксиса конструкции:
      <join_hint> ::= {LOOP | HASH | MERGE | REMOTE}


   На этом рассмотрение теоретической части использования раздела FROM МОЖНО считать завершенным.
Замечание
С помощью раздела WHERE ставится логическое условие, ограничивающее диапазон выборки Это делается для уменьшения количества строк, получаемых в результате выполнения запроса.

Раздел WHERE

   Этот раздел предназначен для ограничения количества строк, включаемых в результат выборки. Будут включены только те строки, которые удовлетворяют указанному логическому условию. Синтаксис раздела WHERE следующий:
WHERE <search condition>   |   column_name  {*=   |   =*}  column_name
Рассмотрим назначение и использование параметров:
column_name   {*=   |   =*}   column_name
Этой конструкцией можно заменить использование режимов связывания LEFT OUTER и RIGHT OUTER. С помощью параметра *= можно заменить параметр LEFT OUTER, а параметр =* заменяет параметр RIGHT OUTER. Первый параметр column_name определяет столбец левой таблицы, связываемый со столбцом правой таблицы, который указывается с помощью второго параметра coiumn_name. В разделе FROM достаточно просто перечислить имена используемых таблиц через запятую.
<search_condition>
С помощью этой конструкции можно задать любое произвольное условие для выборки данных. Конструкция <search_condition> является выражением, которое должно возвращать булево значение — TRUE ИЛИ FALSE. Указанное выражение вычисляется для каждой строки таблицы и, только если возвращается значение TRUE, TO строка включается в результат выборки. В противном случае строка игнорируется. Обычно условие включает имена столбцов таблицы, вместо которых при сканировании для каждой строки подставляется конкретное значение. Однако в условии имена столбцов могут и не участвовать. Структура конструкции <search_condition> такова:
<search_condition> ::= { [NOT] <predicate> | (<search_condition>)} [(AND | OR} [NOT] {<predicate> | (<search_condition>))] }  [,...n]
Основное назначение самой конструкции <search_condition> состоит в объединении множества логических условий, каждое из которых возвращает булево значение. Объединение выполняется с помощью операторов AND, OR И NOT. Кроме того, из синтаксиса конструкции видно, что можно создавать сложные вложенные условия. Само же условие определяется с помощью конструкции <predicate>, имеющей следующий синтаксис:
<predicate> : : =
{expression { = | <> | != | > | >= | !> | < | <= | !<} expression
| string_expression [NOT] LIKE string_expression [ESCAPE 'escape_character']
| expression [NOT] BETWEEN expression AND expression
| expression IS [NOT] NULL
| CONTAINS ({column | *}, '<contains_search_condition>')
| FREETEXT ({column | *}, 'freetext_string')
| expression [NOT] IN (subquery | expression [,...n])
| expression { = | <> | != | > | >= | !> | < | <= | !< } {ALL | SOME | ANY) (subquery) | EXISTS (subquery)}
Работа с частью операторов не вызывает особых затруднений. К таким операторам относятся =,<,>, != и т. д. Тем не менее, часть операторов и аргументов все же требует дополнительных комментариев:
• expression. Этот параметр означает использование любого выражения. В большинстве случаев выражение возвращает некоторый результат, который сравнивается с результатом вычисления другого выражения. Если в
результате сравнения возвращается TRUE, TO строка включается в результат выборки.
stnng_expression. Этот аргумент подразумевает наличие выражения, возвращающего символьное значение. Для строк в Transact-SQL имеются специальные операторы, с помощью которых могут выполняться некоторые операции, специфические только для строк. Примером такой операции является оператор LIKE, С ПОМОЩЬЮ которого можно проверить соответствие строкового выражения шаблону.
expression [NOT] BETWEEN expression AND expression. С ПОМОЩЬЮ ЭТОЙ конструкции можно выполнить проверку на принадлежность значения, возвращаемого при вычислении выражения, определенному диапазону Границы диапазона также задаются как результат вычисления выражения
expression is [NOT] NULL. Эта конструкция используется для проверки выражения на равенство значению NULL. ЕСЛИ результатом вычисления выражения является значение NULL, TO выражение expression IS NULL вернет TRUE, результатом чего будет включение соответствующей строки в результат выборки. В качестве параметра expression может выступать как сложное выражение, так и имя отдельного столбца.
В обычных условиях сравнение выражения и значения NULL С ПОМОЩЬЮ оператора = или '= невозможно, т к любое выражение с участием NULL возвратит NULL, а не TRUE ИЛИ FALSE. Поэтому для проверки выражения на равенство NULL необходимо указывать оператор IS NULL. В качестве демонстрации вышесказанного приведем пример"
DECLARE @Varl int
SET @Varl=NULL
IF @Varl=NULL SELECT 'aaa' ELSE SELECT 'bbb'
IF @Varl'=NULL SELECT 'aaa' ELSE SELECT 'bbb'
Будет возвращен следующий результат:
bbb (1 row(s) affected)
bbb (1 row(s) affected)
Однако для решения представленной проблемы Transact-SQL позволяет изменять поведение сервера при сравнении выражений со значением NULL. Для этого предназначена команда SET ANSI_NULLS (ON | OFF} При использовании параметра ON, устанавливаемого по умолчанию, при сравнении выражения со значением NULL будет возвращено значение NULL. Если же задан параметр OFF, TO сравнение происходит обычным образом. Изменим предыдущий пример для демонстрации сказанного:
DECLARE @Varl int SET ANSI_NULLS OFF SET @Varl=NULL
IF @Varl=NULL SELECT 'aaa' ELSE SELECT 'bbb' IF @Varl!=NULL SELECT 'aaa' ELSE SELECT 'bbb'
Будет возвращен следующий результат:
aaa
(1 row(s)   affected)
bbb (1  row(s)   affected)
•      CONTAINS   ({column   |   *},    '<contains_search_condition>'). С ПОМОЩЬЮ этой конструкции можно выполнять сложный поиск в символьных столбцах таблицы.
•     FREETEXT      ({column     |      *},      'freetext_string' ).    С    ПОМОЩЬЮ    ЭТОЙ конструкции можно производить сложный поиск в текстовых столбцах таблицы.
•      expression    [NOT]    IN    (subquery    |    expression    [,...n]).   С  ПОМОЩЬЮ оператора IN МОЖНО проверить вхождение выражения в набор данных, который может задаваться с помощью подзапроса или простого перечисления через запятую всех возможных значений.
•      expression   {=   |   <>   |    ! =   |   >    |    >=   |    ! >    |    <   |   <=   |    !<}    {ALL   |    SOME   |
ANY} (subquery). С помощью этой конструкции производится проверка удовлетворения значения, возвращаемого при вычислении выражения, логическому условию для всех или хотя бы для одного значения в подзапросе.
•    EXISTS    (subquery).  Оператор EXISTS  возвращает значение TRUE,  если подзапрос возвращает хоть одну строку. В противном случае возвращается
FALSE.
Раздел WHERE уже приводился довольно часто, и простейшие варианты использования условий для отбора строк уже понятны. Примером такого условия может служить следующий запрос:
SELECT * FROM authors WHERE state!='CA'
Запрос возвращает полный набор столбцов для всех авторов, проживающих вне штата Калифорния.
В представленном ниже запросе выводится название книги и сумма их продажи для всех книг, написанных авторами не из Калифорнии:
SELECT title, value=price*ytd_sales FROM titles WHERE title_ID IN
(SELECT title_id FROM titleauthor WHERE au_id IN
(SELECT au_id FROM authors WHERE state!='CA')) ORDER BY value DESC
Будет получен результат:
title                                                                                                                    value
Fifty Years in Buckingham Palace Kitchens      .       180397.2000
The Gourmet Microwave                                                                                 66515.5400
Silicon Valley Gastronomic Treats                                                       40619.6800
Is Anger the Enemy?                                                                                     22392.7500
Onions, Leeks, and Garlic                                                                        7856.2500
Life Without Fear                                                                                         777.0000 (6 row(s) affected)
Следующий запрос демонстрирует использование оператора EXISTS ДЛЯ получения того же результата, что и предыдущий запрос:
SELECT title,   value=pnce*ytd_sales FROM titles  as  t
WHERE EXISTS   (SELECT  *  FROM titleauthor as ta WHERE ta.title_id=t.title_id AND au_id  IN
(SELECT  au_id  FROM authors WHERE  state'='CA')) ORDER BY value  DESC
 Замечание
Хотя запросы внешне и различаются, время их выполнения одинаково Оптимизатор запросов SQL Server 2000 модифицирует запрос таким образом, что решение поставленной задачи будет произведено наиболее эффективно Это снимает с пользователя необходимость анализа производительности выполнения того или иного запроса и упрощает разработку конечных приложений

Раздел GROUP ВY

С помощью этого раздела можно осуществлять группировку данных. Данные группируются по одному или более столбцам таким образом, что для всех строк с одинаковыми значениями в столбце, по которому выполняется группировка, в результате выборки будет возвращена всего одна строка При этом в результат выборки разрешается включение только столбцов, по которым производится группировка, а также столбцов, использующих функции агрегирования
Синтаксис раздела GROUP BY таков:
GROUP BY [ALL] group_by_expression [,...n] [WITH (CUBE | ROLLUP}]
Перед тем, как начать рассмотрение параметров раздела GROUP BY, сначала расскажем об особенностях работы с разделом SELECT при использовании раздела GROUP BY. В раздел SELECT нельзя включать непосредственно имена столбцов, не указанных в разделе GROUP BY. Также нельзя использовать имена таких столбцов в любых выражениях. В непосредственном виде допускается применение только имен столбцов, по которым выполняется агрегирование
Однако в разделе SELECT ПОМИМО выражения группировки могут существовать специальные функции агрегирования, возвращающие для всех строк группы единственное значение, которое и включается в результат выборки Непосредственное использование имен столбцов в разделе SELECT при выполнении группировки не допускается, т. к. в каждой группе может содержаться более одной строки и нельзя отдать предпочтение какой-то конкретной строке Приведем список функций агрегирования с кратким описанием каждой из них
□   COUNT (coiumn_name).  Возвращает количество строк в группе с не пустым значением (не NULL) В указанном столбце.
□  COUNT(*). Возвращает общее количество строк в группе, включая строки с неопределенным значением (NULL).
О MAX (column_name). Возвращает максимальное значение в указанном столбце в пределах группы.
□  MIN (column_name). Возвращает минимальное значение в указанном столбце в пределах группы.
О SUM(column_name). Возвращает сумму всех значений в пределах группы в указанном столбце. Эта функция может применяться только к столбцам с числовым типом данных.
□  AVG (column_name).   Возвращает   среднее   арифметическое   для   указанного столбца в пределах строк, принадлежащих одной группе. Эта функция может применяться только к столбцам с числовым типом данных.
Замечание
При организации группировки следует учитывать, что при агрегировании пропускаются значения NULL. В противном же случае могла возникнуть ситуация, когда результатом выполнения функции агрегирования было бы значение NULL Дело в том, что все операции с участием NULL возвращают NULL. Например, если выполнить сложение 45 и NULL, TO результатом этой операции будет NULL Особенности обработки значений NULL следует учитывать при использовании функций COUNT(column_name) И AVG(column_name).
Помимо указания в разделе SELECT непосредственно функций агрегирования, также допускается использование различных выражений, построенных на основе этих функций и столбцов, по которым осуществляется группировка. Кроме того, функции агрегирования могут применяться не только непосредственно к определенному столбцу, но и к различным выражениям, построенным на основе этих столбцов. Приведем несколько примеров, демонстрирующих сказанное: SELECT price,  price*MAX(ytd_sales),  MIN(type)   FROM titles GROUP ВУ price

Очередной   запрос   демонстрирует   разницу   между   использованием   функций
COUNT(column_name)  И COUNT(*):
SELECT type,   count(price),   count(*)   FROM titles  GROUP BY type

Следующий пример демонстрирует использование функций агрегирования не к отдельным столбцам, а к различным выражениям, построенным на основе этих столбцов:
SELECT type,   SUM(price*ytd_sales*0.78)-MAX(price*ytd_sales*0.78) , CASE WHEN AVG((ytd_sales-advance))<0
THEN   'Тираж продается не полностью'
ELSE   'Тираж продается успешно'   END FROM titles GROUP BY type
Будет возвращен результат:

Теперь же рассмотрим назначение и использование параметров раздела GROUP BY:
group_by_expression
С помощью этого параметра указывается выражение, по которому будет выполняться группировка. В качестве такого выражения может выступать как имя отдельного столбца, так и сложное выражение, в котором используются ссылки на несколько столбцов и различные операторы. Желательно, чтобы выражение возвращало ограниченный набор вариантов, каждому из которых соответствовало бы несколько строк. Выражение, по которому производится группировка, может быть включено в раздел SELECT. Дополнительно этому выражению можно присвоить псевдоним.
Приведем пример группировки строк таблицы authors по столбцу state с вычислением количества авторов, проживающих в каждом из штатов:
SELECT state, count(*) FROM authors GROUP BY state
В этом примере в качестве выражения группировки использовалось имя столбца. Следующий пример осуществляет группировку по первой букве названия книг:
SELECT [Char]=LEFT(title,1), COUNT(*) FROM titles GROUP BY LEFT(title,1)

В следующем примере выполняется группировка книг по общей сумме их продаж. Создается четыре интервала, в каждом из которых посчитывается количество книг и сумма, на которую в общей сложности было продано книг в данной группе.
SELECT   [Диапазон]=
(CASE   (CASE WHEN price*ytd_sales<10000 THEN  1
WHEN pnce*ytd_sales<20000 THEN 2 WHEN pnce*ytd_sales<30000 THEN 3 ELSE 4 END)
WHEN 1 THEN 'Продано < $10000' WHEN 2 THEN 'Продано от $10000 до $20000' WHEN 3 THEN 'Продано от $20000 до $30000' WHEN 4 THEN 'Продано более чем на $30000' END), [Кол-во книг]=СOUNТ(*) ,
[На общую сумму]=SUM(price*ytd_sales) FROM titles GROUP BY (CASE WHEN price*ytd_sales<10000 THEN 1 WHEN price*ytd_sales<20000 THEN 2 WHEN price*ytd_sales<30000 THEN 3 ELSE 4 END)
Будет возвращен результат:
Диапазон                                            Кол-во книг На общую сумму
Продано < $10000                           3                        16729.5000
Продано от $20000 до   $30000 2                         49047.3900
Продано более чем на   $30000 13                      979731.8300 (3 row(s) affected)
ALL
При использовании этого параметра в результат выборки будут включены все группы, независимо от того, соответствуют ли связанные с ними данные существующим в разделе WHERE условиям выборки. В строках, которые не соответствуют условиям выборки, во всех столбцах кроме столбцов, по которым выполняется группировка, будут выведены значения NULL
Для сравнения сначала рассмотрим запрос без использования параметра ALL:
SELECT state, [Кол-во]=COUNT(state), MAX(city) FROM authors WHERE city LIKE '[CS]%' GROUP BY state
Этот запрос выводит информацию по количеству авторов, проживающих в каждом штате в городах, начинающихся с буквы с или s В качестве примера использования функции агрегирования приводится функция МАХ.

Теперь в тот же запрос добавим параметр ALL:
SELECT state, [Кол-во]=COUNT(state), MAX(city) FROM authors WHERE city LIKE '[CS]%' GROUP BY ALL state
Будет получен такой результат:
State Кол-во
СА    3    San Jose
IN    0    NULL
KS    0    NOLL
MD    0    NULL-
MI    0    NULL
OR    1    Corvallis
TN    0    NULL
UT    2    Salt Lake City
(8  row(s)   affected)
Как видно, в результат выборки были включены все возможные значения выражений группировки. Однако для строк, не удовлетворяющих условию раздела WHERE, выведены значения NULL, а количество строк равно 0 Тем не менее, пользователь имеет представление обо всех возможных вариантах группировки.
□   WITH    {CUBE    |    ROLLUP}
С помощью этой конструкции выполняется суперагрегирование данных. Обычные функции агрегирования применяются к строкам группы и возвращают единственное значение для всей группы При использовании операторов CUBE и ROLLUP происходит вычисление функции агрегирования для значений, возвращенных для каждой группы
Для сравнения приведем пример без использования функций суперагрегирования'
SELECT type, MAX(price), MIN(price) FROM titles GROUP BY type
Будет возвращен результат
(6 row(s)   affected)
Теперь добавим функцию суперагрегирования ROLLUP:
SELECT type, max (price), mm (price) FROM titles GROUP BY type WITH ROLLUP
После этого результат будет выглядеть так:
(7  row(s)   affected)
type       
business    19.9900    2.9900
mod cook    19.9900    2.9900
popular_comp    22.9500    20.0000
psychology    21.5900    7.0000
trad cook    20.9500    11.9500
UNDECIDED    NULL    NULL
type       
business    19.9900    2.9900
moa_cook    19.9900    2.9900
popular_comp    22.9500    20.0000
psychology    21.5900    7.0000
trad cook    20.9500    11.9500
UNDECIDED    NULL    NULL
NULL    22.9500    2.9900
Как видно, была добавлена дополнительная строка (самая нижняя), в которой выводится результат выполнения функции агрегирования ко всем группам. Использование функции CUBE даст в этом запросе точно такой же результат Более интересные сведения отображаются при выполнении группировки по двум и более выражениям. Сначала опять же рассмотрим запрос без использования функций суперагрегирования:
SELECT type,   pub_id,    [Объем продаж]=SUM(price*ytd_sales), [Сред,   цена]=AVG(price),   [Макс,   тираж]=MAX(ytd_sales) FROM titles GROUP BY type,   pub_id
Будет возвращен результат:
type    pub_id    Объем продаж    Сред. цена    Макс. тираж
business    0736    55978.7800    2.9900    18722
psychology    0736    131223.6700    11.4825    4072
mod cook    0877    107135.2200    11.4900    22246
psychology    0877    8096.2500    21.5900    375
trad cook    0877    249637.5000    15.9633    15096
UNDECIDED    0877    NULL    NULL    NULL
business    1389    210036.3000    17.3100    4095
popular_comp    1389    283401.0000    21.4750    8780
(8 row(s) affected)           
Теперь добавим функцию суперагрегирования ROLLUP:
SELECT type, pub_id, [Объем продаж]=SUM(price*ytd_sales), [Сред, цена]=AVG(price), [Макс, тираж]=MAX(ytd_sales) FROM titles GROUP BY type, pub_id WITH ROLLUP
Будет получен следующий результат:
type    pub_id Объем продаж    Сред. цена    Макс. тираж
business    0736    55978.7800    2.9900    18722
business    1389    210036.3000    17.3100    4095
business    NULL    266015.0800    13.7300    18722
mod cook    0877    107135.2200    11.4900    22246
mod cook    NULL    107135.2200    11.4900    22246
popular_comp    1389    283401.0000    21.4750    8780
popular_comp    NULL    283401.0000    21.4750    8780
psychology    0736    131223.6700    11.4825    4072
psychology    0877    8096.2500    21.5900    375
psychology    NULL    139319.9200    13.5040    4072
trad cook    0877    249637.5000    15.9633    15096
trad cook    NULL    249637.5000    15.9633    15096
UNDECIDED    0877    NULL    NULL    NULL
UNDECIDED    NULL    NULL    NULL    NULL
NULL    NULL    1045508.7200    14.7662    22246
(15  row(s)   affected)
Как видно, в результате добавилось семь строк (отмечены жирным шрифтом). Для каждой из групп по столбцу type была добавлена строка с результатом вы-
полнения функции агрегирования по столбцу pub_id. Так как агрегирование выполняется по столбцу pub_id, то в нем находится значение NULL, ПО которому можно легко определить строки, полученные в результате суперагрегирования. Например, для группы business в строке, являющейся результатом суперагрегирования, для столбца объем продаж выведено значение 266015.0800, что является результатом выполнения функции агрегирования SUM (сумма) для всех строк исходных данных (не результата), относящихся к группе business в целом. В столбце Сред, цена указывается результат функции AVG (среднее) для столбца price исходных данных со значением business в столбце type. На примере средней цены видно, что результат суперагрегирования получается на основе исходных данных, а не результата агрегирования. В столбце макс. тираж выводится максимальное число по столбцу ytd_sales, получаемое в результате выполнения функции агрегирования МАХ (максимум).
В итоге в результат были добавлены шесть строк с результатами суперагрегирования. Самая последняя строка содержит результат применения функций агрегирования ко всем данным в таблице, а не к отдельным группам. Так как функции агрегирования применяются ко всему набору исходных данных, то в каждом столбце, по которым выполняется агрегирование, стоит значение NULL.
Итак, итогом использования оператора WITH ROLLUP является суперагрегирование по выражениям, на основании которых производится агрегирование. Но суперагрегирование не выполняется по первому выражению. Однако в некоторых ситуациях необходимо произвести суперагрегирование по всем выражениям, участвующим в группировке. Для этого служит оператор WITH CUBE. Перепишем предыдущий запрос для использования оператора WITH CUBE:
SELECT type, pub_id, [Объем продаж]=SUM(price*ytd_sales), [Сред. цена]=AVG(price), [Макс, тираж]=MAX(ytd_sales) FROM titles GROUP BY type, pub_id WITH CUBE
Будет получен следующий результат:
type    pub_id Объем продаж    Сред. цена    Макс, тираж
business    0736    55978.7800    2.9900    18722
business    1389    210036.3000    17.3100    4095
business    NULL    266015.0800    13.7300    18722
mod cook    0877    107135.2200    11.4900    22246
mod cook    NULL    107135.2200    11.4900    22246
popular_comp    1389    283401.0000    21.4750    8780
popular_comp    NULL    283401.0000    21.4750    8780
psychology    0736    131223.6700    11.4825    4072
psychology    0877    8096.2500    21.5900    375
psychology    NULL    139319.9200    13.5040    4072
trad cook    0877    249637.5000    15.9633    15096
trad cook    NULL    249637.5000    15.9633    15096
UNDECIDED    0877    NULL    NULL    NULL
UNDECIDED    NULL    NULL    NULL    NULL
NULL    NULL    1045508.7200    14.7662    22246
NULL    0736    187202.4500    9.7840    18722
NULL    0877    364868.9700    15.4100    22246
NULL    1389    493437.3000    18.9760    8780
(18 row(s)   affected)
По сравнению с предыдущим примером в результат выборки добавилось еще три строки. Эти строки являются результатом агрегирования выборки по столбцу pub_id, когда посчитываются значения функций агрегирования для всех строк с одинаковым значением в столбце pub_id. Новые строки были добавлены в самый конец выборки.

Раздел HAVING

Этот раздел обычно используется совместно с разделом GROUP BY, ДОПОЛНЯЯ его. Назначением раздела HAVING является ограничение набора строк, подвергаемых группировке. По своим функциям раздел HAVING очень близок к разделу WHERE. Синтаксис раздела HAVING таков:
HAVING <search_condition>
Применение конструкции <search_condition> при работе с разделом HAVING ничем не отличается от указания этой же конструкции при работе с разделом WHERE. Более того, если раздел HAVING указывается без раздела GROUP BY, TO оптимизатор запросов автоматически заменяет раздел HAVING аналогичным разделом WHERE.
Однако раздел HAVING обладает некоторыми специфическими чертами. На условие, указанное в разделе HAVING, не действует параметр ALL, С ПОМОЩЬЮ которого в результат выборки включаются все группы, в том числе и не содержащие ни одной строки вследствие существующих логических условий. Помимо этого, установленные с помощью раздела HAVING условия игнорируются при суперагрегировании данных с помощью WITH CUBE.
В одном запросе допускается использование как раздела WHERE, так и раздела HAVING. При этом каждый из них будет вести себя по-своему. Приведенный ниже пример демонстрирует применение в одном запросе обоих разделов и иллюстрирует поведение сервера при обработке условий совместно с параметром ALL:
SELECT state,   count(au_lname),  max(au_lname)
FROM authors WHERE  state'='CA'   GROUP BY ALL state  HAVING  state!='UT'
Будет возвращен результат:
state
CA    0    NULL
IN    1    DeFrance
KS    1    Smith
MD    1    Panteley
MI    1    del Castillo
OR    1    Blotchet-Halls
TN    1    Greene
(7  row(s)   affected)
Для сравнения выполним тот же запрос без разделов WHERE И HAVING
SELECT state, count(au_lname), max(au_lname) FROM authors GROUP BY ALL state
Будет возвращен такой результат:
state
СА    15    Yokomoto
IN    1    DeFrance
KS    1    Smith
MD    1    Panteley
MI    1    del Castillo
OR    1    Blotchet-Halls
TN    1    Greene
UT    2    Ringer
(8  row(s)   affected)
Как видно, в первом запросе в разделе WHERE существовало ограничение, запрещающее обработку строк об авторах, проживающих в Калифорнии (СА) Однако в разделе GROUP BY был указан параметр ALL, предписывающий выводить все группы несмотря на наложенные логические условия. В итоге результат выполнения запроса содержит строку для штата Калифорния, но самих данных нет. В разделе HAVING было указано дополнительное условие для игнорирования авторов из штата Юта (UT). В итоге результат выборки вообще не содержит никакого упоминания о штате Юта несмотря на использование параметра ALL.

Раздел UNION

Иногда бывает необходимо в одном запросе объединить данные нескольких таблиц. Речь идет не об объединении столбцов таблиц, а о слиянии строк двух и более таблиц в один массив строк Примером необходимости такого объединения может служить формирование отчета за год, когда данные за каждый месяц находятся в отдельной таблице. Другой пример — анализ данных различных подразделений, когда данные размещаются в разных базах данных и возможно на различных серверах.
Объединение строк двух таблиц в один массив строк выполняется с помощью раздела UNION, имеющего следующую структуру:
{<query specification> | (<query expression>)} UNION [ALL]
<query specification | (<query expression>)
[UNION [ALL] <query specification | (<query expressions-)
[...n]]
Рассмотрим назначение и использование упомянутых параметров:
П <query specification> | (<query expression>)
С помощью этих конструкций указывается запрос, возвращаемые строки которого будут объединены со строками, возвращенными другим запросом. Как видно из синтаксиса, при использовании раздела UNION ДОЛЖНЫ быть
указаны два запроса, возвращающих одинаковое количество столбцов, причем столбцы должны иметь совместимые типы данных и располагаться в одном и том же порядке. Запрос может быть как очень простым (SELECT * FROM <table_name>), так и очень сложным с применением всех описанных ранее разделов.
□   ALL
По умолчанию в объединение не допускается вставка повторяющихся строк. То есть, если второй запрос возвращает строку, идентичную одной из строк, образованных первым запросом, то такая строка не будет включена в объединение. Однако с помощью параметра ALL МОЖНО заставить сервер включать в объединение все строки, возвращенные запросами, независимо от того, повторяются они или нет.
С помощью раздела UNION МОЖНО объединять более двух запросов. Для этого после второго запроса достаточно указать ключевое слово UNION И создать третий запрос. Таким же образом можно добавить четвертый запрос, пятый и т д. Общее количество объединяемых запросов может составлять несколько десятков. Однако на практике обычно количество объединяемых запросов не превышает пяти.
Имена столбцов объединения будут соответствовать именам столбцов, возвращаемых первым запросом. Имена столбцов остальных запросов игнорируются. Как уже было сказано, количество столбцов, возвращаемых всеми объединяемыми запросами, должно быть одним и тем же. Кроме того, порядок перечисления этих столбцов в разделе SELECT ВО всех запросах должен быть одинаков. Типы данных столбцов запросов, данные из которых будут объединены в один столбец, должны быть совместимыми. В идеале типы данных должны быть полностью одинаковы, но все же допускаются некоторые расхождения.
При объединении двух символьных столбцов (char, varchar, nchar и nvarchar) разной длины столбец объединения будет иметь тип данных, соответствующий строке большей длины. Также при объединении двоичных данных (binary и varbinary) столбец результата получит тип данных, соответствующий наибольшему из объединяемых столбцов. При объединении столбцов нецелочисленных ТИПОВ данных (money, smallmoney, float, numeric И decimal) столбец результата будет иметь тип данных, соответствующий исходному типу данных с наибольшей точностью. Кроме того, аналогичный результат будет и при объединении целочисленных данных (int, smallint и tinyint) с нецелочисленными — объединение станет иметь нецелочисленный тип данных с наибольшей точностью. Если все соответствующие столбцы исходных запросов запрещают хранение значений NULL, ТО И В соответствующем столбце результата хранение значений NULL будет запрещено. Однако если хоть один из объединяемых столбцов разрешает хранение NULL, ТО И В объединении для соответствующего столбца будет разрешено хранение NULL.
При попытке объединения столбцов с несовместимыми типами данных сервер выдаст сообщение об ошибке.
Рассмотрим использование раздела UNION ДЛЯ СЛИЯНИЯ данных из title_business и title_psychology. Для этого сначала создадим эти таблицы на основе таблицы titles, выбрав из нее строки, относящиеся в каждой из серий:
SELECT title_id,   title,  price,   ytd_sales INTO titles_business
FROM titles WHERE type='business' SELECT title_id,   title,   price,   ytd_sales  INTO titles_psychology
FROM titles WHERE type='psychology'
Эти два запроса создают необходимые таблицы и копируют в них нужные данные. Просмотрим содержимое этих таблиц:
SELECT * FROM titles_psychology Будет получен следующий результат:
(5  row(s)   affected)
Теперь просмотрим содержимое таблицы titles_business:
SELECT *  FROM titles business
title id title    price    ytd_sales
PS1372  Computer Phobic AND Non-Phobic Indiuals:    21.5900    375
PS2O91  Is Anger the Enemy?    10.9500    2045
PS21O6  Life Without Fear    7.0000    111
PS3333  Prolonged Data Deprivation: Four CaStudie    19.9900    4072
PS7777  Emotional Security: A New Alaorithm    7.9900    3336
Будет получен результат:
title_id    title    price    ytd_sales
BU1032    The Busy Executive's Database Guide    19.9900    4095
BU1111    Cooking with Computers: Surreptitious    11.9500    3876
BU2075    You Can Combat Computer Stress!    2.9900    18722
BU7832    Straight Talk About Computers    19.9900    4095
(4 row(s)   affected)
Теперь можно приступать к объединению данных:
SELECT title_id,   title=CAST(title as char(30))+
CASE  WHEN  LEN(title)>30  THEN   '...'   ELSE   "   END, value=pnce*ytd_sales,   type='psychology' FROM titles_psychology UNION SELECT title_id,   title=CAST(title as char(30))+
CASE WHEN  LEN(title)>30  THEN   '...'   ELSE   ''   END, value=price*ytd_sales,  type='business' FROM titles_business
Этот запрос демонстрирует не только простое слияние данных двух таблиц, но и предварительную обработку этих данных перед слиянием. В частности, только данные столбца title_id не были подвергнуты изменениям. Все остальные столбцы сформированы на основе различных выражений. В результате выполнения запроса будет получен следующий результат:
title_id title                                                               value         type
BU1032       The Busy Executive's Database ...   81859.0500               business
BU1111       Cooking with Computers: Surrep...   46318.2000                business
BU2075       You Can Combat Computer Stress...   55978.7800               business
BU7832       Straight Talk About Computers           81859.0500               business
PS3333       Prolonged Data Deprivation: Fo...   81399.2800               psychology
PS1372       Computer Phobic AND Non-Phobic...   8096.2500                  psychology
PS2091       Is Anger the Enemy?                                 22392.7500               psychology
PS7777       Emotional Security: A New Algo. ... 26654 .6400               psychology
PS2106       Life Without Fear                           777.0000                    psychology (9 row(s) affected)
В предыдущем запросе было рассмотрено слияние однотипных данных, однако в принципе не важно, какие данных объединяются. Важно лишь, чтобы они имели одинаковую структуру. Следующий пример демонстрирует объединение данных из таблиц с разной структурой. Кроме того, нет никакого смысла в выполняемом объединении, и оно производится только для демонстрации возможностей раздела UNION:
SELECT title_id,   title,  price FROM titles WHERE type='popular_comp'
UNION
SELECT au_id, au_lname, contract FROM authors
WHERE State NOT IN ('CA', 'UT', 'KS') UNION SELECT phone, type, contract*ytd_sales*0.78
FROM authors a, titleauthor ta, titles t
WHERE state NOT IN 'CA', 'UT', 'KS') AND ta.au_id=a.au_id AND t.title_id=ta.title_id
Будет получен результат:
title_id    title                                                                      price
219 547-9982 mod_cook                                                               17351.88
301 94 6-8853 trad_cook                                                             292.50
503 745-6402 trad_cook                                                             11774.88
527-72-324 6 Greene                                                                    .00
615 996-8275 mod_cook                                                               1584.96
648-92-1872 Blotchet-Halls                                                   1.00
712-45-1867 del Castillo                                                       1.00
722-51-5454 DeFrance                                                                1.00
807-91-6654  Panteley                                                               1.00
PC1035      But Is It User Friendly?                             22.95
PC8888     Secrets of Silicon Valley                          20.00
PC9999     Net Etiquette                                                    NULL (12 row(s) affected)
Как видно, первые строки объединения относятся к последнему запросу, затем идут строки второго запроса и только после этого включены строки первого запроса. Тем не менее, столбцы объединения имеют имена, соответствующие именам столбцов первого запроса.

Раздел ORDER BY

По умолчанию данные в результат выводятся в соответствии с их физическим расположением в таблице. Если в таблице не определен кластерный индекс, то данные будут выводиться беспорядочно. Наличие в таблице кластерного индекса приводит к физическому перестроению порядка данных в соответствии с указанными критериями. Таким образом, данные становятся упорядоченными. Однако в таблице возможно наличие только одного кластерного индекса, что означает существование сортировки только по одному столбцу. Тем не менее, часто возникает необходимость отсортировать данные по различным критериям, причем иногда по нескольким сразу.
Для выполнения сортировки данных, возвращаемых запросом, в распоряжении пользователей имеется раздел ORDER BY, специально предназначенный для определения параметров порядка вывода строк. Синтаксис этого раздела таков:
ORDER BY  {order_by_expression   [ASC   |   DESC]}   [,...n] Рассмотрим назначение аргументов раздела:
order_by_expression
Этот параметр определяет выражение, в соответствии с которым будет выполняться сортировка данных. В качестве выражения сортировки может указываться имя столбца, псевдоним или любое выражение. Также допускается указание номера столбца результата выборки, по которому следует осуществлять сортировку Следует отдельно обратить внимание, что сортировка не обязательно должна производиться по выражению, включенному в результат выборки.
ASC   |   DESC
Эти параметры указываются дополнительно к параметру order_by_expression и определяют порядок сортировки. Можно указать только один из параметров. При использовании ASC данные располагаются по возрастанию, тогда как параметр DESC определяет порядок сортировки по убыванию. Если порядок сортировки не указан явно, то по умолчанию данные будут располагаться по возрастанию.
а [,...п]
Этот параметр говорит о том, что в одном запросе сортировка может выполняться по нескольким критериям. Для этого необходимо через запятую указать выражения, по которым будет выполняться сортировка, а также дополнительно и порядок сортировки. Порядок указания выражений сортировки определяет приоритет того или иного выражения. Сначала сортировка производится по первому выражению. Затем выполняется дополнительная сортировка строк одной группы по второму выражению и т. д. Если указано только одно выражение, но существует множество строк, имеющих одинаковые значения выражения сортировки, то эти строки будут располагаться в соответствии с их физическим порядком в таблице.
Замечание
При использовании раздела ORDER BY совместно с разделом UNION сортировка осуществляется по всему массиву данных, а не по данным каждого из запросов. Это относится и ко всем разделам, рассмотренным ранее. То есть сначала идет обработка разделов SELECT, INTO, FROM И Т. Д., а уже потом обрабатывается раздел
ORDER   BY.
В качестве примера рассмотрим сортировку данных таблицы titles сначала по типу (столбец type), затем по номеру публикации (столбец pub_id) и потом по названию произведения (столбец title):
SELECT pub_id,   type,   title=CAST(title as char(30))+
CASE  WHEN  LEN(title)>30  THEN   '...'   ELSE   "   END,   price FROM titles ORDER BY type,   pub_id,   3
Будет возвращен результат:
(18  row(s)   affected)
В предыдущем примере сортировка производилась по столбцам, которые существуют физически и включены в результат. Следующий пример демонстрирует выполнение сортировки по выражению, не включенному в результат выборки и построенному на основе нескольких столбцов:
SELECT TOP 10 pub_id, type, title=CAST(title as char(30))+
CASE WHEN LEN(title)>30 THEN '...' ELSE '' END, price FROM titles ORDER BY ytd_sales%10*(price-10)
pub_id 0736    type business    title You Can Combat Computer Stress...    price 2.9900
1389    business    Cooking with Computers: Surrep...    11.9500
1389    business    Straight Talk About Computers    19.9900
1389    business    The Busy Executive's Database ...    19.9900
0877    mod cook    Silicon Valley Gastronomic Tre...    19.9900
0877    mod cook    The Gourmet Microwave    2.9900
1389    popular_comp    But Is It User Friendly?    22.9500
1389    popular_comp    Net Etiquette    NULL
1389    popular_comp    Secrets of Silicon Valley    20.0000
0736    psychology    Emotional Security: A New Algo...    7.9900
0736    psychology    Is Anger the Enemy?    10.9500
0736    psychology    Life Without Fear    7.0000
0736    psychology    Prolonged Data Deprivation: Fo. . .    19.9900
0877    psychology    Computer Phobic AND Non-Phobic...    21.5900
0877    trad cook    Fifty Years in Buckingham Pala...    11.9500
0877    trad cook    Onions, Leeks, and Garlic: Coo...    20.9500
0877    trad cook    Sushi, Anyone?    14.9900
0877    UNDECIDED    The Psvcholoav of Computer Coo...    NULL
Будет возвращен следующий результат:
pub_id    type    title    price
0877    UNDECIDED    The Psychology of Computer Coo...    NULL
1389    popular_comp    Net Etiquette    NULL
0877    mod_cook    The Gourmet Microwave    2.9900
0736    business    You Can Combat Computer Stress...    2.9900
0736    psychology    Emotional Security: A New Algo...    7.9900
0736    psychology    Life Without Fear    7.0000
1389    popular_comp    But Is It User Friendly?    22.9500
0736    psychology    Is Anger the Enemy?    10.9500
1389    business    Cooking with Computers: Surrep...    11.9500
0877    trad cook    Fifty Years in Buckingham Pala...    11.9500
(10 row(s)  affected)
Конечно, трудно найти смысл в такой сортировке. Однако приведенный запрос отлично демонстрирует использование в качестве критерия упорядочения произвольных выражений, не входящих в результат выборки.

Раздел COMPUTE

Этот раздел используется для агрегирования данных, содержащихся в результате выборки. Агрегирование выполняется по определенному столбцу результата выборки, причем сами данные остаются неизменными, а результат агрегирования добавляется отдельно. В разделе COMPUTE пользователь указывает функции агрегирования применительно к конкретным столбцам выборки.
Агрегирование с помощью раздела COMPUTE принципиально отличается от агрегирования данных с помощью раздела GROUP BY. В последнем выборка представляет только результат агрегирования, тогда как первый отображает сами данные и только как дополнительный параметр содержит результат агрегирования.
Наиболее простым и наглядным примером использования раздела COMPUTE является вывод суммы по столбцу. Часто в бухгалтерских отчетах внизу таблиц можно увидеть строку "Итого", в которой приводится сумма значений в колонках. Конечно, при работе с базами данных можно хранить строку "Итого" как отдельную строку таблицы, но при этом каждый раз при изменении данных нужно будет пересчитывать значения в этой строке. К тому же, если выполняется фильтрация данных, то значения в этой строке могут оказаться неверными.
Применение раздела COMPUTE позволяет получать нужные значения динамически на основании результата выборки, что обеспечивает генерирование правильного результата и возможность выполнения любых функций агрегирования по усмотрению пользователя. Раздел COMPUTE может быть с успехом использован совместно с разделом UNION, ЧТО открывает дополнительные возможности.
Приведем синтаксис раздела COMPUTE:
COMPUTE
{{ AVG | COUNT | MAX | MIN | STDEV | STDEVP |VAR | VARP | SUM}
(expression)} [,...n]
[BY expression [,...n]]
Рассмотрим назначение и использование параметров раздела:
О expression. Этот параметр задает выражение, для которого будет применяться функция агрегирования. Выражение может быть любой сложности, одна-
ко оно должно содержаться в результате выборки. Это значит, что параметр expression должен быть ничем иным, чем одним из столбцов результата. Если столбец формируется на основе результата вычисления выражения и для него не указан псевдоним, то ссылка на этот столбец в разделе COMPUTE возможна только с помощью выражения, на основе которого вычисляются значения для этого столбца. Если же столбец имеет конкретное имя (например, указанное с помощью псевдонима), то ссылка на этот столбец возможна и по имени.
□  AVG. Возвращает среднее арифметическое.
COUNT. Возвращает количество строк, в которых значение выражения агрегирования не равно NULL.
□  МАХ. Возвращает максимальное значение выражения во всех строках результата.
□  MIN. Возвращает минимальное значение выражения во всех строках результата.
□   STDEV. Возвращает статистическое стандартное отклонение для всех значений выражения.
□   STDEVP.   Возвращает  смещенную  оценку  стандартного   отклонения   (квадратный корень от значения, возвращаемого функцией VARP) ДЛЯ всех значений столбца.
□  VAR. Возвращает несмещенную оценку дисперсии величин для всех значений выражения.
□  VARP. Возвращает смещенную оценку дисперсии (второй центральный момент выборки относительно выборочного среднего) для всех значений выражения.
П SUM. Возвращает сумму всех значений выражения.
□   BY expression. Этот параметр позволяет применять функции агрегирования не ко всему набору данных, а к отдельным группам. Например, если анализируется таблица продаж товаров, то можно выполнить группировку по наименованию товара и подсчитать, какое количество товара было продано и на какую сумму. Параметр BY указывается после выражения (или выражений), по которому осуществляется агрегирование. Использование параметра BY требует обязательного указания раздела ORDER BY. Кроме того, выражений, по которому выполняется группировка, должно быть указано в разделе ORDER BY, Т. е. данные в результате должны быть отсортированы по выражению группировки. Если группировка выполняется по нескольким выражениям и после параметра BY указано более одного выражения, то каждое из этих выражений должно быть также приведено и в разделе ORDER BY. Причем порядок перечисления выражений после параметра BY и в разделе ORDER BY должен совпадать. При этом необязательно, чтобы после параметра BY были указаны все выражения, представленные в разделе ORDER BY. Тем не менее, не допускается пропуск параметров. Например, если используется ORDER BY column1, column2, column3, то все возможные варианты для раздела COMPUTE будут BY   column1,  column2, column3; BY  column1,  column2 И BY  column1.
Замечание
При наличии раздела COMPUTE не допускается применение параметра DISTINCT, запрещающего включение в результат выборки повторяющихся строк
Приведем несколько примеров использования раздела COMPUTE
SELECT title=CAST(title as char(30))+
CASE WHEN LEN(title)>30 THEN '...' ELSE " END, price, ytd_sales FROM titles COMPUTE AVG(price), SUM(ytd_sales), COUNT(ytd_sales)
Будет получен следующий результат:
title    price    ytd_sales
The Busy Executive's Database ...    19.9900    4095
Cooking with Computers: Surrep...    11.9500    3876
You Can Combat Computer Stress...    2.9900    18722
Straight Talk About Computers    19.9900    4095
Silicon Valley Gastronomic Tre...    19.9900    2032
The Gourmet Microwave    2.9900    22246
The Psychology of Computer Coo...    NULL    NULL
But Is It User Friendly?    22.9500    8780
Secrets of Silicon Valley    20.0000    4095
Net Etiquette    NULL    NULL
Computer Phobic AND Non-Phobic...    21.5900    375
Is Anger the Enemy?    10.9500    2045
Life Without Fear    7.0000    111
Prolonged Data Deprivation: Fo...    19.9900    4072
Emotional Security: A New Algo...    7.9900    3336
Onions, Leeks, and Garlic: Coo...    20.9500    375
Fifty Years in Buckingham Pala...    11.9500    15096
Sushi, Anyone?    14.9900    4095
avg 14.7662
sum
97446 cnt
16 (19 row(s) affected)
Следующий вариант демонстрирует использование параметра BY:
SELECT title=CAST(title as char(30))+
CASE WHEN LEN(title)>30 THEN '...' ELSE " END, type, price, ytd_sales
FROM titles ORDER BY type
COMPUTE AVG(price), SUM(ytd_sales) BY type
Будет возвращен следующий результат:
title    type    price    ytd_sales
The Busy Executive's Database ...    business    19.9900    4095
Cooking with Computers: Surrep...    business    11.9500    3876
You Can Combat Computer Stress...    business    2.9900    18722
Straight Talk About Computers    business    19.9900    4095
        avg   
        13.7300   
            sum
            30788
Silicon Valley Gastronomic Tre...    mod cook    19.9900    2032
The Gourmet Microwave    mod cook    2.9900    22246
        avg   
        11.4900   
            sum
            24278
But Is It User Friendly?    popular_comp    22.9500    8780
Secrets of Silicon Valley    popular_comp    20.0000    4095
Net Etiquette    popular_comp    NULL    NULL
!        avg   
        21.4750   
            sum
            12875
Computer Phobic AND Non-Phobic...    psychology    21.5900    375
Is Anger the Enemy?    psychology    10.9500    2045
Life Without Fear    psychology    7.0000    111
Prolonged Data Deprivation: Fo...    psychology    19.9900    4072
Emotional Security: A New -Algo...    psychology    7.9900    3336
        avg   
        13.5040   
            sum
            9939
Onions, Leeks, and Garlic: Coo...    trad cook    20.9500    375
Fifty Years in Buckingham Pala...    trad cook    11.9500    15096
Sushi, Anyone?    trad cook    14.9900    4095
        avg   
        15.9633   
            sum
            19566
The Psychology of Computer Coo...    UNDECIDED    NULL    NULL
        avg   
            sum
(24 row(s) affected)
Наконец, приведем пример с использованием группировки по двум столбцам:
SELECT TOP 10 title=CAST(title as char(30))+
CASE WHEN LEN(title)>30 THEN '...' ELSE " END,
type, pub_id, ytd_sales FROM titles
ORDER BY type, pub_id, price COMPUTE SUM(ytd_sales) BY type, pub_id
Будет возвращен результат:
title    type    pub_id    ytd_sales
You Can Combat Computer Stress...    business    0736    18722
            sum
            18722
Cooking with Computers: Surrep...    business    1389    3876
The Busy Executive's Database ...    business    1389    4095
Straight Talk About Computers    business    1389    4095
            sum
            12066
           
The Gourmet Microwave    mod cook    0877    22246
Silicon Valley Gastronomic Tre...    mod cook    0877    2032
            sum
            24278
Net Etiquette    popular_comp    1389    NULL
Secrets of Silicon Valley    popular_comp    1389    4095
But Is It User Friendly?    popular_comp    1389    8780
            sum
            12875
Life Without Fear    psychology    0736    111
            sum
            111
(15 row(s) affected)

Раздел FOR

Этот раздел применяется только в приложениях, работающих с DB-Library, a также при работе с XML. Синтаксис раздела таков:
[FOR {BROWSE | XML {RAW | AUTO | EXPLICIT}
[, XMLDATA] [, ELEMENTS] [, BINARY BASE64] }]
Указание ключевого слова BROWSE позволяет приложениям DB-Library выполнять изменение выбранных данных непосредственно через форму просмотра. Однако указания лишь параметра FOR BROWSE недостаточно. Необходимо выполнить еще некоторые требования:
□  таблица должна включать столбец timestamp;
□  в таблице должен быть создан уникальный индекс;
□  раздел FOR BROWSE должен быть последним разделом команды SELECT.
Обновление данных средствами браузера будет возможно только при соблюдении всех этих требований.
(         Замечание         )
Параметр FOR BROWSE не может применяться в запросе, который указан как подзапрос раздела UNION. Кроме того, совместно с разделом FOR BROWSE недопустимо использование хинта HOLDLOCK, предписывающего удерживать блокировку до конца транзакции
При указании параметра XML сервер будет выполнять преобразование выбираемых данных в формат документа XML, что позволяет просматривать данные с помощью Интернет-браузера. При этом SQL Server 2000 может использовать различные режимы преобразования:
□   RAW. Каждой строке результата выборки присваивается идентификатор.
□  AUTO. Результат выполнения запроса возвращается в виде простого дерева XML.
EXPLICIT. В этом случае форма документа XML определяется явно. Пользователь должен в запросе определить, какие элементы в какие ветви будут включены. Для этого первые два столбца результата выборки используются в качестве служебных.
Помимо определения режима преобразования данных можно также указать дополнительные параметры:
XMLDATA. При наличии этого ключевого слова в результат выборки не будет включено определение корневого элемента. Это необходимо при включении результата выполнения запроса в уже существующий документ. Если же параметр XMLDATA не указывается, то результат выборки представляет собой самостоятельный документ.
ELEMENTS. Столбцы будут возвращаться как подэлементы. Если же параметр ELEMENTS не указывается, то столбцы рассматриваются как атрибуты XML.
BINARY  BASE64. Указание этого параметра предписывает серверу возвратить данные в двоичном 64-битовом формате.

Раздел OPTION

Раздел OPTION предназначен для контроля хода выполнения запроса С помощью специальных хинтов (hint) пользователь может установить тот или иной режим слияния, объединения или агрегирования данных, а также контролировать некоторые другие тонкости выполнения запроса.
Раздел OPTION имеет следующую структуру:
OPTION (
({HASH | ORDER} GROUP | {CONCAT | HASH | MERGE) UNION
| ( LOOP | MERGE | HASH } JOIN
| FAST number_rows | FORCE ORDER
| MAXDOP number | ROBUST PLAN
| KEEP PLAN | KEEPFIXED PLAN | EXPAND VIEWS
[, ...n]})
Рассмотрим назначение и использование параметров раздела:
□    {HASH    |    ORDER}   GROUP
Эта конструкция определяет, какой алгоритм агрегирования будет применяться в разделах GROUP BY И COMPUTE. При указании HASH используется хэширование, тогда как указание ORDER приведет к обычному упорядочиванию данных.
{CONCAT | HASH | MERGE} UNION
С помощью этой конструкции указывается способ объединения данных при существовании раздела UNION.
□ {LOOP | MERGE | HASH} JOIN
Определяет способ связывания данных при использовании раздела JOIN.
□   FAST  number_rows
При задании этого хинта сервер оптимизирует запрос для быстрой выборки первых number_rows строк. После этого выполнение запроса продолжится в обычном режиме. Такой подход позволяет быстро получить первую порцию данных, не дожидаясь обработки значительной части запроса.
□   FORCE   ORDER
При указании этого хинта будет сохранен порядок связывания данных, приведенный в запросе. В противном случае оптимизатор запроса может использовать другой порядок связывания, отличный от указанного пользователем.
□   MAXDOP  number
Этот хинт разрешает явно указать стоимость распараллеливания запроса. Это позволяет переопределить значения, установленные с помощью хранимой процедуры sp_configure с использованием опции 'max degree of parallelism'.
□   ROBUST   PLAN
При указании этого хинта сервер выстраивает план выполнения запроса таким образом, чтобы обеспечить поддержку обработки данных большой длины. В
обычном режиме объем резервируемой памяти не позволяет обрабатывать строки большой длины, и при попытке выполнить запрос оптимизатор запросов (Query Optimizer) выдаст сообщение об ошибке еще на стадии создания плана выполнения запроса. Однако следует осторожно использовать ROBUST PLAN, т. к. в этом случае скорость выполнения запроса существенно падает.
□   KEEP   PLAN
Указание этого хинта предписывает серверу сохранить план исполнения запроса, подготовленный оптимизатором запроса. Точнее, снижается порог повторной компиляции плана выполнения запроса, что повышает производительность выполнения запросов. Порог определяет частоту перекомпиляции запроса в зависимости от объема изменений данных в таблице
□   KEEPFIXED   PLAN
Этот хинт предписывает серверу не выполнять перекомпиляции плана исполнения запроса даже при изменении статистики или индексов.
EXPAND   VIEWS
Данный хинт предписывает серверу при работе с индексированными представлениями не подставлять вместо имени представления запрос, на основе которого было построено соответствующее представление. Это гарантирует, что будут использованы все преимущества индексированных представлений.
[—n]
Этот параметр говорит о том, что в разделе OPTION может быть указано множество хинтов. Тем не менее, следует учитывать, что при использовании раздела UNION раздел OPTION может применяться только для последнего запроса и будет определять ход выполнения всех объединяемых запросов, рассматриваемых в конечном счете как один запрос, к которому и относится раздел
OPTION.

Ссылки по теме
Учебный курс - Основы SQL (http://www.intuit.ru/department/database/sql)