Глава 9. "Пристрелка" к нужным данным

В этой главе...

  • Указание требуемых таблиц
  • Отделение нужных строк от всех остальных
  • Создание эффективных предложений where
  • Как работать со значениями null
  • Создание составных выражений с логическими связками
  • Группирование вывода результата запроса по столбцу
  • Упорядочение результата запроса

У системы управления базой данных есть две главные обязанности: хранение данных и обеспечение к ним легкого доступа. В хранении данных нет ничего особенного, ту же работу может выполнять и шкаф. А что действительно трудно — так это обеспечить удобный доступ к данным. Для этого необходимо иметь возможность выловить небольшое количество нужных данных из океана ненужных.

SQL позволяет использовать некоторые характеристики самих данных для определения, представляют ли они для вас интерес. Операторы SELECT (выбрать), DELETE (удалить) и UPDATE (обновить) сообщают ядру (engine) базы данных, т.е. той части СУБД, которая как раз и взаимодействует с данными, какие именно строки необходимо выбрать, удалить или обновить. Чтобы обрабатывались требуемые строки, в операторы SELECT, DELETE и UPDATE добавляются уточняющие предложения.

Уточняющие предложения

В SQL имеются следующие уточняющие предложения: FROM, WHERE, HAVING, GROUP BY и ORDER BY. Предложение FROM (из) сообщает ядру базы данных, с какой таблицей (или таблицами) он должен работать. Что касается WHERE (где) и HAVING (при условии), то эти предложения указывают характеристику, определяющую необходимость выполнения текущей операции над конкретной строкой. И наконец, предложения GROUP BY (группировать по) и ORDER BY (упорядочивать по) указывают, каким образом следует выводить строки, полученные из базы данных. Основные сведения по уточняющим предложениям приведены в табл. 9.1.

Таблица 9.1. Уточняющие предложения и их назначение

Уточняющее предложение Для чего предназначено
from Указывает, из каких таблиц брать данные
where Фильтрует строки, которые не соответствуют условию поиска
group by Группирует строки в соответствии со значениями в столбцах группирования
having Фильтрует группы, которые не соответствуют условию поиска
order by Сортирует результаты предыдущих предложений перед получением
окончательного вывода

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

SELECT список_столбцов

    FROM список_таблиц

    [WHERE условие_поиска]

    [GROUP BY столбец_группирования]

    [HAVING условие_поиска]

    [ORDER BY условие_упорядочивания] ;

Ниже описывается, как работают уточняющие предложения.

  • Предложение WHERE — это фильтр, который выбирает строки, удовлетворяющие условию поиска, и отбрасывает все остальные.
  • Предложение GROUP BY создает группы из строк, отобранных с помощью предложения WHERE, каждая из которых соответствует какому-либо значению из столбца группирования.
  • Предложение HAVING — это другой фильтр, который обрабатывает каждую из групп, созданных с помощью предложения GROUP BY, и выбирает те из них, которые удовлетворяют условию поиска, отбрасывая все остальные.
  • Предложение ORDER BY сортирует все, что остается после того, как все предыдущие предложения проведут обработку таблицы (или таблиц).

Квадратные скобки ([]) означают, что предложения WHERE, GROUP BY, HAVING и ORDER BY не являются обязательными.

SQL выполняет эти предложения в следующем порядке: FROM, WHERE, GROUP BY, HAVING и SELECT. Предложения работают по принципу конвейера, когда каждое из них получает результат выполнения предыдущего предложения, обрабатывает этот результат и передает то, что получилось, следующему предложению. Если этот порядок выполнения переписать в виде функций, то он будет выглядеть следующим образом:

SELECT (HAVING (GROUP BY (WHERE (FROM...) ) ) )

Предложение ORDER BY выполняется уже после SELECT. Оно может обращаться только к тем столбцам, которые перечислены в списке, находящемся после SELECT. К другим же столбцам из таблиц, перечисленных в предложении FROM, предложение ORDER BY обращаться не может.

Предложения From

Предложение FROM легко понять, если в нем указана только одна таблица, как, например, в следующем примере:

SELECT * FROM SALES ;

Этот оператор возвращает все данные, находящиеся во всех строках каждого столбца таблицы SALES (продажи). Впрочем, в предложении FROM можно указывать больше, чем одну таблицу. Например:

SELECT *

    FROM CUSTOMER, SALES ;

Этот оператор создает виртуальную таблицу, в которой данные из таблицы CUSTOMER (покупатель) объединены с данными из таблицы SALES. Для создания новой таблицы каждая строка из CUSTOMER объединяется с каждой строкой из SALES. Поэтому в создаваемой таким способом новой виртуальной таблице количество строк равно количеству строк первой таблицы, умноженному на количество строк второй. И если в CUSTOMER десять строк, а в SALES — сто, то в новой таблице их будет тысяча.

Такая операция называется декартовым произведением двух исходных таблиц. Декартово произведение на самом деле является разновидностью операции объединения (JOIN). Об операциях объединения подробно рассказывается в главе 10.

В большей части приложений большинство тех строк, которые созданы в результате применения к двум таблицам декартова произведения, не имеют никакого смысла. Что касается виртуальной таблицы, созданной из CUSTOMER и SALES, то в ней представляют интерес только строки, в которых значение CustomerlD (идентификатор покупателя) из таблицы CUSTOMER равно значению CustomerlD из таблицы SALES. Все остальные строки можно отфильтровать с помощью предложения WHERE.

Предложения WHERE

В этой книге предложение WHERE использовалось много раз без всякого объяснения, потому что его значение и способ использования очевидны. Оператор выполняет операцию (такую как SELECT, DELETE или UPDATE) только с теми табличными строками, для которых определенное условие истинно. У предложения WHERE такой синтаксис:

SELECT список_столбцов

    FROM имя_таблицы

    WHERE условие ;

DELETE FROM имя_таблицы

    WHERE условие ;

UPDATE имя_таблицы

    SET столбец1=значение1, столбец2=значение2, ...,

        столбец_n=значение_n

    WHERE условие ;

Во всех случаях условие в предложении WHERE может быть или простым, или сколь угодно сложным. Чтобы из множества условий создать одно, их можно соединить друг с другом при помощи логических связок AND, OR и NOT. В этой главе мы еще вернемся к ним.

Вот некоторые типичные примеры предложений WHERE.

WHERE CustomerlD = SALES.CustomerlD

WHERE FOODS.Calories = COMIDA.Caloria

WHERE FOODS.Calories < 219

WHERE FOODS.Calories > 3 * base_value

WHERE FOODS.Calories < 219 AND FOODS.Protein > 27.4

Условия, выражаемые предложениями WHERE, называются предикатами. Предикат — это выражение, которое утверждает факт, относящийся к значениям из этого выражения.

Например, предикат FOODS.Calories < 219 является истинным, если в текущей строке значение столбца FOODS.Calories меньше 219. Если утверждение является истинным, то оно удовлетворяет условию. Утверждение может быть истинным (т.е. его значение равно True), ложным (его значение равно False) или с неопределенным логическим значением. Последний случай бывает тогда, когда в утверждении какие-либо элементы имеют значение NULL. Наиболее распространенными являются предикаты сравнения (=, <, >, О, <= и >=), но в SQL имеются и некоторые другие, которые значительно увеличивают возможности "отфильтровывать" требуемые данные от всех остальных. Ниже приведен список с предикатами, предоставляющими такую возможность.

  • Предикаты сравнения.
  • BETWEEN.
  • IN [NOT IN].
  • LIKE [NOT LIKE].
  • NULL.
  • ALL, SOME, ANY.
  • EXISTS.
  • UNIQUE.
  • OVERLAPS.
  • MATCH.
  • SIMILAR.
  • DISTINCT.

Предикаты сравнения

Примеры, приведенные в предыдущем разделе, демонстрируют обычное использование предикатов сравнения, в которых одно значение сравнивается с другим. Каждая строка, где в результате сравнения получается значение True, выполняет условие предложения WHERE, и с ней выполняется определенная операция (SELECT, UPDATE, DELECT и т.д.). Строки, где в результате сравнения получается значение False, пропускаются. Проанализируйте, например, следующий оператор SQL:

SELECT * FROM FOODS

    WHERE Calories < 219 ;

Этот оператор выводит все строки таблицы FOODS, в которых значение, хранящееся в столбце Calories, меньше 219.

В табл. 9.2 приведены шесть предикатов сравнения.

Таблица 9.2. Предикаты сравнения языка SQL

Сравнение Символ
Равно =
Не равно <>
Меньше <
Меньше или равно <=
Больше >
Больше или равно >=

BETWEEN

Иногда нужно выбрать ту строку, в которой значение какого-либо столбца входит в определенный диапазон. Один из способов это сделать — использовать предикаты сравнения. Можно, например, составить предложение WHERE, предназначенное для выбора всех строк таблицы FOODS, в которых значение, хранящееся в столбце CALORIES, больше 100 и меньше 300:

WHERE FOODS.Calories > 100 AND FOODS.Calories < 300

В это сравнение не включены продукты питания, содержащие в точности 100 или 300 калорий, — в нем имеются только те значения, которые находятся в промежутке между этими числами. Чтобы в сравнение попали и эти два значения, можно написать следующий оператор:

WHERE FOODS.Calories >= 100 AND FOODS.Calories <= 300

Другой способ указать диапазон, включая его границы, — использовать предикат BETWEEN (между):

WHERE FOODS.Calories BETWEEN 100 AND 3 00

Это предложение выполняет абсолютно те же функции, что и оператор в предыдущем примере, в котором используются предикаты сравнения. Как видите, эта формулировка позволяет ввести меньшее количество кода, а также она чуть более наглядная, чем та, в которой используются два предиката сравнения, соединенные логической связкой AND.

Внимание:Ключевое слово BETWEEN может привести к путанице, потому что неочевидно, включены ли в предложение границы диапазона. На самом деле границы в предложение включены. Кроме того, первая граница обязательно должна быть не больше второй. Если, например, в FOODS.Calories содержится значение 200, то следующее предложение возвращает значение True:

WHERE FOODS.Calories BETWEEN 100 AND 300

Однако предложение, казалось бы, эквивалентное предыдущему примеру, на самом деле возвращает противоположный результат False:

WHERE FOODS.Calories BETWEEN 300 AND 100

Помни:Для ключевого слова BETWEEN первая граница обязательно должна быть не больше второй.

Предикат BETWEEN можно использовать со следующими типами данных: символьными, битовыми, даты-времени, а также с числовыми. Вам могут встретиться примеры, похожие на следующий:

SELECT FirstName, LastName

    FROM CUSTOMER

    WHERE CUSTOMER.LastName BETWEEN 'A' AND 'Mzzz' ;

При его выполнении возвращаются данные обо всех покупателях, фамилии которых находятся в верхней половине списка по алфавиту.

IN и NOT IN

Предикаты IN (в) и NOT IN (не в) используются для работы с любыми указанными значениями, такими, например, как OR (Орегон), WA (Вашингтон) и ГО (Айдахо), из определенного набора значений, а именно штатов США. Например, у вас имеется таблица, в которой перечислены поставщики товаров, регулярно закупаемых вашей компанией. Вам нужно узнать телефонные номера тех поставщиков, которые размещаются в северной части Тихоокеанского побережья. Эти номера можно найти с помощью предикатов сравнения, например таких, которые показаны в следующем примере:

SELECT Company, Phone

    FROM SUPPLIER

    WHERE State = 'OR' OR State = 'WA' OR State = 'ID' ;

Впрочем, для выполнения той же самой задачи можно также использовать предикат IN:

SELECT Company, Phone

    FROM SUPPLIER

    WHERE State IN ('OR1, 'WA1, 'ID') ;

Это чуть более компактная форма записи, чем та, в которой используются предикаты сравнения и логические OR.

Таким же образом работает и второй вариант этого предиката — NOT IN. Скажем, у вас есть представительства в штатах Калифорния, Аризона и Нью-Мексико. Чтобы избежать уплаты налога с продаж, вы обдумываете возможность работы только с теми поставщиками, чьи представительства находятся за пределами этих трех штатов. Используйте следующую конструкцию:

SELECT Company, Phone

    FROM SUPPLIER

    WHERE State NOT IN CCA1, 'AZ', 'NM') ;

Используя таким образом ключевое слово IN, можно вводить чуть меньше кода. Впрочем, это не такое уж и большое преимущество. Как показано в первом примере этого раздела, примерно столько же придется потрудиться над кодом, если использовать предикаты сравнения.

Совет: Даже если предикат IN дает возможность вводить не намного меньше кода, то у вас все равно есть еще одна веская причина, чтобы использовать именно IN, а не предикаты сравнения. Ваша СУБД, скорее всего, поддерживает оба этих метода, и один из них может работать значительно быстрее другого. Эти два метода включения или исключения можно испытать в работе, а затем использовать тот из них, который покажет самый быстрый результат. А если в СУБД имеется хороший оптимизатор, то, независимо от вида используемых вами предикатов, во время работы, скорее всего, будет выбран самый оптимальный метод. Испытание этих двух методов как раз покажет, насколько хорошо оптимизирует код используемая СУБД. Если между временем выполнения двух операторов имеется значительная разница, то качество оптимизации под вопросом.

Кроме того, ключевое слово IN представляет ценность и в другой области. Если оно является частью подзапроса, то для получения результатов, которые нельзя добыть в одной таблице, ключевое слово IN позволяет выбрать информацию уже из двух таблиц. О подзапросах рассказывается в главе 11, а сейчас мы рассмотрим пример того, как в них используется ключевое слово IN.

Предположим, что вам нужно вывести имена всех тех, кто за последние 30 дней купил товар F-117A. Имена и фамилии покупателей находятся в таблице CUSTOMER (покупатель), а данные о сделках— в таблице TRANSACT (заключение сделок). Для этого вы сможете использовать следующий запрос:

SELECT FirstName, LastName

    FROM CUSTOMER

    WHERE CustomerlD IN

        (SELECT CustomerlD

            FROM TRANSACT

            WHERE ProductID = 'F-117A'

            AND TransDate >= (CurrentDate - 30)) ;

В первой из этих таблиц используются поля CustomerlD (идентификатор покупателя), FirstName (имя), LastName (фамилия), а во второй — CustomerlD, ProductID (идентификатор товара) и TransDate (дата сделки). Кроме того, используется переменная CurrentDate (текущая дата). Внутренний оператор SELECT, работающий с таблицей TRANSACT, вложен во внешний оператор SELECT, работающий с таблицей CUSTOMER. Первый из них ищет в столбце CustomerlD номера всех тех, кто за последние 30 дней купил товар F-l 17A. А внешний оператор SELECT выводит имена и фамилии всех покупателей, номера которых получены с помощью внутреннего оператора SELECT.

LIKE и NOT LIKE

Для сравнения двух символьных строк, чтобы выяснить их частичное соответствие друг другу, можно использовать предикат LIKE (похожий). Частичное соответствие представляет ценность тогда, когда о разыскиваемой строке что-то все-таки известно, но не известно, как она в точности выглядит. Кроме того, частичные соответствия можно использовать, чтобы получить из таблицы множество строк, в которых один из столбцов содержит похожие друг на друга символьные строки.

Чтобы указать частичные соответствия, в SQL используются два символа-маски (wildcard character). Знак процента (%) означает любую строку, состоящую из любого количества символов (в том числе и равного нулю). Символ подчеркивания О означает любой одиночный символ. Некоторые примеры того, как можно использовать предикат LIKE, показаны в табл. 9.3.

Таблица 9.3. Предикат like, используемый в SQL

Выражение Возвращаемые значения
WHERE WORD LIKE 'intern%' intern
  internal
  international
  internet
  interns
WHERE WORD LIKE '%Peace%' Justice of the Peace
  Peaceful Warrior
WHERE WORD LIKE 't_p_' tape
  taps
  tipi
  tips
  tops
  type

Предикат NOT LIKE (не похожий) дает возможность получить все строки, которые не удовлетворяют частичному соответствию, имеющему, как в следующем примере, не менее одного символа-маски:

WHERE PHONE NOT LIKE '503%'

В этом случае будут возвращены все строки таблицы, в которых телефонный номер, содержащийся в столбце PHONE (телефон), не начинается с 503.

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

SELECT Quote

    FROM BARTLETTS

    WHERE Quote LIKE '20#%'

    ESCAPE '#' ;

Символ % превращается в обычный из символа-маски с помощью стоящего перед ним символа #. Точно таким же способом можно отключить и символ подчеркивания, а также сам управляющий символ. Например, предшествующий запрос должен найти такую цитату из "Bartlett's Familiar Quotations" (Известные цитаты Бартлетта):

20% of the salespeople produce 80% of the results.

Данный запрос найдет также следующее:

20%

SIMILAR

Вместе с SQL: 1999 появился и предикат SIMILAR (подобный), который позволяет находить частичное соответствие более эффективно, чем это делает предикат LIKE. С помощью предиката SIMILAR можно сравнить символьную строку с регулярным выражением. Скажем, например, вы просматриваете в таблице программной совместимости столбец OperatingSys-tem (операционная система), чтобы проверить совместимость с Microsoft Windows. Можно составить примерно такое предложение WHERE:

WHERE OperatingSystem SIMILAR TO

    '(Windows (3.1195|98|Millenium Edition|CE|NT|2 000|XP)) '

В результате выполнения этого предиката будут возвращены все строки, у которых в столбце OperatingSystem содержится любая из указанных операционных систем Microsoft.

NULL

С помощью предиката NULL выполняется поиск всех тех строк, в которых выбранный столбец содержит неопределенное значение. Именно такие значения имелись в столбце Carbohydrate (углеводы) в нескольких строках таблицы FOODS (продукты питания) (см. главу7). Названия продуктов из этих строк можно получить с помощью такого оператора:

SELECT (FOOD)

     FROM FOODS

    WHERE Carbohydrate IS NULL ;

Этот запрос возвращает следующие значения:

Гамбургер с нежирной говядиной

Нежное мясо цыплят

Жареный опоссум

Свиной окорок

Как вы, возможно, и предполагаете, если вставить ключевое слово NOT (нет), то получится совершенно противоположный результат:

SELECT (FOOD)

    FROM FOODS

    WHERE Carbohydrate IS NOT NULL ;

Этот запрос возвращает все строки таблицы FOODS, за исключением тех четырех, которые были выведены предыдущим запросом.

Внимание: Выражение Carbohydrate IS NULL— это не то же самое, что CARBOHYDRATE = NULL. Для иллюстрации этого утверждения предположим, что в текущей строке таблицы FOODS значения в столбцах Carbohydrate и Protein (белки) являются неопределенными. Из этого можно сделать несколько выводов.

  • Carbohydrate IS NULL истинно.
  • Protein IS NULL истинно.
  • Carbohydrate IS NULL AND Protein IS NULL истинно.
  • Истинность Carbohydrate = Protein не может быть определена.
  • Carbohydrate = NULL является недопустимым выражением.

Использовать ключевое слов NULL в сравнениях бессмысленно, так как всегда возвращается ответ NULL.

Почему же истинность выражения Carbohydrate = Protein определяется как неизвестная, даже если Carbohydrate и Protein имеют одно и то же неопределенное значение? Да потому, что NULL просто означает "я не знаю". Вы же не знаете, каким должно быть значение в Carbohydrate, и не знаете, каким — в Protein. Следовательно, вам не известно, являются ли эти неизвестные значения одинаковыми. Возможно, в Carbohydrate следует ввести 37, а в Protein — 14, а может, у каждого из них должно быть значение 93. Если вам неизвестно количество углеводов и белков, то нельзя сказать, являются ли эти величины одинаковыми.

ALL, SOME, ANY

Тысячелетия назад греческий философ Аристотель сформулировал систему логики, ставшей основой значительной части западной мысли. Сущность этой логики состоит в следующем. Следует начать с набора посылок, о которых известно, что они истинные, затем применить к ним операции и вывести, таким образом, новые истины. Вот пример такой процедуры.

Посылка 1. Все греки —люди.

Посылка 2. Все люди смертны.

Заключение. Все греки смертны.

А вот еще пример.

Посылка 1. Некоторые греки — женщины.

Посылка 2. Все женщины — люди.

Заключение. Некоторые греки — люди.

Другой способ выражения идеи второго примера состоит в следующем.

Если какие-либо греки — женщины и все женщины — люди, то некоторые греки люди.

В первом примере в обеих посылках используется квантор всеобщности ALL (все), который дает возможность сделать в заключении разумный вывод обо всех греках. Во втором примере в одной из посылок используется квантор существования SOME (некоторые), который также позволяет сделать в заключении вывод обо всех греках. А в третьем примере, чтобы сделать то же заключение, что и во втором примере, используется квантор существования ANY (какие-либо) — синоним квантора SOME.

Посмотрите, как кванторы SOME, ANY и ALL применяются в SQL.

Рассмотрим пример с бейсбольной статистикой. Бейсбол — это вид спорта, требующий значительных физических нагрузок, особенно у питчера, т.е. игрока, подающего мяч. Ему за время игры приходится 90-150 раз бросать мяч со своей возвышенности до основной базы — места, где находится игрок с битой. Такие нагрузки очень утомляют, и часто получается так, что к концу игры питчера на подаче приходится заменять. Бессменно подавать мячи в течение всей игры — это уже выдающееся достижение, причем неважно, привели такие попытки к победе или нет.

Предположим, что вы собираете данные о количестве тех игр, в которых питчеры высшей лиги бессменно подавали мяч. В одной из ваших таблиц перечислены все питчеры Американской лиги, а в другой — Национальной лиги. В каждой из этих таблиц обо всех игроках хранятся следующие данные: имя игрока, его фамилия и количество игр, бессменно проведенных им на подаче

Возможная двусмысленность квантора any
Первоначально в SQL в качестве квантора существования использовалось слово ANY. Это использование оказалось достаточно запутанным и приводило к ошибкам, так как в английском языке слово any иногда означает всеобщность, а иногда — существование."Do any of you know where Baker Street is?" (Кто-нибудь из вас знает, где находится улица Бейкер-Стрит?) "I can eat more eggs than any of you." (Я могу съесть больше яиц, чем любой из вас.) В первом предложении, скорее всего, задается вопрос, есть ли хотя бы один человек, который знает, где находится улица Бейкер-Стрит. Any используется как квантор существования. Второе предложение — это хвастливое заявление о том, что я могу съесть больше яиц, чем самый большой едок из окружающих. В этом случае any используется как квантор всеобщности. Поэтому разработчики стандарта SQL-92 хотя и оставили в нем от предыдущих версий SQL слово ANY, чтобы была совместимость с предшествующими продуктами, но в то же время добавили его менее запутанный синоним — слово SOME. SQL:2OO3 также поддерживает оба квантора существования.

В Американской лиге разрешается, чтобы назначенный хиттер (designated hitter, DH) мог бить битой по мячу вместо одного из девяти игроков, играющих в обороне. (Кто такой хиттер? Это игрок с битой. А назначенный хиттер — это тот хиттер, которому не требуется играть в оборонительной позиции.) Обычно DH делают это вместо питчеров, потому что те, как известно, плохие хиттеры. Питчерам приходится тратить слишком много времени и усилий на совершенствование своего броска. Поэтому у них остается мало времени, чтобы тренироваться с битой, как это делают остальные игроки.

Скажем, вам теоретически известно, что в среднем у стартовых питчеров (питчеров, играющих с самого начала игры) Американской лиги насчитывается больше игр, в которых они бессменно подавали мяч, чем у стартовых питчеров Национальной лиги. Такой вывод основан на тех ваших наблюдениях, что назначенные хиттеры дают возможность хорошо бросающим, но слабым в обращении с битой питчерам Американской лиги оставаться на подаче до завершения игры. Так как DH уже работают с битой вместо них, то становится не важным, что питчеры — слабые хиттеры. Однако в Национальной лиге питчер, как правило, заменяется в конце игры хиттером, у которого обычно больше шансов подготовить лучший удар. Чтобы проверить свою теорию, вы составляете следующий запрос:

SELECT FirstName, LastName

    FROM AMERICAN_LEAGUER

    WHERE CompleteGames > ALL

        (SELECT CompleteGames

            FROM NATIONAL_LEAGUER) ;

Подзапрос (внутренний оператор SELECT) возвращает список, показывающий для каждого питчера Американской лиги количество тех игр, в которых он бессменно подавал мяч. Внешний же запрос возвращает имена и фамилии всех питчеров Американской лиги, которые, бессменно подавая мяч, сыграли больше игр, чем каждый питчер Национальной лиги. В этом запросе используется квантор всеобщности ALL. Таким образом, возвращаются имена и фамилии тех питчеров Американской лиги, которые имеют больше игр с бессменной подачей мяча, чем самый лучший по этому показателю питчер Национальной лиги.

Посмотрите на следующий похожий оператор:

SELECT FirstName, LastName

    FROM AMERICAN_LEAGUER

    WHERE CompleteGames > ANY

        (SELECT CompleteGames

        FROM NATIONAL_LEAGUER) ;

В этом случае вместо квантора всеобщности ALL используется квантор существования ANY. Подзапрос (внутренний, вложенный запрос) здесь такой же, как и в предыдущем примере. В результате выполнения этого подзапроса получается полный список игр, в течение которых питчеры Национальной лиги бессменно подавали мяч. А внешний подзапрос возвращает имена и фамилии всех тех питчеров Американской лиги, которые, бессменно подавая мяч, сыграли больше игр, чем какой-либо из питчеров Национальной лиги. Вот в этом-то запросе и используется квантор существования ANY. Вы можете быть уверены, что хотя бы один из питчеров Национальной лиги ни одной игры не провел бессменно на подаче. Поэтому результат, скорее всего, будет включать в себя всех питчеров Американской лиги, которые бессменно провели хотя бы одну игру.

Если заменить ключевое слово ANY эквивалентным ему SOME (какой-то), то результат будет точно такой же. И если истинно утверждение, что "хотя бы один питчер Национальной лиги ни одной игры бессменно не провел на подаче", то тогда можно сказать, что "какой-то питчер Национальной лиги ни одной игры не провел на подаче бессменно".

EXISTS

Для определения того, возвращает ли подзапрос какие-либо строки, вместе с ним можно использовать предикат EXISTS (существует). Если подзапрос возвращает хотя бы одну строку, то этот результат удовлетворяет условию EXISTS и выполняется внешний запрос. Ниже приведен пример использования предиката EXISTS.

SELECT FirstName, LastName

    FROM CUSTOMER

    WHERE EXISTS

        (SELECT DISTINCT CustomerlD

            FROM SALES

            WHERE SALES.CustomerID = CUSTOMER.CustomerlD)

В таблице SALES (продажи) хранятся данные обо всех продажах, выполненных компанией. В этой таблице в поле CustomerID находятся идентификаторы покупателей, которые участвовали в какой-нибудь из сделок. В таблице CUSTOMER (покупатель) хранятся имя и фамилия каждого покупателя, но нет никакой информации о конкретных сделках.

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

Предикат EXISTS, как показано в следующем запросе, эквивалентен сравнению
COUNT с нулем:

SELECT FirstName, LastName

    FROM CUSTOMER

    WHERE 0 <>

        (SELECT COUNT(*)

            FROM SALES

            WHERE SALES.CustomerID = CUSTOMER.CustomerlD);

Для каждой строки таблицы SALES, в которой значение CustomerlD равно какому-либо значению CustomerlD из таблицы CUSTOMER, этот оператор выводит столбцы FirstName (имя) и LastName (фамилия) из таблицы CUSTOMER. Поэтому для каждой сделки, отмеченной в таблице SALES, этот оператор выводит имя и фамилию того покупателя, который в ней участвовал.

UNIQUE

Вместе с подзапросом, как и предикат EXISTS, можно также использовать предикат UNIQUE (уникальный). Если первый из этих предикатов является истинным тогда, когда подзапрос возвращает хотя бы одну строку, то второй из них будет истинным тогда, когда среди возвращенных подзапросом строк нет двух одинаковых. Другими словами, предикат UNIQUE будет истинным, если все возвращаемые подзапросом строки будут уникальными. Проанализируйте следующий пример:

SELECT FirstName, LastName

    FROM CUSTOMER

    WHERE UNIQUE

        (SELECT CustomerID FROM SALES

        WHERE SALES.CustomerID = CUSTOMER.CustomerID);

Этот оператор возвращает только имена и фамилии всех новых покупателей, которые участвовали лишь в одной из сделок, указанных в таблице SALES. Два значения NULL считаются не равными друг другу и, следовательно, уникальными. И когда ключевое слово UNIQUE применяется к таблице, полученной в результате выполнения подзапроса, а в этой таблице никаких строк, кроме двух неопределенных, больше нет, то и тогда предикат UNIQUE является истинным.

DISTINCT

Предикат DISTINCT (отличающийся) похож на UNIQUE, за исключением отношения к значениям NULL. Если в таблице, полученной в результате выполнения подзапроса, все значения являются уникальными, тогда они отличаются друг от друга. Однако, в отличие от результата предиката UNIQUE, если к такой таблице применить ключевое слово DISTINCT, а в ней, кроме двух неопределенных, больше никаких строк нет, то предикат DISTINCT является ложным. Два значения NULL не считаются отличающимися друг от друга, хотя и считаются уникальными. Такая странная ситуация выглядит противоречиво, но этому есть свое объяснение. Дело в том, что в некоторых ситуациях два значения NULL должны считаться отличными друг от друга, а в некоторых — одинаковыми. Тогда в первом случае надо использовать UNIQUE, а во втором — DISTINCT.

OVERLAPS

Предикат OVERLAPS (перекрывает) применяется для того, чтобы определить, не перекрывают ли друг друга два промежутка времени. Он полезен тогда, когда нужно избежать "накладок" в расписании. Когда два промежутка времени перекрываются, то этот предикат возвращает значение True. Если они не перекрываются, то будет возвращено значение False.

Промежуток времени можно указать двумя способами: в виде начального и конечного времени или в виде начального времени и длительности. Вот несколько примеров:

(TIME '2:55:00', INTERVAL 'I' HOUR)

OVERLAPS

(TIME '3:30:00' , INTERVAL '2' HOUR)

В только что приведенном примере будет возвращено значение True, так как 3:30 наступает после 2:55 меньше чем через час.

(TIME '9:00:00', TIME '9:30:00')

OVERLAPS

(TIME '9:29:00', TIME '9:31:00')

Во втором примере будет возвращено значение True, потому что два промежутка времени перекрываются в течение одной минуты.

(TIME '9:00:00', TIME '10:00:00')

OVERLAPS

(TIME '10:15:00', INTERVAL '3' HOUR)

В третьем примере будет возвращено значение False, так как два промежутка времени не перекрываются.

(TIME '9:00:00', TIME '9:30:00')

OVERLAPS

(TIME '9:30:00', TIME '9:35:00')

И наконец, в последнем примере будет возвращено значение False — хотя два промежутка времени и являются смежными, но они не перекрываются.

MATCH

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

Скажем, вы, ведя свой бизнес, собираете данные о своих покупателях в таблицу CUSTOMER (покупатель), а данные о продажах заносите в таблицу SALES (продажи). Вам не хочется добавлять строку в SALES до тех пор, пока данные о покупателе, участвующем в соответствующей сделке, не появятся в таблице CUSTOMER. Вам также не хочется удалять из CUSTOMER данные о покупателе, если он участвовал в сделках, информация о которых все еще хранится в таблице SALES. Перед тем как выполнять вставку или удаление, вам, возможно, захочется проверить, не приведет ли к нарушениям целостности выполнение со строкой какой-либо из этих операций. Такую проверку может выполнить предикат MATCH (соответствие).

Как используется предикат MATCH, можно узнать с помощью примера, где применяются опять же таблицы CUSTOMER и SALES. CustomerED (идентификатор покупателя) — это первичный ключ таблицы CUSTOMER, и работает он как внешний ключ таблицы SALES. В каждой строке таблицы CUSTOMER должно быть уникальное значение CustomerlD, не равное NULL. А в таблице SALES ключ CustomerlD не является уникальным, потому что в ней повторяются его значения, относящиеся к тем, кто покупал больше одного раза. Это нормальная ситуация, которая не угрожает целостности, потому что в этой таблице CustomerlD является не первичным, а внешним ключом.

Совет: По-видимому, в столбце CustomerlD таблицы SALES могут быть и значения NULL, потому что кто-то может зайти к вам с улицы, купить что-то и выйти еще до того, как вы сможете ввести его или ее имя, фамилию и адрес в таблицу CUSTOMER. Тогда в дочерней таблице может появиться строка, у которой нет соответствующей строки в родительской таблице. Чтобы справиться с этой трудностью, можно включить в таблицу CUSTOMER строку для "общего" пользователя и заносить все эти анонимные продажи в базу на его идентификатор.

Скажем, к кассиру подходит покупательница и утверждает, что 18 мая 2003 года она купила истребитель-невидимку F-117A "Стеле". Теперь же она хочет вернуть самолет, потому что его, словно авианосец, видно на вражеских радарах. Ее заявление может быть подтверждено с помощью проверки вашей базы SALES с помощью MATCH. Прежде всего необходимо найти в столбце CustomerlD идентификатор покупательницы и присвоить его значение переменной .vcustid, а затем можно использовать следующий синтаксис, в котором применяются столбцы CustomerlD, ProductID (идентификатор товара), SaleDate (дата продажи):

... WHERE (-.vcustid, 'F-117-A1, '2003-05-18')

    MATCH

    (SELECT CustomerlD, ProductID, SaleDate

        FROM SALES).

Если есть запись о продаже с этим идентификатором пользователя, товаром и датой, то предикат MATCH возвращает значение True. А вы возвращаете покупательнице деньги. (Примечание: если какое-либо значение в первом аргументе предиката MATCH будет неопределенным, то всегда будет возвращаться значение True.)

Технические подробности: Разработчики языка SQL добавили в него предикаты MATCH и UNIQUE по одной и той же причине — эти предикаты дают возможность явно выполнять проверки, которые определены для неявных ограничений, связанных со ссылочной целостностью и уникальностью.

Предикат MATCH имеет такой общий вид:

Значение_типа_записи ROW MATCH [UNIQUE] [SIMPLE| PARTIAL |

    FULL ] Подзапрос

Ключи UNIQUE (уникальный), SIMPLE (простой), PARTIAL (частичный) и FULL (полный) связаны с правилами обработки выражения типа записи, имеющего столбцы с неопределенными значениями. Правила для предиката MATCH являются точной копией соответствующих правил ссылочной целостности.

Правила ссылочной целостности

Правила ссылочной целостности требуют, чтобы значения в столбце (или столбцах) одной таблицы соответствовали значениям в столбце (или столбцах) другой. Столбцы в первой таблице называются внешним ключом, а во второй — первичным, или уникальным, ключом. Например, столбец EmpDeptNo (номер отдела, где работает сотрудник) из таблицы EMPLOYEE (сотрудник) можно объявить внешним ключом, который ссылается на столбец DeptNo (номер отдела) из таблицы DEPT (отдел). Это соответствие дает гарантию, что когда в таблицу EMPLOYEE о сотруднике заносится информация, что он работает в отделе 123, то в таблице DEPT появляется запись, в которой значением столбца DeptNo является 123.

Такая ситуация является довольно простой, если внешний и первичный ключи состоят из одного столбца каждый. Однако оба эти ключа могут состоять также из множества столбцов. Например, значение в столбце DeptNo может быть уникальным только для одного и того же значения в столбце Location (представительство). Поэтому, чтобы однозначно определить строку из таблицы DEPT, необходимо указать значение и для столбца Location, и для столбца DeptNo. Если, например, отдел 123 имеется в двух представительствах, расположенных соответственно в Бостоне и в Тампе, то отделы необходимо указывать как ('Boston', '123') и ('Tampa', '123'). В таком случае для указания в таблице EMPLOYEE строки из таблицы DEPT необходимо использовать два столбца. Их можно назвать EmpLoc (представительство, где работает сотрудник) и EmpDeptNo. Если сотрудник работает в каком-либо отделе, расположенном в Бостоне, то значениями столбцов EmpLoc и EmpDeptNo будут соответственно Boston' и '123'. Таким образом, объявление внешнего ключа в EMPLOYEE будет следующим:

FOREIGN KEY (EmpLoc, EmpDeptNo)

    REFERENCES DEPT (Location, DeptNo)

Вывод правильных заключений из ваших данных в громадной степени усложняется, если в этих данных содержатся неопределенные значения. Иногда данные с такими значениями надо интерпретировать одним способом, а иногда — другим. Разные интерпретации данных, в которых встречаются значения NULL, можно задавать с помощью ключевых слов UNIQUE, SIMPLE, PARTIAL и FULL. Если в ваших данных нет неопределенных значений, то вы в значительной степени избавитесь от необходимости ломать голову, просто возьмете и перейдете к следующему разделу "Логические связки". Ну а если в ваших данных такие значения есть, то тогда от режима скорочтения сейчас лучше отказаться и начать медленно и внимательно читать последующие абзацы. В каждом из них описана отдельная ситуация, связанная со значениями NULL, и рассказывается, как с ней справляется предикат MATCH.

Если значения EmpLoc и EmpDeptNo вместе являются или не являются неопределенными, то правила ссылочной целостности будут такие же, как и для ключей, состоящих из одного столбца с неопределенными или определенными значениями. Но если значение EmpLoc неопределенное, a EmpDeptNo — нет или наоборот, то тогда нужны новые правила. И какие же правила нужны, когда в таблицу EMPLOYEE при вставке или обновлении ее строк вводятся значения EmpLoc и EmpDeptNo как (NULL, '123') или ('Boston', NULL)? Существует шесть вариантов, при которых используются SIMPLE, PARTIAL и FULL вместе с ключевым словом UNIQUE или без него. Присутствие этого ключевого слова означает следующее. Чтобы предикат был истинным, значение типа записи, найденное с помощью MATCH в таблице-результате выполнения подзапроса, должно быть уникальным. И если в значении выражения R, имеющем тип записи, оба компонента являются неопределенными, то предикат MATCH возвращает значение True, каким бы ни было содержимое сравниваемой таблицы, полученной при выполнении подзапроса.

Если в значении выражения R типа записи с ключевым словом SIMPLE, но без UNIQUE, ни один из компонентов не является неопределенным и при этом хотя бы одна строка в таблице, полученной при выполнении подзапроса, соответствует R, то предикат MATCH возвращает значение True. В противном случае он возвращает значение False.

Если в значении выражения R типа записи с ключевыми словами SIMPLE и UNIQUE ни один из компонентов не является неопределенным и при этом хотя бы одна строка в таблице, полученной при выполнении подзапроса, уникальна и соответствует R, то тогда предикат MATCH возвращает значение True. В противном случае он возвращает значение False.

Если в значении выражения R типа записи с ключевым словом SIMPLE какой-нибудь из компонентов является неопределенным, то предикат MATCH возвращает значение True.

Если в значении выражения R типа записи с ключевым словом PARTIAL, но без UNIQUE, какой-нибудь из компонентов не является неопределенным и при этом определенные значения хотя бы одной строки в таблице, полученной при выполнении подзапроса, соответствуют R, то тогда предикат MATCH возвращает значение True. В противном случае он возвращает значение False.

Если в значении выражения R типа записи с ключевыми словами PARTIAL и UNIQUE какой-нибудь из компонентов не является неопределенным и при этом определенные части R соответствуют определенным частям хотя бы одной уникальной строки в таблице, полученной при выполнении подзапроса, то тогда предикат MATCH возвращает значение True. В противном случае он возвращает значение False.

Если ни один из компонентов значения выражения R типа записи с ключом FULL, но без UNIQUE, не является неопределенным и при этом хотя бы одна строка в таблице, полученной при выполнении подзапроса, соответствует R, то тогда предикат MATCH возвращает значение True. В противном случае он возвращает значение False.

Если ни один из компонентов значения выражения R типа записи с ключами FULL и UNIQUE не является неопределенным и при этом хотя бы одна строка в таблице, полученной при выполнении подзапроса, уникальна и соответствует R, то тогда предикат MATCH возвращает значение True. В противном случае он возвращает значение False.

Если какой-либо из компонентов выражения R является неопределенным и указано ключевое слово FULL, то предикат MATCH возвращает значение False.

Правила комитета по стандартам
С появлением SQL-89 стало подразумеваться, что по умолчанию используется правило UNIQUE. Это случилось еще до того, как кто-либо успел предложить или обсудить другие варианты. Но такие предложения появились уже во время разработки SQL-92. Кто-то упорно предпочитал правила PARTIAL и считал, что они должны быть единственными. С этой точки зрения правила SQL-89 (UNIQUE) были настолько нежелательны, что рассматривались как ошибка, которую необходимо исправить с помощью правил PARTIAL. Были и те, кому больше нравились правила UNIQUE, а правила PARTIAL для них были непонятными, двусмысленными и неэффективными. Впрочем, были и те, кто предпочитал еще более строгие правила FULL. В конце концов этот спор был решен следующим образом: пользователи получили в свое распоряжение все три ключевых слова и теперь могли выбирать тот подход, который им нужен. А потом, с появлением SQL: 1999, добавились еще и правила SIMPLE. Впрочем, рост числа правил делает работу с неопределенными значениями какой угодно, но только не простой (simple). Итак, если не указаны ключевые слова SIMPLE, PARTIAL или FULL, то будут выполняться правила SIMPLE.

Логические связки

Как видно из массы предыдущих примеров, чтобы из таблицы получить нужные строки, одного условия в запросе часто бывает недостаточно. В некоторых случаях условий, которым должны удовлетворять строки, должно быть не меньше двух. В других же случаях, чтобы быть выбранной, строка должна удовлетворять одному из нескольких условий. А иногда нужно получить только те строки, которые указанному условию не удовлетворяют. Для выполнения этих требований в SQL имеются логические связки AND, OR и NOT.

AND

Если для получения строки необходимо, чтобы все условия из какого-либо их набора имели значение True, используйте логическую связку AND (и). Проанализируйте следующий пример, в нем используются поля InvoiceNo (номер счета-фактуры), SaleDate (дата продажи), Salesperson (продавец), TotalSale (всего продано) из таблицы SALES (продажи):

SELECT InvoiceNo, SaleDate, Salesperson, TotalSale

    FROM SALES

    WHERE SaleDate >= '2003-05-18'

    AND SaleDate <= '2003-05-24' ;

Предложение WHERE (где) должно соответствовать следующим двум условиям.

  • Дата SaleDate должна была наступить не раньше 18 мая 2003 года.
  • Дата SaleDate должна была наступить не позже 24 мая 2003 года.

Таким образом, обоим условиям будут одновременно соответствовать только те строки, в которых записаны данные о продажах в течение недели, прошедшей с 18 мая. Запрос возвратит именно эти строки.

Внимание:Обратите внимание, что связка AND (и) имеет чисто логическое значение. Такое ограничение иногда может привести к путанице, потому что союз "и" люди обычно используют в более широком смысле. Предположим, например, что ваш босс говорит: "Мне нужны данные о продажах, проведенных Фергюсоном и Фордом". А раз он сказал о "Фергюсоне и Форде", то вы, возможно, напишете следующий запрос SQL:

SELECT *

    FROM SALES

    WHERE Salesperson = 'Ferguson'

    AND Salesperson = 'Ford' ;

Только не несите его результаты своему боссу. Ладно? Тому, что он имел в виду, больше соответствует другой запрос:

SELECT *

    FROM SALES

    WHERE Salesperson IN ('Ferguson', 'Ford') ;

Первый запрос будет безрезультатным, потому что ни одну из продаж, отмеченных в таблице SALES, Фергюсон и Форд не провели вместе. Второй же запрос вернет информацию обо всех продажах, сделанных или Фергюсоном, или Фордом. Скорее всего, она-то и требовалась вашему боссу.

OR

Если для возвращения строки необходимо, чтобы из нескольких условий для этой строки было верно хотя бы одно, используйте логическую связку OR (или):

SELECT invoiceNo, SaleDate, Salesperson, TotalSale

    FROM SALES

    WHERE Salesperson = 'Ford'

    OR TotalSale > '200' ;

В результате выполнения этого запроса будут получены данные обо всех продажах, которые были сделаны на любую сумму, но только Фордом, или были сделаны кем угодно, но при этом превышали 200 долларов.

NOT

Для отрицания условия служит связка NOT (не). Если к условию, которое возвращает значение True, добавить NOT, то после этого условие будет возвращать значение False. А если до изменения условие возвращало False, то после добавления к нему NOT оно будет возвращать True. Посмотрите на следующий пример:

SELECT InvoiceNo, SaleDate, Salesperson, TotalSale

    FROM SALES

    WHERE NOT (Salesperson = 'Ford') ;

Этот запрос возвращает строки для всех сделок по продажам, совершенных всеми продавцами, кроме Форда.

Внимание: Иногда при использовании связки (AND, OR или NOT) бывает неясно, какая у нее область действия. Чтобы гарантировать применение связки именно к нужному предикату, заключите его в круглые скобки. В последнем примере связка NOT применяется как раз к целому предикату (Salesperson = Ford'), а не к какой-либо его части.

Предложения GROUP BY

Иногда вместо того, чтобы получить отдельные записи, вам может понадобиться узнать что-либо о группе записей. В этом случае вам поможет предложение GROUP BY (группировать по).

Предположим, что вы менеджер по продажам и хотите посмотреть эффективность ваших продаж. Для этого вы можете воспользоваться оператором SELECT, как показано в следующем примере:

SELECT InvoiceNo, SaleDate, Salesperson, TotalSale

    FROM SALES;

Полученный результат приведен на рис. 9.1.

Рис. 9.1. Результат выбора информации о продажах с 07.01.2001 по 07.07.2001

Этот результат дает вам лишь некоторое представление о том, как работают ваши продавцы, поскольку здесь выводится информация о небольшом количестве продаж. Однако в реальной жизни продажи компаний очень велики, и в этом случае уже непросто будет определить, как были достигнуты результаты. Чтобы проверить, была ли коммерчески эффективной работа продавцов, вы можете скомбинировать предложение GROUP BY с одной из функций агрегирования (также называемыми итоговыми функциями), чтобы получить количественную картину о выполнении продаж. К примеру, вы можете просмотреть, кто из продавцов работает с более дорогостоящими и прибыльными позициями, используя функцию среднего значения (AVG):

SELECT Salesperson, AVG(TotalSale)

    FROM SALES

    GROUP BY Salesperson;

Полученный результат приведен на рис. 9.2.

Рис. 9.2. Средний уровень продаж по каждому продавцу

Как видно из рис. 9.2, средние продажи Фергюсона значительно выше, чем у двух других продавцов. Чтобы сравнить общие продажи по каждому продавцу, выполните следующий запрос:

SELECT Salesperson, SUM(TotalSale)

    FROM SALES

    GROUP BY Salesperson;

Результат этого запроса приведен на рис. 9.3.

Рис. 9.3. Общие продажи по каждому продавцу

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

Предложение HAVING

Предложение HAVING позволяет еще лучше анализировать сгруппированные данные. Предложение HAVING (при условии) — это фильтр, который по своему действию похож на предложение WHERE, но, в отличие от предложения WHERE, HAVING работает не с отдельными строками, а с их группами. Проиллюстрируем действие предложения HAVING, используя следующий пример. Предположим, что менеджеру по продажам нужно сосредоточиться на работе других продавцов. Для этого ему необходимо исключить из общих данных количество продаж Фергюсона, поскольку его продажи находятся "в другой весовой категории". Чтобы сделать это, воспользуемся предложением HAVING:

SELECT Salesperson, SUM(TotalSale)

    FROM SALES

    GROUP BY Salesperson

    HAVING Salesperson <> 'Ferguson';

Результат этого запроса приведен на рис. 9.4. Строки, которые относятся к продавцу по фамилии Фергюсон, на экран не выводятся.

Рис. 9.4. Общие продажи по каждому продавцу за исключением Фергюсона

Чтобы показать таблицу, выводимую запросом, в алфавитном порядке или в обратном алфавитном порядке, используйте предложение ORDER BY (по порядку). В то время как предложение GROUP BY собирает строки в группы и сортирует группы по алфавиту, ORDER BY сортирует отдельные строки. ORDER BY должно быть последним предложением в запросе. Если в запросе также имеется и предложение GROUP BY, то оно вначале собирает строки вывода в группы. Затем предложение ORDER BY сортирует строки, находящиеся внутри каждой группы. А если предложения GROUP BY нет, то оператор рассматривает всю таблицу как группу и предложение ORDER BY сортирует все ее строки таким образом, чтобы были упорядочены значения в столбцах, указанных в этом предложении.

Это можно проиллюстрировать с помощью данных из таблицы SALES (продажи). В ней имеются столбцы InvoiceNo (номер счета-фактуры), SaleDate (дата продажи), Salesperson (продавец), TotalSale (всего продано). Все данные SALES можно увидеть, но в произвольном порядке, если воспользоваться следующим простым примером:

SELECT * FROM SALES ;

В одних реализациях это порядок, в котором строки вставлялись в таблицу, а в других реализациях строки могут быть выстроены в зависимости от времени самого последнего обновления каждой из них. Кроме того, порядок может неожиданно измениться, если кто-то физически реорганизует базу данных. В большинстве случаев порядок расположения получаемых строк приходится указывать. Возможно, вам нужно увидеть строки, которые расположены по порядку, задаваемому значениями из столбца SaleDate:

SELECT * FROM SALES ORDER BY SaleDate ;

При выполнении этого примера все строки таблицы SALES будут возвращены именно втом порядке, который задан значениями SaleDate.

А порядок расположения тех строк, у которых в столбце SaleDate одинаковые значения, зависит от используемой реализации. Впрочем, и для строк с одинаковыми значениями Sale-Date можно также указать порядок сортировки. Возможно, вам, например, нужно для каждого значения SaleDate видеть строки таблицы SALES, расположенные по порядку, которые заданы значениями InvoiceNo:

SELECT * FROM SALES ORDER BY SaleDate, InvoiceNo ;

В этом примере таблица SALES вначале упорядочивается по значениям SaleDate; затем для каждого такого значения строки этой таблицы располагаются по порядку, задаваемому InvoiceNo. Однако не путайте этот пример со следующим запросом:

SELECT * FROM SALES ORDER BY InvoiceNo, SaleDate ;

При выполнении этого запроса SALES упорядочивается по столбцу InvoiceNo. Затем для каждого значения InvoiceNo строки таблицы SALES располагаются по порядку, задаваемому столбцом SaleDate. Скорее всего, нужный вам результат вы не получите, потому что мало вероятно, чтобы для одного номера счета-фактуры было множество дат продажи.

Следующий запрос является очередным примером того, как SQL может возвращать данные:

SELECT * FROM SALES ORDER BY Salesperson, SaleDate ;

В этом примере упорядочивание сначала идет по столбцу Salesperson, а затем — по SaleDate. Просмотрев данные, расположенные в таком порядке, вы, возможно, захотите его изменить:

SELECT * FROM SALES ORDER BY SaleDate, Salesperson ;

Теперь упорядочивание сначала идет по столбцу SaleDate, а затем — по Salesperson.

Во всех этих примерах упорядочивание идет в порядке возрастания (ASC), который является порядком сортировки по умолчанию. Последний оператор SELECT вначале показывает самые ранние продажи (строки таблицы SALES), а в пределах определенной даты ставит продажи, проведенные Адамсом, перед продажами, проведенными Бейкером. А если вы предпочитаете порядок убывания (DESC), можете указать его для одного или множества столбцов из предложения ORDER BY:

SELECT * FROM SALES

    ORDER BY SaleDate DESC, Salesperson ASC ;

В этом примере для данных продаж указывается порядок убывания дат, в результате чего записи о самых недавних продажах будут показаны первыми, но для продавцов указывается порядок возрастания, т.е. их фамилии будут располагаться в обычном алфавитном порядке.

 
На правах рекламы:
Регистрация. | Забыли пароль?
Логин
Пароль
Запомнить меня