SQL: Полное руководство [3 ed.] 9785907114265, 9780071592550


107 87

Russian Pages 962 Year 2019

Report DMCA / Copyright

DOWNLOAD PDF FILE

Recommend Papers

SQL: Полное руководство [3 ed.]
 9785907114265, 9780071592550

  • Commentary
  • decrypted from 74EECE7C1D138B27395A34D90A8C87A0 source file
  • 0 0 0
  • Like this paper and download? You can publish your own PDF file online for free in a few minutes! Sign Up
File loading please wait...
Citation preview

СКАЧАНО С WWW.SHAREWOOD.BIZ - ПРИСОЕДИНЯЙСЯ!

SQL Полное руководство

SQL The Complete Reference Third Edition James Groff Paul Weinberg Andrew Oppel



New York Chicago San Francisco Lisbon London Madrid Mexico City Milan New Delhi

San Juan Seoul Singapore Sydney Toronto

SQL Полное руководство Третье издание

Джеймс Грофф

Пол Вайнберг Эндрю Оппель

А AllAREIOiU(a\ Москва· Санкт-Петербург

2019

ББК

32.973.26-{>18.2.75

У ДК

681.3.07

Г89

Компьютерное издательство "Диалектика"

По общим вопросам обращайтесь в издательство "Диалектика" по адресу:

[email protected], http://www.d iale ktika.com Грофф, Джеймс Р., Вайнберг, Пол Н., Оппель, Эндрю Дж. Г89

SQL: полное руководство, 3-е изд. : Пер. 2019. - 960 с.: ил. - Парал. тит. англ. ISBN 978-5-907114-26-5

с англ.

-

СПб.

: ООО "Диалектика",

ББК

32.973.26-018.2.75

Все названия 11ро1рамм11ых продукrов являются зарегисrрированными торговыми марками сооrветсrвующих

фирм. Никакая часть наL'Тоящего издания ни в каких целях не может бьггь воспроизведена в какой бы то ни было форме и какими бы то ни было срt'дствами, будь то электронные или механические, включая фотокопирование и запись на магнитный носитель, если на это нет письменного разрешения издательсrва OsЬome

Media.

Authorized translati'm from the English language edition puЫL~hed Ьу McGraw-Hill Companies, Copyright © 2010 All rights reserved. No part of this Ьооk may Ье reprtxittced or transmitted in any form or Ьу any means, electronic or mechanica\, inclt1ding photocopying. recording or Ьу any infonnation storage retrieval system, without permission from the PuЬlisher.

Научно-популярное llЗдание

Джеймс Р. fрофф, Пол Н. Вайнберг, Эндрю Дж. Оппель

SQL: полное руководсmо 3-е издание

Поднисано в печать

11.06.2019. Формат 70х100/16

Гарнитура Тimes Усл. печ. л. Доп. тираж

77,4. Уч.-изд. л. 54,3 200 экз. Заказ № 4895

Оmечатано в АО "Первая Образповая типоrрафия" Филиал "Чеховский Печатный Двор"

142300, Московская область, г. Чехов, ул. Полиграфистов, д. 1 Сайт: www.chpd.ru, E-mail: [email protected], тел. 8 (499) 270-73-59 ООО "Диалектика",

ISBN 978-5-907114-26-5 lSBN 978-0-07-159255-0

(рус.)

(англ.)

195027, Санкт-Петербург,

Магнитогорская ул., д.

30, лит.

А, пом.

848

©ООО "Диалектика", 2019 TARGET ВУ CITY; TARGET

SALES

$350,000.00 $575,000.00

$367,911.00 $692,637.00

CITY Atlanta New York

В случае простых запросов инструкция

SELECT очень похожа на предложение

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

На рис.

6.1

приведена синтаксическая диаграмма инструкции SELECT. Инст­

рукция состоит из шести предложений. Предложения SELECT и FROM являются

обязательными; четыре остальные включаются в инструкцию только при необхо­ димости. Ниже перечислены функции каждого из предложений.



В предложении SELECT перечисляются элементы данных, которые долж­

ны быть выбраны инструкцией SELECT. Это могут быть либо столбцы ба­ зы данных, либо столбцы, вычисляемые при выполнении запроса. Пред­ ложение



SELECT описано далее в настоящей главе.

В предложении FROM указывается список таблиц и представлений, кото­ рые содержат элементы данных, извлекаемые запросом (представления

рассматриваются в главе

14,

"Представления"). Запросы, извлекающие

данные из одной таблицы, описаны в настоящей главе. Более сложные

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



7,

"Многотабличные запросы (соединения)".

Предложение WHERE указывает, что в результаты запроса следует вклю­

чать только некоторые строки. Для отбора строк, включаемых в резу ль­ таты запроса, используется условие отбора. Основные возможности этого предложения описаны ниже в настоящей главе. Использование в пред­

ложении

WHERE

вложенных

подзапросов

"Подзапросы и выражения с запросами".

рассматривается

в

главе

9,

ГАава 6. ПроС1Ъ1е запросы

121

..

~SЕLЕСТ--...г---~-------1 1·ы~*й_элемент

~:~

WНERE

условие_отбора

GROUP ВУ~~-группированиR

НAVING

ORDER

Рис.



6.1.

условие_отбора

ву---.t-условие_сортироекм

Синтаксическая диаграмма инструкции

SELECT

Предложение GROUP ВУ позволяет создать итоговый запрос. Вместо ге­

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

вую строку для каждой группы.

в главе



8,

Итоговые запросы рассматриваются

"Итоговые запросы".

Предложение НAVING указывает, что в результаты запроса следует вклю­ чать

только

некоторые

из

групп,

созданных с

помощью

предложения

GROUP ВУ. В этом предложении, как и в предложении WHERE, для отбора включаемых групп используется условие отбора. Предложение НА VING описано в главе



8,

"Итоговые запросы".

Предложение ORDER ВУ сортирует результаты запроса на основании

данных, содержащихся в одном или нескольких столбцах. Если это пред­ ложение отсутствует, результаты запроса не будут отсортированы. Пред­ ложение

ПреДАожение

ORDER ВУ рассматривается далее в настоящей главе.

SELECT

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

Часn.11. Выборка данн111х

122

менты задаются в виде сп11ска выбора, состоящего из выбираемых элементов, разде­ ленных запятыми. д,ля каждого элемента из этого списка в таблице результатов запроса будет создан один столбец; при этом столбцы в таблице результатов будут расположены в том же 1юрядке слева нанраво, что и элементы списка возвращае­

мых столбцов. Возвращаемый элемент может представлять собой следующее.



Им.я

столбtjа, идентифицирующее один из столбцов, содержащихся

в таблицах, которые перечислены в предложении FROM. СУБД просто бе­ рет значение этого столбца для каждой из строк таблицы и помещает его в соответствующую строку таблицы результатов запроса.



Константу, показывающую, что в каждой строке результатов запроса должно содержатьс~1 одно и то же значение.



Выражение, указывающее, что

SQL

должен вычислить значение, поме­

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

ПредАожение

FROM

Предложш1ие FROM состоит из ключевого слова FROM, за которым следует спи­

сок спецификаций таблиц, разделенных запятыми. Каждая спецификация табли­ цы идентифицирует таблицу или представление, содержащие данные, которые

извлекает запрос. Эти таблицы называются исходнь1м11 табл1щам11 запроса (и ин­ струкции SELECT), rюскольку они являются источниками всех данных, содержа­ щихся в таблице результатов запроса. Во всех запросах, рассматриваемых в на­ стоящей главе, в предложении FROM указана одна таблица.

Результаты запроса Результатом SQL-зar1poca на выборку всегда является таблица, содержащая данные и ничем не отличающаяся от таблиц базы данных. Если пользователь на­ бирает инструкцию

SQL

в интерактивном режиме, СУБД выводит результаты за­

проса (которые некоторые производители именуют результирующим

(result set))

набором

на экран в табличной форме. Если программа посылает запрос СУБД

с помощью программного

SQL,

то СУБД возвращает таблицу резу льтатов запроса

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

6.2.

Обычно результаты запроса представляют собой таблицу с несколькими строками и столбцами. Например, запрос, приведенный ниже, возвращает таблицу из трех столбцов (поскольку запрашиваются три элемента данных) и десяти строк (по ко­ личеству служащих).

Вывести список имен, офисов и дат приема на работу всех служащих.

SELECT NАМЕ, REP_OFFICE, HIRE_DATE FROM SALESREPS; NAME

REP_OFFICE

НIRE

DATE

ГАава 6. Простые запросы

Bill Adams Mary Jones Sue Smith Sam Clark ВоЬ Smith Dan Roberts Tom Snyder Larry Fitch Paul Cruz Nancy Ange 11 i

,....

----------

13 11 21 11 12 12 NULL 21 12 22

Интера~тивный

123

---------

2006-02-12 2007-10-12 2004-12-10 2006-06-14 2005-05-19 2004-10-20 2008-01-13 2007-10-12 2005-03-01 2006-11-14

SQL

..... REGION FROM OFFICES

SELECТ СIТУ,

--

--·· ~

,

.....

Запрос

---

....

..

Результаты запроса СIТУ

Denver New York Chicago Atlanta Los Angeles

'

Программа

REGION Western Eastern Eastern Eastern Western

~

СУБД

Запрос

База данных

Программный

Рис.

SQL

6.2. Табличная

структура результатов SQL-зanpoca

В отличие от запроса, показанного выше, следующий запрос возвращает толь­ ко одну строку, так как есть всего один служащий, имеющий указанный иденти­

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

SQL

все

равно с•1итает их таблицей, состоящей из трех столбцов и одной строки. Имя, плтювый 11 фактuческий объемы продаж служащего с идеюпификатором

107.

SELECT NАМЕ, QUOTA, SALES FROM SALESREPS WНERE EMPL NUМ 107; NАМЕ

Nancy Ange 11 i

QUOTA

SALES

$300,000.00

$186,042 . 00

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

124

Часть 11. Выборка .данных

Среднее значение фактических объемов продаж по всем служащим компании.

SELECT AVG(SALES} FROM SALESREPS; AVG (SALES} $289,353.20 Эти результаты запроса также являются таблицей, которая состоит из одного столбца и одной строки. И наконец, запрос может вернуть результаты, содержащие нулъ строк, как в следующем примере.

Список имен и дат приема на работу всех служащих, фактический объем продаж

которых превь1шает

$500 ООО.

SELECT NАМЕ, HIRE_DATE FROM SALESREPS WHERE SALES > 500000.00; NAME

НIRE

DATE

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

SQL

рас­

пространяется и на резу ль таты запроса. Если один из элементов данных в таблице имеет значение

NULL,

то оно попадет в результаты запроса при извлечении этого

элемента. Например, в таблице SALESREPS значение NULL содержится в столбцах

QUOTA и МANAGER. Приведенный далее запрос возвращает эти значения во втором и третьем столбцах таблицы результатов запроса. Заметим, что не все продукты выводят значения NULL таким образом встретив значение

NULL,

-

Oracle

и

DB2,

SQL-

например,

не выводят ничего.

Список служащих с их плановыми обьемами продаж и менеджерами.

SELECT NАМЕ, QUOTA, FROM SALESREPS; NАМЕ

МANAGER

QUOTA

МANAGER

-------------

-----------

-------

Bill Adams Mary Jones Sue Smith Sam Clark ВоЬ Smith Dan Roberts Тот Snyder Larry Fitch Paul Cruz Nancy Angelli

$350,000.00 $300,000.00 $350,000.00 $275,000.00 $200,000.00 $300,000.00 NULL $350,000.00 $275,000.00 $300,000.00

104 106 108 NULL 106 104 101 106 104 108

То, что SQL-зaпpoc всегда возвращает таблицу данных, очень важно. Это озна­ чает, что результаты запроса можно сохранить в базе данных в виде таблицы. Это

ГАава 6. Простые запросы

125

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

предметом дальнейших запросов. Таким образом, табличная структура реляцион­

ной базы данных тесно связана с реляционными запросами

SQL.

Таблицам можно

посылать запросы, а запросы возвращают таблицы.

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

OFFICES три столбца. Въюести для каждого из офисов список городов, регионов и объемов продаж.

SELECT CITY, REGION, SALES FROM OFFICES; CITY

REGION

SALES

-----------

-------

Denver New York Chicago Atlanta Los Angeles

Western Eastern Eastern Eastern Western

-----------

$186,042.00 $692,637.00 $735,042.00 $367,911.00 $835,915.00

Инструкция SELECT для простых запросов, таких как показанный выше, состо­ ит только из двух обязательных предложений. В предложении SELECT перечисля­

ются имена требуемых столбцов; в предложении FROM указываются имена таблиц и представлений, содержащих эти столбцы.

Концептуально

SQL

обрабатывает запрос путем построчного просмотра табли­

цы, указанной в предложении FROM. ,Для каждой строки таблицы берутся значе­ ния из указанных в запросе столбцов и создается одна строка результатов запроса.

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

ВычисАЯемые стоАбцы Кроме столбцов, значения которых извлекаются непосредственно из базы дан­ ных, SQL-зaпpoc на выборку может содержать вычисляемые столбцы, значения ко­

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

5,

"Основы

SQL",

выражения могут

включать в себя операции сложения, вычитания, умножения и деления. Для по­

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

данные. При попытке сложить, вычесть, умножить или разделить столбцы, содер­ жащие текстовые данные,

SQL

выдаст сообщение об ошибке.

В следующем запросе будет получен простой вычисляемый столбец.

126

Часть 11. Выборка данных

Выдать для каждого офиса список городов, регионов и сумм, на которые был пере­

вьтолнен/недовыполнен план по продажам.

(SALES - TARGET)

SELECT CITY, REGION, FROM OFFICES; CITY

----------Denver New York Chicago Atlanta Los Angeles

REGION ------Western Eastern Eastern Eastern Western

(SALES-TARGET)

--------------$113. 958. 00 $117,637.00 -$64,958.00 $17,911.00 $110,915.00

При выполнении этого запроса для каждой строки таблицы OFFICES генери­ руется одна строка результатов, как показано на рис.

6.3.

Значения первых двух

столбцов результатов запроса извлекаются непосредственно из таблицы OFFICES. Третий столбец для каждой строки результатов запроса вычисляется на основании значений столбцов текущей строки таблицы OFFICES. Таблица OFl'ICZS

OFFICE 22 11 12 13 21

СIТУ

Denver New York Chicago Atlanta Los Angeles

REGION TARGET MGR Western 108 $300,000.00 Eastern 106 $575,000.00 Eastern 104 $800,000.00 Eastern NULL $350,000.00 Western 108 $725,000.00

LyJ

1

1

l СIТУ

Результаты запроса

SALES $186,042.00 $692,637.00 $735,042.00 $367,911.00 $835,915.00

Denver New York Chicago Atlanta Los Angeles

REGION SALES-TARGET Western -$113,958.00 Eastern $117,637.00 Eastern -$ 64,958.00 Eastern $ 17,911.00 Western $110,915.00

Рис. 6.З. Выполнение запроса, содержащего вычисляемый столбец

Далее приведены другие примеры запросов, в которых используются вычис­ ляемые столбцы.

Показать общую стоимость по каждому товару (показаны только

8

тирующего набора).

SELECT MFR_ID, PRODUCT_ID, DESCRIPTION, FROM PRODUCTS; MFR ID PRODUCT ID ------REI 2А45С ACI 4100У QSA ХК47

DESCRIPTION

(QTY_ON_HAND * PRICE)

(QTY_ON_HAND*PRICE)

----------- --------------- -------------------Ratchet Link Widget Remover Reducer

$16,590.00 $68,750.00 $13,490.00

строк резуль­

ГАава

BIC IMM ACI ACI BIC

41672

Plate 900-lb Brace Size 3 Widget Size 4 Widget Handle

779С

41003 41004 41003

6. Простые эаnросы

127

$0.00 $16,875.00 $22,149.00 $16,263.00 $1,956.00

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

3%

от его фактического объема продаж?

SELECT NАМЕ, QUOTA, FROM SALESREPS;

(QUOTA + (.03 * SALES))

(QUOTA + (. 03

QUOTA

NАМЕ

* SALES))

-------------

-----------

-----------------------

Bill Adams Mary Jones Sue Smith Sam Clark ВоЬ Smith Dan Roberts Tom Snyder Larry Fitch Paul Cruz Nancy Angelli

$350,000.00 $300,000.00 $350,000.00 $275,000.00 $200,000.00 $300,000.00 NULL $350,000.00 $275,000.00 $300,000.00

$361,037.33 $311,781.75 $364,221.50 $283,997.36 $204,277.82 $309,170.19 NULL $360,855.95 $283,603.25 $305,581.26

Как было сказано в главе полнительные

5,

"Основы

арифметические

SQL",

операции,

во МНОl'ИХ СУБД реализованы до­

операции

над строками

и всrроенные функции, которые можно применять в выражениях

SQL.

символов

Их также

можно использовать в выражениях в списке возвращаемых столбцов, как в сле­ дующем примере для

DB2, в котором из даты

извлекается значение месяца и года.

Въюести список имен, а также месяц и год приема на работу всех служащих. (В с.11у­ чле базъ~ данных

Oracle

вместо функций MONTH и YEAR следует применшпъ функцию

ТО_СНАR.)

SELECT NАМЕ, MONTH(HIRE_DATE), YEAR(HIRE_DATE) FROM SALESREPS; Кроме того, в списке возвращаемых столбцов можно иснользовать константы.

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

Список обьемов продаж для каждого города.

SELECT CITY, 'has sales of', SALES FROM OFFICES; CITY

HAS SALES OF

SALES

-----------

------------

-----------

Denver New York Chicago Atlanta Los Angeles

has has has has has

of of of of of

$186,042.00 $692,637.00 $735,042.00 $367' 911. 00 $835,915.00

sales sales sales sales sales

Часть 11. Выборка данных

128

Создается впечатление, что результаты запроса состоят из отдельных предло­

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

содержат значения из таблицы OFFICES. Во втором столбце для всех строк содер­ жится одна и та же текстовая строка из двенадцати символов.

Выборка всех стоАбцов

(SELECT *)

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

SQL

разрешается использовать вместо списка воз­

вращаемых столбцов символ звездочки

(*}, который означает, что требуется из­

влечь все столбцы. Показать все данные, содержащиеся в таблице

OFFICES.

SELECT * FROM OFFICES; CITY

REGION

-----------

-------

22

Denver

11

New York

12 13 21

Chicago Atlanta Los Angeles

Western Eastern Eastern Eastern Western

OFFICE

TARGET

SALES

-----------

-----------

$300,000.00 $575,000.00 $800,000.00 $350,000.00 $725,000.00

$186,042.00 $692,637.00 $735,042.00 $367,911.00 $835,915.00

MGR 108 106 104 105 108

Результаты запроса содержат все шесть столбцов таблицы OFFICES, которые расположены в том же порядке, что и в исходной таблице. Символ выборки всех столбцов очень удобно использовать в интерактивном

SQL.

Использования же его в программном

SQL

следует избегать, поскольку изме­

нения в структуре базы данных могут привести к краху приложения. Предполо­ жим, например, что таблица OFFICES была удалена из базы данных, а затем созда­ на вновь, при этом был изменен порядок столбцов и добавлен новый, седьмой, столбец. Если программа ожидает, что запрос SELECT

*

FROM OFFICES возвратит

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

перестанет работать после изменения порядка столбцов и добавления нового столбца.

Этих сложностей можно избежать, если в программах запрашивать требуемые столбцы по именам. Например, приведенный ниже запрос возвращает те же ре­ зультаты, что и запрос SELECT

*

FROM OFFICES. Он не восприимчив к изменени­

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

SELECT OFFICE, CITY, REGION, MGR, TARGET, SALES FROM OFFICES;

ГАава 6. Простые запросы

Повторяющиеся строки

129

(DISTINCT)

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

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

SELECT MGR FROM OFFICES; MGR lOB 106 104 105

lOB Таблица результатов запроса содержит пять строк (по одной для каждого офи­ са), однако две из них совпадают. Почему? Потому что Ларри Фитч

(Larry Fitch)

является менеджером двух офисов: в Лос-Анджелесе и в Денвере. Поэтому его

идентификатор

(108)

содержится в двух строках таблицы OFFICES. Это не совсем

те результаты, которых вы ожидали. Если в фирме работают четыре менеджера, то, вероятно, вы ожидали, что результаты запроса будут содержать четыре строки. Повторяющиеся строки из таблицы результатов запроса можно удалить, если в инструкции SELECT перед списком выбора указать ключевое слово DISTINCT. Ниже приведен вариант предыдущего запроса, возвращающий те результаты, ко­ торые вы ожидали.

Список идентификаторов всех менеджеров офисов.

SELECT DISTINCT MGR FROM OFFICES; MGR 106 104 105

lOB Этот запрос выполняется следующим образом. Вначале генерируются все стро­ ки результатов (пять строк), а затем удаляются те из них, которые в точности сов­ падают с другими. Ключевое слово

DISTINCT можно указывать независимо от со­ держимого списка возвращаемых столбцов инструкции SELECT (с некоторыми ог­ раничениями для итоговых запросов, описанными в главе

8).

Если ключевое слово DISTINCT не указано, повторяющиеся строки не удаля­ ются. Можно также использовать ключевое слово ALL, явно указывая, что повто­

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

ALL

используется по умолчанию.

-

ключевое

130

Часть 11. Выборка данных

Отбор строк (WHERE) SQL-запросы, извлекающие из таблицы все строки, полезны при просмотре ба­ зы данных и создании отчетов, однако редко применяются для чего-нибудь еще. Обычно требуется выбрать из таблицы и включить в результаты запроса только несколько строк. Чтобы указать, какие строки нужно отобрать, используется предложение WHERE. Ниже показано несколько запросов, в которых встречается это предложение.

Офисы, в которых фактические обьемы 11родаж превысили плановые.

SELECT CITY, SALES, TARGET FROM OFFICES WНERE SALES > TARGET; CITY

SALES

TARGET

-----------

-----------

-----------

New York Atlanta Los Angeles

$692,637.00 $367,911.00 $835,915.00

$575,000.00 $350,000.00 $725,000.00

Имя, обьемы фактических и плановых продаж служащего с идентификатором

105.

SELECT NАМЕ, SALES, QUOTA FROM SALESREPS WHERE EMPL NUМ 105; NАМЕ

Bill Adams

SALES

QUOTA

$367,911.00

$350,000.00

Список всех служащих, менеджером которых являете.я Боб Смит (идентификатор

104). SELECT NАМЕ, SALES FROM SALESREPS WНERE МANAGER = 104; SALES

NАМЕ

Bill Adams Dan Roberts Paul Cruz

$367,911.00 $305,673.00 $286,775.00

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

дущем запросе, например, условием отбора являлось выражение МANAGER =104. На рис.

6.4

изображено, как работает предложение WHERE. Концептуально все

строки в таблице SALESREPS просматриваются одна за другой, и к каждой из них

применяется условие отбора. Если в условии отбора встречается имя столбца (как, например, имя МANAGER в предыдущем примере), то используется значение этого

столбца из текущей строки. Для каждой из строк условие отбора может иметь од­ но из трех перечисленных ниже значений.

ГАава 6. Простые запросы



131

Если условие отбора имеет значение TRUE, строка будет включена в ре­

зультаты запроса. Например, значение столбца МANAGER в строке для Билла Адамса

(Bill Adams)

соответствует условию отбора, поэтому строка

для него включается в результаты запроса.



Если условие отбора имеет значение FALSE, то строка исключается из ре­

зультатов запроса. Например, значение столбца МANAGER в строке для Сью Смит

(Sue Smith)

не соответствует условию отбора, поэтому строка

для нее исключается из резу ль татов запроса.



Если условие отбора имеет значение NULL, то строка исключается из ре­ зультатов запроса. Например, значение столбца МANAGER в строке для Сэма Кларка

(Sam Clark)

равно NULL, поэтому строка для него исключа­

ется из результатов запроса.

МANAGER=104

ТабnиЦ11 llAI.lllllDS

TRUE

NАМЕ

Резуnьта1Ь1 запроса

SALES Bill Adams $367,911.00 Dan Roberts $305, 673. 00 Paul Cruz $286,775.00

NАМЕ

Bill Adams маrу Jones Sue Smith Sam Clark ВоЬ Smith Dan Roberts

МANAGER=104Jo-•F~AL_,..E=--,

МАNАGЕR=104~--н_еиз_вестн __о_-.

Рис. 6.4. Оrбор строк с помощью преД/\ожения WHERE

Рис.

6.5

иллюстрирует другой способ рассмотрения роли, выполняемой усло­

вием отбора в предложении WHERE. Можно сказать, что условие отбора ел ужит фильтром для строк таблицы. Строки, удовлетворяющие условию отбора, про­ ходят через фильтр и становятся частью результатов запроса. Строки, неудов­ летворяющие условию отбора, отфильтровываются и исключаются из резу льта­ тов запроса.

ТабnиЦll SAI.ISUl'S NАМЕ

Резуnьта1Ь1 запроса МANAGER

NАМЕ SALES 1041-----11111--• Bill Adams $367,911.00 Dan Roberts $305,673.00 Paul Cruz $286,775.00

Bill Adams маrу Jones Sue Smith Sam Clark ВоЬ Smith Dan Roberts

+

Фмnьч~ МANAGER=104

Рис.

6.5.

ПреД/\ожение WНERE в роли фильтра

Часть 11. Выборка А&нных

132

Условия отбора В

SQL

используется множество условий отбора, позволяющих эффективно

и естественно создавать различные типы запросов. Ниже рассматриваются пять

основных условий отбора (в стандарте



ANSl/150 они называются

предикатами).

Сравнение. Значение одного выражения сравнивается со значением дру­

гого выражения. Например, такое условие отбора используется для вы­ бора всех офисов, находящихся в восточном регионе, или всех ел ужащих, фактические объемы продаж которых превышают плановые.



Проверка

на

принадлежность диапазону. Проверяется, попадает ли ука­

занное значение в определенный диапазон значений. Например, такое

условие отбора используется для нахождения ел ужащих, фактические

объемы продаж которых превышают



$100000,

но меньше

$500000.

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

Йорке, Чикаго или Лос-Анджелесе.



Проверка на соответствие шаблону. Проверяется, соответствует ли стро­ ковое значение, содержащееся в столбце, определенному шаблону. На­

пример, такое условие отбора используется для выбора клиентов, имена которых начинаются с буквы "Е".



Проверка

на

равенство

11 столб1~е значение

значению

NULL.

Проверяется, содержится ли

NULL. Например, такое условие отбора используется

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

Сравнение(=,,=) Наиболее распространенным условием отбора в сравнении

SQL

SQL

является сравнение. При

вычисляет и сравнивает значения двух SQL-выражений для каж­

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

жать арифметические операции. В

SQL

имеется шесть различных способов срав­

нения двух выражений, показанных на рис.

6.6. выро:жение_2

- - - BЬlfI03"/IШl_J

<

>= Рис.

6.6.

Синтаксическая диаграмма сравнения

----+

ГАава 6. Простые эапрось1

133

Ниже приведены типичные примеры сравнения. Найти имена всех служащих, пр1тятых на работу до

2006

года.

SELECT NАМЕ FROM SALESREPS WHERE HIRE DATE < '2006-01-01'; NАМЕ

Sue Smith Smith Dan Roberts Paul Cruz ВоЬ

Заметим, что не все SQL-продукты обрабатывают даты одинаково, поскольку

разные производители были вынуждены поддерживать даты еще до того, как был создан стандарт

SQL.

Формат YYYY-MM-DD, показанный в предылущем примере,

работает в большинстве продуктов, но кое-гле его следует изменить. В пример, вам надо либо заменить формат латы на принятый в

(' о 1-JAN- в в '),

Oracle по

Oracle,

на­

умолчанию

либо изменить формат по умолчанию для вашей сессии при по­

мощи следующей команды.

ALTER SESSION SET

NLS_DATE_FORМAT='YYYY-MM-DD';

Вьюести список офисов, фактические объемы продаж в которых составили менее

80

процентов от плановых.

SELECT CITY, SALES, TARGET FROM OFFICES WHERE SALES < ( .8 * TARGET); CITY Denver

SALES

TARGET

-----------

-----------

$186,042.00

$300,000.00

Вьюести список офисов, менеджером которых не является служащий с идентифика­ тором

108.

SELECT CITY, MGR FROM OFFICES WНERE MGR 108; CITY

MGR

New York Chicago Atlanta

106 104 105

Как показано на рис.

6.6,

в соответствии со спецификацией

ANSI/ISO проверка SQL используются (поддерживается в SQL

на неравенство записывается как А < > в. В ряде реализаций альтернативные системы записи, как, например, А

Server, DB2, Oracle

и

пустимых, а иногда

-

MySQL).

!= в

Иногда такая форма записи ~1вляется одной из до­

единственной.

Часть 11. Выборка данных

134

Когда СУБД сравнивает значения двух выражений, могут быть получены три результата:



если сравнение истинно, то результат проверки имеет значение



если сравнение ложно, то результат проверки имеет значение



если хотя бы одно из двух выражений имеет значение NULL, то резул1,та­

TRUE;

FALSE;

том сравнения будет NULL.

Выборка одной строки Чаще всего используется сравнение, в котором определяется, равно ли значе­ ние столбца некоторой константе. Если этот столбец представляет собой первич­ ный ключ, то запрос возвращает всего одну строку, как в следующем примере.

Узнатъ имя и лимит кредита клиента с идентификатором

2107.

SELECT COMPANY, CREDIT_LIMIT FROM CUSTOMERS 2107; WHERE CUST NUМ CREDIT LIMIT

COMPANY International

Асе

$35,000.00

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

SQL,

пред­

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

$25000),

имеют абсолютно оди­

наковый формат.

Значения

NULL

Использование значений NULL в запросах может привести к "очевидным" предположениям, которые истинны только на первый взгляд, но на самом деле та­

ковыми не являются. Например, можно предположить, что каждая строка из таб­

лицы SALESREPS будет содержаться в результатах только одного из двух следую­ щих запросов.

Вывести список служащих, превысивших плановый обьем продаж.

SELECT NАМЕ FROM SALESREPS WHERE SALES > QUOTA; NАМЕ

Bill Adams Mary Jones Sue Smith Sam Clark Dan Roberts

ГАава 6. Прость1е запросы

135

Larry Fitch Paul Cruz Вьюести список служащих, не вь111олн11вш11х 11лан.

SELECT NАМЕ FROM SALESREPS WHERE SALES < QUOTA; NАМЕ

Srnith

ВоЬ

Nancy Angelli Олнако результаты этих запросов состоят из сем.и и двух строк соответственно, что

лает в сумме денять строк, в то врем.я как в таблице находится десять строк. Строка для Том.а Снайдера

(Tom Snyder)

содержит значение NULL в столбце QUOTA, по­

скольку ем.у еще не был назначен плановый объем. продаж. Эта строка не вошла ни в один запрос.

Как показывает приведенный прим.ер, при определении условия отбора необ­ ходим.о пом.нить об обработке значений NULL. В трехзначной логике, принятой

в

SQL,

условие отбора может иметь значения TRUE, FALSE или NULL. А в результа­

ты запроса попалают только те строки, для которых условие отбора равно TRUE. Мы еще встретимся с NULL позже в этой главе.

Проверка на принаААежность диапазону ( BEIWEEN) Следующей форм.ой условия отбора явм1ется проверка на принадлежность

лиапазону значений (оператор BETWEEN ... AND), схематически изображенная на рис.

6.7.

При этом. проверяется, находится ли элемент данных между двум.я задан­

ным.и значениям.и. В условие отбора входят три выражения. Первое выражение онрелеляет проверяемое значение; второе и третье выражения определяют ниж­

нюю и верхнюю границы проверяем.ого диапазона. Типы данных трех выражений

должны быть сравнимым.и. -

Рис.

__ 1

прооероемое _ _.,." -c-N_o_т_

6.7.

____..,,,

AND

BEТWEEN

Синтаксическая диаграмма проверки на принаД/\ежность диапазону (BEТWEEN)

Следующий прим.ер иллюстрирует типичную процедуру проверки на принад­ леж1юсть диапазону.

Найти все заказы, сделанные в последнем квартале

2007

года.

SELECT ORDER_NUМ, ORDER_DATE, MFR, PRODUCT, AМOUNT FROM ORDERS WHERE ORDER DATE BETWEEN '2007-10-01' AND '2007-12-31'; ORDER

NUМ

ORDER DATE

MFR

PRODUCT

112961 112968

2007-12-17 2007-10-12

REI АС!

2A44L 41004

AМOUNT

$31,500.00 $3,978.00

136

Часть

112963 112983 112979 112992 112975 112987

11. Выборка даннь~х

2007-12-17 2007-12-27 2007-10-12 2007-11-01 2007-10-12 2007-12-31

ACI ACI ACI ACI REI ACI

41004 41004 4100Z 41002 2A44G 4100У

$3,276.00 $702.00 $15,000.00 $760.00 $2,100.00 $27,500.00

При проверке на принадлежность диапазону верхняя и нижняя границы счи­ таются частью диапазона, поэтому в результаты запроса вошли заказы, сделанные

1 октября

и

31

декабря. Далее приведен другой пример проверки на принадлеж­

ность диапазону.

Найти заказы, стоимости которых мпадают в различнъ~е диапазоны.

SELECT ORDER_NUМ, AМOUNT FROM ORDERS WHERE AМOUNT BETWEEN 20000.00 AND 29999.99; ORDER

NUМ

AМOUNT

113036 112987 113042

$22,500.00 $27,500.00 $22,500.00

SELECT ORDER_NUМ, AМOUNT FROM ORDERS WHERE AМOUNT BETWEEN 30000.00 AND 39999.99; NUМ

AМOUNT

112961 113069

$31,500.00 $31,350.00

ORDER

SELECT ORDER_NUМ, AМOUNT FROM ORDERS WHERE AМOUNT BETWEEN 40000.00 AND 49999.99; ORDER

NUМ

AМOUNT

113045

$45,000.00

Инвертированная

версия

проверки

на

принадлежность

диапазону

(NOT BETWEEN) позволяет выбрать значения, которые лежат за пределами диапа­ зона, как в следующем примере.

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

80

до

120

процентов плана.

SELECT NАМЕ, SALES, QUOTA FROM SALESREPS WHERE SALES NOT BETWEEN (.8 NАМЕ

Mary Jones Sue Smith

* QUOTA) AND (1.2 * QUOTA);

SALES

QUOTA

$392,725.00 $474,050.00

$300,000.00 $350,000.00

Гuва 6. Простые запросы

ВоЬ Smith Nancy Angelli

$142,594.00 $186,042.00

137

$200,000.00 $300,000.00

Проверяемое выражение, задаваемое в операторе BETWEEN, может быть любым.

допустимым выражением.

SQL,

однако на практике оно обычно представляет со­

бой имя столбца.

В стандарте

ANSI/ISO

определены относительно сложные правила обработки

значений NULL в проверке BETWEEN.



Если проверяемое выражение имеет значение NULL либо оба выражения, определяющие диапазон, равны

NULL,

то проверка

BETWEEN

возвращает

NULL.



Если выражение, определяющее нижнюю границу диапазона, имеет значе­ ние

NULL,

то проверка BEТWEEN возвращает

FALSE,

когда проверяемое зна­

чение больше верхней границы диапазона, и NULL- в противном случае.



Если выражение, определяющее верхнюю границу диапазона, имеет значе­ ние

NULL,

то проверка BEТWEEN возвращает

FALSE,

чение меньше нижней границы диапазона, и NULL -

когда проверяемое зна­

в противном елучае.

Однако преЖде чем полагаться на эти правила, неплохо было бы поэкспери­ ментировать со своей СУБД.

Необходим.о отметить, что проверка на принадлежность диапазону не расширяет возможности SQL, поскольку ее можно выразить в виде двух сравнений. Проверка А

BETWEEN

В

AND

С

полностью эквивалентна сравнению



>= В)

AND



2000.00 ALL DISTINCT MFR, PRODUCT ORDERS AМOUNT > 30000.00;

АС!

4100У

REI

2A44L 41002 2A44R

АС!

REI !ММ

775С

REI REI

2A44L 2A44R Обратите внимание на то, что обработка по умолчанию повторяющихся строк

в онерации UNION и инструкции SELECT осуществляется по-разному. Инструкция

SELECT по умолчанию оставляет такие строки (SELECT ALL). Чтобы удалить их, не­ обходимо явно указать предикат DISTINCT. Операция UNION по умолчанию удаляет повторяющиеся строки. Чтобы оставить их, следует явно задать предикат ALL.

Специалисты по работе с базами данных критиковали обработку повторяющихся

строк в

SQL

и указывали на эту несогласованность языка как на одну из проблем.

Причина такой несогласованности заключается в том, что в

SQL

в качестве устано­

вок 110 умолчанию выбираются наиболее часто используемые варианты.

Часть 11. Выборка данных

152 •

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



На практике большинство операций UNION возвращает повторяющиеся строки, что нежелательно, поэтому по умолчанию такие строки удаляются.

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

зать предикат ALL -

UNION

не возвратит повторяющихся строк, стоит явно ука­

тогда запрос будет выполняться быстрее.

Объединение и сортировка* Предложение

ORDER ВУ нельзя использовать ни в одной

из

инструкций

SELECT, объединенных операцией UNION. Нет смысла выполнять сортировку ре­ зультатов таких запросов, поскольку пользователь все равно не увидит их в чистом

виде. Однако объединенные результаты запросов, возвращенные операцией UNION, можно отсортировать с помощью предложения

ORDER ВУ, следующего за второй

инструкцией SELECT. Поскольку столбцы таблицы результатов запроса на объе­ динение не имеют имен, в этом предложении следует указывать номера столбцов.

Однако многие СУБД, включая

Oracle, SQL Server

и

MySQL,

используют имена

столбцов из первого запроса SELECT, так что эти имена можно использовать и в предложении

ORDER

ВУ.

Ниже показан тот же запрос, что и на рис.

6.14,

в котором результаты дополни­

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

Вывести список всех товаров, цена которых превышает казано более чем на

$30 ООО

$2000

или которых бъzло за­

за один раз; список отсортировать по наименованию

производителя и номеру товара.

SELECT FROM WHERE UNION SELECT FROM WHERE ORDER ACI ACI IMM REI REI

MFR_ID, PRODUCT_ID PRODUCTS PRICE > 2000.00 DISTINCT MFR, PRODUCT ORDERS AМOUNT > 30000.00 ВУ 1,2; 4100У

4100Z 775С

2A44L 2A44R

Вложенные объединения* Операцию UNION можно использовать многократно, чтобы объединить резуль­ таты трех или более запросов так, как изображено на рис.

6.15.

В результате объе­

динения таблиц В и с получается одна объединенная таблица. Затем с помощью другой операции UNION эта таблица объединяется с таблицей А. Синтаксис запро­ са, представленного на рисунке, имеет следующий вид.

ГАава 6. ПроС1Ые запросы

153

SELECT * FROM А UNION (SELECT * FROM В UNION SELECT * FROM С); Bill

Mary George Fred Sue Julia Harry Таблица А

Bill

Mary George Fred

Таблица в

Bill

Sue Julia Harry

Результаты запроса

Bill маrу

Таблица с

Mary George

с

'. '

UNION

~

f

с

UNION

t

Bill

.-

Sue Julia Harry

'

r

George Fred Sue Julia нarry

Маrу

Bill

George

Harry Рис.

6.15.

Вложенные операции

UNION

Скобки в запросе показывают, какая операция UNION должна выполняться пер­ вой. В действительности, если все операции UNION удаляют повторяющиеся стро­ ки или все сохраняют их, то порядок выполнения инструкций не имеет значения.

Следующие три выражения полностью эквивалентны

UNION (В UNION С) UNION В) UNION С (А UNION С) UNION В

А



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

UNION ALL (В UNION ALL С) UNION ALL В) UNION ALL С (А UNION ALL С) UNION ALL В

А



Однако если в запросы на объединения входят как операции UNION, так и опе­ рации UNION ALL, то порядок этих инструкций имеет значение. Если выражение А

UNION ALL

В

UNION

С

Часть

154

11. Вь16орка А&нных

интерпретировать как

А

UNION ALL



UNION

С)

то оно вернет десять строк (шесть из внутренней инструкции плюс четыре строки из таблицы А). Но если его интерпретировать как (А

UNION ALL

В)

UNION

С

то оно вернет только четыре строки, поскольку внешняя операция

UNION

удалит

все повторяющиес~1 строки. По этой причине всегда необходимо использовать круглые скобки, чтобы указать носледовательность выполнения в запросах на объ­ единение, содержащих три или более операций UNION.

Резюме Данна~1 глава яоляетDI 11ер1юй из четырех глав, посвященных SQL-запросам. В ней

были рассмотрены следующие вопросы.



Инструкцю1 SELECT используется для формирования SQL-зaпpoca. Каж­ дая инструкция SELECT возвращает таблицу результатов запроса, содер­

жащую один или более столбцов и ну ль или более строк.



Предложение FROM определяет таблицы, в которых содержатся выбирае­ мые данные.



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



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



Условие отбора 11озволяет выбрап, строки путем сравнения значений, проверки значений па принадлежность множеству или диапазону значе­

ний, провер1 TARGET;

FROM ORDERS, CUSТOMERS, SALE SREPS WНERE CUST = CUST NUМ AND REP = EMPL iiuм AND АМОUNТ > 25000. 00;

SELECT ORDER NUМ, АМОUNТ, COMPANY' NАМЕ

SELECT ORDER NUМ, АМОUNТ, DE SCRIPTION FROM ORDERS, PRODUCTS WНERE MFR = MFR ID AND PRODUCT =-PRODUCT_ID;

СIТУ,

TITLE FROМ OFFICES, SALESREPS WНERE MGR = EMPL NUМ AND TARGET > 600000.00;

SELECT

SELECT CITY, NАМЕ, TITLE FROM OFFICES, SALESREPS WНERE MGR = EMPL_NUМ;

SELECT NАМЕ, CITY FROM SALESREPS, OFFICES WНERE REP_OFFICE = OFFICE;

Старыi синтаксис

Синтаксис соединений

Простое соединение

соединения 1

Внутренние

Тип

Таблица

СIТУ

СIТУ

NАМЕ,

ORDER NUМ, АМОUNТ, COMPANY' NАМЕ ORDERS JOIN CUSTOMERS CUST = CUST NUМ SALESREPS REP = EMPL NUМ WНERE АМОUNТ > 25000. 00;

SELECТ

ORDER NUМ, АМОUNТ, DESCRIPTION FROM ORDERS JOIN PRODUCТS USING (MFR, PRODUCТ);'

ORDER NUМ, АМОUNТ, DESCRIPTION FROM ORDERS NAТURAL JOIN PRODUCТS;'

SELECТ

NАМЕ, QUOTA, TARGET FROM SALESREPS JOIN OFFICES ON QUOTA > TARGET;

SELECТ

FROM ON JOIN ON

SELECТ

SELECТ

ORDER NUМ, АМОUNТ, DESCRIPТION FROM ORDERS JOIN PRODUCТS ON MFR = MFR ID AND PRODUCТ =-PRODUCT_ID;

СIТУ,

NАМЕ,

TITLE FROM OFFICES JOIN SALESREPS ON MGR = EMPL NUМ WНERE TARGET > 600000.00;

SELECТ

СIТУ,

TITLE FROM OFFICES JOIN SALESREPS ON MGR = EMPL_NUМ;

SELECТ

FROM SALESREPS INNER JOIN OFFICES ON REP_OFFICE = OFFICE;

SELECТ NАМЕ,

FROM SALESREPS JOIN OFFICES ON REP_OFFICE = OFFICE;

SELECТ NАМЕ,

Стандартнwi синтаксис

простого

JOIN

JOIN

столбцов с одним и тем же именем в соединяемых таблицах

Соединение по равенству с явным указанием имен связанных

одни и те же имена в соединяемых таб11ицах

Соединение по равенству, когда связанные сто11бцы имеют

венства(=)

Оператор сравнения в предикате соединения отличен от ра­

те11ьных предложений

Более двух таблиц соединяются путем добавления дополни­

ветствия неско11ьких столбцов в предикате соединения

Многостолбчатые первичный и внешний ключи требуют соот­

проса с помощью предиката WНERE

Неже11ательные строки отфильтровываются из результата за­

вие основного ключа одной таблицы внешнему ключу другой

Соединение по равенству, в котором проверяется соответст­

INNER JOIN вместо

Вариант синтаксиса с применением ключевых слов

сто11бцов

Образует пары строк с одинаковым содержимым связанных

Описание

USING

USING

с nредложением

единение

Левое внешнее со­

соединения4.s

Внешние

единение

Левое внешнее со­

вым СЛОВОМ OUТER

зумеваемым ключе­

единение с nодра­

Полное внешнее со­

жением

единение с nредло­

Полное внешнее со­

ние

внешнее соедине-

Естественное nолное

единение

Полное внешнее со­

соединения2.з

Внешние

WНERE

СIТУ;

WНERE

FROМ

SELECT

UNION

У(+)

У(+);

WНERE

GIRLS.CIТY

BOYS.

= BOYS.CIT

GIRLS, BOYS GIRLS. СIТУ ( +)

*

SELECT • FROM GIRLS, BOYS

-

*

= MGRS.EM

FROM GIRLS, BOYS GIRLS. СIТУ = BOYS. CIT

SELECT

PL_NUМ;

WНERE

EMPS.МANAGER

SALESREPS EMPS, SALESRE PS MGRS

FROМ

*

MGRS.EМPL_NUМ;

*

*

*

OUТER

JOIN BOYS

*

USING

FROМ

SELECТ

LEFТ

(СIТУ);

• GIRLS

FROM GIRLS LEFТ ON GIRLS.CIТY

SELECТ

OUТER

JOIN BOYS

JOIN BOYS

= BOYS.CITY;

OUТER

FROM GIRLS FULL JOIN BOYS USING (СIТУ);

SELECТ

FROM GIRLS FULL USING (СIТУ);

SELECТ

FROM GIRLS NAТURAL FULL OUТER JOIN BOYS;

SELECТ

FROM GIRLS FULL OUТER JOIN BOYS ON GIRLS.CIТY = BOYS.CIТY;

SELECТ

FROМ SALESREPS ЕМРS JOIN SALESREPS MGRS ON EMPS.МANAGER =

MGRS.NAМE

SELECТ

EMPS.NAМE,

Стандартныi синтаксис

MGRS.NAМE

SELECT

Самосоединение

EMPS.NAМE,

Старыi синтаксис

Тип

7.1

no равенству таблицы с самой собой, когда каж­

SQL nозволяют

NULL

столбцов с одинаковыми именами в соединяемых таблицах

Левое внешнее соединение на основе явно указанных имен

чениями

добавляет к результатам заnроса строку, расширенную зна­

Для каждой несвязанной строки из nервой (левой) таблицы

FULL

оnустить ключевое слово OUТER, которое nодразумевается ключевым словом

Многие реализации

столбцов с одинаковыми именами в соединяемых таблицах

Полное внешнее соединение на основе явно указанных имен

столбцах с одинаковыми именами в соединяемых таблицах

Полное внешнее соединение, основанное на всех связанных

единяемых таблиц

NULL строку для каждой несвязанной строки каждой из со­

Добавляет к результатам заnроса расширенную значениями

дая строка связана с другой в той же самой таблице

Соединение

Описание

Продолжение табл.

USING

*

*

OUТER

JOIN BOYS

FROM BOYS;

SELECT *

SELECT * FROM GIRLS UNION ALL

SELECT * FROM GIRLS CROSS JOIN BOYS;

SELECT * FROM GIRLS RIGНТ USING (СIТУ);

FROM GIRLS RIGHT OUТER JOIN BOYS ON GIRLS.CITY: BOYS.CITY;

NULL

SELECT

соединяются оператором

UNION

обрабатываются независимо, а результирующие множества

Технически соединением не является; инструкции

ставляет собой таблицу, состоящую из всех возможных пар строк из обеих таблиц

Явный запрос декартова произведения таблиц, которое пред­

Правое внешнее соединение на основе явно указанных имен столбцов с одинаковыми именами в соединяемых таблицах

чениями

добавляет к результатам запроса строку, расширенную зна­

fJ,/lя каждой несвязанной строки из второй (правой) таблицы

И

PRODUCT.

' Этот запрос приведен только для иллюстрации и не будет работать в учебной базе данных, так как в таблице PRODUCTS нет столбцов MFR

ми, многие СУБД вернут декартово произведение.

именами. При попытке выполнения такого или подобного естественного соединения таблиц, в которых нет столбцов с одинаковыми имена­

' Этот запрос приведен mлько для иллюстрации, так как в таблицах ORDERS и PRODUCTS учебной базы данных нет столбцов с одинаковыми

' В примерах старого синтаксиса использован синтаксис Oracle.

Внешние соединения не теряют информацию, поскольку добавляют к результату запроса несвязанные строки.

BOYS.

Описание

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

BOYS;

BOYS ( +)

СIТУ

Стандартный синтаксис SELECT *

1

FROM BOYS;

SELECT *

FROM GIRLS UNION ALL

SELECТ

FROM GIRLS,

SELECT *

СIТУ;

WНERE

FROM GIRLS, GIRLS.

SELECТ

Старый синтаксис

7.1

2

(объединение)

единение

Расширенное со­

единение

Перекрестное со­

соединения

Внешние

жением

единение с предло­

Правое внешнее со­

единение

Правое внешнее со­

Тип

Окончание табл.

Часть 11. Вы6орка АIИИЫI

202

Резюме В настоящей главе рассмотрены запросы

SQL,

соединяющие данные из двух

и более таблиц.



Имена таблиц, из которых берутся данные, указываются в многотаблич­ ном запросе (соед11нен1111) в предложении FROM.



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



В

многотабличных

запросах

чаще

всего

используются

отношения

"предок-потомок", создаваемые первичными и внешними ключами.



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

столбцов из днух соединяемых таблиц, используя условие равенства или любое другое услоние сравнения.



Соединение можно представить как произведение двух таблиц, из кото­ рого удалена часть строк.



Таблицу можно соединять саму с собой; при таком самосоединении не­ обходимо использовать псевдонимы таблицы.



Внешнее соединение является расширением стандартного (внутреннего)

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



Стандарт

SQL

обес11ечивает полную помержку внутренних и внешних

соединений и 1юз1юм1ет соединять результаты соединений с другими

многотабличными О11ерациями, такими как объединения запросов, пере­ сечения и разности.

ГЛАВ

Итоговые запросы м

ногие запросы к базе данных не требуют того уровня детализации, ко­

торый обеспечивают SQL-запросы, рассмотренные в двух предыдущих главах. Например, во всех перечисленных ниже запросах требуется уз­

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

• • • • • • В

Каковы наибольший и наименьший плановые объемы продаж? Сколько служащих перевыполнили свой план? Какова средняя стоимость заказа? Какова средняя стоимость заказа в каждом офисе? Сколько служащих закреплено за каждым офисом?

SQL

запросы такого типа можно создавать с помощью аrрегирующих функ­

ций и предложений

GROUP

ВУ и НAVING инструкции

SELECT,

описанных в данной

главе.

Аrреrирующие функции

SQL

позволяет получить итоговые данные с использованием набора статисти­

ческих, или агрегирующих, функций

(column functions).

Такая функция принимает

в качестве аргумента какой-либо столбец данных целиком и возвращает единст­

венное итоговое значение для столбца. Например, функция AVG ()

принимает

в качестве аргумента столбец чисел и вычисляет их среднее значение. Ниже при­ веден запрос, в котором функция AVG ( ) используется для вычисления среднего значения в двух столбцах таблицы SALESREPS.

Часть 11. Выборка данных

204

Каковы средний плановый и средний фактический обьемы продаж у продавцов?

SELECT AVG(QUOTA), AVG(SALES) FROM SALESREPS; AVG(QUOTA)

AVG(SALES) $289,353.20

$300,000.00 На рис.

8.1

изображена схема выполнения такого запроса. Первая функция

принимает в качестве аргументов все значения, содержащиеся в столбце QUOTA, и вычисляет их среднее значение; вторая функция подсчитывает среднее значение столбца SALES. Результатом запроса является одна строка, представляющая итоги

по информации, содержащейся в таблице SALESREPS. Табли

SALESRВPS

EМPL_NUМ

105 109 102 106 104 101 110 108 103 107

NАМЕ

Bill Adams Mary Jones Sue Smith Sam Clark ВоЬ Smith Dan Roberts Тот Snyder Larry Fitch Paul Cruz Nancy Angelli

Рис.

В стандарте

SQL

8.1.

МANAGER

QUOTA

SALES

104 106 108

$350,000.00 $300,000.00 $350,000.00 $275,000.00 $200,000.00 $300,000.00

$367,911.00 $392,725.00 $474,050.00 $299,912.00 $142,594.00 $305,673.00 $75,985.00 $361,865.00 $286,775.00 $186,042.00

NULL 106 104 101 106 104 108

NULL $350,000.00 $275,000.00 $300,000.00

Выполнение итогового запроса

определен ряд таких функций; помимо них, многие произво­

дители СУБД добавляют собственные функции в своих реализациях наиболее распространенных функций, показанных на рис.

8.2,

SQL.

Шесть

позволяют пол у­

чать различные виды итоговой информации.

• • • • •

Функция suм

()

вычисляет сумму всех значений столбца.

Функция AVG () вычисляет среднее всех значений столбца. Функция MIN () находит наименьшее среди всех значений столбца. Функция МАХ () находит наибольшее среди всех значений столбца. Функция COUNT ()

подсчитывает количество значений, содержащихся

в столбце.



Функция COUNT ( *) подсчитывает количество строк в таблице результа­ тов запроса (фактически это альтернативная форма функции COUNT () ).

Гмва 8. Итоrовые запросы

SUM

(

--Т- выражение

l_ AVG

DISTINCT

WUl_столбца _J

(выражение)---------------------

1------

MIN

1-----

МАХ (выражение)

1 - - - - - COUNT

~---

WIЯ_столбца _J

DISTINCT

( --Т- выражение

L_

205

(

L

- - - - - - - - - - - - - - - - - - - -.....

WIЯ_столбца)

DISTINCT

_J

COUNT (*)

1~

Рис.

8.2.

Синтаксическая диаграмма статистических функций

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

Каков средний про1µ?Нm въzполненил плана в компании?

SELECT AVG(lOO * (SALES/QUOTA)) FROM SALESREPS; AVG(lOO * (SALES/QUOTA)) 102.60 При выполнении этого запроса СУБД создает временный столбец, содержащий значения 100* (SALES/QUOTA) для каждой строки таблицы SALESREPS, а затем.

вычисляет среднее значение временного столбца.

ВычисАение суммы значений стоАбца Статистическая функция suм

()

вычисляет сумм.у всех значений столбца. При

этом столбец должен иметь числовой тип данных (содержать целые числа, деся­ тичные числа, числа с плавающей точкой или денежные величины). Результат, возвращаемый этой функцией, имеет тот же тип данных, что и столбец, однако

точность результата может быть выше. Например, если применить функцию suм

()

к столбцу, содержащем.у 16-разрядные целые числа, она может вернуть

в качестве результата 32-разрядное целое число.

Ниже приведен ряд прим.еров, в которых используется функция suм

() .

206

Часть 11. Выборка AIHHЫI

Каковъ~ общий плановь~й и общий фактический обьемы продаж?

SELECT SUМ(QUOTA), FROM SALESREPS;

SUМ(SALES)

SUМ(QUOTA)

$2,700,000.00

SUМ(SALES)

$2,893,532.00

Какова сумма всех заказов, принятых Биллом Адамсом?

SELECT SUМ(AМOUNT) FROM ORDERS, SALESREPS WНERE NАМЕ = 'Bill Adams' AND REP = EMPL_NUМ; SUМ(AМOUNT)

$39,327.00

ВычисАение среднеrо значений стоАбца Статистическая функция AVG () вычисляет среднее всех значений столбца. Как

и в ел учае с функцией suм

( ) , данные,

содержащиеся в столбце, должны иметь чи­

словой тип. Поскольку функция AVG () вначале суммирует все значения, содер­

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

столбца. Например, если применить функцию AVG () к столбцу целых чисел, ре­ зультат будет либо десятичным числом, либо числом с плавающей точкой, в зави­ симости от используемой СУБД. Ниже приведено несколько примеров использования функции AVG (). Вь~числитъ среднюю цену товаров от производителя

ACI.

SELECT AVG(PRICE) FROM PRODUCTS WНERE MFR ID = 'ACI'; AVG(PRICE) $804.29 Вычислить среднюю стоимость заказов, сделанных компанией Асте (клиент

2103).

SELECT AVG(AМOUNT) FROM ORDERS WНERE CUST = 2103; AVG(AМOUNT)

$8,895.50

Mfg.

ГАава 8. Итоrовые запросы

207

ВычисАение предеАьных значений Статистические функции MIN () и МАХ () позвом1ют найти соотнетственно наи­

меньшее и наибольшее значения в столбце. При этом столбец может содержать

числа, строки либо значения даты/времени. Результат, ноз11ращаемый этими функциями, имеет точно тот же тип данных, что и сам столбец. Ниже приведен ряд примеров, иллюс1рирующих ис1юл1>ЗО1Jание у1юмянутых функций.

Каковы наиболыиий и наименыиий плановъ~е объемы продаж?

SELECT MIN(QUOTA), FROM SALESREPS;

МAX(QUOTA)

MIN(QUOTA)

МAX(QUOTA)

$200,000.00

$350,000.00

Когда был сделан самый первый из всех содержащихс.я в базе данных заказов?

SELECT MIN(ORDER_DATE) FROM ORDERS; MIN(ORDER_DATE) 2007-01-04 Каков наибольший процент вътолнени.я плана среди всех служащих?

SELECT МАХ(lОО * (SALES/QUOTA)) FROM SALESREPS; MAX(lOO

* (SALES/QUOTA)) 135.44

В случае применения функций MIN () и МАХ () к числовым дапным числа срав­ ниваются по арифметическим правилам (среди двух отри~1ательных чисел меньше

то, у которого модуль больше; нуль меньше любого положительного числа и больше любого отрицательного). Сравнение дат происходит последовательно (более ранние значения дат считаются меньшими, чем более поздние). Сравнение

интервалов времени выполняется на основании их продолжительности (более ко­ роткие интервалы времени считаются меньшими, чем более дливпые). В случае применения функций

MIN () и МАХ () к строковым данным результат

сравнения двух строк зависит от используемой таблицы кодировки. На персо­ нальных компьютерах и мини-компьютерах, где используется таблица кодировки

ASCII,

установлен порядок сортировки, при котором цифры идут перед буквами,

а все прописные буквы

-

перед строчными. На мэйпфреймах компании IВМ, где

используется таблица кодировки

EBCDIC,

строчные сим1юлы рас11оложены перед

прописными, а цифры следуют за буквами. Ниже приведено сравнение последо­

вательностей сортировки, принятых в таблицах колиро11ки АSСП и примере строк, упорядоченных по возрастанию.

EBCDIC,

на

208

Часть 11. Выборка AIHHЫI

ASCll

EBCDIC

1234АВС

acme mfg.

5678АВС АСМЕ

MFG.

zeta corp. Acme Mfg.

Acme Mfg.

АСМЕ

ZETA CORP.

Zeta Corp.

Zeta Corp.

ZETA CORP.

acme mfg.

1234АВС

zeta corp.

5678АВС

MFG.

Оrличия в порядке сортировки приводят к тому, что один и тот же запрос, со­ держащий предложение ORDER ВУ, в различных системах может привести к раз­ ным результатам.

Хранение в таблицах символов национальных алфавитов (например, кирилли­ цы) может вызвать дополнительные проблемы. В некоторых СУБД для каждого языка используется свой алгоритм сортировки. В других СУБД такие символы

сортируются в соответствии с кодом символа. Для решения этой проблемы в стан­ дарт

SQL

включены помержка национальных наборов символов, пользователь­

ских наборов символов и альтернативных последовательностей сортировки. К со­ жалению, помержка этих возможностей сильно отличается от производителя

к производителю. Если в вашем приложении используются символы националь­ ных алфавитов, необходимо поэкспериментировать с вашей конкретной СУБД,

чтобы выяснить, как она их обрабатывает.

Подсчет коАичества данных Статистическая функция COUNT () подсчитывает количество значений в столб­ це; тип данных столбца при этом роли не играет. Функция COUNT () всегда воз­ вращает целое число, независимо от типа данных столбца. Ниже приведен ряд за­ просов, в которых используется эта функция.

Сколько клшнrrюв у нашей компании?

SELECT COUNT(CUST_NUМ) FROM CUSTOMERS; COUNT(CUST_NUМ)

21 Сколько служащих перевыполнили план?

SELECT COUNT(NAМE) FROM SALESREPS WНERE SALES > QUOTA$ COUNT(NAМE)

7

ГАава 8. Итоrовые запросы

Сколько имеется заказов стоимостью более

209

$25000?

SELECT COUNT(AМOUNT) FROM ORDERS WHERE AМOUNT > 25000.00; COUNT(AМOUNT)

4

Обратите внимание на то, что функция COUNT () с переданным именем столбца не учитывает значения NULL в этом столбце; это делает функция COUNT ( *), кото­ рая подсчитывает все строки независимо от их значений. Если же не рассматри­

вать значения NULL, то функция COUNT () игнорирует значения данных в столбце и просто подсчитывает их количество. Таким образом, не имеет значения, какой

именно столбец передан функции COUNT () в качестве аргумента. Так, последний пример может быть переписан следующим образом.

SELECT COUNT(ORDER_NUМ) FROM ORDERS WHERE AМOUNT > 25000.00; COUNT(ORDER_NUM) 4 Мысленно трудно представить запрос вроде "подсчитать, сколько стоимостей заказов" или "подсчитать, сколько номеров заказов"; гораздо проще представить запрос "подсчитать, сколько заказов". Поэтому в

SQL

была введена специальная

статистическая функция COUNT ( *), которая подсчитывает строки, а не значения данных. Вот предыдущий запрос, переписанный с использованием этой функции.

SELECT COUNT(*) FROM ORDERS WHERE AМOUNT > 25000.00; COUNT(*) 4 Если применять COUNT ( *) в качестве функции подсчета строк, то запрос ста­

новится более удобочитаемым. На практике для подсчета строк всегда использует­

ся функция COUNT ( * ) ' а не COUNT ( } .

Статистические функции в списке возвращаемых стоАбцов Назначение простого запроса, в котором участвуют статистические функции, понять достаточно легко. Однако если в список возвращаемых столбцов входит не­

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

ния SQL-запросов, в очередной раз расширенные с учетом применения статисти­ ческих функций. Как и ранее, эти правила являются точным определением того,

Часn. 11. Вы6орка АIННЫХ

210

что означает запрос, а не описанием того, как СУБД на самом деле получает ре­ зу ль таты запроса.

Для генерации результатов запроса SELECT следует выполнить следующие шаги.

1.

Если запрос представляет собой объединение (UNION) инструкций SELECT, для каждой из них выполнить шаги

2-5 для

генерации отдельных результа­

тов запросов.

2.

Сформировать произведение таблиц, указанных в предложении FROM. Если там указана только одна таблица, то произведением будет она сама.

3.

При наличии предложения WHERE применить указанное в нем условие

к каждой строке таблицы произведения, оставляя только те строки, для ко­ торых условие истинно, и отбрасывая строки, для которых условие ложно или равно

4.

NULL.

Для каждой из оставшихся строк вычислить значение каждого элемента

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

5.

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

6.

Если запрос является объединением инструкций SELECT, объединить ре­

зультаты выполнения отдельных инструкций в одну таблицу результатов запроса. Удалить из нее повторяющиеся с·1роки, если только в запросе не указан предикат

7.

UNION ALL.

Если имеется предложение ORDER ВУ, отсортировать результаты запроса, как указано в нем.

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

-

представить запрос разбитым на два этапа. Сначала

подумайте, как работал бы запрос без статистических функций, возвращая не­ сколько строк летального результата запроса. Затем представьте, как СУБД при­ меняет статистические функции к детальным результатам запроса, возвращая од­ ну итоговую строку. Например, рассмотрим следующий сложный запрос.

Найти среднюю стоимость заказов, общую стоимость заказов, среднюю стои.мостъ заказов в процентах от лимита кредита клиентов, а также среднюю стоимость за­

казов в проц.етпах от плановых объемов продаж служащих.

SELECT

AVG(AМOUNT),

SUМ(AМOUNT),

(100 * AVG(AМOUNT/CREDIT_LIMIT)), ( 100 * AVG (AМOUNT /QUOTA)) FROM ORDERS, CUSTOMERS, SALESREPS WНERE CUST = CUST NUМ AND REP = EMPL_NUМ;

Гмва 8. Итоrовые запросы

AVG(AМOUNT)

SUМ(AМOUNT)

(lOO*AVG(AМOUNT/CREDIT_LIMIT))

$8,256.37 $247,691.00

24.44

211

(lOO*AVG(AМOUNT/QUOTA))

2.51 Размер строки оказался слишком большим из-за очень длинных заголовков столбцов. Если вы используете SQL-клиент, вывод которого ограничен

80

или ме­

нее символами, то каждая строка окажется разбита на несколько, и результат бу­ дет не столь удобочитаемым, как показано здесь. Позже вы узнаете, как использо­ вать псевдонимы строк для устранения длинных заголовков, генерируемых СУБД

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

SELECT AМOUNT, AМOUNT, AМOUNT/CREDIT_LIMIT, FROM ORDERS, CUSTOMERS, SALESREPS WHERE CUST = CUST NUМ AND REP = EMPL_NUМ;

AМOUNТ/QUOTA

Этот запрос возвращал бы одну строку результатов запроса для каждого заказа.

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

SQL

статистическая функция может использоваться везде, где

можно указать имя столбца. Например, она может входить в выражение, в кото­

ром суммируются или вычитаются значения двух статистических функций. Одна­ ко в некоторых реализациях

SQL,

в частности, основанных на стандарте

SQL 1, ар­

гумент статистической функции не может содержать другую такую функцию, по­ скольку получаемое в таком случае выражение не имеет смысла. Иногда это пра­

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

вать статистические функции и обычные имена столбцов (за исключением запро­ сов с группировкой и подзапросов), поскольку в этом также нет смысла. Напри­ мер, рассмо·грим запрос.

SELECT NАМЕ, SUМ(SALES) FROM SALESREPS; Первый элемент списка возвращаемых столбцов просит СУБД создать таблицу, которая будет состоять из десяти строк и содержать обычные результаты запро­ са

-

по одной строке для каждого ел ужащего. Второй элемент списка возвращае­

мых столбцов просит СУБД пол учить одно результирующее значение, представ­

ляющее собой сумму значений столбца SALES. Эти элементы инструкции SELECT

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

бо в списке возвращаемых столбцов не должно быть ни одной статистической функции (и тогда запрос возвращает обычные результаты), за исключением неко­ торых ел учаев, описанных ниже в данной главе.

Часть 11. Вы6орк1А1нных

212

Статистические функции и значения

NULL

Функции SUM (), AVG (), MIN (), МАХ () и COUNT () в качестве аргумента прини­ мают столбец значений и возвращают в качестве результата одно значение. А что происходит, когда в столбце встречается одно или несколько значений NULL?

В стандарте

ANSl/ISO

сказано, что значения NULL статистическими функциями

игнорируются.

Следующий запрос показывает, что статистическая функция COUNT () игнори­ рует все значения NULL, содержащиеся в столбце.

SELECT COUNT(*), COUNT(SALES), COUNT(QUOTA) FROM SALESREPS; COUNT(*)

COUNT(SALES)

COUNT(QUOTA)

10

10

9

В таблипе SALESREPS содержится десять строк, поэтому функция COUNT ( *) возвращает число

10.

В столбце SALES содержится десять значений, причем ни од­

но из них не равно NULL, поэтому функция COUNT ( SALES) также возвращает чис­

ло

10.

А вот в столбце QUOTA содержится одно значение NULL- для служащего,

принятого совсем недавно. Функция COUNT ( QUOTA) и возвращает число

9.

игнорирует это значение

Именно из-за таких расхождений вместо функции COUNT ()

для подсчета строк почти всегда используется функция COUNT ( *). Исключение составляют случаи, когда необходимо не учитывать строки, содержащие значения

NULL в определенном столбце. Игнорирование значений NULL не оказывает влияния на результаты, возвра­

щаемые статистическими функциями MIN () и МАХ ( ) . Однако оно может привести к проблемам при использовании функций suм

()

и AVG (),что иллюстрирует сле­

дующий запрос.

SELECT

SUМ(SALES}, (SUМ(SALES}

SUМ(QUOTA},

- SUM(QUOTA}},

SUМ(SALES-QUOTA)

FROM SALESREPS SUM (QUOTA}

SUМ(SALES}

$2,893,532.00

( SUМ (SALES) -

$2,700,000.00

SUМ(SALES-QUOTA}

$117,547.00 Можно ожидать, что выражения (SUМ(SALES)

-

SUМ(QUOTA))

и

SUМ(SALES-QUOTA}

SUМ

( QUOTA) )

$193,532.00

ГАава

8. Итоrовые запросы

213

вернут одинаковые результаты, однако пример показывает, что так не происхо­

дит. И снова причиной является строка со значением NULL в столбце QUOTA. Вы­ ражение

SUМ(SALES) вычисляет сумму продаж для всех десяти служащих, а выражение

SUМ{QUOTA)

вычисляет сумму только девяти значений и не учитывает значение NULL. Выражение SUМ(SALES)

-

SUМ{QUOTA)

вычисляет разницу между ними. В то же время выражение SUМ(SALES-QUOTA)

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

В строке, где значение планового объема продаж равно NULL, операция вычитания возвращает значение NULL, которое функция suм

()

игнорирует. Таким образом,

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

Какой же ответ является "правильным"? Оба! Первое выражение вычисляет именно то, что и означает, т.е. "сумма по SALES минус сумма по QUOTA". И второе выражение

также

вычисляет

именно

то,

что

оно

означает,

т.е.

"сумма

(SALES - QUOTA)". Однако при наличии значений NULL результаты выражений отличаются.

В стандарте

ANSl/150

определены следующие точные нравила обработки зна­

чений NULL в статистических функциях.



Если какие-либо из значений, содержащихся в столбце, равны NULL, при вычислении результата функции они игнорируются.



Если все значения в столбце равны NULL, то функции SUМ (), AVG (),

MIN ( ) и МАХ () возвращают значение NULL; функция COUNT ( ) возвраща­ ет нуль.



Если в столбце нет значений (т.е. столбец пустой), то функции suм

(),

AVG (), MIN () и МАХ () возвращают значение NULL; функция COUNT () возвращает ну ль.



Функция COUNT ( *) подсчитывает количество строк и не зависит от на­

личия или отсутствия в столбце значений NULL. Если строк в таблице нет, эта функция возвращает ну ль.

Хотя стандарт предельно точен в данном вопросе, коммерческие SQL-продукты могут выдавать результаты, отличающиеся от стандарта, особенно если все значе­ ния, содержащиеся в столбце, равны NULL или таблица пуста. Прежде чем пола­ гаться на правила, определенные в стандарте, следует протестировать свою СУБД.

214

Часть 11. Выборка А8ННЫХ

УдаАение повторяющихся строк Из главы

6,

(DISTINCT)

"Простые запросы", вы должны помнить, что ключевое слово

DISTINCT указывается в начале списка возвращаемых столбцов и служит для уда­ ления повторяющихся строк из таблицы резу льтатов запроса. С помощью этого предиката можно также указать, что перед применением статистической функ­ ции к столбцу из него следует удалить все повторяющиеся значения. Для этого не­ обходимо включить предикат DISTINCT перед аргументом статистической функ­ ции сразу же после открывающей круглой скобки. Ниже приведены два запроса, которые иллюстрируют удаление повторяющих­

ся значений перед применением статистических функций. Сколько различных должностей сущеС11I0ует в нашей компании?

SELECT COUNT(DISTINCT TITLE) FROM SALESREPS; COUNT(DISTINCT TITLE) 3

В скольких офисах есть служащ,ие, превьu:шnиие плановые обьемы продаж?

SELECT COUNT(DISTINCT REP_OFFICE) FROM SALESREPS WНERE SALES > QUOTA; COUNT(DISTINCT REP_OFFICE) 4 Ключевое слово DISTINCT в одном запросе можно употребить только один раз.

Если оно применяется вместе с аргументом одной из статистических функций, его нельзя использовать ни с каким другим аргументом. Если оно указано перед спи­

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

DISTINCT используется второй раз внутри подзапроса. О подзапросах рассказыва­ ется в главе

9,

"Подзапросы и выражения с запросами".

Запросы с rруппировкой

(GROUP ВУ)

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

главе, напоминают итоговую информацию, находящуюся обычно в конце отчета. Эти запросы "сжимают" подробные данные, содержащиеся в отчете, в одну строку итоговых результатов. Но, как известно, в отчетах иногда используются и проме­

жуточные итоги. Точно так же бывает необходимо получать и промежуточные итоги

результатов

GROUP

ВУ инструкции

запроса.

Эту

возможность

предоставляет

предложение

SELECT.

Назначение предложения GROUP ВУ проще всего понять на примере. Рассмот­ рим два запроса.

ГАава 8. Итоговые запросы

215

Какова средняя стоимостъ заказа?

SELECT AVG(AМOUNT) FROM ORDERS; AVG(AМOUNT)

$8,256.37 Какова средняя стоимость заказа для каждого служащего?

SELECT REP, AVG(AМOUNT) FROM ORDERS GROUP ВУ REP; AVG

REP

(AМOUNT)

----------101 102 103 105 106 107 108 109

$8,876.00 $5,694.00 $1,350.00 $7,865.40 $16,479.00 $11,477.33 $8,376.14 $3,552.50 $11,566.00

110

Первый запрос представляет собой простой итоговый запрос, аналогичный рас­ смотренным

строк

-

ранее

примерам.

Второй

запрос

возвращает

по одной строке для каждой группы. На рис.

8.3

несколько

итоговых

изображена схема выпол­

нения второго запроса. Концептуально запрос выполняется следующим образом. Таблица ORDERS

Сгруппированная табл~и~ца--~~

ORDER NUM

AМOUNT

112961 112989

$31,500.00 $1,458.00

112975 113057

$2,100.00 $600.00

РеэулыаТhl запроса

AVG 103

113051 113045 113013 113024 113007 112992 113049 Рис.

$1,420.00 $45,000.00 $652.00 $7,100.00 $2,925.00 $760.00 $776.00

8.3. Выполнение запроса с группировкой

(AМOUNT)

$16,479.00 $1,350.00

$8,376.14

Часть 11. Выборка данных

216 1.

Заказы делятся на группы, по одной группе для каждого ел ужащего. В ка­

ждой группе все заказы имеют одно и то же значение в столбце REP.

2.

Для каждой группы вычисляется среднее значение столбца AМOUNT по всем строкам, входящим в группу, и генерируется одна итоговая строка резуль­

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

Запрос, включающий в себя предложение GROUP ВУ, называется запросом

с

группировкой, поскольку он объединяет строки исходных таблиц в группы и для каждой группы строк генерирует одну строку таблицы резу ль татов запроса. Столбцы, указанные в предложении GROUP ВУ, называются столбtjами группиров­ кu,

поскольку

именно

они определяют,

по

какому признаку строки делятся на

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

Каков дuапазон 11лановъ1х объемов продаж для каждого офиса?

SELECT REP_OFFICE, MIN(QUOTA), FROM SALESREPS GROUP ВУ REP_OFFICE;

МAX(QUOTA}

REP OFFICE

MIN(QUOTA)

МAX(QUOTA)

----------

-----------

-----------

NULL

NULL

NULL

11

$275,000.00 $200,000.00 $350,000.00 $350,000.00 $300,000.00

$300,000.00 $300,000.00 $350,000.00 $350,000.00 $300,000.00

12 13 21 22

Сколько служащих работает в каждом офисе?

SELECT REP_OFFICE, COUNT(*} FROM SALESREPS GROUP ВУ REP_OFFICE; REP- OFFICE

COUNT (*} --------

NULL

1 2 3 1 2 1

----------

11

12 13 21 22

Сколько клиентов обслуживает каждый служащий?

SELECT COUNT(DISTINCT CUST REP FROM CUSTOMERS GROUP ВУ CUST_REP; COUNT(DISTINCT

CUST_NUМ},

CUST_NUМ)

3 4 3

'customers for salesrep',

CUSTOMERS FOR SALESREP

----------------------

CUST REP --------

customers for salesrep customers for salesrep customers for salesrep

101 102 103

Гмва 8. Итоrовые запросы

1

2 2

customers f or salesrep customers f or salesrep customers f or salesrep

Между статистическими функциями

SQL

217

104 105 106

и предложением GROUP ВУ существует

внутренняя связь. Статистическая функция берет столбец значений и возвращает одно значение. Предложение GROUP ВУ указывает, что следует разделить результа­ ты запроса на группы, применить статистическую функцию по отдельности к каж­ дой группе и пол учить для каждой группы одну строку результатов. Ниже показа­ ны шаги выполнения SQL-зaпpoca, расширенные с учетом операции группировки.

1.

Если запрос представляет собой объединение (UNION) инструкций SELECT, для каждой из них выполнить шаги

2-7 для

генерации отдельных результа­

тов запросов.

2.

Сформировать произведение таблиц, указанных в предложении FROM. Если там указана только одна таблица, то произведением будет она сама.

3.

При наличии предложения WHERE применить указанное в нем условие к

каждой строке таблицы произведения, оставляя только те строки, для ко­ торых 'условие истинно, и отбрасывая строки, для которых условие ложно или равно

4.

NULL.

Если имеется предложение GROUP ВУ, разделить строки, оставшиеся в таб­

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

5.

Если имеется предложение НAVING, применить указанное в нем условие к каждой строке группы, оставляя только те группы, для которых условие ис­

тинно, и отбрасывая группы, для которых условие ложно или равно NULL.

6.

Для каждой из оставшихся строк (или для каждой группы строк) вычислить

значение каждого элемента в списке возвращаемых столбцов и создать одну строку таблицы результатов запроса. При простой ссылке на столбец берется значение столбца в текущей строке (или группе строк); при использовании статистической функции в качестве ее аргумента при наличии предложения

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

7.

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

8.

Если запрос является объединением инструкций SELECT, объединить ре­ зультаты выполнения отдельных инструкций в одну таблицу результатов запроса. Удалить из нее повторяющиеся строки, если только в запросе не указан предикат

9.

UNION ALL.

Если имеется предложение ORDER ВУ, отсортировать результаты запроса, как указано в нем.

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

218

Часть 11. Выборка данных

НескоАько стоАбцов rруппировки

SQL

позволяет гру1111ировать результаты запроса на основе двух или более

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

SELECT REP, CUST, SUМ(AМOUNT) FROM ORDERS GROUP ВУ REP, CUST; REP

CUST

SUМ(AМOUNT)

---------101 101 101 102 102 102 103 105 105

2102 2108 2113 2106 2114 2120 2111 2103 2111

$3,978.00 $150.00 $22,500.00 $4,026.00 $15,000.00 $3,750.00 $2,700.00 $35,582.00 $3,745.00

Даже при груrширо11ке 110 нескольким столбцам старые версии

SQL

обеспечи­

вают только один уровень группировки. Приведенный запрос генерирует одну

итоговую строку для 1 $30, 000? FALSE 1 ~~~---~

$1,420.00 $45, ООО. 00 $652. 00 $7, 100. 00 $2,925.00 $760. 00 $776.00

Рис.

8.4. Условие

отбора групп в действии

В предложении НAVING указываются точно такие же условия отбора, как и в предложении WHERE; описаны в главах

6,

"Простые запросы", и

9,

"Подзапросы

и выражения с запросами". Ниже приведен еще один пример использования усло­

вия для отбора групп. Для каждого офиса, в котором работают два и1ш более человек, въzчислить общий илановый и фактический обьемы иродаж для всех служащих.

SELECT CITY, SUМ(QUOTA), SUМ(SALESREPS.SALES) FROM OFFICES, SALESREPS WHERE OFFICE = REP OFFICE GROUP ВУ CITY НAVING COUNT(*) >= 2; CITY

SUМ(QUOTA)

Chicago Los Angeles New York

$775,000.00 $700,000.00 $575,000.00

SUМ(SALESREPS.SALES)

$735,042.00 $835,915.00 $692,637.00

Ниже показаны шаги выполнения SQL-зaпpoca, расширенные с учетом условия

отбора групп.

1.

Если запрос представляет собой объединение (UNION) инструкций SELECT, для каждой из них выполнить шаги тов запросов.

2-7 для

генерации отдельных результа­

Глава 8. Итоrовые запросы

2.

225

Сформировать произведение таблиц, указанных в предложении FROM. Если там указана только одна таблица, то произведением будет она сама.

3.

При наличии предложения WHERE применить указанное в нем условие

к каждой строке таблицы произведения, оставляя только те строки, для ко­ торых условие истинно, и отбрасывая строки, для которых условие ложно или равно

4.

NULL.

При наличии предложения GROUP ВУ разделить строки, оставшиеся в таб­ лице произведения, на группы таким образом, чтобы в каждой группе

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

5.

Если имеется предложение НАVING, применить указанное в нем условие к каждой строке группы, оставляя только те группы, для которых условие

истинно, и отбрасывая группы, для которых условие ложно или равно NULL.

6.

Для каждой из оставшихся строк (или для каждой группы строк) вычислить значение каждого элемента в списке возвращаемых столбцов и создать одну

строку таблицы резу ль татов запроса. При простой ссылке на столбец берется значение столбца в текущей строке (или группе строк); при использовании статистической функции в качестве ее аргумента при наличии предложения

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

7.

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

8.

Если запрос является объединением инструкций SELECT, объединить ре­ зультаты выполнения отдельных инструкций в одну таблицу результатов запроса. Удалить из нее повторяющиеся строки, если только в запросе не указан предикат

9.

UNION ALL.

Если имеется предложение ORDER ВУ, отсортировать результаты запроса, как указано в нем.

Строки, генерируемые этой процедурой, составляют результаты запроса. В соответствии с описанной процедурой, СУБД выполняет приведенный выше

запрос таким образом.

1.

Объединяет таблицы OFFICES и SALESREPS, чтобы определить город, в ко­ тором работает каждый служащий.

2. 3.

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

-

это те группы, кото­

рые не удовлетворяют критерию предложения НAVING.

4.

Вычисляет общие плановые и фактические объемы продаж для каждой группы.

Вот еще один пример, в котором используются все

SELECT.

предложения инструкции

Часть 11. Выборка данных

226

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

75

процентов от количества товара на складе.

SELECT FROM WHERE AND GROUP

DESCRIPTION, PRICE, QTY_ON_HAND, SUМ(QTY) PRODUCTS, ORDERS MFR = MFR ID PRODUCT = PRODUCT ID ВУ MFR_ID, PRODUCT_ID, DESCRIPTION, PRICE, QTY_ON_HAND НAVING SUМ(QTY) > (.75 * QTY_ON_HAND) ORDER ВУ QTY_ON_HAND DESC; DESCRIPTION

PRICE

QTY_ON_НAND

SUМ(QTY)

--------------- ---------

-----------

--------

Reducer $355.00 Widget Adjuster $25.00 Motor Mount $243.00 Right Hinge $4,500.00 500-lb Brace $1,425.00

38 37 15 12 5

32 30 16 15 22

Чтобы осуществить этот запрос, СУБД выполняет следующие действия. Объединяет таблицы ORDERS и PRODUCTS, чтобы получить описание, цену

1.

и количество единиц на складе для каждого заказанного товара.

Группирует строки объединенной таблицы по идентификаторам произво­

2.

дителя и товара.

Исключает группы, в которых количество заказанных единиц составляет

3.

менее

75 процентов от количества

на складе.

Вычисляет общее количество заказанных единиц для каждой группы.

4. 5. 6.

Генерирует одну итоговую строку запроса для каждой группы.

Сортирует резу льтаты запроса таким образом, чтобы товары, которых на складе бол1,ше, были представлены первыми.

Как было сказано ранее, столбцы DESCRIPTION, PRICE и QTY_ON_НAND должны быть указаны в качестве столбцов группировки, поскольку они перечислены в списке возвращаемых столбцов. Однако на деле они не участвуют в процессе группировки, посколы

???

Здесь величина ? ? ? равна сумме плановых объемов продаж всех ел ужащих, ра­

ботающих в данном офисе. Как можно указать ее в данном запросе? Из главы

8,

"Итоговые запросы", вам уже известно, что сумму плановых объемов продаж для отдельного офиса (скажем, офиса с идентификатором

21)

можно получить с по­

мощью следующего запроса.

SELECT SUМ(QUOTA) FROM SALESREPS WHERE REP OFFICE

= 21;

Однако ввести такой запрос, записать результаты, а потом ввести предыдущий запрос с корректным значением

-

не самый эффективный способ работы. Так как

вставить результаты этого запроса в предыдущий запрос вместо вопросительных

знаков? По-видимому, было бы разумно вначале написать первый запрос, а затем заменить

???

вторым запросом.

SELECT CITY FROM OFFICES WHERE TARGET > (SELECT SUМ(QUOTA) FROM SALESREPS WHERE REP OFFICE

=

OFFICE)

Фактически это корректный SQL-зaпpoc. Внутренний запрос (подзапрос) вычис­

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

Подчиненные SQL-запросы обычно выступают в качестве части предложения

WHERE

или НАVING. В предложении

WHERE

они помогают отбирать из таблицы ре­

зультатов запроса отдельные строки, а в предложении НАVING-группы строк.

Что такое noAЗanpoc На рис.

9.1

изображена структура подзапроса

SQL.

Подзапрос всегда заключа­

ется в круглые скобки, сохраняя при этом знакомую структуру инструкции

SELECT,

содержащей предложение

FROM

и необязательные предложения

WHERE,

ГАава 9. Подзаnросы и выражения с эаnросами

231

GROUP ВУ и НAVING. Структура этих предложений в подзапросе идентична их структуре в инструкции

SELECT;

в подзапросе эти предложения выполняют свои

обычные функции. Однако между подзапросом и инструкцией SELECT имеется ряд отличий.

f-- (SELECT -~-----~-~~f выбираемы*й_элемент

~:~~~

1

FROM ----+~~пецифико:ия_таблицы

WHERE условие_отбора

HAVING

---------------....

условие_отбора --------------~

)

1 Рис.



9.1.

Синтаксическая диаграмма подзапроса

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



Хотя в подзапросе может находиться предложение ORDER ВУ, на самом деле оно используется крайне редко. Резу ль таты подзапроса используют­ ся только внутри главного запроса и для пользователя остаются невиди­

мыми, поэтому нет смысла их сортировать. Более того, сортировка боль­ шого количества данных

может отрицательно сказаться

на

производи­

тельности.



Имена столбцов в подзапросе могут ссылаться на столбцы таблиц г лавно­ го запроса. Эти внешние ссылки подробно рассматриваются ниже в дан­ ной главе.

Частъ 11. Вы6орка,uнных

232 •

В большинстве реализаций

SQL

подзапрос не может быть объединением

(UNION) нес1 (SELECT SUМ{QUOTA) FROM SALESREPS WHERE REP OFFICE

OFFICE) ;

CITY Chicago Los Angeles В этом (более типичном) случае результаты подзапроса нельзя вычислить один раз, так как он возвращает различные результаты для каждого конкретного офиса.

На рис.

9.2

изображена схема выполнения этого запроса. Главный запрос извлека­

ет данные из таблицы OFFICES, а его предложение WHERE отбирает офисы, кото­

рые будут включены в таблицу результатов запроса. Условие, заданное в этом предложении, поочередно применяется ко всем строкам таблицы OFFICES. Пред­ ложение WHERE сравнивает значение текущей строки в столбце TARGET со значе­ нием, которое возвращается подзапросом для служащих "текущего" офиса. Ре­ зу льтатом подзапроса является одно число, и предложение

WHERE

сравнивает его

со значением столбца TARGET, выбирая или отбрасывая текущий офис на основа­ нии результата сравнения. Как видно из рисунка, выполнение подзапроса повто­ ряется для каждой строки, проверяемой предложением

WHERE главного запроса. Таблица SALВSRВPS

Подзапрос SELECT SUМ (QUOTA) FROM SALESREPS WHERE REP_OFFICE =22

Таблица Ol'l'ICИS OFFICE

CITY

22 11 12

Denver New York Chicago Atlanta Los Angeles

13 21

Таблица SALВSRВPS Подзапрос SELECT SUМ (QUOTA) FROM SALESREPS WHERE REP_OFFICE=21

Рис.

9.2. Выполнение подзапроса в преддожении WНERE

Часть 11. Вы6оркаА1нных

234

Внешние ССЫАКИ Часто в теле подзапроса требуется сослаться на значение столбца в текущей строке главного запроса. Рассмотрим еще раз запрос из предыдущих разделов. Вьюести список офисов, в которь1х плановый объем продаж офиса превышает сумму плановых объемов продаж всех служащих.

SELECT CITY FROM OFFICES WHERE TARGET > (SELECT SUМ(QUOTA) FROM SALESREPS WHERE REP OFFICE

=

OFFICE) ;

Роль подзапроса в приведенной инструкции SELECT заключается в вычислении

суммы плановых объемов продаж для служащих, работающих в конкретном офи­ се, а именно

-

в том, который в данный момент проверяется предложением

WHERE

главного за11роса. Ползанрос выполняет вычисления путем сканирования таблицы

SALESREPS. Но обратите внимание: столбец OFFICE в предложении WHERE подза­ проса является столбном не таблицы SALESREPS, а таблицы OFFICES, которая вхо­ дит в главный запрос. Во время последовательной проверки строк таблицы

OFFICES значение столбна OFFICE в текущей строке этой таблицы используется для выполнения подзапроса.

Столбец OFFICE в ползапросе является примером внешней

ссылки. Внешняя

ссылка представм1ет собой имя столбца, не входящего ни в одну из таблиц, пере­ численных в предложении FROM подзапроса, и принадлежащего таблице, указан­ ной в предложении FROM главного запроса. Как показывает предыдущий пример,

значение в столбне внешней ссылки берется из строки, проверяемой в настоящий момент главным запросом.

УСАОВИЯ отбора в ПОАЗ8Просе Подзапрос всегда явм1ется частью условия отбора в предложении WHERE или НAVING. В главе

6,

"Простые запросы", были рассмотрены простые условия отбора,

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

SQL

использу­

ются следующие условил отбора в подзапросе.



Сравнение с резулыпатом подзапроса. Значение выражения сравнивается с одним значением, которое возвращается подзапросом. Эта проверка напоминает простое сравнение.



Проверка на принадлежность результатам подзапроса. Значение выраже­ ния проверяется па равенство одному из множества значений, которые

возвращаются нодзапросом. Эта проверка напоминает простую провер­ ку на членство в множестве.



Проверка на существование. Проверяется наличие строк в таблице ре­ зультатов ползанроса.



Многократное сравнеюtе. Значение выражения сравнивается с каждым из множеств значений, которые возвращаются подзапросом.

ГАава 9. Подэаnросы и выражения с запросами

Сравнение с резуАыатом подзапроса Как видно из рис.

9.3,

235

(=, , =)

сравнение с результатом подзапроса является модифи­

цированной формой простого сравнения. Значение выражения сравнивается со значением, которое возвращается подзапросом, и если условие сравнения выпол­

няется, то проверка дает резу ль тат TRUE. Эта проверка используется для сравне­ ния значения из проверяемой строки с одним значением, полученным от подза­ проса, как показано в следующем примере.

Вывести список служащих, у которых плановый объем продаж не меньше планового объема продаж офиса в Атланте.

SELECT NАМЕ FROM SALESREPS WHERE QUOTA >= (SELECT TARGET FROM OFFICES WHERE CITY = 'Atlanta'); NАМЕ

Bill Adams Sue Smith Larry Fitch ---выражение--~---

--~-- подзапрос--•"8•

t---------1 ,_____ _
(SELECT QTY_ON_HAND FROM PRODUCTS WHERE MFR ID = 'ACI' AND PRODUCT ID = '41004'); DESCRIPTION

QTY_ON_НAND

Size 3 Widget Size 1 Widget Size 2 Widget

207 277 167

Сравнение с резу ль татом подзапроса, соответствующее исходному стандарту

SQL 1

и померживаемое всеми ведущими СУБД, допускает наличие подзапроса

только в правой части сравнения. Так, неравенство А


А

недопустимо. Это не ограничивает возможности операции сравнения, поскольку знак любого неравенства всегда можно "перевернуть" так, чтобы подзапрос ока­ зался с

правой стороны.

Однако это

говорит о том,

что

иногда требуется

"переворачивать" логику словесного запроса так, чтобы он формально соответст­ вовал разрешенной инструкции

SQL.

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

чений, а не только с единичным значением. Эти и другие возможности составле­ ния сложных запросов описываются ниже в настоящей главе. Однако следует учи­ тывать, что подобные запросы не померживаются одинаково всеми текущими версиями ведущих СУБД. С точки зрения переносимости, лучше оставаться в рам­ ках описанных ранее ограничений, накладываемых стандартом

SQL 1.

ГАава 9. Подзаnросы и выражения с запросами

Проверка на принаДАежность резуАыатам nо,АЗапроса Как показано на рис.

(предикат

9.4,

237

(IN)

проверка на принадлежность результатам подзапроса

IN) является видоизмененной формой простой проверки членства

в множестве. При этом одно значение сравнивается со столбцом данных, которые возвращаются

подзапросом,

и

если

это

значение

равно

одному

из

элементов

столбца, проверка дает результат TRUE. Данная проверка используется, когда не­ обходимо сравнить значение из проверяемой строки с множеством значений, ото­

бранных подзапросом. Вот простой пример.

--

выражение

Рис.

9.4.

-.-L--N-O_T_J--..-IN--- подзапрос--+~8•

Синтаксическая диаграмма проверки

на принаД/\ежность результатам подзапроса

Въюести список служащих тех офисов, где фактический объем продаж превышает плановый.

SELECT NАМЕ FROM SALESREPS WНERE REP_OFFICE IN (SELECT OFFICE FROM OFFICES WHERE SALES > TARGET) ; NАМЕ

Mary Jones Sam Clark Bill Adams Sue Smith Larry Fitch Подзапрос возвращает список идентификаторов офисов, где фактический объ­ ем продаж превышает плановый (в учебной базе данных есть три таких офиса номерами

11, 13

и

21).

-

с

Главный запрос затем проверяет каждую строку таблицы

SALESREPS, чтобы определить, работает ли соответствующий служащий в одном из отобранных офисов. Ниже приведено еще пару подобных примеров. Въюести список служащих, не работающих в офисах, которыми руководит Ларри

Фитч (служащий с идентификатором

108).

SELECT NАМЕ FROM SALESREPS WНERE REP_OFFICE NOT IN (SELECT OFFICE FROM OFFICES WHERE MGR = 108); NАМЕ

Bill Adams Mary Jones Sam Clark

Часть 11. Выборка данных

238

ВоЬ Smith Dan Roberts Paul Cruz

Въюести список всех клиентов, заказа.вших изделия компании АС! (производитель

АС!, идентификаторы товаров начинаются с нем

2008

4100)

в период между январем и шо­

года.

SELECT COMPANY FROM CUSTOMERS WНERE CUST NUМ IN (SELECT DISTINCT CUST FROM ORDERS WHERE MFR = 'АС!' AND PRODUCT LIKE '4100%' AND ORDER DATE BETWEEN '2008-01-01' AND '2008-06-30'); COMPANY Acme Mfg. Асе International Holm & Landis JCP Inc. Обратите внимание на то, что использование ключевого слова DISTINCT в под­ запросе не является строго необходимым. Если один и тот же клиент окажется в результатах запроса многократно, главный запрос даст тот же результат. Возни­ кает вопрос о том, что хуже

-

снижение производительности из-за удаления дуб­

ликатов в подзапросе или из-за обработки лишних строк в результатах подзапроса при выполнении проверок WHERE в главном запросе. Обычно большие по размеру результаты промежуточного запроса оказываются эффективнее сортировки, тре­ бующейся для удаления дублей. Встречаются и другие проблемы. Например, в

Oracle

применение GROUP ВУ обычно эффективнее, чем DISTINCT. Как видите,

написание максимально эффективного запроса требует детального знания о том, как конкретная СУБД работает с инструкциями

SQL.

Во всех приведенных примерах подзапрос возвращает в качестве результата

столбец данных, а предложение WHERE главного запроса проверяет, равно ли зна­ чение из строки таблицы главного запроса одному из значений в полученном столбце. Таким образом, проверка IN с подзапросом выполняется аналогично про­ стой проверке

IN,

за исключением того, что множество значений задается подза­

просом, а не указывается явно в инструкции

Проверка существования Как показано на рис.

SELECT.

(EXISTS)

9.5,

в результате проверки существования

(предикат

EXISTS) можно выяснить, содержится ли в таблице результатов подзапроса хотя бы одна строка. Способа выполнить те же действия путем простой проверки сравнени­ ем не существует. Проверка на существование допустима только в подзапросах.

Гмва 9. Подзаnросы и выраженин с 3аnросами

239

---.-L--N--т-J ........--Exrsтs-- подзапрос--•11•8

0

Рис.

9.5.

Синтаксическая диаграмма проверки существования

Вот пример запроса, который можно легко сформулировать, ис110льзуя про­ верку на существование.

Въюести список товаров, на которъrе полуцен заказ стоимостью не менее

$25000.

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

ORDERS

существует по крайней

мере один заказ, который а) является заказом на данный товар; б) имеет стои­ мость не менее $25 ООО. Инструкция

SELECT,

используемая для получения требуемого списка товаров,

приведена ниже.

SELECT DISTINCT DESCRIPTION FROM PRODUCTS WHERE EXISTS (SELECT ORDER NUМ FROM ORDERS WHERE PRODUCT = PRODUCT ID AND MFR = MFR ID AND AМOUNT >= 25000.00); DESCRIPTION 500-lb Brace Left Hinge Right Hinge Widget Remover Концептуально главный запрос последовательно 11еребирает все строки табли­ цы

PRODUCTS,

и для каждого товара выполняет подзанрос. Результатом rюдзапроса

является столбец данных, содержащий номера всех заказов "текущего" товара на сумму не менее

$25000. Если такие заказы есть (т.е. столбец не 11устой), то провер­ EXISTS возвращает TRUE. Если подзапрос не дает ни одной строки заказов, про­ верка EXISTS возвращает значение FALSE. Эта 11po11ept 3000.00);

COMPANY Carter & Sons Fred Lewis Corp. Вьюести список офисов, где имеется служащий, чей план превышает

55

процентов

от плана офиса.

SELECT CITY FROM OFFICES WНERE EXISTS (SELECT FROM WHERE AND CITY

* SALESREPS REP OFFICE OFFICE QUOTA > (.55 * TARGET));

Denver Atlanta Оrметим, что во всех приведенных примерах подзапрос содержит внешнюю

ссылку на столбец таблицы из главного запроса. На практике в подзапросе про­ верки

EXISTS

всегда имеется внешняя ссылка, связывающая подзапрос со строкой,

проверяемой в настоящий момент главным запросом.

ГАава 9. Подзаnросы и вь1раженин с запросами

Мноrократное сравнение (предикаты ANY и

241

ALL)*

При проверке IN выясняется, не равно ли некоторое значение одному из зна­

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

-

ANY

и

ALL,

SQL

имеются также две

расширяющие предыдущую

проверку до уровня других операторов сравнения, таких как больше меньше()

или

в обеих проверках некоторое значение срав­

нивается со столбцом данных, отобранных подзапросом.

--

L

11ыражение

1---------t

ANY

Т подзапрос--+е

ALL_j

1----= 21),

DECIМAL(9,2) СНЕСК

(QUOTA >=

О.О),

Гмва

11. Целостность данных

299

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

CREATE ТАВLЕ OFFICES (OFFICE INTEGER NOT NULL, CITY VARCHAR(lS) NOT NULL, REGION VARCНAR(lO) NOT NULL, MGR VALID_EMPLOYEE ID, TARGET DECIMAL(9,2), SALES DECIМAL(9,2) NOT NULL,

Еще одним очевидным преимуществом доменов является то, что все определения

"допустимых данных" (как допустимые идентификаторы служащих в показанном примере) хранятся в одном месте базы данных. Если определение домена впоследст­

вии потребуется изменить (к примеру, компания разрастется и возникнет необхо­ димость расширить диапазон идентификаторов до

299),

то намного проще сделать

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

имущества доменов при внесении изменений окажутся существенны.

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

106, то

невозможно было бы сказать, какая из них

относится к реальному субъекту- Биллу Адамсу, имеющему идентификатор

106.

По этой причине требование, чтобы первичные ключи имели уникальные значе­ ния, называется условием ~елостности таблИ!:JЫ. В ранних коммерческих СУБД первичные ключи отсутствовали, но сейчас они по­ всемесrно распространены. В дарт

ANSl/ISO SQL

DB2

первичные ключи появились в

1988 году,

а в стан­

они были добавлены в виде промежуточного изменения, внесен­

ного перед появлением полного стандарта

SQL2.

Первичные ключи указываются как

часть инструкции CREATE ТАВLЕ или ALTER ТАВLЕ (см. главу

13,

"Создание базы

данных"). В определениях всех таблиц учебной базы данных в приложении А, "Учебная

база

данных",

указаны

первичные

ключи,

следующие

синтаксису

ANSl/ISO. При указании первичного ключа в определении таблицы СУБД автоматически проверяет

уникальность

его

значений

при

выполнении

каждой

инструкции

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

ет свою уникальность, завершится выдачей сообщения об ошибке.

Часть 111. О6номениеданнь1х

300

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

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

одинаковых значений во множестве столбцов таблицы, но между ними имеются фундаментальные отличия.



Таблица может иметь только один первичный ключ, в то время как огра­ ничений уникальности может быть наложено несколько.



Столбцы, указанные в первичном ключе, должны быть определены как

NOT NULL, в то время как столбцы, включенные в условие уникальности, могут быть определены и как NULL, и как NOT NULL. В соответствии со стандартом

AN51/ISO,

условие уникальности столбцов или

комбинаций столбцов определяется в инструкции CREATE TABLE или ALTER ТАВLЕ. Однако в

DB2 условия уникальности были реализованы задолго до того, как они во­ AN51/150, и в этой СУБД они были реализованы как часть инструк­

шли в стандарт

ции CREATE INDEX. Это одна из административных инструкций реляционной базы данных, работающая с физическим хранением базы данных на диске. Обычно поль­

зователю не приходится беспокоиться об этих инструкциях -

это дело администра­

тора базы данных. Многие коммерческие СУБД первоначально придерживались соглашений

а не стандарта

AN51/150,

Однако со временем разработчики рукции

DB2,

и требовали использования инструкции CREATE INDEX.

DB2

перенесли условия уникальности в инст­

CREATE TABLE и ALTER TABLE. Большинство прочих производителей

коммерческих СУБД пошли по тому же пути и в настоящее время померживают синтаксис условия уникальности

УникаАьность и значения

AN51/150.

NULL

Значения NULL создают проблемы в столбце первичного ключа таблицы или в столбце, для которого задано условие уникальности. Предположим, что вы пы­ таетесь добавить в таблицу строку с первичным ключом, имеющим значение NULL (или, если первичный ключ является составным, частично имеющим значение

NULL). Из-за значения NULL СУБД не может однозначно решить, является ли пер­ вичный ключ дубликатом уже имеющегося в таблице ключа. Может оказаться верным и то и другое, в зависимости от "настоящего" значения отсутствующих данных. По этой причине стандарт

5QL требует,

чтобы любой столбец, являющий­

ся частью первичного ключа, был объявлен с ограничением NOT NULL. Стандарт

5QL

не накладывает такое ограничение на столбцы в условии уни­

кальности, хотя некоторые реализации

5QL,

такие как

DB2,

и поступают таким

ГАава 11. ЦеАостностьданных

301

образом. Однако имеются значительные расхождения в том, как разные реализа­ ции

SQL

обеспечивают выполнение условия уникальности в случае столбцов, ко­

торые содержат значения

NULL,

в частности, когда условие уникальности охваты­

вает несколько столбцов, допускающих значения NULL. Для иллюстрации этих

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

CREATE TABLE ADVISOR ASSIGNMENTS (STUDENT_NAМE VARCHAR(25), ADVISOR_NAМE VARCНAR(25),

UNIQUE

(STUDENT_NAМE,

ADVISOR_NAМE));

Ниже приведена таблица, полученная в результате вставки данных в таблицу

ADVISOR_ASSIGNMENTS при помощи нескольких инструкций INSERT. в первом столбце

находятся

значения

столбца

STUDENT_NAМE,

во

втором

-

столбца

ADVISOR_NAМE, а в остальных трех столбцах показаны результаты для текущих версий

Oracle, SQL Server

и

MySQL

соответственно (СУБД

DB2

не включена, по­

скольку она не допускает распространения условия уникальности на столбцы, в которых могут быть значения NULL). Интересно, что никакие две СУБД не дают одинаковые результаты во всех строках.

Номер

STUDENT_NAME ADVISOR_NAME Oracle

SQLServer

MySQL

NULL

Допустимая

Допустимая

Допустимая

строка

строка

строка

Допустимая

Копия строки

строки

1

2

NULL

NULL NULL

1

3 4

NULL

Bill

NULL

Bill

Допустимая строка

строка

Допустимая

Допустимая

Допустимая

строка

строка

строка

Копия строки

3

Копия строки

3

Допустимая строка

5

6

Harrison

Sue

Допустимая

Допустимая

строка

строка

строка

Копия строки

Harrison

Sue

Допустимая

5

Копия строки

5

Копия строки

5

Ссылочная целостность В главе

4,

"Реляционные базы данных", уже были рассмотрены первичные

и внешние ключи, а также отношения "предок-потомок" между таблицами, созда­ ваемые этими ключами. На рис.

11.1

изображены таблицы SALESREPS и OFFICE,

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

Столбец OFFICE является первичным ключом таблицы OFFICES и уникальным

образом идентифицирует каждую строку в этой таблице. Столбец REP _OFFICE

таблицы SALESREPS представляет собой внешний ключ для таблицы OFFICES. Он идентифицирует офис, за которым закреплен каждый служащий.

Чаеtъ 111. ОбновАенме данных

302

Таблица OIТICES

OFFICE 22 11 12 13 21

CITY Denver New York Chicago Atlanta Los Angeles

REGION western Eastern Eastern Eastern Western

MGR 108 106 104 NULL 108

TARGET $300,000.00 $575,000.00 $800,000.00 $350,000.00 $725,000.00

"'~ Таблица SALESREPS EМPL_NUМ

105 109 102 106 104 101 110 108 103 107 Рис.

NАМЕ

Bill Adams Mary Jones Sue Smith Sam Clark ВоЬ Smith Dan Roberts Тот Snyder Larry Fitch Paul Cruz Nancy Angelli

11.1. Отношение

AGE 37 31 48 52 33 45 41 62 29 49

SALES $186,042.00 $692,637.00 $735,042.00 $367,911.00 $835,915.00

Внешний ключ

REP OFFICE 13 11 21 11 12 12 NULL 21 12 22

TITLE Sales Rep ~ Sales Rep 7 Sales Rep ' VP Sales Sales Mgr Sales Rep Sales Rep Sales Mgr '1 Sales Rep 7 Sales Rep

1

"внешний ключ-первичный ключ"

Столбцы REP_OFFICE и OFFICE создают между строками таблиц OFFICES и SALESREPS отношение "предок-потомок". Для каждой строки таблицы OFFICES (предок) существует нуль или более строк таблицы SALESREPS (потомки) с таким же идентификатором офиса. Для каждой строки таблицы SALESREPS (потомок)

существует ровно одна строка таблицы OFFICES (предок) с таким же идентифика­ тором офиса. Предположим, что вы пытаетесь вставить в таблицу SALESREPS новую строку, содержащую недопустимый идентификатор офиса, как в следующем примере: (EMPL_NUМ,

INSERT INTO SALESREPS VALUES

NАМЕ, REP_OFFICE, AGE, HIRE DATE, SALES) 'George Smith'' 31, 37' '2008-04-01'' о. 00);

(115,

На первый взгляд, это корректная инструкция INSERT. Фактически, в некото­ рых реализациях

SQL

такая строка будет успешно добавлена в таблицу. В базе

данных появится информация о том, что Джордж Смит в офисе номер

31,

(George Smith)

работает

хотя такого офиса в таблице OFFICES нет. Очевидно, что новая

строка нарушает отношение "предок-потомок", существующее между таблицами

OFFICES и SALESREPS. Скорее всего, идентификатор офиса 31 в инструкции INSERT является ошибочным - вероятно, пользователь намеревался ввести иден­ тификатор Кажется

11, 21

или

13.

достаточно

очевидным,

что

допустимое

значение

для

столбца

REP _OFFICE должно быть равно одному из значений, содержащихся в столбце OFFICE. Это правило известно как ограничение ссылоч1юй целостности. Оно обес­ печивает

целостность

отношений

и первичными ключами.

"предок-потомок",

создаваемых

внешними

ГАава

11. ЦеАОСТНОСТЬАIННЫХ

303

Ссылочная целостность является ключевым элементом реляционной модели с момента ее представления доктором Коддом. Однако условия ссылочной целост­

ности отсутствовали как в экспериментальной СУБД IВМ

System/R, так и в первых

версиях

DB2 и SQL/DS. Компания IВМ добавила поддержку ссылочной целостно­ сти в DB2 в 1989 году, а в стандарт SQL она была добавлена уже после выхода пер­ вой редакции стандарта SQL1. В настоящее время большинство производителей СУБД поддерживают ссылочную целостность в своих программных продуктах.

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

этих четырех ситуаций на примере таблиц OFFICES и SALESREPS, представленных на рис.



11.1. Добавление новой дочерней строки. Когла происходит добавление но­ вой строки в дочернюю таблицу (SALESREPS), значение ее внешнего ключа (REP_OFFICE) должно быть равно одному из значений первичного

ключа (OFFICE) в родительской таблице (OFFICES). Если значение внеш­ него ключа не равно ни одному из значений первичного ключа, то до­

бавление такой строки повредит базу данных, посколыsqlname.data); gets (inbuf); i f (inbuf[O]

==

'*')

nродолженне рнс.

18.8

для

%s: "

*/

557

558

Часть V. Проrрамм11рован11е 11 SQL

/*Если пользователь 11рисвоить

вводит

столбцу

*(parmvar->sqlind) continue;

'*', NULL

значение

-1;

} else

{ /*

В противном случае

установить

значение

переменной-индикатора

=О;

*(parmvar->sqlind)

switch(parmvar->sqltype) case 481: /* Преобразовать

введенные данные

в

8-байтовое число с плавающей точкой sscanf(inЬuf,

11

*/ \lf", parmvar->sqldata) ;f-®

break; case 449: /* Передать строки

введенные данные

в

виде

переменной длины

*/

strcpy(parmvar->sqldata, inbuf); parmvar->sqllen = strlen(inbuf); break; case 501: /* Преобразовать

введенные

4-байтовое целое

sscanf break;

/*

(inЬuf,

данные

®

в

число

*/ "\ld", parmvar- >Sqldata) ;f-®

ВЫПОЛНИ'l'Ь ИНС'l'РУКЦИЮ

ехес

sql execute updatestmt using :parmda; i f (sqlca.sqlcode < О) { printf ("Ошибка EXECUTE: \ld\n", sqlca.sqlcode); exit ();

/*

Обновления завершены

*/ ®

*/

ехес

sql execute immediate "commit work"; if (sqlca.sqlcode) printf("Oшибкa СОММIТ: \ld\n", sqlca.sqlcode); else printf("\nBce обновления завершены.\n"); exit (); Окончание рнс.

18.8

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

параметров при

выполнении

инструкции

EXECUTE программа должна использовать область SQLDA. Данная программа ил-

ГАава 18. Динам1111еск11i SQL •

559

люстрирует общую методику применения области SQLDA (кружки с цифрами на рис.

18.8 соответствуют пунктам данной методики).

1.

Программа выделяет память для области SQLDA, достаточно большую для того, чтобы вместить все структуры SQLVAR с описанием передаваемых па­ раметров.

Она

заполняет поле

SQLN, у~ О) strcat (stmtbuf, ") ; strcat(stmtbuf, querycol);

no more columns: ехес sql close

tЬlcurs;

/* Завершаем инструкцию SELECT strcat ( stmtbuf, "from ") ; strcat(stmtbuf, querytЫ);

предложением

*/

FROM

/* Выделение SQLDA для динамического запроса */ query_da = (SQLDA *)malloc(sizeof (SQLDA) + colcount * sizeof(SQLVAR)); query_da->sqln = colcount; /*

Подготавливаем запрос и

ехес ехес

/*

передаем

Цикл с

for

его

СУБД

для

описания

*/

sql prepare querystmt from :stmtbuf; sql describe querystmt into qry_da; по

всем

выделением О;

(i =

i


sqlvar + i; qry_var->sqldat malloc(qry_var->sqllen); qry_var->sqlind malloc(sizeof(short));

Ф

/* SQLDA создана; запрос и получение результатов sql open qrycurs; ехес sql whenever not found goto no_more_data; for ( ; ; )

*/

ехес

/*

Получение

строки результатов

в

наши буферы

ехес

sql fetch sqlcurs using descriptor qry_da; printf ("\n");

/*

Цикл

вывода

for (i = /*

О;

i

данных
sqlvar + i; printf (" Столбец # %d ( %s) : ", i+l, qry_var->sqlname); /* Проверка переменной-индикатора i f (* (qry_var -> sqlind) != О)

*/

{ puts ("равен NULL ! \n") ; continue;

} /* Получение данных и обработка типа switch (qry_var -> sqltype) { case 448: case 449: /* VARCНAR -- просто выводим puts(qry var -> sqldata); break; case 496: case 497: / * Четырехбай•говое целое преобразуем и выводим

printf("%ld", *((int *) break; case 500: case 501: /* Двухбай•говое целое -

*/

*/

*/ (qry_var->sqldata)));

преобразуем и выводим */ printf("%d", *((short *) (qry_var->sqldata))); break; case 480: case 481: /* Данные с плаваюшей точкой преобразуем и выводим */ printf ( "%lf", * ( (douЫe *) (qry_var->sqldat))); break;

no more data: - printf("\nKoнeц данных.\n"); /* Освобождение выделенной памяти for (i = О; i < colcount; i++)

{ qry var qry da->sqlvar + i; free(qry_var-;sqldata); nроАолженне рнс.

19.16

*/

Гмва 19. SQLAPI

617

free(qry_var->sqlind); free(qry_da); close qrycurs; exit (); Окончанне рнс.

19.16

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

SQL

применяются специальные инструкции

и структуры данных, совершенно не похожие на те, что используются для выпол­

нения запросов в статическом

SQL.

А в

SQL Server методика

выполнения динами­

ческих запросов в основном та же, что и методика выполнения всех остальных за­

просов. Единственная особенность заключается в том, что добавляются функции библиотеки dЫ ib, возвращающие информацию о столбцах в таблице результатов запроса. Это облегчает понимание подхода с применением имеющим незначительный опыт работы с

API

программистам,

SQL.

ODBC API и станАарт SQL/CLI ODBC (Open Database Connectivity, открытый доступ к базам данных) - это Microsoft универсальный API для доступа к базам дан­ ных. Хотя в современном компьютерном мире Microsoft играет важную роль как

разработанный компанией

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

именно это послужило мотивом создания разработчикам приложений

Windows

ODBC: Microsoft

захотела облегчить

доступ к базам данных. Все дело в том, что

различные СУБД существенно отличаются друг от друга, так же как и их

API.

Ес­

ли разработчику нужно было написать приложение, работающее с базами данных нескольких СУБД, для каждой из них приходилось писать отдельный интерфейс­

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

системы

стандартизировать

Microsoft решила

интерфейс

на уровне

взаимодействия

между

приложениями и СУБД, благодаря чему во всех программах мог бы использовать­ ся один и тот же универсальный набор функций, поддерживаемый всеми произ­

водителями СУБД. Таким образом, от внедрения

ODBC

выиграли и разработчики

приложений, и производители СУБД, для которых также решалась извечная про­ блема совместимости.

Стандартизация Даже если бы

Microsoft,

CLI ODBC API

был всего лишь собственным стандартом компании

его значение все равно было бы очень велико. Однако

Microsoft

поста­

ралась сделать его независимым от конкретной СУБД. Одновременно ассоциация производителей СУБД

колов

(SQL Access Group)

работала над стандартизацией прото­

удаленного доступа к базам данных в архитектуре

"клиент/сервер".

Часть V. Проrраммирование и SQL

618 Microsoft

убедила ассоциацию принять

в качестве независимого стандарта

ODBC

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

организации, Европейс1е параметрам и начинается выполнение тела

проце­

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

SQL)

везде, где допускается исполь­

зование констант. Встретив имя параметра, СУБД подставляет на его место теку­ щее значение этого параметра. На рис.

20.1

параметры используются в инструк­

ниях INSERT и UPDATE для вычис.ления значений столбцов и в условиях отбора. В дополнение к входным параметрам, некоторые диалекты

SPL

помер.живают

выходные параметры, с помощью которых хранимые процедуры могут возвращать значения, вычис.ленные в ходе выполнения процедуры. При интерактивном вызо­ ве хранимых процедур от выходных параметров мало пользы, а вот ее.ли одна хра­

нимая процедура вызывает другую, выходные параметры позволяют им эффек­

тивно обмениваться информацией. Некоторые диалекты

SPL

помер.живают па­

раметры, которые одновременно являются и входными, и выходными, т.е. их зна­

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

ГАава 20. Хранимые процедуры SQL

695

На рис.

20.2 приведен еще один вариант процедуры ADD_ cusт, написанный на Sybase Transact-SQL. (Диалект Transact-SQL используется и в Microsoft SQL Server; он мало отличается от исходной версии этого диалекта из Sybase SQL Server, которая послужила основой для обеих линий продуктов- Microsoft и Sybase.) Обратите внимание на отличия Transact-SQL от диалекта Oracle:

диалекте



ключевое слово PROCEDURE может быть сокращено до PROC;



список параметров процедуры не заключен в скобки, а просто следует за именем процедуры;



все имена параметров начинаются с символа @, как в объявлении проце­ дуры, так и в ее теле;



отсутствует формальный маркер окончания инструкции дуры представляет собой единую инструкцию

- тело проце­ Transact-SQL. Если требу­

ется несколько инструкций, мя их группирования используется блочная структура

Transact-SQL.

/* Процедура для добавления данных о новом клиенте */ create proc add cust @с name varchar(20), /* Имя клиента */ @c_num integer, */ /* Идентификатор клиен~·а @cred lim decimal(9,2), /* Лимит кредита */ @tgt_sls decimal(9,2), /* Объем продаж */ @с _rep integer, /* Иден•r•ификатор служащего */ @с offc varchar ( 15) /* Город расположения офиса*/ as begin / * Добавляем новую строку в •1•аблицу CUSTOMERS */ insert into customers (cust_num, company, cust_rep, credit_limit) values (@c_num, @c_name, @c_rep, @cred_lim) Обновляем запись в таблице SALESREPS update salesreps set quota = quota + @tgt_sls where empl_num = @c_rep

*/

/* Обновляем запись в таблице OFFICES update offices set target = target + @tgt_sls where city = @c_offc

*/

/* Завершаем commit trans

*/

/*

транзакцию

end Рис.

20.2. Хранимая

процедура на диалекте

Transact-SQL

На рис.

20.3 приведен третий вариант процедуры ADD_ cusт, написанный на Informix. Объявление процедуры и ее параметров здесь ближе к диалек­ Oracle. В отличие от Transact-SQL, идентификаторы локальных переменных

диалекте ту

и параметров не содержат никаких специальных символов. Определение проце­ дуры завершается декларацией END PROCEDURE -

способствует уменьшению количества ошибок.

такой более четкий синтаксис

696

Часть VI. SQL сеrодня и завтра /* Процедура для добавления create procedure add_cust( с name varchar(20), с num integer, money(lб, 2), cred lim money(lб, 2), tgt_sls c_rep integer, varchar(lS)) с offc

данных

о новом клиенте

*/

/* /*

Имя клиента Идентификатор клиента

*/

/* /* /* /*

Лимит кредита

*/ */ */ */

Объем продаж Идентификатор служащего Город расположения офиса

*/

/* Добавляем новую строку в таблицу CUSTOMERS */ insert into customers (cust_num, company, cust_rep, credit limit) values (c_num, c_name, c_rep, cred_lim); /* Обновляем запись в таблице SALESREPS update salesreps set quota = quota + tgt_sls where empl_num = c_rep;

*/

/* Обновляем запись в таблице OFFICES update offices set target = target + tgt_sls where city = c_offc;

*/

/* Завершаем транзакцию commit transaction; end procedure; Рис. 20.З. Процедура

ADD_ cusт

*/

на диаАекте

lnformix

Во всех диалектах, в которых используется инструкция CREATE PROCEDURE,

хранимая

процедура

может

быть

удалена

соответствующей

инструкцией

DROP PROCEDURE.

DROP PROCEDURE ADD_CUST;

Вызов хранимой процедуры Хранимую процедуру можно вызывать по-разному: из приложения с помощью

соответствующей инструкции

SQL,

из другой хранимой процедуры, а также в ин­

терактивном режиме. Синтаксис вызова хранимых процедур зависит от исполь­ зуемого диалекта.

Разные диалекты

SQL

используют различный синтаксис вызова процедур. Вот

пример вызова процедуры ADD _ cusт на диалекте ЕХЕСUТЕ

PL/SQL.

ADD_CUST('XYZ Corporation', 2317, 30000.00, 50000. 00, 103, 'Chicago');

Передаваемые процедуре параметры указываются в том порядке, в каком они

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

ADD_CUST('XYZ Corporation', 2317, 30000.00, 50000. 00, 103, 'Chicago') ;

ГАава 20. Хран1111ые процедуры SQL

697

Процедура может вызываться и с указанием имен параметров; в этом случае

параметры могут передаваться в процедуру в любом порядке. ЕХЕСUТЕ

ADD CUST (c_name с num cred lim с offc c_rep tgt_sales

На диалекте ЕХЕСUТЕ

'XYZ Corporation', 2137, 30000.00, 'Chicago', 103, 50000.00);

Transact-SQL вызов этой же процедуры

имеет такой вид.

ADD_CUST 'XYZ Corporation', 2317, 30000.00, 50000.00, 103, 'Chicago';

Скобки здесь не нужны

просто перечислите значения параметров через запя­

-

тую после имени процедуры. Ключевое слово EXECUTE можно сократить до ЕХЕС. Кроме того, задавая параметры, можно явно указать их имена, что позволяет пе­

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

ADD _ cusт на диалекте ЕХЕС

ADD CUST

Transact-SQL, полностью эквивалентный предыдущему:

@С NАМЕ @С

NUМ

@CRED LIM @TGT SLS @С REP @С OFFC На диалекте ЕХЕСUТЕ

'XYZ Corporation', 2317, 30000.00, 50000.00, 103, 'Chicago';

lnformix та

же инструкция будет иметь такой вид.

PROCEDURE ADD CUST ('XYZ Corporation•, 2317, 30000.00, 50000.00, 103, 'Chicago' ) ;

Здесь вновь параметры заданы в виде списка, разделенного запятыми и заключен­

ного в скобки. Эта форма инструкции может применяться в любом контексте. На­ пример, ее можно использовать для вызова хранимой процедуры из клиентского

приложения. А при вызове из другой хранимой процедуры можно использовать еще и такой эквивалентный синтаксис.

CALL ADD CUST ('XYZ Corporation', 2317, 30000.00, 50000. 00, 103, 'Chicago') ;

Переменные хранимых процедУр В хранимых процедурах удобно (а иногда и просто необходимо) объявлять пе­ ременные для хранения некоторых промежуточных значений. Эту возможность обеспечивают все диалекты

SQL.

Обычно переменные объявляются в начале тела

процедуры, сразу за заголовком и

инструкций

перед последовательностью составляющих ее

SQL. Для переменных можно

использовать все те же типы данных, что

и для столбцов таблиц (разумеется, померживаемые данной СУБД). На рис. 20.4 приведен фрагмент несложной хранимой процедуры на диалекте Transact-SQL, которая вычисляет общую стоимость заказов указанного клиента и, в зависимости от того, превысит ли эта сумма $30000, сохраняет в переменной од-

Часть VI. SQL сеrоднн и 3а1тра

698

но из двух сообщений. Обратите внимание: диалект

Transact-SQL

требует, чтобы

все имена переменных, как и имена параметров, начинались с символа @. Инст­

рукция DECLARE объявляет локальные переменные процедуры. В данном случае имеются две переменные: типа

/*

и типа VARCНAR.

MONEY

Процедура проверки общей стоимости заказов клиента

create proc chk_tot @c_num integer as

/*

Один входной параметр

/* Объявляем две локальные переменные declare @tot_ord money, @msg_text varchar(ЗO) begin /* Вычисляем о~щую стоимость select @tot_ord = sum(amount) from orders where cust = @c_num

/*

В

эависимос~·и

в

переменную

от

*/ */

*/

величины суммы заносим

соответствующее

if tot_ord < зоооо.оо select @msg_text else select @msg_t.ext

/*

заказов клиента

*/

"Большой

"Малый

сообщение объем

объем

*/

заказов"

заказов"

Вы110J1няем ,цаJ1ьнейшую обрабо•1•ку сообщения

*/

end Рис.

20.4.

Использование локальных переменных в

Инструкция SELECT, написанная на диалекте

Transact-SQL

Transact-SQL,

выполняет еще од­

ну функцию- присваииает значения переменным. Вот простейший пример ис­

пользованю1 этой инструкнии для занесения текста сообщения в переменную

@MSG

ТЕХТ.

SELECT @MSG

ТЕХТ

=

"Большой оС'iъем

заказов"

Занесение в переменную общей стоимости заказов и начале процедуры

-

более

сложный пример. Здесь инструкция SELECT одновременно используется и для вы­ полнения запроса, генерирующего присваиваемое переменной значение.

На рис.

20.5

показана версия той же хранимой процедуры в СУБД

lnformix.

Она имеет несколько следующих отличий:

• •

локальные переменные объявляются с помощью инструкции DEFINE;

имена переменных являются обычными идентификаторами

SQL,

они не

начинаются со специального символа;



для занесения результата запроса в локальную переменную используется

специальная инструкния



SELECT".INTO;

чтобы просто присвоить переменной значение, можно воспользоваться инструкцией

LET.

ГАава 20. Хранимые процедуры SQL

/*

Процедура проверки общей стоимости заказов клиента

create procedure chk tot(c num integer) /* Объявляем две-локал~ные переменные define tot_ord numeric(lб, 2); define msg_text varchar(30);

/*

Вычисляем общую С'l'Оимость

699

*/

*/

заказов клиента

*/

select sum(amount) into tot ord from orders where cust = c_num;

/*

В

зависимости от

в

11еременную

величины суммы заносим

соответствующее

*/

сообщение

if tot ord < 30000.00 let msg_text "Большой объем заказов" else let msg_text "Малый объем заказов"

/*

*/

Выполняем дальнейшую обработку сообщения

end procedure; Рис.

На рис.

PL/SQL. •

20.6

20.5.

Использование локальных переменных в

показана версия той же хранимой процедуры на диалекте

И здесь есть несколько отличий от диалектов инструкция

рах

lnformix SPL

SELECT... INTO

Informix,

Transact-SQL

и

Oracle Informix:

имеет тот же вид, что и в хранимых процеду­

и применяется для занесения значений, возвращаемых од­

нострочным запросом, в локальную переменную;



синтаксис операторов присваивания взят из языка

символов используется в

/*

Процедура

Oracle PL/SQL

Pascal (: =).

вместо инструкции LET.

проверки общей стоимости заказов

клиента

*/

create procedure chk_tot(c_num in integer) as declare

/*

*/

Объявляем две локальные переменные

tot_ord number(lб, 2); msg_text varchar(30); begin

/*

Вычисляем общую стоимость

заказов

клиента

*/

select sum(amouпt) iпto tot_ord from orders where cust = c_num;

/*

В

зависимости от

в

переменную

величины суммы,

соо'l'ве•rствующее

заносим

сообщение

*/

if tot ord < 30000.00 msg text .- 'Большой объем заказов' else msg_text .- 'Малый объем заказов'

/*

Выполняем дальнейшую обработку сообщения

*/

end; Рис.

20.6.

Использование локальных переменных в

Oracle PL/SQL

Эта пара

Часть VI. SQL сеrодня м завтра

700

Локальные переменные в хранимых процедурах могут использоваться в каче­

стве источника данных в выражениях

SQL

везде, где допускаются константы. При

вычислении такого выражения имя переменной заменяется ее текущим значени­

ем. Кроме того, в инструкциях

локальные переменные можно использовать

SQL

для хранения значений, полученных в результате вычисления выражения или вы­

полнения запроса. Все эти применения переменных были показаны в примерах программ на рис.

20.4-20.6.

6Аоки инструкций Практически во всех хранимых процедурах, кроме самых простых, возникает

необходимость объединения некоторой последовательности инструкций в группу, интерпретируемую как одна инструкция. Например, если в вашей процедуре ис­

пользуется конструкция IF ... THEN ... ELSE, то, скорее всего, вам понадобится возможность группировки инструкций, поскольку большинство диалектов

SPL

требует, чтобы каждая ветвь условной конструкции состояла только из одной ин­

струкции. Поэтому, если необходимо выполнение в ветви нескольких инструкций, их следует объединить в один блок.

В

/*

Transact-SQL структура блока

Блок инструкций

инструкций очень проста.

Transact-SQL */

begin

/*

Здесь должна располагаться последовательность инструкций

SQL */

end Единственной задачей ключевых слов BEGIN ... END является ограничение бло­ ка инструкций; они не влияют на область видимости локальных переменных или

других объектов базы данных. Процедуры, циклы и другие конструкции

SQL

Transact-

оперируют единственной инструкцией, и поэтому в них очень часто исполь­

зуются блоки, объединяющие несколько инструкций в одну. В

Informix блок

не только определяет группу инструкций, но и (необязательно)

объявляет локальные переменные и обработчики исключений для этой группы. Вот структура блока инструкций

Informix.

Блок инструкций Informix SPL Объявления локальных переменных блока

/* /*

*/ */

define . . .

/*

Определение

обработчика исключений

*/

on exception .

/*

Последовательность инструкций

SQL

*/

begin end Раздел объявления переменных не обязателен. Вы видели его пример в процедуре на рис.

20.5. Необязателен и раздел обработки исключений (о нем рассказывается да­

лее в этой главе). У ключевых слов BEGIN ... END та же роль, что и в

объединяют последовательность инструкций

SQL

Transact-SQL: они

в единый блок. Между ними не обя-

ГАава 20. Хранимые процедуры SQL

зательно должно быть несколько инструкций -

701

может быть и одна инструкция, если,

к примеру, вы хотите объявить переменные или определить специальную обработку исключений только для нее.

В Infoпnix не требуется столь часто использовать блоки инструкций, как в

SQL.

Transact-

И для ци1

SQL-

поэтому функ­

отбора.

10000.00;

Когда СУБД вычисляет условие отбора для каждой строки в таблице результа­ тов запроса, она использует идентификатор клиента из этой строки в качестве ар­

гумента функции GET_TOT_ORDS и проверяет, не превысило ли значение, возвра­ щаемое этой функцией,

$10000.

Этот же запрос можно построить иначе, включив

в предложение FROM еще и таблицу ORDERS и сгруппировав результат по клиентам и ел ужащим. Многие СУБД выполняют запросы с группировкой более эффектив­ но, чем приведенный выше, так как последний требует обработки таблицы заказов для каждого клиента.

На рис.

20.8

показана версия той же хранимой функции для

дите, она мало чем отличается от версии для

Inforrnix.

Oracle.

/* Возвращае~' общую стоимость заказов клиента create function get tot ords(c num integer) returning numeric(lб,-2) /• Объявляем локальную переменную define tot_ord numeric(lб, 2);

для

begin / • Простой запрос, возвращающий select sum(amount) into tot ord from orders where cust = c_num; /* Возвращаем сумму return tot ord; end function; Рис.

хранения

*/

итога

и~·оговую сумму

в качестве значения функции

20.8. Хранимая

функция в

lnformix SPL

•/

•/

*/

Как ви­

ГАава 20. Хранимь~е nроцедУры SQL

703

Диалект Transact-SQL, используемый в Microsoft SQL Server и Sybase Adaptive Server Enterprise (ASE), померживает хранимые (нользовательские) функции, аналогичные показанным на рис. 20.7 и 20.8.

Возврат значений через параметры Хранимая функция возвращает только одно значение. Однако некоторые диа­ лекты

позволяют возвращать из процедуры более одного значения с помощью

SPL

выходных параметров. Выходные параметры перечисляются в списке параметров процедуры так же, как и рассмотренные в предыдущих 11римерах входные пара­

метры. Однако вместо того чтобы передавать данные в 11ро11едуру, они возвраща­ ют данные из хранимой процедуры.

На рис.

20.9

показана хранимая процедура

PL/SQL,

которая нолучает иденти­

фикатор клиента и возвращает его имя, имя закрепленного за ним ел ужащего и название города, в котором расположен офис служащеr·о. Процедура имеет че­ тыре параметра. Первый параметр, CNUМ, -

идентификатор клиента. Остальные три

-

входной; в нем процедуре передается

выходные; они используются для пере­

дачи запрошенных данных вызывающей процедуре. В этом простом примере ин­

струкция SELECT".INTO помещает извлеченные из таблиц данные прямо в выход­ ные параметры хранимой процедуры. В более сложных процедурах выходные значения могут формироваться в локальных переменных и затем помещаться

в выходные параметры с помощью операторов присваивания

/*

Возвращае~' имя клиента, служащего и город,

имя обслуживающе!'О

PL/SQL.

er'o

в котором расположен офис

служаще1•0

*/

create procedure get_cust_info(c_num in integer, с name out varchar, r name out varchar, с of f c out varchar) as begin /* Простой однострочный запрос, возвращающий интересующую

select into from where and and

нас

информацию

*/

company, name, city c_name, r_name, c_offc customers, salesreps, offices cust- num = с - num empl_num = cust_rep office = rep_office;

end; Рис.

20.9.

Хранимая процедура с выходными параметрами в

Oracle PL/SQL

Чтобы процедура могла вызвать другую процедуру с выходными параметрами, она должна предоставить для них буферы, куда будут помещены возвращаемые

значения,

-

это могут быть локальные переменные или собственные выходные

параметры вызывающей процедуры. Вот анонимный (неименованный) блок

PL/SQL, который /*

Oracle 20.9.

вызывает процедуру GET_CUST_INFO, приведенную на рис.

Получаем информацию о клиенте

declare the_name varchar(20), the_rep varchar(lS),

2111 */

704

Часть VI. SQL сеrодня и завтра

the_city varchar(lS); execute get_cust_info(2lll, the_name, the_rep, the_city); Конечно, вряд ли эта процедура будет вызываться с непосредственно заданным идентификатором клиента, но синтаксически такой вызов вполне допустим. Для трех остальных параметров в вызове процедуры указаны переменные, в которые

должны быть помещены возвращаемые значения. А вот как нельзя вызывать про­ цедуру

GET_CUST_INFO, поскольку второй параметр является выходным и немо­

жет представлять собой строковый литерал.

/*

Получаем информацию о клиенте

execute get_cust_info(2lll,

'XYZ

2111 */ Со', the_rep, the_city);

В дополнение к входным и выходным параметрам,

Oracle

померживает пара­

метры, которые одновременно являются и входными, и выходными (INOUT). Они

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

процедурой как входные данные.

На рис. 20.10 приведена версия процедуры GET_CUST_INFO, написанная Transact-SQL. В заголовке этой процедуры выходные параметры объявлены сколько иначе, чем в примере для Oracle, и инструкция SELECT также имеет

на не­ не­

сколько иную форму, а имена переменных начинаются с"@". Во всем остальном структура процедуры такая же, как и в случае

Oracle.

/* Возвращает информацию о клиенте по его номеру */ create procedure get_cust_info( @c_num integer, @c_name varchar(20) out, @r name varchar(lS) out, @с offc varchar(lS) out as begin /* Простой однострочный запрос, возвращающий интересующую нас информацию */ select @с name company, @r_name = name, @c_offc = city from customers, salesreps, offices where cust - num = @с - num and empl_num = cust rep and office = rep_office end Рис.

20.10. Хранимая

процедура с выходными параметрами в Traпsact-SQL

Когда эта процедура вызывается из другой процедуры

Transact-SQL,

второй,

третий и четвертый параметры должны быть объявлены в вызывающей процедуре

как выходные, как и в объявлении вызываемой процедуры. Вот как выглядит син­ таксис вызова процедуры, приведенной на рис.

/* Получаем информацию о клиенте 2111 */ declare the_name varchar(20) declare the_rep varchar(lS) declare the_city varchar(lS) ехес get_cust_info @с num 2111,

20.10, в Transact-SQL.

Гмва 20. Хранимые nроцедУры SQL

@с name @r name @с offc

На рис.

20.11

705

the name output, the_rep output, the_city output;

показана версия этой же хранимой процедуры в

СУБД выбран другой способ возврата нескольких значений

-

Informix.

В этой

не через выходные

параметры, а через несколько возвращаемых значений функции. Таким образом, в

Informix

процедура GET_ cusт _ INFO превращается в функцию. Возвращаемые

ею значения определены в предложении вращаются назад с помощью инструкции

RETURNING RETURN.

в заголовке процедуры и воз­

/* Возвращает информацию о клиенте по его номеру */ create function get_cust_info(c_num integer) returning varchar(20), varchar(l5), varchar(l5) define c_name varchar(20); define r name varchar(l5); define c_offc varchar(l5);

/*

Простой однострочный запрос,

возвращающий

*/

интересующую нас информацию

select into from where and and

company, name, city c_name, r_name, c_offc customers, salesreps, offices cust num c_num empl_num cust_rep off ice rep_office;

/* Возвращаем три значения return c_name, r_name, c_offc; end function; Рис.

20.11. Хранимая

функция, возвращающая несколько значений, в

Инструкция CALL в также

содержит

*/

Informix,

специальное

lnformix SPL

используемая для вызова подобных функций,

предложение

RETURNING,

предназначенное

для

приема возвращаемых значений.

/*

клиенте 2111 */ the_name varchar(20); the_rep varchar(l5); the_city varchar(l5); get_cust_info(2lll) returning the_name, the_rep, the_city;

Получаем информацию о

define define define call

Как и в

Transact-SQL,

в

Informix

имеется версия инструкции CALL, позволяю­

щая передавать параметры по именам.

call get_cust_info(c_num = 2111) returning the_name, the_rep, the_city;

УсАовное выпоАнение Одним

из

базовых

элементов

хранимых

процедур

является

конструкция

IF".THEN".ELSE, используемая для организации ветвлений внутри процедуры. Да­ вайте снова вернемся к рис. 20.1 с процедурой ADD_CUST, добавляющей в базу дан-

706

Часть VI. SQL сеrодня и завтра

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

будет добавлена к плану служащего, но если эта сумма больше дут добавлены фиксированные приведена на рис.

20.12.

$20000.

$20000, эта сумма $20000, к плану бу­

Модифицированная версия процедуры

Конструкция IF".THEN".ELSE работает точно так же, как

и в обычных языках программирования.

/* Процедура для добавления create procedure add cust ( in varchar2, /* с name с num in number, /* cred lim in number, /* tgt_ sls in number, /* in number, с _rep /* in varchar2) /* с offc as begin

данных

Имя

о

клиеН'l'е

новом

клиен'l'а

Иден·rифика·rор Лимит

креди~'а

Объем

продаж

Иден•r·ифика·гор Город

клиента

служащего

расположения

*/ */ */ */ */ */

офиса*/

/* Добавляем новую строку в таблицу CUSTOMERS insert into customers (cust_num, company, cust_rep, credit_limit) values (c_num, c_name, c_rep, cred_lim);

*/

/* Обновляем запись в таблице SALESREPS if tgt sls pre.amount) ;)

/*

Записываем в об

таблицу информацию

увеличенном

заказе

*/

(insert into ord_more values (pre.cust, pre.order_date, pre.amount, post. amount) ; )

/*

После обновления общую

стоимость

таблицы повторно вычисляем заказов

*/

Часть VI. SQL сеrоднн и завтра

724

after (execute procedure add_orders() into new_total;) Предложение BEFORE в этом триггере у1 target] Найти все заказы на товары производителя

ACI

с суммами свыше

$30000.

/orders/order[mfr = 'ACI' and amount > 30000.00] Поскольку учебная база данных представляет собой неглубокую табличную структуру, ХМL-иерархия имеет глубину, равную всего лишь трем. Для иллюстра­ ции возможностей запросов в более иерархичных документах раз книгу на рис.

25.1.

XML

рассмотрим еще

Вот несколько запросов и соответствующих выражений пути.

Найти все состмные части глм со статусом черновика.

/bookPart/chapter[revStatus="draft"]/* Получить третий абзац второй глмы части

2.

/bookPart[@partNum="2"]/chapter(2]/para[3] Эти выражения не дают нам возможности управления результатами запроса, ко­

торую предоставляет инструкция SELECT. Здесь нет и эквивалента курсорам для построчной обработки результатов. Вместо этого

жения

For/Let/Where/Retums

(выражения

FLWR).

XQuery

SQL

предоставляет выра­

Лучше всего проиллюстриро­

вать эти возможности на конкретных примерах. Рассмотрим еще раз структуру

ХМL-документов для учебной базы данных. Приведенный запрос реализует двух­ табличное соединение и генерирует три указанных сrолбца результатов запроса. Перечислить все имена продмцов, даты заказов и их суммы, дЛS1 заказов менее чем

на

$5000.

Отсортировать результаты по суммам заказов.

{ for $о in document("orders.xml")//orders[amount < 5000.00], $r in document("salesreps.xml")//salesreps[empl_num=$o/re p] return { $r/name, $0/order_date, $0/amount

}

sortby(amount)

}

На внешнем уровне содержимое элемента

нием

XQuery,

пользует две

smallOrders

определяется выраже­

заключенным во внешние фигурные скобки. Выражение переменные

для

итерации

по двум документам,

for ис­

соответствующим

Часть VI. SQL сеrодн11 и 3автра

890

таблицам ORDERS и SALESREPS. Эти две переменные эффективно реализуют со­ единение двух таблиц (документов). Предикаты (аргументы поиска) в конце каж­ дой из двух строк после ключевого слова

for

соответствуют предложению

SQL

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

$5000.

Предикат во второй строке реализует соединение,

исrюльзуJ1 неременную $о мя связи строк в таблице (документе) SALESREPS со строками таблицы (документа) ORDERS. Часть

return

выраженю1

for

указывает, какие элементы должны быть возвра­

щены в качестве резул1.тата иычисления выражения. Она соответствует списку ин­ струкции SELECT в запросе

SQL.

последовательность элементо11

Возвращаемое значение представляет собой ХМL­

smallOrder,

где каждый элемент берется из соот­

ветствующего элемента исходных таблиц (документов). Здесь переменные исполь­ зуются для того, чтобы к11алифицировать путь к элементу, значение которого бу­

дет возвращено. Наконен, часть

sortby выражения функционирует в точности

так же, как и соответствующее предложение ORDER ВУ в запросе В этом примере пе проиллюстрированы ности. В итерации

for

SQL.

некоторые дополнительные возмож­

для получения значений дополнительных переменных

(которые могут потребоват1.сJ1 предикатам или другим выражениям) можно ис­ пользовать выражение

let. Условное выполнение поддерживается при помощи конструкции if."then".else. Статистические функции позволяют группировать запросы

XQuery, как это делается в

итоговых запросах

"Итоговые запросы". Все это делает запросы с запросами

SQL.

SQL, описываемых XQuery сравнимыми по

в главе

8,

гибкости

Однако, 1