SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) –...

452
SQL QUICK START Крис Фиайли

Transcript of SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) –...

Page 1: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

SQL

QUICK START

Крис Фиайли

Page 2: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

VISUAL QUICKSTART GUIDE

SQL

Chris Fehily

Page 3: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

Москва, 2003

QUICK START

SQL

Крис Фиайли

Page 4: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

УДК 004.43ББК 32.973.26-018.1

Ф48Фиайли К.SQL: Пер. с англ. – М.: ДМК Пресс, 2003. – 456 с.: ил. (Серия «Quick Start»).

ISBN 5-94074-233-5

Книга посвящена языку программирования SQL, применяемому для рабо-ты с реляционными базами данных. Обсуждается версия языка ANSI SQL-92(SQL2).

В настоящем издании рассказывается об использовании запросов SQL длярешения соответствующих классов задач по выборке данных, их модифика-ции или по работе с объектами структуры базы данных. Все конструкции под-робно описываются и иллюстрируются большим количеством примеров. Кро-ме того, для каждого типа запросов рассматриваются отклонения от стандартав реализации наиболее распространенных СУБД: MS Access, MS SQL Server,Oracle, MySQL и PostgreSQL.

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

Ф48

ISBN 0-201-11803-0 (англ.)

ISBN 5-94074-233-5 (рус.)

Authorized translation from the English language edition, entitled SQL: VISUAL QUICKSTARTGUIDE, 1st Edition, 0321118030 by FEHILY, CHRIS, published by Pearson Education, Inc,publishing as Peachpit Press, Copyright © 2003.

All rights reserved. No part of this book may be reproduced or transmitted in any form or byany means, electronic or mechanical, including photocopying, recording or by any informationstorage retrieval system, without permission from Pearson Education, Inc. RUSSIAN languageedition published by DMK PRESS, Copyright © 2003.

© Peachpit Press, 2003

© Перевод на русский язык, оформлениеДМК Пресс, 2003

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

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

Page 5: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

Содержание

Введение ..................................................................................................................... 11

Глава 1. Основные характеристики СУБД .................................................. 24

Выполнение программ SQL ..................................................................... 25Microsoft Access ........................................................................................ 27Microsoft SQL Server ................................................................................ 30Oracle .......................................................................................................... 33MySQL ........................................................................................................ 36PostgreSQL ................................................................................................. 38

Глава 2. Реляционная модель ......................................................................... 41

Таблицы, столбцы и строки ................................................................... 43Первичные ключи .................................................................................... 47Внешние ключи ........................................................................................ 51Связи .......................................................................................................... 54Нормализация ........................................................................................... 57Наша типовая база данных ..................................................................... 63

Глава 3. Основы SQL ........................................................................................... 69

Синтаксис SQL .......................................................................................... 69Типы данных ............................................................................................. 75Строковые типы данных ......................................................................... 77Битовые типы данных .............................................................................. 79Точные числовые типы данных ............................................................. 80Действительные числовые типы данных .............................................. 82Календарные типы данных ..................................................................... 84Интервальные типы данных ................................................................... 88Значение null ............................................................................................. 90

Page 6: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

6 SQL

Глава 4. Выбор данных из произвольной таблицы ............................... 92

Выбор столбцов с помощью предложений SELECT и FROM ............ 93Создание псевдонимов столбцов с помощью предложения AS ........ 96Удаление повторяющихся строкс помощью ключевого слова DISTINCT ............................................... 99Сортировка строк с помощью предложения ORDER BY ..................101Фильтрация строк с помощью предложения WHERE .......................109Комбинирование условий с помощью операторов AND, OR и NOT ...114Сравнение по шаблону оператором LIKE ............................................122Сравнение с диапазоном с помощью оператора BETWEEN .............128Фильтрация с помощью оператора IN .................................................131Проверка на значение null с помощью оператора IS NULL ...............134

Глава 5. Операторы и функции ......................................................................137

Создание производных столбцов .........................................................138Арифметические операции ...................................................................140Определение последовательности вычисления ..................................143Объединение строк с помощью оператора || ......................................145Выбор произвольной подстрокис помощью функции SUBSTRING() .....................................................149Переключение регистра символов строкис использованием функций UPPER() и LOWER() ..............................153Удаление пробелов с помощью функции TRIM() .............................155Определение длины произвольной строкис помощью функции CHARACTER_LENGTH() ................................159Поиск подстроки с использованием функции POSITION() .............161Операции с данными даты и времени ..................................................165Извлечение значений текущих даты и времени ..................................169Отображение информации о пользователе .........................................171Преобразование типов данных с помощью функции CAST() ..........173Вычисление условных значений с помощью выражения CASE ........179Проверка на значения nullс использованием функции COALESCE() ............................................184Сравнение выражений с помощью функции NULLIF() ....................185

Глава 6. Суммирование и группировка данных .....................................188

Использование агрегатных функций ...................................................189Поиск минимума посредством функции MIN() ................................192Поиск максимума с использованием функции MAX() .....................194Вычисление суммы с помощью функции SUM() ..............................196Порядок расчета среднего значения с помощью функции AVG() ....198Подсчет строк с помощью функции COUNT() .................................200

Page 7: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

7Содержание

Исключение повторных значенийс помощью предложения DISTINCT ....................................................202Группирование строк с использованием предложения GROUP BY .....206Фильтрация групп с помощью предложения HAVING ......................215

Глава 7. Выбор данных из нескольких таблиц .......................................219

Уточнение имен столбцов .....................................................................220Создание псевдонимов таблиц с помощью предложения AS ...........222Использование объединений .................................................................224Создание объединений с помощью синтаксиса JOIN и WHERE ......228Создание произвольного перекрестного объединенияс использованием предложения CROSS JOIN......................................233Создание произвольного естественного объединенияс использованием предложения NATURAL JOIN ...............................236Создание внутреннего объединенияс помощью команды INNER JOIN ........................................................241Создание внешних объединенийс помощью команды OUTER JOIN .......................................................265Создание самообъединения ...................................................................279Комбинирование строк с помощью оператора UNION ....................286Поиск общих строк с помощью команды INTERSECT .....................295Поиск разных строк с помощью команды EXCEPT...........................297

Глава 8. Подзапросы ..........................................................................................299

Принципы работы с подзапросами ......................................................300Структура подзапросов ..........................................................................302Подзапросы и объединения ...................................................................303Простые и сложные запросы .................................................................307Определение названий столбцов в подзапросах .................................313Значения null в подзапросах ...................................................................314Использование подзапросов в качестве выраженийв списке заголовков столбцов ...............................................................316Сравнение значений, возвращаемых подзапросом,с использованием операторов сравнения .............................................320Проверка на вхождение во множество с помощью оператора IN ...325Сравнение всех значений запросас помощью ключевого слова ALL .........................................................333Сравнение некоторых значений запросас помощью ключевого слова ANY ........................................................337Проверка наличия выборки с помощью оператора EXISTS ..............341Сравнение эквивалентных запросов .....................................................347

Page 8: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

8 SQL

Глава 9. Добавление, обновление и удаление строк ...........................348

Отображение названий столбцов в таблице ........................................349Вставка строк с помощью команды INSERT ......................................352Изменение строк с помощью команды UPDATE ...............................358Удаление строк с помощью команды DELETE ...................................363

Глава 10. Создание, изменение и удаление таблиц ..............................367

Порядок создания таблиц ......................................................................368Основные принципы работы с ограничениями ..................................369Создание новой таблицы с помощью команды CREATE TABLE .....371Запрет значения null с помощью ограничения NOT NULL ...............373Присвоение значения по умолчаниюс помощью ограничения DEFAULT ......................................................376Задание первичного ключас помощью ограничения PRIMARY KEY ............................................379Задание внешнего ключа с помощью ограничения FOREIGN KEY .....383Присвоение уникальных значенийс помощью ограничения UNIQUE ........................................................389Проверка значений столбца с помощью ограничения CHECK ........392Создание временной таблицыс помощью команды CREATE TEMPORARY TABLE .........................395Создание новой таблицы на основе существующейс помощью команды SELECT INTO .....................................................398Изменение таблицы с помощью команды ALTER TABLE .................401Удаление таблицы с помощью команды DROP TABLE ....................404

Глава 11. Индексы .................................................................................................405

Создание индекса с помощью команды CREATE INDEX .................405Удаление индекса с помощью команды DROP INDEX .....................409

Глава 12. Представления ....................................................................................411

Создание представления с помощью команды CREATE VIEW ........411Считывание данных из представления .................................................417Изменение данных через представление .............................................419Удаление представления с помощью команды DROP VIEW............424

Глава 13. Транзакции ...........................................................................................425

Выполнение транзакций .........................................................................426

Приложение ....................................................................................................................431

Предметный указатель .............................................................................................445

Page 9: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

Посвящается моему отцу

Page 10: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

БлагодарностиБеки Морган (Becky Morgan) – за критику первого варианта рукописи.Марджори Баер (Marjorie Baer) – за то, что посоветовала прочитать книгу «Do you knowSQL?».Кэти Симпсон (Kathy Simpson) – за то, что иногда ради работы жертвовала сном.Лизу Бразиил (Lisa Brazieal) – за постоянное «вырезать и вклеить».Морин Форис (Maureen Forys) – за отсутствие переносов в ключевых словах.Брайена Штайнвега (Bryan Steinweg) – за переносы на следующую страницу.Даррена Пеннингтона (Darren Pennington) – за то, что садился на своего любимого«конька».Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи.

Информация в примере с базой данных – вымышленная. Я позаимствовал паруназваний из новелл Яна М. Бэнкса (Iain M. Bank's).

Page 11: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

SQL – это стандартный язык программиро-вания, применяемый для создания, моди-фикации, поиска и извлечения информа-ции, хранящейся в произвольной реляци-онной базе данных, управляемой соответ-ствующей системой управления базамиданных (СУБД).С помощью SQL вы можете, в частности,превратить любой вопрос типа «А где жи-вут наши клиенты?» в такую команду, ко-торую программное обеспечение вашейбазы данных сможет понять и выполнить(для приведенного вопроса это можетбыть команда SELECT city, state FROM

customers). Если вы уже умеете извлекатьинформацию аналогичного типа с помо-щью графического инструментария пост-роения запросов, то, скорее всего, замети-ли, что он становится весьма ограничива-ющим и громоздким по мере того, каксложность ваших запросов возрастает. Вотздесь и нужен SQL, хотя решением указан-ной проблемы его возможности не ограни-чиваются. Например, вы можете применятьSQL для того, чтобы добавлять, модифици-ровать и удалять данные и объекты базыданных. И именно потому, что язык SQLтакой мощный, его поддерживают наибо-лее популярные СУБД, в частности Micro-soft Access, Oracle и MySQL, хотя уровень

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

SQLSQL – это:

язык программирования;

простой в изучении;непроцедурный;

встроенный и/или интерактивный;

стандартизованный;

используемый для манипулирования дан-ными и объектами баз данных;

не аббревиатура.

Рассмотрим пункты этого списка подроб-нее.

Язык программирования

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

Введение

Page 12: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

12 SQL

эту программу, написанную на языке SQL.Это значит, что СУБД выполняет те запро-сы, которые вы ей передали, и отображаетрезультаты их работы, в том числе какое-нибудь сообщение об ошибке. Надо сказать,что языки программирования, называемыетакже формальными языками, отличают-ся от языков общения, называемых нефор-мальными или естественными языками,главным образом тем, что создаются подконкретную цель, полностью лишены дву-смысленности, имеют весьма ограниченныесловарный запас и гибкость. Таким обра-зом, если вы не получили результата от ра-боты своей программы, на который рас-считывали при ее написании, это произош-ло потому, что ваша программа содержиткакую-либо ошибку (логическую или син-таксическую – в последнем случае, скореевсего, будет выведено соответствующее со-общение, описывающее ошибку), а не по-тому, что компьютер неправильно понялваши инструкции, формализованные в видепрограммы (эта информация проясняет,почему отладка программ считается однойиз основных задач программирования).

Будучи формальным языком, SQL, каки другой язык этого типа, имеет свои син-таксис и семантику. Синтаксис включаетсобственно слова и символы, которые выможете применять, а также правила, покоторым эти слова и символы можно ис-пользовать при создании команд и про-грамм. Семантика помогает выяснить ре-альное значение, смысл любой синтакси-чески правильной команды. Вы вполнеможете написать на SQL какую-нибудькоманду, соответствующую синтаксисуязыка, которая, тем не менее, будет выра-жать неверный смысл (то есть будет пра-вильной синтаксически, но неверной семан-тически). Подробнее о синтаксисе и семан-тике SQL рассказывается в главе 3.

Простой в изучении

Уточним: SQL прост в изучении по сравне-нию с другими языками программирова-ния. Дело в том, что, если вы пока не на-писали ни одной программы, переход отнеформального языка к любому формаль-ному языку вас поначалу сильно разоча-рует. Тем не менее подчеркнем, что ко-манды на SQL читаются как предложениянеформального языка, что уже сильно об-легчает понимание смысла. Так, любойновичок в программировании, скорее все-го, поймет, что записанная на SQL коман-да SELECT au_fname, au_lname FROM authorsORDER BY au_lname совпадает по смыслу спредложением «Перечислить имена и фа-милии всех авторов, сортируя их по фами-лиям». Но этот человек почти навернякасочтет эквивалентную по смыслу програм-му, написанную на C или на Perl, абсолют-но непонятной.

Непроцедурный

Заметим сразу: если вы никогда не програм-мировали, то можете пропустить этот раз-дел, не беспокоясь об утрате целостностивосприятия остального материала; но есливы программировали на C или Perl, то имелидело с процедурным языком. А для любогопроцедурного языка программист долженявно прописывать шаги, которые выполняеткомпьютер, чтобы получить желаемый ре-зультат. SQL относится к непроцедурнымязыкам (такие языки еще называют «декла-ративными»). Программируя на таком язы-ке, вам надо описать то, что именно вы хо-тите сделать, а не то, как вы собираетесьэто делать. В случае с SQL оптимизатор,который входит в состав программногообеспечения вашей СУБД, самостоятельнорассчитает это самое «как». Именно поэто-му SQL не содержит таких конструкций

Page 13: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

Введение 13

управления, как IF-THEN-ELSE, WHILE иоператоров перехода GOTO1.

Чтобы наглядно продемонстрировать разли-чие между процедурными и непроцедурны-ми языками, мы написали две эквивалент-ные программы, выполняющие по сути однуи ту же задачу. Первая программа, текст ко-торой приведен в листинге 1, написана наMicrosoft Access Visual Basic, то есть на про-цедурном языке. Вторая программа, тексткоторой приведен в листинге 2, написана наSQL. Программа на VB извлекает имена ав-торов из таблицы, содержащей информа-цию о них. Конечно, нет необходимостивникать в детали первой программы, но об-ратите внимание, что она использует циклDo Until для того, чтобы явно определитьто, как именно извлекать нужные данные.Вторая программа делает то же самое, ноодной-единственной командой SQL. В пер-вом случае мы имеем примерно 20 строккода, а во втором – всего одну команду. Новажнее всего другое: программируя на SQL,вам надо указать только то, что именно не-обходимо сделать, а дальше сама СУБД ав-томатически и незаметно для вас определя-ет и исполняет ту последовательность поша-говых операций, которую требуется выпол-нить для достижения желаемого результата.Более того, листинг 2 – это простейший извсех возможных запрос SQL. А если к этомузапросу добавить обычные операции сорти-ровки, фильтрации и объединения, то длявыполнения всего, что будет делать услож-ненная, но единственная команда SELECT,записанная на SQL, может понадобиться до100 строк процедурного кода.

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

Листинг 1. Этот код на Microsoft Access Visual Basicизвлекает имена и фамилии из таблицы в базеданных, содержащей информацию об авторах,и помещает результат выборки в выходной массив

Sub GetAuthorNames()

Dim db As Database

Dim rs As Recordset

Dim i As Integer

Dim au_names() As String

Set db = CurrentDb()

Set rs = db.OpenRecordset("authors")

rs.MoveLast

ReDim au_names(rs.RecordCount - 1, 1)

With rs

.MoveFirst

i = 0

Do Until .EOF

au_names(i,0) = ![au_fname]

au_names(i,1) = ![au_lname]

i = i + 1

.MoveNext

Loop

End With

rs.Close

db.Close

End Sub

Листинг 2. Эта команда SQL выполняет ту жесамую задачу, что и вся процедура на VB,показанная в листинге 1. Внутренний оптимизаторСУБД сам определяет, как наилучшим образомизвлечь соответствующие данные

SELECT au_fname, au_lname

FROM authors;

Page 14: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

14 SQL

Встроенный и/или интерактивныйЕсли вы включаете команды SQL как эле-менты в какие-нибудь программы, напи-санные на процедурных языках, считается,что вы применяете так называемый встро-енный SQL, а те процедурные языки, на ко-торых написаны эти более крупные или,точнее, несущие программы, называют ба-зовыми. Таким языком на практике чащевсего оказывается какой-нибудь язык про-граммирования общего назначения, в част-ности C, Java или COBOL, или какой-либоязык сценариев, например Perl, PHP илиPython. То есть любой сценарий PHP,запускаемый как CGI-программа, можетприменять вложенные команды SQL, чтобызапрашивать произвольную базу СУБДMySQL. СУБД в этом случае передаст ре-зультат этого запроса какой-нибудь пере-менной PHP для отображения и дальней-шей обработки. Например, как это показа-но в листинге 3, команда SQL встроенав программу, написанную на Access VisualBasic.Если в режиме реального времени адресо-вать команды на языке SQL непосред-ственно вашей СУБД, а она отобразит со-ответствующие результаты сразу же, кактолько получит, значит, вы применяетеинтерактивный или динамический SQL.Отметим, что все современные серверыСУБД поставляются в комплекте с такимиграфическими приложениями или утилита-ми командной строки, которые восприни-мают интерактивные команды SQL или/итекстовые файлы, содержащие SQL-про-граммы, то есть сценарии.В настоящем издании рассматриваютсятолько интерактивные команды SQL, по-скольку их можно встроить в любую при-кладную программу или сценарий. Однакоимейте в виду, что между интерактивнымии встроенными командами могут существо-вать небольшие синтаксические различия.

Листинг 3. Здесь Visual Basic выполняет функциюбазового языка для встроенного SQL. Однако во всехпримерах данной книги используется синтаксистолько интерактивного SQL, который может слегкаотличаться от синтаксиса встроенного SQLв зависимости от конкретной СУБД

Sub GetAuthorNames2()Dim db As Database

Dim rs As RecordsetSet db = CurrentDb()

Set rs = db.OpenRecordset("SELECT

→ au_fname, au_lname FROM authors;")

‘ Далее программа обрабатывает результаты

→ запроса, находящиеся в переменной rs.

rs.Closedb.Close

End Sub

Рис. 1. Титульный лист 626-страничного документаANSI, который называется X3.135-1992 DatabaseLanguage – SQL и, собственно, официальноопределяет стандарт SQL-92.При желании вы можете купить электроннуюверсию этого документа на сайте www.ansi.org,но он рассчитан главным образом на техпрограммистов, кто самостоятельноразрабатывает, пишет и внедряет СУБД

Page 15: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

Введение 15

Стандартизованный

SQL никому не принадлежит. Это значит,что на права собственности никто не пре-тендует. Как легко понять из рис. 1, SQL –это открытый стандарт, разрабатываемыйодним из комитетов в Американском нацио-нальном институте стандартизации (ANSI).В свою очередь, ANSI – это не частная фир-ма, а орган правительства США, призван-ный способствовать развитию нацио-нальных стандартов. Более того, SQL явля-ется не только американским, но и между-народным стандартом ISO/IEC 9075:1992,так как в 1992 году был включен в числостандартов Международной организациипо стандартизации (ISO). В этой связи хо-телось бы заметить, что настоящее изда-ние основано на стандарте ANSI SQL 1992.Таким образом, когда вы встретите обо-значения ANSI SQL, SQL-92 или простоSQL, их следует рассматривать как сино-нимы, если специально не указано ниче-го другого. Положение несколько услож-няется тем, что появился стандарт ANSISQL-99. Однако на него пока можно необращать внимания, так как до его вне-дрения пройдет еще несколько лет и онбудет обратно совместим со стандартомSQL-92. Значит, программа, отвечающаястандарту SQL-92, будет работать под лю-бой СУБД, отвечающей стандарту SQL-99.Следует заметить, что все поставщикиСУБД в разной степени привносят свои до-полнения в канонический ANSI SQL, чтобысделать его более мощным. Чаще всеготакими расширениями являются дополни-тельные команды, ключевые слова, функ-ции, типы данных и конструкции управ-ляющей логики, например IF, WHILEи FOR. Иногда, правда, этих расширенийоказывается слишком много. В частно-сти, Oracle и Microsoft уже внесли такмного добавлений, что появились PL/SQL и Transact-SQL – самостоятельные

полноправные языки, а не надмножестваSQL. Однако обычно расширения одногопоставщика несовместимы с расширения-ми другого. Имея это в виду, мы указыва-ем читателю на те элементы «расширенно-го» SQL, которые не соответствуют стандар-ту ANSI (см. раздел «Выполнение командSQL на коммерческих СУБД»).

Используемый для манипулированияданными и объектами базы данных

Все команды SQL принято делить на двеосновные группы – язык манипулированияданными (DML) и язык определения дан-ных (DDL).

Для любой базы данных команды группыDML отбирают, обсчитывают, вставляют,удаляют и редактируют те данные, кото-рые хранятся в этой базе. В настоящем из-дании рассматриваются команды SELECT,INSERT, UPDATE и DELETE (см. главы 4–9).

Команды группы DDL создают, модифи-цируют и уничтожают такие объекты базыданных, как таблицы, индексы и представ-ления. В нашей книге речь идет о командахCREATE, ALTER и DROP (см. главы 10–13).

Несколько слов о произношении

SQL нельзя произносить как «СИКВЭЛ»(английское слово sequel означает «по-следствие» или «продолжение»). Избегай-те этой ошибки, а лучше четко прогова-ривайте каждую букву из трех: S-Q-L. Мыне согласны с теми людьми, которые про-износят «СИКВЭЛ» только потому, чтотак говорят многие. Дело в том, что сме-шение sequel и SQL является историчес-ким курьезом, образованные люди знаютоб этом, и для них вариант «СИКВЭЛ» бу-дет свидетельствовать о таком же низкомуровне владения ремеслом, как если бывы оказались писателем и при написании

Page 16: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

16 SQL

своих романов стали бы разбивать инфи-нитивы, вставляя наречия после частицыto (что тоже в английском языке вроде быдопускается, но в серьезной литературепочти никогда не встречается). Корочеговоря, помните, что «СИКВЭЛ» неприят-но звучит для уха профессионала. А ещеMySQL следует произносить как «МАЙ-ЭС-КЪЮ-ЭЛЬ», а PostgreSQL – как «ПОСТ-ГРЕС-КЪЮ-ЭЛЬ».

Немного о названии

К сожалению, довольно распространеноеще одно заблуждение относительно SQL.Многие считают, что сочетание трех буквS-Q-L есть аббревиатура, которая расшиф-ровывается как Structured Query Language(Структурированный язык запросов). Таквот, SQL означает SQL, и более ничего. По-чему? Да потому, что так заявлено в ANSI.Официальное название SQL – DatabaseLanguage SQL (Язык баз данных SQL).Обращаем особое внимание на данноезаблуждение только потому, что онововсе не так безобидно. Если начинаю-щие программисты будут считать, чтоSQL и вправду означает «язык структу-рированных запросов», это сослужит имплохую службу, так как именно этотязык является наихудшим из всех воз-можных описаний канонического SQL,хотя живучесть данного описания кажет-ся академикам и профессионалам забав-ной по следующим причинам:

SQL – не структурированный язык, по-скольку его нельзя разбить на блокиили процедуры;SQL не ограничивается только запро-сами, поскольку в нем есть много дру-гих команд помимо SELECT;SQL не является полным языком по оп-ределению Тюринга.

Несколько общихзамечаний о книгеЭта книга рассказывает об использованииSQL для сохранения в базе данных и за-прашивания из нее нужной информации.В главах с 1 по 3 приводится общий опи-сательный материал по СУБД, по реляци-онной модели и по синтаксису SQL. Начи-ная с главы 4 изложение становится болеенаглядным и основывается на конкрет-ных примерах.

Автор, безусловно, рассчитывал на то, чточитатель хорошо разбирается в своей ОС(будь то Windows, Unix, Mac OS или др.),в частности понимает, как работает фай-ловая система, умеет создавать, редакти-ровать, удалять и организовывать файлы,папки и каталоги. В некоторых задачахтребуется умение вводить команды поприглашению операционной системыили командной оболочки (в старых вер-сиях Windows его еще называют пригла-шением DOS).

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

Сайт поддержки

На сайте www.peachpit.com/vqs/sql вынайдете все материалы книги (с исправ-ленными ошибками). Свои вопросы,предложения и замечания высылайтепо электронной почте: [email protected].

Page 17: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

Введение 17

Для кого предназначена эта книга

Эта книга – для вас, если вы:

не обладаете большим опытом в про-граммировании, но умеете работать накомпьютере;изучаете SQL самостоятельно или с ин-структором;не интересуетесь базами данных, но породу своей деятельности вынужденыобрабатывать большие объемы струк-турированной информации (как это ча-сто имеет место в работе бухгалтера,научного сотрудника, Web-программи-ста, аналитика рынка, торгового пред-ставителя, финансового советника, ру-ководителя офиса и даже секретаря);хотите выйти за ограничения, налагае-мые дружественным, но крайне мало-мощным графическим инструментари-ем построения запросов по образцу(QBE – Query by Example);намерены перейти от программногообеспечения настольной СУБД к сер-верному программному обеспечению,поддерживающему SQL;уже знакомы с SQL и хотите узнать о немкак можно больше;должны по роду деятельности созда-вать, модифицировать или/и удалятьтакие объекты баз данных, как табли-цы, индексы и представления;вынуждены встраивать SQL в програм-мы, написанные на языках Java, C илиPerl;являетесь Web-программистом и рабо-таете с MySQL или PostgreSQL;просто хотите иметь настольный спра-вочник по SQL.

Эта книга вам совершенно не подходит,если вы планируете изучить следующиевопросы:

как проектировать базы данных (хотяобзоры толковых идей, которыми сле-дует руководствоваться, приводитсяв главе 2);частные расширения, которые постав-щики СУБД добавили к ANSI SQL;как профессионально и с применениемсовременных методов программироватьи администрировать базы данных (мы некасаемся, например, инсталляции, триг-геров, хранимых процедур, тиражирова-ния, резервирования, восстановления,оптимизации, курсоров, схем сортиров-ки, сравнений, алфавитов и трансляции).

Дополнительные соглашения

Иногда части текста выделяются разнымишрифтами:

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

При рассмотрении схем синтаксиса SQLи листингов будем придерживаться следу-ющих правил:

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

Page 18: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

18 SQL

Настольные и серверные СУБД, поддерживающие SQL

Любая серверная СУБД, поддерживающая SQL, работает в качестве серверной частиархитектуры «клиент-сервер». Это значит, что она хранит и поддерживает базы дан-ных и отвечает на запросы, которые клиенты оформляют на языке SQL. Здесь подклиентом понимается любое приложение или любой компьютер, которые могут по-сылать запросы на языке SQL (в дальнейшем мы будем называть такие запросы SQL-запросами) на сервер и получать ответы с этого сервера. Например, если в вашейлокальной сети применяется архитектура «клиент-сервер», то клиентом являетсякомпьютер на вашем рабочем столе, а сервером – мощная специальная машина,расположенная в соседней комнате, в соседнем здании или в другой стране. Приэтом правила, описывающие порядок передачи запросов и соответствующих отве-тов в архитектуре «клиент-сервер», определяются протоколом связи с СУБД, напри-мер ODBC и JDBC.Любая настольная СУБД – это СУБД, установленная и работающая локально. Онапредставляет собой независимое приложение, которое хранит свои собственныебазы данных и производит всю обработку данных, связанную с SQL. Никакая на-стольная СУБД не может принимать запросы от других клиентов, что, конечно,означает, что никакая настольная СУБД не может работать так, как это делает лю-бой SQL-сервер.В качестве примеров SQL-сервера можно привести Microsoft SQL Server, Oracle,MySQL и PostgreSQL (заметим, что понятие SQL-сервера включает в себя понятиесерверной СУБД, поддерживающей SQL, но не совпадает с ним). А в качестве на-стольных СУБД, поддерживающих SQL, можно назвать Microsoft Access и FileMakerPro. В отношении программных продуктов, англоязычные названия которых со-держат словосочетание SQL server, следует знать об одином нюансе. Дело в том, чтоSQL server (слово server написано с маленькой буквы) относится к коммерческомусерверному продукту, поддерживающему SQL, произвольного поставщика, а SQLServer (слово Server написано с большой буквы) – только к конкретному продуктуSQL Server компании Microsoft.В соответствии с устоявшимися требованиями мы будем использовать слова «кли-ент» («клиентский») и «сервер» («серверный») по отношению не только к клиент-скому и, соответственно, серверному программному обеспечению, но и к той ма-шине, на которой работает соответствующее программное обеспечение. В тех слу-чаях, когда надо подчеркнуть различие между компьютером и программным обес-печением, мы будем делать специальные пояснения.

Page 19: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

Введение 19

каждая команда SQL начинается с но-вой строки;как вы узнаете из главы 3, SQL являетсяязыком свободной формы без ограни-чений на разрыв строки или на числослов в одной строке. У нас же каждоепредложение любой команды начина-ется с новой строки и с отступом отпервой строки, например:SELECT au_fname, au_lname

FROM authorsORDER BY au_lname;

в SQL регистр не учитывается, следова-тельно, идентификаторы myname, MyNameи MYNAME считаются идентичными с точ-ки зрения канонического SQL. Однаковерхний регистр используется нами длявыделения ключевых слов, таких какSELECT, NULL и CHARACTER (см. раздел«Синтаксис SQL» в главе 3),а нижний регистр – для выделения эле-ментов, конкретные значения которыхдолжны быть определены пользовате-лем, как это имеет место, например,для имен таблиц, столбцов и для псев-донимов. Стоит особо подчеркнуть, чтов некоторых СУБД имена идентифика-торов, определяемые пользователем,являются, в отличие от каноническогоSQL, чувствительными к регистру. По-этому безопаснее все-таки учитыватьрегистр при написании программ;в табл. 1 поясняется смысл специальныхсимволов, используемых в схемах икомментариях синтаксиса SQL;все кавычки в кодах SQL только пря-мые – или одинарные ('), или двойные("). Никакие другие кавычки (имеютсяв виду фигурные) недопустимы. Не-правильные кавычки не воспринимают-ся транслятором, и код, содержащийих, работать не будет;когда колонка, отведенная для строкикода, окажется для нее слишком узкой, тострока будет разбита на две или более

частей. Стрелка (→) в начале строкиозначает, что эта строка является про-должением предыдущей.

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

Выполнение команд SQLна коммерческих СУБД

В тексте книги иногда встречаетсяпиктограмма, которая означает,

что речь идет об отступлении производи-телями СУБД от канонов стандартногоANSI SQL-92. Поэтому, увидев такой зна-чок, вам придется определенным образоммодифицировать код, приведенный в ка-ком-нибудь листинге, под авторскую вер-сию SQL конкретного поставщика СУБД,чтобы вы смогли запустить этот код насвоей СУБД. Например, если в тексте гово-рится, что оператором объединения (кон-катенации) двух строк в ANSI SQL явля-ется || (двойной конвейер или труба), нопродукты Microsoft применяют для этойцели знак «+» (плюс), а MySQL применя-ет вместо оператора конкатенации функ-цию CONCAT(), вам, если вы работаете насоответствующей СУБД, придется во всехвыражениях типа a||b в запросе поменятьзнак «||» или на знак «+», или на функциюCONCAT().

В подавляющем большинстве случаев на-ши программы SQL будут работать илисовсем без изменений, или с незначитель-ными синтаксическими поправками навсех основных СУБД. К сожалению, хотяи редко, некоторые программы не будутработать совсем. Скажем, MySQL 4.0 иболее ранних версий не поддерживает

Page 20: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

20 SQL

подзапросы (хотя их поддержка планиро-валась в версии 4.0).

Перечислим СУБД, учитываемые в насто-ящем издании:

Microsoft Access 2002;

Microsoft SQL Server 2000;

Oracle Release 9i;

MySQL 4.0;

PostgreSQL 7.1.

Мы остановились на этих СУБД, потомучто они самые ходовые. Все программыSQL, приведенные в данной книге, былипротестированы указанными коммерчес-кими версиями СУБД. Поскольку соответ-ствие базовому ANSI SQL в последующихверсиях никогда не ухудшается, нашипрограммы будут работать и на всех буду-щих версиях этих СУБД.

Сразу заметим, программы из этой книгивы можете запустить и на DB2, и на SybaseAdaptive Server, и на Informix. Если какая-то программа откажется работать, прочи-тайте документацию по соответствующейСУБД и определите, в чем ее реализацияSQL отличается от стандартного ANSI SQL.

В список избранных СУБД версия Oracle 8iтоже включена. Просто имейте в виду: еслиномер версии этой СУБД не указан, значит,примечания или советы имеют отношениекак к Oracle 9i, так и к Oracle 8i.

Знаки Назначение

| Символ «вертикальная черта» или«труба» разделяет альтернативныеэлементы, выбор одного из которыхможет быть, судя по контексту,как обязательным, так и необяза-тельным. В любом случае можновыбрать не более одного из этихэлементов. Никогда не печатайтеэтот символ в тексте программ! Вы-ражение A|B|C будет означать или«Или А, или В, или С», или «Или А,или В, или С, или ничего». Не путай-те знак трубы со знаком двойнойтрубы ||. Дело в том, что двойнаятруба является оператором SQL иприменяется для конкатенации(объединения) строк

[] В квадратные скобки заключаютсяэлементы, выбор хотя бы одного изкоторых необязателен. Никогда непечатайте этот символ в тексте про-грамм! Выражение [A|B|C] будет оз-начать: «Печатай или А, или В, или Сили не печатай ничего»

{} В фигурные скобки заключаютсяэлементы, выбор одного из которыхобязателен. Никогда не печатайтеэтот символ в тексте программ! Вы-ражение {A|B|C} будет означать:«Печатай или А, или В, или С»

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

() В отличие от всех предыдущих зна-ков, круглые скобки являются эле-ментом языка SQL. Их надо печа-тать там, где указываетсоответствующая грамматическаясхема

Таблица 1. Специальные символы, применяемыедля объяснений правил синтаксиса

Page 21: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

Введение 21

Разница между базой данных и СУБД

Любая база данных – это совсем не топрограммное обеспечение, на которомвы работаете. Поэтому говорить, чтоOracle – это база данных, неправильно.Полное название программного обеспе-чения, обслуживающего базы данных,звучит так: система управления базамиданных (СУБД). Важно понять, что лю-бая база данных является не более чемкомпонентом некой СУБД, что она яв-ляется элементом данных, будучи неболее чем контейнером (состоящим изодного или нескольких файлов), содер-жащим структурированную информа-цию. Помимо задач контроля, организа-ции, выборки, поддержания целостно-сти и достоверности данных любаяСУБД решает еще задачи обеспеченияфизического хранения, безопасности,тиражирования данных и исправленияошибок при их обработке.

Иногда вместо аббревиатуры СУБД ис-пользуют аббревиатуру РСУБД, имеяв виду именно реляционную СУБД. Та-кая СУБД организует данные в соответ-ствии с реляционной моделью (о ре-ляционной модели мы подробнее пого-ворим в главе 2). Хотим отметить, чтов настоящем издании рассматриваютсятолько реляционные базы данных, по-этому при использовании аббревиату-ры СУБД речь идет о РСУБД.

Что такое FileMaker Pro

Если говорить кратко, FileMaker Pro –это распространенное настольноеприложение со встроенной базой дан-ных, которое поддерживает командуSQL SELECT с предложениями FROM,WHERE и ORDER BY (подробнее об этом см.в главе 4). Этих возможностей вполнедостаточно для выполнения самыхраспространенных типов запросов.Чтобы запустить любой допустимыйSQL-запрос на FileMaker Pro, можновоспользоваться инструментариемSQL Query Builder или операторомExecute SQL script. Основательно ра-зобраться в этих вопросах поможетсправочная система FileMaker Pro иликнига Цинтии Л. Барон и ДаниеляПека (Cynthia L. Baron, Daniel Peck)«FileMaker Pro 5/5.5 Advanced: VisualQuickPro Guide». Наконец, вы можетемногое узнать о FileMaker Pro на сай-те www.filemaker.com.

Page 22: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

22 SQL

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

текстовый редактор;типовая база данных;СУБД.

Текстовый редактор

Хотя печатать короткие интерактивныекоманды SQL в ответ на приглашение ко-мандной строки может показаться дажеудобным, обычно вы все-таки стараетесьсохранять нетривиальные запросы в тек-стовых файлах. В соответствии с обще-принятыми правилами имена файлов SQLимеют расширение .sql, хотя вы можетевыбрать любое другое расширение, напри-мер .txt.Любой текстовый редактор – это некаяслужебная программа для создания и мо-дификации текстовых файлов. Как извест-но, такие файлы содержат только печат-ные буквы, числа и символы, но никакихшрифтов, никакого форматирования, ни-каких «невидимых» знаков, цветов и гра-фики, то есть ничего из той системы, ко-торую принято связывать с понятием тек-стового процессора, в них нет. Хотя каждаяоперационная система обычно поставляет-ся вместе с неким текстовым редактором(см. табл. 2), вы вполне можете поэкспери-ментировать с разными коммерческимии условно бесплатными текстовыми ре-дакторами (см. сайт www.download.com).

Таблица 2. Распространенные текстовые редакторы

Среда Текстовый редактор

Wndows Notepad

Приглашениекомандной строкиWindows edit

Unix, Linux vi, emacs, pico

Mac OS Teach Text, Simple Text

Mac OS X Terminal vi, emacs, pico

Все программы SQL, содержащиеся в на-стоящей книге, можно скачать с сайта под-держки (см. раздел «Несколько общих за-мечаний о книге»).

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

Page 23: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

Введение 23

Типовая база данных

Большинство примеров, рассматриваемыхв данной книге, рассчитаны на одну и туже базу данных (см. раздел «Наша типоваябаза данных» в главе 2). Чтобы ее постро-ить, запустите SQL-скрипт, приведенный вприложении, или, если вы работаете сMicrosoft Access, откройте файл books.mdb(все эти файлы вы можете скачать с сайтаподдержки). Если вы работаете с действу-ющей серверной СУБД организации, то насоздание базы данных и на запуск командSQL вам может понадобиться официаль-ное разрешение системного администра-тора.

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

Для выполнения SQL-запросов просто пе-редайте их какой-нибудь СУБД, которая«понимает» SQL. И уже она выполнитваш запрос и отобразит соответствующиерезультаты. В первой главе мы расскажемо том, как запускать SQL-запросы на са-мых распространенных СУБД.

Page 24: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

Основныехарактеристики СУБД

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

11111

Page 25: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

25

Выполнение программ SQLВ настоящей главе объясняется, как вы-полнять SQL-запросы и скрипты на сле-дующих СУБД:

Microsoft Access 2002;

Microsoft SQL Server 2000;

Oracle 9i;

MySQL 4.0;

PostgreSQL 7.1.Графический интерфейс Microsoft Accessпозволит запускать только по одномуSQL-запросу за раз, а для того, чтобы вы-полнить SQL-скрипт (то есть последова-тельность команд), вам придется встраи-вать команды SQL в какую-нибудь про-грамму на Visual Basic.Все остальные СУБД позволят все то, чтопозволяет Microsoft Access, а также испол-нять запросы и в интерактивном режиме,и в виде скрипта. В интерактивном режи-ме вы будете печатать отдельные командыSQL в командной строке и просматриватьрезультаты выполнения каждой командытак, что ввод будет чередоваться с выво-дом результатов выполнения запросов.В режиме скрипта, или сценария (которыйеще иногда называют пакетным режи-мом), вы будете записывать все требуемыедля выполнения запросы в текстовыйфайл (который в этом случае будет назы-ваться файлом скрипта (сценария), илипакетным файлом), а утилита команднойстроки выполнит записанные в этом фай-ле запросы и вернет результаты.

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

Листинг 1.1. Файл listing0101.sql содержитпростую SQL-команду SELECT, которую мы будемиспользовать для построения запросов к типовойбазе в примерах, относящихся ко всем СУБД

SELECT au_fname, au_lname

FROM authors

ORDER BY au_lname;

Выполнение программ SQL

Page 26: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

26 Основные характеристики СУБД

изучить этот синтаксис, обратитесь к спра-вочному разделу документации по вашейСУБД.

При указании имени любого файла SQL-скрипта в режиме сценария вы можетеобозначить его и как абсолютное полное,и как относительное имя (см. врезку «Пол-ный путь, или полное имя»).

Чтобы запустить любую утилиту команд-ной строки из заданного каталога, вашмаршрут (сокращенное название для поня-тия маршрутной переменной среды; не пу-тать с полным именем) должен включатьфактический каталог, в котором содержит-ся эта утилита. Имейте в виду, что любоймаршрут представляет собой список ка-талогов, в которых операционная система«ищет» программы. Особенность зак-лючается в том, что инсталляторы однихСУБД детально формируют маршруты,а для других СУБД (как, например, MySQLи PostgreSQL) вам придется самостоя-тельно добавить каталог соответствую-щей утилиты в маршрут.

Для того чтобы увидеть свой маршрут,в командной строке напечатайте path (дляWindows) или echo $PATH (для Unix илиMac OS X Terminal) и нажмите Enter.

Чтобы изменить маршрут, добавьте в мар-шрутную переменную среды каталог,в котором постоянно находится нужнаяутилита. Более подробную информацию поэтому вопросу можно получить в справоч-ной системе, если вы работаете подWindows (поиск по ключу «переменнаясреды»), а если вы работаете под Unix илиMac OS X Terminal, то можете модифици-ровать команду path в файле инициали-зации сеанса, который обычно называется.bash_login, или .bashrc, или .cshrc, или.login, или .profile, или shrc.

Полный путь, или полное имя

Любой полный путь указывает место-положение файла или каталога виерархической файловой системе. Приэтом абсолютный полный путь опре-деляет положение файла полностьюпосредством явного указания всегомаршрута, начиная с самого верхнегоузла, называемого корнем, дерева ката-логов. В свою очередь, относительныйпуть прописывает местоположениефайла или каталога не абсолютно, а поотношению к текущему месту. В опе-рационной системе Windows любойабсолютный полный путь обязательноначинается или с обратного слэша (\),или с буквы, обозначающей дисковод,за которой следует двоеточие и слэш.В операционных системах Unix/Linuxи Mac OS X любое абсолютное имявсегда начинается со слэша, наклонен-ного вправо (/).Например, C:\Program Files\MicrosoftSQL Server (для Windows) и /usr/local/bin/mysql (для Unix/Linux) – это аб-солютные полные пути, а scripts\lis-ting0101.sql (для Windows) и doc/read-me.txt (для Unix) – это относительныеимена.

Page 27: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

27

Microsoft AccessMicrosoft Access – это коммерческая СУБД,которая служит для управления базамималого и среднего размера. Дополнитель-ную информацию об Access вы можетеузнать на сайте www.microsoft.com/office/access/.Настоящее издание написано с расчетомна Microsoft Access 2002. Если вы не знаететочно, с какой версией СУБД работаете,выберите пункт меню: Help [ About Mic-rosoft Access (Помощь [ О MicrosoftAccess).Имейте в виду, что в Access 2002 по умолча-нию установлен режим запросов ANSI-89.Чтобы иметь возможность исполнитьмногие из тех примеров, что приведеныв настоящей книге, установите режим за-просов ANSI-92.

Установка режима запросов SQL

Порядок действий:1. Откройте диалоговое окно Options (На-

стройки), выполнив команды Tools [Options (Инструменты [ Опции).

2. Откройте вкладку Tables/Queries (Таб-лицы/Запросы).

3. Установите флажок This Database (Этабаза данных) – см. рис. 1.1.

Не торопитесь вводить этот режим дляостальных ваших баз данный. Дело в том,что режимы запросов ANSI SQL-89 и ANSISQL-92 несовместимы и их области типовданных, универсальных символов и зарезер-вированных слов существенно различаются.То есть в другом режиме SQL-запросовваши команды могут или совсем не рабо-тать, или давать неожиданные результаты.

Подробнее о режимах запросов посмотри-те в справочной системе Access (поиск поключу «ANSI SQL query mode»).

Microsoft Access

Рис. 1.1. Режим запросов SQL ANSI-92 даст вамвозможность исполнять команды SQL, которыеиспользуют синтаксис ANSI-92 SQL

Page 28: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

28 Основные характеристики СУБД

Если вы работаете с Access лишь время отвремени, то, скорее всего, создавая своизапросы, постоянно пользуетесь графи-ческим конструктором запросов. Так вот,когда вы составляете любой запрос в ре-жиме конструктора, Access автоматическии незаметно для вас генерирует эквива-лентную вашему запросу команду SQL.Вы можете исполнять, просматриватьи редактировать эту команду в режимеSQL view.Чтобы исполнять команды языков DMLи DDL (языки модификации и определе-ния данных; см. раздел «SQL» во введе-нии), Access применяет два почти одина-ковых интерфейса. При этом командыязыка DML – это SELECT, INSERT, UPDATE иDELETE, а команды языка DDL – это CREATE,ALTER и DROP.

Исполнение произвольной команды SQL

Необходимо выполнить следующие дей-ствия:

1. В окне базы данных щелкните по пик-тограмме Queries (Запросы), котораярасположена слева на панели Objects(Объекты), а потом по пиктограммеNew (Создать), которая расположенасверху на панели инструментов (см.рис. 1.2).

2. В диалоговом окне New Query (Новыйзапрос) щелкните по опции DesignView (Конструктор), а потом по кноп-ке OK (см. рис. 1.3).

3. В графическом бланке конструкторазапроса, не добавляя ни таблиц, ни за-просов, щелкните по кнопке Close (Зак-рыть).

Рис. 1.3. В диалоговом окне New Query(Новый запрос) выберите опцию Design View(Конструктор)

Рис. 1.2. Чтобы создать новый запрос, щелкнитепо кнопке New (Создать) на панели инструментов

Рис. 1.4. Чтобы запустить запросна языке DML (включающийв данном случае только командыSELECT, INSERT, UPDATE,DELETE), выберите опцию SQL

Page 29: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

29

4. Чтобы запустить произвольную ко-манду:

– языка DML – выберите командыView [ SQL View (Режим [ РежимSQL), см. рис. 1.4;

– языка DDL – выберите командыQuery [ SQL Specific [ Data De-finition (Запрос [ Запрос SQL [Управление), см. рис. 1.5.

5. Наберите или вставьте через буфер об-мена какую-нибудь команду SQL (на-пример, из листинга 1.1; см. рис. 1.6).

6. Теперь, чтобы запустить команду SQL,выберите последовательно пункты Que-ry [ Run (Запрос [ Выполнить) – см.рис. 1.7.

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

Через один объект Query вы можете вы-полнить только одну команду SQL. Чтобызапустить несколько команд, используйтесерию объектов Query или напишите про-грамму на встроенном языке Visual Basicfor Applications, которая выполнит ряд за-просов последовательно.

Рис. 1.5. Чтобы запустить запрос на языке DDL(включающий в данном случае только командыCREATE, ALTER, ROP), выберите опцию DataDefinition

Рис. 1.7. ...и запустите ее

Рис. 1.8. Результаты выполнения команды SELECT

Рис. 1.6. Введите какую-нибудь команду SQL…

Microsoft Access

Page 30: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

30 Основные характеристики СУБД

Microsoft SQL ServerMicrosoft SQL Server, в отличие от настоль-ной СУБД Microsoft Access, является сервер-ным программным обеспечением и под-держивает очень большие базы данных иогромное число транзакций. Microsoft SQLServer работает только под операционнымисистемами компании Microsoft. Подробнеео Microsoft SQL Server можно узнать на сай-те www.microsoft.com/sql/. Еще вы можетескачать бесплатную оценочную версию это-го продукта, которая будет «жить» 120 дней,с сайта www.microsoft.com/sql/evaluation/.

Настоящая книга описывает MicrosoftSQL Server 2000. Чтобы определить вер-сию Microsoft SQL Server, с которой вы ра-ботаете, введите SELECT @@VERSION, нажми-те Enter, в командной строке osql напеча-тайте go и снова нажмите Enter.

Чтобы исполнять программы SQL, можновоспользоваться графическим инструмен-тарием SQL Query Analyzer или утилитойкомандной строки osql. Рассмотрим обаварианта.

Работа с SQL Query Analyzer

Порядок действий:

1. Щелкните последовательно по пунктамменю Start [ Programs [ Microsoft SQLServer [ Query Analyzer.

2. Укажите сервер, имя пользователя ипароль и нажмите OK.

3. Выберите в соответствующем раскры-вающемся списке панели инструмен-тов какую-нибудь базу данных, напри-мер books (см. рис. 1.9).

Рис. 1.9. Чтобы выполнить всессылки (предложения), указанныев командах SQL, Query Analyzerиспользует выбранную вами базуданных

Page 31: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

31

4. Для работы с SQL есть два альтернатив-ных способа:

– запустите SQL интерактивно, набравкакую-нибудь команду SQL, напри-мер приведенную в листинге 1.1, не-посредственно в окне запроса;

– запустите SQL-сценарий, выбравпоследовательно опции File [ Open(Файл [ Открыть), потом какой-нибудь файл сценария и щелкнувпо кнопке Open (Открыть).

5. Выберите опции Query [ Execute (За-прос [ Выполнить). Самое нижнее по-докно покажет результат выполнениявашей команды (см. рис. 1.10).

Чтобы запустить SQL Query Analyzer, мож-но воспользоваться утилитой команднойстроки isqlw.

Применение утилитыкомандной строки osqlв интерактивном режимеПорядок действий:1. Наберите osql -E -d dbname,

где:– -E указывает SQL Server не запраши-

вать пароль, а использовать довери-тельное соединение;

– вместо dbname следует подставитьимя базы данных, с которой вы со-бираетесь работать.

2. Введите любую команду SQL, напри-мер ту, что показана в листинге 1.1,и нажмите Enter. Имейте в виду, чтокоманда не обязательно должна уме-щаться на одной строке.

3. Напечатайте go и посмотрите на резуль-таты (см. рис. 1.11).

Рис. 1.10. Результаты исполнения командыSELECT в Query Analyzer

Рис. 1.11. Команда SQL в интерактивном режимеутилиты командной строки osql

Microsoft SQL Server

Page 32: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

32 Основные характеристики СУБД

Использование утилиты команднойстроки osql в режиме сценария

В командной строке наберитеosql -E -d dbname -n -i sql_script,

где:

-E инструктирует SQL Server не запра-шивать пароль, а использовать довери-тельное соединение;вместо dbname вы подставите имя базыданных, с которой собираетесь рабо-тать;-n «гасит» при выдаче результатов ну-мерацию и символы приглашения «>»;вместо sql_script вы подставите илиабсолютное, или относительное пол-ное имя текстового файла, в котором за-писана некая программа SQL (см. лис-тинг 1.1 и рис. 1.12).

Выход из утилиты командной строки osql

Напечатайте exit или quit и нажмите Enter.

Отображение списка опцийкомандной строки osql

В командной строке напечатайте osql -?и нажмите Enter.

Рис. 1.12. Команда SQL в режиме сценария дляутилиты командной строки osql

Настройки СУБД SQL Server могут потре-бовать указания имени пользователя и па-роля, вместо того чтобы предоставить вамдоверительное соединение. Для ввода па-роля замените сначала опцию -E опцией-U login_id, где вместо login_id нужноподставить имя пользователя. Только пос-ле этого система запросит пароль.

Если вы исполняете команду osql с уда-ленного компьютера и по сети, то для ука-зания системы SQL Server, к которой высобираетесь подключиться, придется при-менить опцию -S server. Об особеннос-тях использования этой опции узнайте усистемного администратора.

Page 33: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

33

OracleOracle – это ведущая коммерческая СУБД,способная поддерживать гигантские базыданных и невероятные количества транзак-ций. Она работает под многими операцион-ными системами и на разных платформахаппаратно-технического обеспечения. На-конец, эта СУБД настолько сложна, что дляподдержания ее в рабочем состоянии ну-жен высококвалифицированный админист-ратор баз данных.На сайте www.oracle.com вы можете узнатьоб Oracle всю необходимую информацию.Вы даже можете скачать рассчитанную наединственного пользователя бесплатнуюверсию «только для разработчика» этойСУБД с сайта сетевой поддержки техноло-гии Oracle (otn.oracle.com). Неудобствозаключается в том, что сам файл оченьбольшой и ту же версию наверняка придет-ся купить на CD-диске.Для того чтобы исполнять под Oracle ко-манды SQL, можно воспользоваться или гра-фическим инструментарием SQL*Plus, илиутилитой командной строки sqlplus. Имейтев виду, что в настоящем издании описыва-ется главным образом версия Oracle 9i. Од-нако в книге вы найдете несколько советови по Oracle 8i. Уточнить вашу версию Oracleможно при регистрации в системе для ра-боты с SQL*Plus или sqlplus. В этом случаеавтоматически появляется сообщение Con-nected to (Подключен к...).

Использованиеграфического интерфейса SQL*PLusПорядок действий:1. Запустите SQL*Plus. Эта процедура, од-

нако, зависит от платформы. Например,под Windows вам следует выбрать оп-ции Programs [ Oracle-OraHome91 [Application Developement [ SQL Plus.

2. Введите имя пользователя, пароль и имябазы данных, с которой вы хотите рабо-тать. Нажмите OK (см. рис. 1.13). Если вы

Рис. 1.13. Экран регистрации интерфейса SQL*Plus

Oracle

Page 34: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

34 Основные характеристики СУБД

собираетесь работать с удаленной базойOracle, узнайте у администратора иден-тификатор подключения, который надоввести в текстовом окне Host String. Поумолчанию учетная запись администра-тора базы данных содержит в качествеимени пользователя SYSTEM, а паролемпо умолчанию является manager.

3. Теперь у вас есть две возможности:– чтобы работать с SQL интерактив-

но, просто наберите какую-нибудькоманду SQL и нажмите Enter; приэтом не обязательно умещать этукоманду в одну строку; строк можетбыть несколько (см. рис. 1.14);

– чтобы запустить произвольный сце-нарий SQL, напечатайте @sql_scriptи нажмите Enter. Вместо sql_scriptвы подставите имя текстового фай-ла, в котором записана ваша про-грамма SQL; оно может быть какабсолютным полным именем, таки относительным (см. рис. 1.15).

Применение утилитыкомандной строки sqlplusв интерактивном режиме

Порядок действий:

1. В командной строке напечатайте:sqlplus user/password@dbname.Здесь вместо user надо подставить имяпользователя, вместо password – па-роль, а вместо dbname – имя той базы, скоторой вы собираетесь работать.

2. Введите любую команду SQL (количе-ство строк не имеет значения).

3. Нажмите Enter и посмотрите на резуль-таты (рис. 1.16).

Рис. 1.15. Команда SELECT, выполненнаяс помощью SQL*Plus, но как сценарий

Рис. 1.14. Результаты интерактивного выполнениякоманды SELECT средствами SQL*Plus

Page 35: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

35

Использование утилитыкомандной строки sqlplusв режиме сценария

В командной строке напечатайте:sqlplus user/password@dbname @sql_script

Здесь вместо user надо подставить имяпользователя, вместо password – пароль, вме-сто dbname – имя той базы, с которой вы со-бираетесь работать, а вместо sql_script –имя текстового файла, в котором записанаваша программа SQL; оно может быть какабсолютным полным именем, так и отно-сительным. Не забудьте вставить пробелмежду dbname и @sql_script (см. рис. 1.17).

Выход из утилитыкомандной строки sqlplus

Напечатайте exit или quit и нажмите Enter.

Отображение списка опцийутилиты sqlplus

В командной строке наберите:sqlplus -?

и нажмите Enter.

Если вы хотите, чтобы система сама спро-сила у вас пароль, исключите из команд-ной строки sqlplus опцию /password.

Рис. 1.16. Результаты выполнения команды SELECTутилитой sqlplus в интерактивном режиме

Рис. 1.17. Результаты выполнения командыSELECT утилитой sqlplus в режиме сценария

Oracle

Page 36: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

36 Основные характеристики СУБД

MySQLMySQL – это одна из ведущих (то естьнаиболее распространенных, качествен-ных и покупаемых в настоящее время)СУБД с открытым исходным кодом. Онаотличается быстродействием, устойчиво-стью и возможностью поддерживать базыданных больших объемов. Но, несмотряна все ее достоинства, среди которых ос-новным и наиболее известным являетсябыстродействие, MySQL не поддерживаетнескольких очень важных функций SQL(планируется, что версия 4.1 будет под-держивать подзапросы и внешние клю-чи). Немаловажно и то, что MySQL рабо-тает под многими популярными опера-ционными системами и на разных аппа-ратных платформах, будучи при этомбесплатной. Вы можете скачать эту СУБДс сайта www.mysql.com.В настоящем издании рассматриваетсяMySQL 4.0. Чтобы выяснить, с какой верси-ей вы работаете, в командной строке mysqlвведите SELECT VERSION(); и нажмите Enter.Чтобы исполнять программы SQL, в средерассматриваемой СУБД можете приме-нять утилиту командной строки mysql.

Использование утилитыкомандной строки mysqlв интерактивном режиме

Порядок действий:1. Напечатайте в командной строке:

mysql dbname.Предполагается, что вместо dbname выподставите имя базы данных, с кото-рой собираетесь работать.

2. Введите какую-нибудь команду SQL,например из листинга 1.1. При этом за-пись этой команды может заниматьболее одной строки.

Рис. 1.18. Результат интерактивного выполнениякоманды SELECT утилитой командной строки mysql

3. Нажмите Enter и посмотрите на резуль-таты (рис. 1.18).

Использование утилитыкомандной строки mysqlв режиме сценария

В командной строке напечатайте mysql -tdbname < sql_script,

где:

-t инструктирует систему поместитьрезультаты в некую таблицу (если выпредпочитаете распечатать их с раз-делителями в виде знака табуляции,исключите эту опцию из команднойстроки);

вместо dbname подставьте имя реальнойбазы данных, с которой планируетеработать;

вместо sql_script подставьте абсолютноеполное или относительное полное имяреального текстового файла, в которомсодержится программа SQL для запускав режиме сценария (см. рис. 1.19).

Page 37: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

37

Выход из утилитыкомандной строки mysql

Напечатайте exit или quit и нажмите Enter.

Вывод на экран списка опцийутилиты командной строки mysql

Нужно выполнить следующие действия:

1. В командной строке наберите mysql -?и нажмите Enter.

2. Эта команда осуществляет выход в виденескольких экранов, и для того, чтобыпросматривать их, переключая один задругим по своему желанию, введите до-полнительные опции в командной стро-ке: mysql -? | more (см. табл. 1), нажмитеEnter и переключайте экраны с распе-чаткой выхода нажатием клавиши про-бела (см. рис. 1.20).

Рис. 1.19. Результат выполнениякоманды SELECT утилитой командной строкиmysql в режиме сценария

Рис. 1.20. Экран справочной системы mysql

MySQL

Page 38: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

38 Основные характеристики СУБД

При запуске сервера MySQL, то есть mysqld,вы можете воспользоваться опцией —ansi,чтобы применять не синтаксис MySQL,а синтаксис ANSI SQL.

Если вы работаете с MySQL по сети с уда-ленного компьютера, то для того, чтобыподключиться к серверу, понадобится ука-зать имя хост-машины, имя пользователяи пароль. Поэтому наберите mysql -h host-u user -p dbname, где предполагается:

что вместо host вы подставите имя ре-альной хост-машины;

что вместо user вы подставите имяпользователя;

что вместо dbname вы подставите имябазы данных, с которой хотите работать.

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

PostgreSQLPostgreSQL – еще одна ведущая СУБД с от-крытым исходным кодом. Она отличаетсябыстродействием, устойчивостью и воз-можностью поддерживать базы данныхбольших объемов с огромным числомтранзакций, а также обширной функ-циональностью и высоким уровнем соот-ветствия стандарту ANSI SQL. Немаловаж-но и то, что PostgreSQL работает под мно-гими популярными операционными систе-мами и на разных аппаратных платформах,будучи при этом бесплатной. Эту СУБДможно скачать с сайта www.postgresql.org.В настоящем издании рассматриваетсяверсия PostgreSQL 7.1. Чтобы определить,с какой версией этой СУБД вы работаете,напечатайте в командной строке psql -Vи нажмите Enter.Чтобы исполнять программы SQL, можноиспользовать утилиту командной строкиpsql.

Page 39: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

39

Рис. 1.21. Результат интерактивного выполнениякоманды SELECT утилитой командной строки psql

Использование утилитыкомандной строки psqlв интерактивном режиме

Порядок действий:1. В командной строке напечатайте psql

dbname. Предполагается, что вместоdbname вы подставите имя базы данных,с которой собираетесь работать.

2. Введите какую-нибудь команду SQL,например ту, которая приведена в лис-тинге 1.1. Эта команда может занятьнесколько строк.

3. Нажмите Enter. На экране появятся ре-зультаты (см. рис. 1.21).

Использование утилитыкомандной строки psqlв режиме сценария

В командной строке напечатайте psql -fsql_script db_name.Предполагается, что:

вместо sql_script вы подставите абсо-лютное полное или относительное пол-ное имя текстового файла, в которомсодержится некая программа SQL;вместо dbname вы подставите имя базыданных, с которой собираетесь рабо-тать (см. рис. 1.22).

Выход из утилитыкомандной строки psql

Необходимо напечатать \q и нажать Enter.

Рис. 1.22. Результат выполнения утилитой psqlкоманды SELECT в режиме сценария

PostgreSQL

Page 40: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

40 Основные характеристики СУБД

Вывод на экран списка опцийкомандной строки psql

В командной строке нужно напечататьpsql -? и нажать Enter (см. рис. 1.23).

Если вы работаете в Postgre SQL по сетис удаленного компьютера, то для подклю-чения к серверу понадобится указать имяхост-машины, имя пользователя и пароль.Поэтому напечатайте psql -h host -U

user -W dbname, где предполагается, что:

вместо host вы подставите имя реаль-ной хост-машины;

вместо user вы подставите имя пользо-вателя;

вместо dbname вы подставите имя базыданных, с которой хотите работать.

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

Рис. 1.22. Результат выполнения утилитой psqlкоманды SELECT в режиме сценария

Рис. 1.23. Экран справочной системы psql

Page 41: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

Реляционная модель

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

Рис. 2.1. Вы можете ознакомиться с описанием реляционной модели, созданной Е. Ф. Коддом,по адресу: http://www.acm.org/classics/nov95/toc.html. Все современные системы управленияреляционными базами данных основаны на модели данных, определенной этим документом

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

22222

Page 42: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

42 Реляционная модель

отдельных элементов или строк данных.Итак, будучи основана на математическойтеории множеств, реляционная модельстрого регламентирует, как выполнять ал-гебраические операции математической те-ории множеств, в частности объединениеи пересечение, но на таблицах баз данных.И эти операции выполняются в принципетак же, как они определены для математи-ческой теории (см. рис. 2.2). При этом по-нятно, что таблицы являются аналогамимножеств, то есть таблицы в реляционноймодели играют ту же роль, какую в теориимножеств играют множества. Таким обра-зом, таблицы, подобно абстрактным мно-жествам, являются некими совокупностямиабсолютно различных элементов, но имею-щих какие-то общие свойства. Отсюда сле-дует, что, хотя некое математическое мно-жество могло бы, например, иметь в каче-стве элементов положительные целые чис-ла, а какая-нибудь таблица произвольнойбазы могла бы содержать информацию остудентах, между этим множеством и этойтаблицей нет принципиальной разницы.Звучит фундаментально, но, по правде го-воря, вы вполне можете пропустить боль-шую часть этой главы или всего лишь про-смотреть ее. Дело в том, что научитьсяпрограммировать на SQL можно и без се-рьезной теории, используя так называе-мый метод проб и ошибок. Это особенноверно в том случае, если ваш интерес невыходит за пределы простых командSELECT. Но, как это имеет место и во всехдругих областях знаний, мощь теории со-стоит не в том, чтобы помочь в решениипростых задач, а в том, что, если вы ее глу-боко понимаете, у вас появляется возмож-ность предсказывать результаты решениязадач. Если же вы умеете предсказывать ре-зультаты, вам не придется бесконечно дол-го и нудно выяснять причины сбоев мето-дом проб и ошибок. Вы будете сразу точнопредставлять, что именно работает не так.

Рис. 2.2. Эта диаграмма описывает результатопераций над множествами. Прямоугольник U –это универсум, а круги A и B – множестваопределенных элементов. Положение друготносительно друга и пересечение множеств А и Bопределяют отношения между ними.В реляционной модели круги представляют собойтаблицы, а прямоугольник – всю информацию,находящуюся в базе данных

UA B

Page 43: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

43

Таблицы, столбцы и строкиНачнем с терминологии. Если вы обладае-те некоторым опытом работы с базамиданных, то наверняка уже заметили, чтов этой сфере деятельности одни и те жепонятия часто называют разными терми-нами. Поэтому в табл. 2.1 показана взаи-мосвязь основных терминов и понятий.Здесь в первом столбце приведены терми-ны, относящиеся к реляционной модели,во втором столбце – термины стандартаANSI SQL и современной документацииСУБД, а в третьем – термины файловой мо-дели обработки данных. В настоящем изда-нии используется терминология стандартаSQL, в том числе при обсуждении реляци-онной модели.

Таблицы

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

является тем компонентом структурыбазы данных, в котором содержатсясобственно данные;

содержит данные, соответствующие ка-кому-то заданному объектному типу,где под ним понимается некий классразличимых объектов, или/и событий,или/и концепций реального мира с об-щими свойствами (например, студен-ты, кинофильмы, гены, пациенты, по-годные условия или бизнес-встречи –это разные объектные типы, поэтомувам следует хранить данные о, скажем,пациентах и бизнес-встречах в разныхтаблицах);

является двумерной сеткой, образован-ной пересечением строк и столбцов(см. рис. 2.3 и 2.4);

Таблица 2.1. Эквивалентность терминов

Модель SQL Файловая система

Отношение Таблица Файл

Атрибут Столбец Поле

Кортеж Строка Запись

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

Таблица

Столбцы

Значение Строки

Рис. 2.4. Эта сетка является отображением некойреальной, а не абстрактной, как было выше,таблицы. Поэтому она приведена в том виде,в каком отображается любая таблица в СУБДи книгах. Мы видим, что эта таблицаимеет 3 столбца, 4 строки и (3×4) 12 значений,а также то, что верхняя строка являетсязаголовком, состоящим из имен столбцов

au_id au_fname au_lname

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

A01 Sarah Bucman

A02 Wendy Heydemark

A03 Hallie Hull

A04 Klee Hull

Таблицы, столбцы и строки

Page 44: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

44 Реляционная модель

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

Столбцы

Столбцы любой заданной таблицы отли-чаются следующими свойствами:

каждый столбец представляет какой-токонкретный атрибут (свойство) тогообъектного типа, который отображает-ся этой таблицей (например, в табли-це employees некий столбец по имениhire_date мог бы содержать даты наймасотрудников на работу);для каждого столбца определен свойдомен, под которым понимается та си-стема формальных ограничений натип данных, их длину, формат, диапа-зон изменения, единственность и об-нуляемость (то есть допустимость/не-допустимость значения null), котораязадает множество допустимых значе-ний для этого столбца (например, выне можете поместить строковое значе-ние mimetic в столбец hire_date, еслидля него допустимыми являются толь-ко значения данных с типом дата);данные, вводимые в любую позициюстолбца, являются однозначными (ато-марными, неделимыми; см. раздел«Нормализация» в этой главе);порядок следования столбцов (при про-смотре таблицы слева направо) не име-ет значения (см. рис. 2.5);

Рис. 2.5. Считается, что строки и столбцыне упорядочены, если последовательностьих прохождения при просмотре произвольнойтаблицы не имеет значения ни для смысла тойинформации, которую содержит эта таблица,ни для тех операций, которые могут быть проведеныс этой таблицей в рамках реляционной модели.Значит, перестановки строк и столбцов не меняютсмысла таблицы. Таким образом, таблица,представленная на этом рисунке, полностьюэквивалентна таблице на рис. 2.4

au_lname au_id au_fname

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

Hull A04 Klee

Buchman A01 Sarah

Hull A03 Hallie

Heydemark A02 Wendy

Page 45: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

45

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

Строки

Перечислим характеристики строк в про-извольной заданной таблице:

каждая строка описывает какой-нибудьобъект (реального мира), являющийсяуникальной реализацией объектноготипа (например, какой-нибудь студентили определенная бизнес-встреча);каждая строка в каждом столбце содер-жит или какое-нибудь значение (нену-левое), или элемент null;порядок следования строк при про-смотре таблицы (скажем, сверху вниз)не имеет значения;в любой таблице в любой паре строкнайдется хотя бы один столбец, значе-ния в котором и в строках этой парыразличны;в любой таблице каждая строка одно-значно идентифицируется своим пер-вичным ключом (см. раздел «Первич-ные ключи» в этой главе).

Для выборки строк и столбцов применяйтекоманду SELECT (см. главы 4–8). Чтобыудалять, добавлять и редактировать стро-ки, применяйте команды INSERT, UPDATEи DELETE (см. главу 9), а чтобы добавлять,редактировать и удалять столбцы, исполь-зуйте команды CREATE, TABLE и ALTERTABLE (см. главу 10).

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

Унарная табличная операция

Бинарная табличная операция

Рис. 2.6. Замкнутость таблиц гарантирует вам,что вы всегда будете получать определеннуютаблицу независимо от того, какую операциюнад таблицами вы проводите. Это свойствопозволяет вкладывать табличные операции другв друга, причем глубина вложения не ограничена.При этом принято различать так называемыеунарные и бинарные табличные операции,отличающиеся тем, что на вход и выходпроизвольной унарной операции поступает толькопо одной таблице, а на вход любой бинарнойоперации должны поступитьдве каких-нибудь таблицы, хотя на выходеэтой бинарной операции тоже будет лишьодна какая-то таблица

Таблицы, столбцы и строки

Page 46: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

46 Реляционная модель

Любая СУБД применяет таблицы двух ти-пов:

таблицы пользователя;таблицы системы, или системные таб-лицы.

Разница между этими типами таблиц зак-лючается в следующем. Системные табли-цы содержат метаданные, то есть данныео самой базе данных (например, сведенияо структуре базы, некоторые физическиеособенности, статистику работы базы, уста-новки по обеспечению безопасности дан-ных и системы). Совокупность системныхтаблиц во всей используемой версии СУБДназывается системным каталогом СУБД.Любая СУБД динамически создает и обнов-ляет системные таблицы, что, кстати ска-зать, полностью отвечает следующему тре-бованию реляционной модели: все данныедолжны храниться в таблицах (см. рис. 2.7).

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

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

Распределение данных по столбцам таблицзависит от мастерства разработчика базы.Дело в том, что разработчики часто раз-бивают значения по столбцам на основесвоего собственного понимания потребнос-тей пользователя. Например, телефонныеномера могли бы находиться в единствен-ном столбце phone_number или в несколь-ких столбцах наподобие country_code,area_code и subscriber_number.

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

Page 47: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

47

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

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

сама не производит никаких вычислений;

не позволяет вводить данные в свобод-ном формате (наоборот, соответствиевводимых данных допустимому фор-мату всегда строго проверяется).

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

Имейте в виду, что прилагательное «реля-ционная» (relational), которое входит в на-звание «реляционная база данных» (rela-tional database), появилось благодаря мате-матической «реляционной теории мно-жеств» (relational set theory), а не из-завозможности устанавливать связь (torelate) между разными таблицами по ихобщим значениям.

Рекомендуем запомнить следующие тер-мины:

число строк любой таблицы называет-ся мощностью этой таблицы;

число столбцов любой таблицы называ-ется порядком этой таблицы;

говоря о базах данных, термин «схема»чаще всего применяют для того, чтобыобозначить структуру и организациюкакой-нибудь базы данных, включаяопределение ее таблиц. Считается, чтосхема описывает все. Но по стандартуANSI любая «схема» – это совокупностьтаких объектов базы данных, которыепринадлежат какому-то одному пользо-вателю;

совокупность всех схем называетсякаталогом (имеется в виду стандартANSI).

Первичные ключиОчевидно, что каждая база данных долж-на работать так, чтобы можно было обра-титься к произвольному значению, хра-нящемуся в любой из ее таблиц. Но, по-скольку все значения размещаются в таб-лицах, точнее – на пересечениях строк истолбцов этих таблиц, очевидно и то, чтоместоположение любого значения одно-значно определяется, если известны кон-кретные таблица, столбец и строка. Неменее очевидно, что любые таблицу истолбец можно однозначно идентифици-ровать по их уникальным номерам. Одна-ко строки уникальных имен не имеют.Тем не менее нам нужен механизм их од-нозначной идентификации. Такой меха-низм в реляционной модели существуети называется первичный ключ (Primary key).Поскольку, как мы видим, первичныйключ – это некая функция на строках про-извольной таблицы, ее удобно связать состолбцами этой таблицы, которые тожепринимают свои значения на строках, тоесть являются их функциями. Поэтомупервичный ключ надо представлять себекак столбец или, в крайнем случае, наборстолбцов особого рода.Перечислим родовидовые признаки пер-вичного ключа как механизма однознач-ной идентификации строк:

необходимость. Каждая таблица долж-на иметь только один первичный ключ.Вспомните, что реляционная модельрассматривает любую таблицу как не-кое неупорядоченное множество строк.Поскольку для такого множества несуществует понятий «следующая стро-ка» или «предыдущая строка», вы неможете идентифицировать никакуюстроку по ее относительному располо-жению среди других строк. Если бы небыло первичных ключей, некоторыеданные стали бы недоступными;

Первичные ключи

Page 48: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

48 Реляционная модель

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

простой или составной. Итак, в любойтаблице ее единственный первичныйключ может включать один или болеестолбцов этой таблицы. Если первич-ному ключу соответствует ровно одинстолбец, то такой ключ называют про-стым. Любой первичный ключ, не яв-ляющийся простым и, следовательно,включающий два или более столбцов,называют составным;

никогда не принимает значение null. Ниодно значение первичного ключа неможет быть пустым. Отсюда для со-ставного первичного ключа получаем,что ни одно значение столбцов, кото-рые входят в составной первичныйключ, не может быть пустым (см. раз-дел «Значение null» в главе 3);

неизменность. Будучи однажды создан,всякий первичный ключ крайне редкоменяет значения, которые он принима-ет на строках;

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

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

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

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

Допустим, вы хотите выбрать первичныйключ для той таблицы, которая представле-на на рис. 2.8. Ни столбец au_fname, ни стол-бец au_lname, взятые по отдельности, не под-ходят, потому что каждый из них, будучиназначен простым первичным ключом, на-рушал бы постулат уникальности. Но иобъединение этих столбцов в составной пер-вичный ключ не дало бы решения по той жепричине. Дело в том, что люди очень частоимеют одинаковые имена, что явилось быпотенциальным источником нарушения по-стулата уникальности, выбери мы имя, пол-ное или нет, в качестве первичного ключа,простого или составного. Кроме того, людимогут менять свои имена как по собственно-му желанию (в частности, при разводе иливступлении в брак), так и по воле случая (на-писание имени может измениться). То жесамое справедливо и по отношению к име-нам юридических лиц: компании могутиметь одинаковые имена, могут сливаться водин холдинг с новым именем, разделятьсяна новые компании, менять форму соб-ственности и т.д. Именно потому, что именатак нестабильны (то есть несут в себе потен-циальную угрозу для постулата неизменно-сти), опыт проектирования баз данных гово-рит о том, что собственно имена – плохая ос-

Page 49: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

49

нова для построения первичного ключа. Врассматриваемом примере правильное ре-шение – это столбец au_id, который я специ-ально добавил для того, чтобы идентифици-ровать авторов (то есть не имена, а людей).Разработчики баз данных часто прибегаютк этому приему. Если естественные или«очевидные» идентификаторы (вроде имен)не годятся, разработчик изобретает уни-кальный идентификатор.После того как таблице назначен какой-нибудь первичный ключ, СУБД начинаетжестко отслеживать целостность данныхэтой таблицы. Например, в нашем при-мере вы не смогли бы ввести в рассматри-ваемую таблицу строкуA02 Christian Kells

потому что в таблице уже есть строка, гдезначение столбца au_id равно A02. Ввестив нее строкуNULL Christian Kells

также не получится, потому что столбецau_id, будучи первичным ключом, не мо-жет принимать значение null. Допустимойявляется строкаA05 Christian Kells

Чтобы назначать первичные ключи, приме-няйте ограничение PRIMARY KEY (см. раз-дел «Задание первичного ключа с по-мощью ограничения PRIMARY KEY» в гла-ве 10).

Опытные разработчики баз данных ставятпервичный ключ на место первого столбца(столбцов, если ключ – составной) в табли-це, хотя теоретически порядок следованиястолбцов не имеет, как мы теперь знаем, ни-какого значения. Поэтому имеет смысл все-гда начинать поиск первичного ключа с пер-вых столбцов. Кроме того, если имя какого-то столбца содержит сочетания «id», «code»или «num», это свидетельствует о том, чтоданный столбец может быть ключевым (тоесть столбцом или первичного, или внешне-го ключа; см. следующий раздел).

au_id au_fname au_lname

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

A01 Sarah Buchman

A02 Wendy Heydemark

A03 Hallie Hull

A04 Klee Hull

Рис. 2.8. Здесь первичным ключом является au_id

Первичные ключи

Page 50: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

50 Реляционная модель

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

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

В рассмотренном выше примере вы моглибы совместно применить столбцы au_idи, скажем, au_lname в качестве составногопервичного ключа, но это было бы нару-шением постулата минимальности.

Коммерческие СУБД предоставляют в рас-поряжение разработчика специальный ме-ханизм автоматического построения одно-значного идентификатора строки в видеспециальных атрибутов или таких типовданных, которые автоматически присваи-вают уникальный идентификатор строки,будучи включеннымив состав столбцовпроизвольной таблицы. Идея здесь в том,что, например, счетчик автоматическиувеличивает себя на единицу при появле-нии каждой новой строки (то есть, строгоговоря, в каждой строке есть столбец-счет-чик, значение которого в этой строкеравно ее очереди внесения в таблицу).В Microsoft Access таким специальным ти-пом данных является AutoNumber, в Mic-rosoft SQL Server – тип данных uniqueiden-tifier или свойство IDENTITY, в Oracle –тип данных ROWID, в MySQL – атрибутAUTO_INCREMENT, в PostgreSQL – тип дан-ных serial.

Page 51: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

51

Внешние ключиПоскольку, как мы договорились, разныетаблицы содержат информацию о разныхобъектных типах, у нас должен быть спо-соб осмысленного перемещения от таб-лицы к таблице. Так вот, рассматриваемаямодель предоставляет нам такой меха-низм – внешний ключ (или вторичныйключ – Foreign key), который применяютдля соединения одной произвольной таб-лицы с другой. Любой внешний ключ об-ладает следующими свойствами:

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

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

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

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

может иметь имя, отличное от имениродительского ключа;

значения внешнего ключа в общем слу-чае не являются уникальными в соб-ственной таблице;

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

Внешние ключи

Page 52: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

52 Реляционная модель

содержать некий внешний ключ boss_id,чтобы он:

– отражал информацию о начальни-ках тех сотрудников, которые иден-тифицируются значениями столбцаemployee_id;

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

Разбираясь с понятием внешнего ключа,внимательно рассмотрите рис. 2.9, где на-глядно показано, какую роль играют ма-теринский первичный и дочерний внеш-ний ключи в организации связи междуматеринской и дочерней таблицами.Итак, вы определили некий внешнийключ. Теперь СУБД станет жестко отсле-живать целостность на уровне ссылок(или ссылочную целостность). Напри-мер, вы не смогли бы вставить следую-щую строку в дочернюю таблицу titles,содержащую информацию о названияхкниг, потому что в материнской таблицеpublishers с информацией об издатель-ствах в столбце первичного ключа pub_idнет значения P05:T05 Exchange of Platitudes P05

Разрешается вставить следующую строку,только если поле таблицы titles, по кото-рому построен внешний ключ, может при-нимать значение null:T05 Exchange of Platitudes NULL

Эта строчка полностью корректна:T05 Exchange of Platitudes P04

Чтобы указать произвольный внешний ключ,применяйте ограничение FOREIGN KEY (см.раздел «Задание внешнего ключа с по-мощью ограничения FOREIGN KEY» в гла-ве 10).

Рис. 2.9. В таблице titles столбец pub_idявляется внешним ключом, который ссылается настолбец pub_id, являющийся первичным ключомтаблицы publishers

pub_id pub name

P01 Abatis Publishers

P02 Core Dump Books

P03 Schadenfreude Press

P04 Tenterhooks Press

Первичный ключ

Первичный ключ

publishers

title_id title_name pub_id

T01 1977! P01

T02 200 Years of Ger... P03

T03 Ask Your System... P02

T04 But I Did It Unco... P04

Внешний ключ

titles

Page 53: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

53

SQL дает возможность программисту вы-полнить те действия по сохранению ссы-лочной целостности, которые СУБД пред-принимает тогда, когда пользователь пыта-ется удалить или модифицировать любоеключевое значение, на которое указываютзначения внешних ключей (см. примечанияв разделе «Задание внешнего ключа с по-мощью ограничения FOREIGN KEY» в гла-ве 10).

Решение позволить полям внешнего клю-ча принимать значения null не следует изтеории, а является чисто практическим.Дело в том, что отсутствие некоторых зна-чений во внешних ключах затруднило быподдержание ссылочной целостности. В тоже время значения null во внешних ключахостаются таковыми (то есть значениямиnull) только временно в преддверии приня-тия какого-нибудь решения или/и выясне-ния каких-то фактов (см. раздел «Значениеnull» в главе 3)1.

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

1 Зачастую значение NULL в полях внешнего клю-ча позволяет создавать таблицы, в которыхразличные строки связаны с разными таблица-ми. Например, таблица customers представляетсобой обобщенный список покупателей и со-держит два поля org_id и person_id, указываю-щие на таблицы organization и person соответ-ственно, где находится информация опокупателях-организациях и покупателях – ча-стных лицах. В этом случае в каждой строкетаблицы customers одно из полей org_id илиperson_id всегда принимает значение NULL, а вто-рое – указывает на соответствующую запись вродительской таблице. – Прим. науч. ред.

Внешние ключи

Page 54: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

54 Реляционная модель

СвязиЛюбая связь – это соединение, устанавли-ваемое между общими столбцами двухразных таблиц. Различают следующие ка-тегории связей:

один-к-одному;один-ко-многим;многие-ко-многим.

Один-к-одному

Считается, что между таблицами A и Bсуществует связь «один-к-одному», есликаждая строка таблицы A может иметь неболее одной соответствующей ей строкитаблицы B, и каждая строка таблицы Bможет иметь не более одной соответствую-щей ей строки таблицы A.С теоретической точки зрения нет никакойразницы при хранении данных двух таб-лиц, между которыми существует связь«один-к-одному», в разных двух таблицахили в одной общей таблице. Следователь-но, такая связь вроде бы не нужна. Имен-но поэтому связь «один-к-одному» приме-няют не из теоретических, а из практичес-ких соображений: или/и по соображениямбезопасности, когда надо выделить и за-щитить конфиденциальную информацию,или/и по соображениям быстродействиясистемы, когда надо разбить слишком гро-моздкие монолитные таблицы, или/и длятого, чтобы, например, не вставлять значе-ния null в таблицы, отдельные столбцыкоторых содержат непустые (то есть изве-стные) значения лишь для весьма неболь-шого подмножества.Таким образом, способ установления связи«один-к-одному» заключается в том, чтопервичный ключ некой таблицы объявля-ется ее внешним ключом, указывающимна первичный ключ какой-нибудь другойтаблицы (см. рис. 2.10 и 2.11).

Рис. 2.10. Пример связи «один-к-одному».Каждая строка таблицы titles имеет не болееодной соответствующей ей строки таблицыroyalties, и каждая строка таблицы royaltiesимеет не более одной соответствующей ей строкитаблицы titles. Здесь первичный ключ таблицыroyalties является ее внешним ключом,указывающим на первичный ключ таблицыtitles

title_id advance

T01 10000

T02 1000

T03 15000

T04 20000

royalties

title_id title_name

T01 1977!

T02 200 Years of Ger...

T03 Ask Your System...

T04 But I Did It Unco...

titles

Рис. 2.11. Еще один способ изобразить связь,показанную на рис. 2.10. Связанные столбцысоединены линией, а значок ключа выделяетпервичный ключ

titles

title_id

title_name

royalties

title_id

advance

Page 55: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

55

Один-ко-многим

Считается, что между таблицами A и Bустановлена связь «один-ко-многим», есликаждая строка в таблице A имеет много(то есть ноль, одну или много) соответ-ствующих строк таблицы B, а каждаястрока таблицы B – всего одну соответ-ствующую строку таблицы A. Например,можно представить себе ситуацию, когданекий издатель может печатать разныекниги, но каждая книга может быть выпу-щена только одним издателем.

Таким образом, способ образования свя-зи «один-ко-многим» состоит в том, что-бы первичный ключ таблицы, строки ко-торой могут участвовать в связи толькопо одной, объявить внешним ключомв той таблице, строки которой могут участ-вовать в связи по нескольку штук (см.рис. 2.12 и 2.13).

Многие-ко-многим

Считается, что между таблицами A и Bустановлена связь «многие-ко-многим»,если каждая строка в таблице A можетиметь много (то есть или ни одной, илинесколько) соответствующих строк в таб-лице B, а каждая строка в таблице B мо-жет иметь много (то есть или ни одной,или несколько) соответствующих строк втаблице A.Единственный способ установить связь«многие-ко-многим» между двумя произ-вольными таблицами заключается в том,чтобы создать третью таблицу (называе-мую стыковочной), в которой ее составнойпервичный ключ является комбинациейпервичных ключей обоих таблиц со связью«один-ко-многим». При этом каждый изстолбцов составного ключа по отдельнос-ти является внешним ключом. Этот приемпостроения составного ключа всегда дает

Рис. 2.12. Пример связи «один-ко-многим».Каждой строке в таблице publishers можетсоответствовать несколько строк в таблицеtitles, но каждая строка в таблице titlesимеет только одну соответствующую строку втаблице publishers. Здесь мы видим, чтопервичный ключ таблицы publishers, строкикоторой могут участвовать в связи строго поодной, объявлен внешним ключом в таблицеtitles, строки которой могут участвовать в связипо нескольку штук

pub_id pub name

P01 Abatis Publishers

P02 Core Dump Books

P03 Schadenfreude Press

P04 Tenterhooks Press

publishers

title_id title_name pub_id

T01 1977! P01

T02 200 Years of Ger... P03

T03 Ask Your System... P02

T04 But I Did It Unco... P04

titles

T05 Exchange of Plati... P04

Рис. 2.13. Пример еще одного способа изобразитьсвязь «один-ко-многим», показанную на рис. 2.12.Линия со стрелкой направлена от таблицыpublishers, строки которой могут участвоватьв связи строго по одной, к таблице titles(на нее указывает стрелка), строки которой могутучаствовать в связи по нескольку штук.Пиктограмма ключа по-прежнему выделяетпервичный ключ

publishers

pub_id

pub_name

titles

title_id

title_name

pub_id

Связи

Page 56: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

56 Реляционная модель

для каждой строки стыковочной таблицыкакое-то уникальное значение и разбива-ет первоначальную связь «многие-ко-мно-гим» на две независимые связи «один-ко-многим» (см. рис. 2.14 и 2.15).

Рис. 2.14. Пример практической реализации связи«многие-ко-многим». Стыковочная таблицаtitle_authors разбивает абстрактную связь«многие-ко-многим» между таблицами titlesи authors на две реализуемых на практике связи«один-ко-многим». В результате такогопреобразования каждая строка из таблицы titlesможет иметь много соответствующих строк встыковочной таблице title_authors точно так же,как любая строка таблицы authors может иметьмного соответствующих строк в таблицеtitle_authors. Мы видим, что столбец title_idв таблице title_authors является внешним ключом,указывающим на первичный ключ таблицы titles.Но и столбец au_id в таблице title_authors тожеявляется внешним ключом, который указывает напервичный ключ таблицы authors

title_id au_id

T01 A01

T02 A01

T03 A05

T04 A03

T04 A04

T05 A04

title_id title_name

T01 1977!

T02 200 Years of Ger...

T03 Ask Your System...

T04 But I Did It Unco...

T05 Exchange ofPlati...

titles

au_id au_fname au_lname

A01 Sarah Buchman

A02 Wendy Heydemark

A03 Hallie Hull

A04 Klee Hull

authors

title_authors

Предложения объединения (join), которыеприменяются для выполнения команд SQLна нескольких таблицах сразу, рассматри-ваются в главе 7.

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

Стыковочную таблицу иногда называют ас-социативной таблицей, или таблицей пере-сечений.

Связь «один-ко-многим» еще называютсвязью предок-потомок, или ведущий-ве-домый.

Рис. 2.15. Еще один способ изобразить связь«многие-ко-многим», показанную на рис. 2.14

titles

title_id

title_name

title_authors

title_id

au_id

authors

au_id

au_fname

au_lname

Page 57: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

57

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

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

первая нормальная форма (1НФ);

вторая нормальная форма (2НФ);

третья нормальная форма (3НФ).

При этом каждая последующая нормаль-ная форма сильнее предыдущей в томсмысле, что удовлетворяет критериям пре-дыдущей нормальной формы. Так, любаябаза данных, находящаяся в форме 3НФ,находится и в форме 2НФ (и, значит, вформе 1НФ тоже). При переходе любойбазы данных на очередной уровень норма-лизации число таблиц в ней не уменьша-ется, поскольку нормализация на каждомшаге осуществляется с помощью деком-позиции (дробления) таблиц. Применяе-мая декомпозиция таблиц обладает двумясвойствами:

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

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

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

Могут существовать и более высокиеуровни нормализации (например, 4НФи 5НФ), но рассматриваемая в этой главереляционная модель эти уровни не вклю-чает. Кроме того, типы избыточности, ко-торые устраняются формами 4НФ и 5НФ,встречаются настолько редко, что, если выприведете какую-нибудь практическуюбазу данных к форме 3НФ, она с большойвероятностью будет удовлетворять и кри-териям формы 5НФ, а значит, и критери-ям формы 4НФ. Принимая во вниманиевсе сказанное о 4НФ и 5НФ, мы не будем

Нормализация

Page 58: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

58 Реляционная модель

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

Первая нормальная форма

Считается, что произвольная таблица на-ходится в первой нормальной форме, еслиодновременно выполняются следующиедва условия:

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

Для понимания этого определения необ-ходимо дать еще два:

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

Чтобы решить проблемы, отмеченные нарис. 2.16 и 2.17, следует распределить данные

одной таблицы по двум таблицам, как по-казано на рис. 2.18.

Вторая нормальная форма

Прежде чем привести критерии, определя-ющие понятие второй нормальной фор-мы, напомним одно полезное правило,которое поможет вам понять, что некаятаблица, которую вы привели к 1НФ, ав-томатически является 2НФ. Итак, любаятаблица 1НФ является таблицей 2НФ, есливыполнено хотя бы одно из следующихдвух условий:

первичный ключ этой таблицы не явля-ется составным (то есть состоит из од-ного столбца);

любой столбец этой таблицы входитв состав ее простого или составногопервичного ключа.

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

она находится в первой нормальнойформе;

она не имеет частичных функциональ-ных зависимостей.

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

первичный ключ этой таблицы – со-ставной;

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

Page 59: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

59

title_id title_name authors

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

T01 1977! A01

T04 But I Did It Unconsciously A03, A04

T11 Perhaps It’s aGlandular Problem A03, A04, A06

Рис. 2.16. Данная таблица нарушает критерии первой нормальной формы потому, что столбец authorsсодержит многомерные значения, указывающие сразу на нескольких разных авторов одной и той жекниги (то есть соавторов). Эти значения можно разделить без потери смысла, что и составляетпротиворечие с определением 1НФ

title_id title_name author1 author2 author3

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

T01 1977! A01

T04 But I Did It Unconsciously A03 A04

T11 Perhaps It’s aGlandular Problem A03 A04 A06

Рис. 2.17. Здесь мы пытаемся решить проблему, обозначенную на рис. 2.16. Предлагается простосогласиться с тем, что у всех книг по три автора, и добавить в таблицу еще два столбца, а если авторовокажется два или один – не ставить в соответствующих столбцах вообще ничего (но «ничего» не бывает;это будет null). В этом состоит логическая связь трех столбцов друг с другом. Значит, они являютсяповторяющейся группой, нарушая тем самым критерии 1НФ. Общее правило гласит: единыймногомерный объект не следует представлять в одной таблице в виде нескольких столбцов

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

title_id au_id

T01 A01

T04 A03

T04 A04

T11 A03

T11 A04

T11 A06

title_id title_name

T01 1977!

T04 But I Did It Unco...

T11 Perhaps It's a Gla...

Нормализация

Page 60: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

60 Реляционная модель

Смысл всех приведенных определенийзаключается в том, что любая таблица2НФ является полностью функциональнозависимой. А полная функциональная за-висимость (которая определяется как от-сутствие какой бы то ни было частичнойфункциональной зависимости) означает,что, если любое значение любого ключево-го столбца изменилось, потребуется вно-сить изменения в значения неключевыхстолбцов.

Составной первичный ключ той таблицы,которая представлена на рис. 2.19, вклю-чает в себя столбцы title_id и au_id.А неключевые столбцы – это столбецau_order, который содержит информациюо том, в каком порядке перечислены авто-ры на обложке книги, и столбец au_phoneс указанием телефонных номеров авторов.

Чтобы для произвольной таблицы опре-делить, является она полностью функци-онально зависимой или нет, спроситесебя: «Могу ли я определить значение не-ключевого столбца, если знаю толькочасть значений первичного ключа?» Еслиответом будет «Нет», значит, ваша табли-ца является полностью функциональнозависимой (что хорошо). Если ответомбудет «Да», следовательно, ваша таблицаявляется частично функционально зави-симой (что плохо).

Применим эту теорию на практике. На-пример, займемся таблицей title_authors,представленной на рис. 2.19. Для неключе-вого столбца au_order задайте себе два во-проса:

могу ли я определить au_order, если из-вестно только поле title_id? От-вет: «Нет, потому что у одной книгиможет быть несколько авторов, и, незная всех авторов, нельзя найти нуж-ную запись»;

могу ли я определить au_order, еслизнаю только au_id? Ответ: «Нет, потомучто один автор может написать не-сколько книг, и, не зная о какой книгеидет речь, нельзя определить нужнуюстроку».

Итак, пока все хорошо, столбец au_orderявляется полностью функционально за-висимым и может оставаться в таблице.Эту зависимость принято записывать так:{title_id, au_id} → {au_order} и читать:«title_id и au_id определяют au_order» или«au_order зависит от title_id и au_id». Приэтом выражение, стоящее слева от стрел-ки, называется детерминантом.Теперь для неключевого столбца au_phoneвам надо задать себе два вопроса:

могу ли я определить au_phone, еслизнаю только title_id? Ответ: «Нет, по-тому что у одной книги может бытьболее одного автора»;могу ли я определить au_phone, еслизнаю только au_id? Ответ: «Да! Номертелефона автора от книги не зависит».

Таким образом, мы получили плохой ре-зультат. Столбец au_phone является частич-но функционально зависимым, и для того,чтобы таблица title_authors стала табли-цей 2НФ, его следует переместитьв другую таблицу, возможно, в таблицуauthors или phone_numbers.

Третья нормальная форма

Считается, что произвольная таблица на-ходится в третьей нормальной форме,если одновременно выполнены два следу-ющих условия:

она находится во второй нормальнойформе;она не содержит транзитивных зависи-мостей.

Page 61: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

61

Рис. 2.19. Столбец au_phoneзависит от au_id, а неот title_id. Следовательно,данная таблица содержитчастичную функциональнуюзависимость и не является 2НФ

title_authors

title_id

au_id

au_order

au_phone

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

Из определения третьей нормальной фор-мы следует, что в произвольной таблице,находящейся в 3НФ, столбцы являютсявзаимно независимыми, то есть зависяттолько от столбцов первичного ключа,будь он простым или составным. Ясно, что3НФ – это следующий логический шагпроцесса нормализации после 2НФ.

Снова проверим теорию на практике. Пер-вичный ключ таблицы, показанной нарис. 2.20, – это столбец title_id, а неклю-чевые столбцы – это price, где хранится ин-формация о цене книги, pub_city, где хра-нится информация о городе, в которомиздана книга, и pub_id, где хранится ин-формация об издателе книги.

Проверяя произвольную таблицу на 3НФ,вам следует спросить себя: «Могу я выяс-нить значение неключевого столбца, еслибуду знать только значение какого-нибудьдругого неключевого столбца?» Ответ«Нет» будет означать, что проверяемыйвами неключевой столбец не являетсятранзитивно зависимым, и это хорошо.Если же ответ будет «Да», значит, проверя-емый столбец является транзитивно зави-симым от другого неключевого столбца, иэто плохо. Применим это правилок таблице titles на рис. 2.20. Для неключе-вого столбца price в соответствии с этимправилом вам следует задать себе следу-ющие вопросы:

могу ли я определить pub_id, если знаюprice? Ответ: «Нет»;

Рис. 2.20. Значение в столбцеpub_city определяетсязначением в столбце pub_id,что говорит о частичнойзависимости.Таблица titles не находитсяв третьей нормальной форме

titles

title_id

price

pub_city

pub_id

Нормализация

Page 62: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

62 Реляционная модель

могу ли я определить pub_city, еслизнаю price? Ответ: «Нет».

Для неключевого столбца pub_city вопро-сы будут следующими:

могу ли я определить price, если знаюpub_city? Ответ: «Нет»;могу ли я определить pub_id, если знаюpub_city? Ответ: «Нет, потому что в од-ном городе может находиться много из-дателей».

Для неключевого столбца pub_id вопросывыглядят так:

могу ли я определить price, если знаюpub_id? Ответ: «Нет»;могу ли я определить pub_city, еслизнаю pub_id? Ответ: «Да! Посколькуместо выпуска книги зависит от того,кто является ее издателем».

Плохая новость: столбец pub_city являет-ся транзитивно зависимым от неключево-го столбца pub_id, и для того, чтобы таб-лица titles была таблицей 3НФ, его надопереместить в другую таблицу, возмож-но, в таблицу publishers.Задавая вопросы наподобие тех, что приве-дены выше, недостаточно спросить: «Могули я определить A, если знаю B?» Чтобыосуществить полную проверку на транзи-тивную зависимость, надо еще спросить:«Могу ли я определить B, если знаю A?»

Если у вас нет опыта работы с базами дан-ных, жесткость нормальных форм можетпоказаться вам излишней и породить недо-умение типа: «Почему я не могу, к приме-ру, поместить штат, город и почтовый ин-декс в один столбец, ведь это бы упрости-ло процесс печати наклеек на почтовыеконверты?» Ответ состоит в том, что вы неможете ручаться за то, как вы сами илидругие пользователи захотят в будущемвыбирать рассматриваемую информациюи манипулировать ею. Вот почему 3НФ яв-ляется наилучшей страховкой от такойнеопределенности в будущем. Независимоот вашего отношения к идее нормализациии вышеизложенной теории, мы настоятель-но рекомендуем вам нормализовать своибазы данных. Дело в том, что если этого невыполнить, то рано или поздно вам при-дется разбивать свои таблицы, чтобы сде-лать возможной сортировку, скажем, адре-сов по штату. Заметим, что профессиона-лы иногда денормализуют свои базы с це-лью ускорить выполнение запросов.Однако такие технические приемы в насто-ящем издании не рассматриваются.

Page 63: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

63

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

в разделе «Таблицы, столбцы и строки»уже говорилось, что для пользователялюбая база данных представляется какнабор таблиц, и ничего кроме таблиц.Так вот, books содержит пять таблиц, ко-торые включают информацию об авто-рах книг, названиях книг, которые этиавторы опубликовали, об издателях, вы-пустивших эти книги, и о гонорарах, ко-торые были получены авторами от из-дателей за опубликованные книги. Нарис. 2.21 перечислены все названные таб-лицы и связи между нимис учетом графических условностей, ко-торые мы ввели в этой главе;

authors

au_id

au_fname

au_lname

phone

address

city

state

zip

title_authors

title_id

au_id

au_order

royalty_share

publishers

pub_id

pub_name

city

state

country

titles

title_id

title_name

type

pub_id

pages

price

sales

pubdate

contract

royalties

title_id

advance

royalty_rate

Рис. 2.21. Типовая база данных books

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

чтобы создать (или восстановить) базуданных books в своей СУБД, достаточ-но выполнить SQL-скрипт из прило-жения. Готовый скрипт можно «ска-чать» (как, впрочем, и готовую базу дан-ных) с сайта издательства «ДМК Пресс»www.dmkpress.ru или с сайта поддержкикниги (см. раздел «Несколько общих за-мечаний о книге» во введении);

некоторые из упомянутых в данномразделе идей, в частности об использо-вании типов данных и значения null,рассматриваются в следующей главе;

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

Наша типовая база данных

Page 64: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

64 Реляционная модель

Таблица authors

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

Таблица 2.2. Структура таблицы authors

Имя столбца Описание Тип данных null (Да/Нет) Ключи

au_id Уникальный идентификатор автора CHAR(3) PK

au_fname Имя автора VARCHAR(15)

au_lname Фамилия автора VARCHAR(15)

phone Телефон автора VARCHAR(12) Да

address Адрес автора VARCHAR(20) Да

city Город автора VARCHAR(15) Да

state Штат автора CHАR(2) Да

zip Почтовый индекс автора CHAR(5) Да

au_id au_fname au_lname phone address city state zip

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

A01 Sarah Buchman 718-496-7223 75 west 205 St Bronx NY 10468

A02 Wendy Heydemark 303-986-7020 2922 Baseline Rd Boulder CO 80303

A03 Hallie Hull 415-549-4278 3800 Waldo Ave, #14F San Francisco CA 94123

A04 Klee Hull 415-549-4278 3800 Waldo Ave, #14F San Francisco CA 94123

A05 Christian Kells 212-771-4680 114 Horatino St New York NY 10014

A06 Kellsey 650-836-7128 390 Serra Mall Palo Alto CA 94305

A07 Paddy O’Furniture 941-925-0752 1442 Main St Sarasota FL 34236

Рис. 2.22. Содержимое таблицы authors

Page 65: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

65

Таблица publishers

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

Таблица 2.3. Структура таблицы publishers

Имя столбца Описание Тип данных null (Да/Нет) Ключи

pub_id Уникальный идентификатор издателя CHAR(3) PK

pub_name Наименование издателя VARCHAR(20)

city Город издателя VARCHAR(15)

state Штат/Графство издателя CHAR(2) Да

country Страна издателя VARCHAR(15)

pub_id pub_name city state country

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

P01 Abatis Publishers New York NY USA

P02 Core Dump Books San Francisco CA USA

P03 Schadenfreude Press Hamburg NULL Germany

P04 Tenterhooks Press Berkeley CA USA

Рис. 2.23. Содержимое таблицы publishers

Наша типовая база данных

Page 66: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

66 Реляционная модель

Таблица titles

Таблица titles описывает собственно книги.У каждой книги в этой таблице есть уни-кальный идентификатор, который и явля-ется первичным ключом. Таблица titles со-держит внешний ключ pub_id, указывающийна таблицу publishers для того, чтобы обо-значить издателя книги.

Таблица 2.4. Структура таблицы titles

Имя столбца Описание Тип данных null Ключи(Да/Нет)

title_id Уникальный идентификатор книги CHAR(3) PK

title_name Название книги VARCHAR(40)

type Предмет книги VARCHAR(10) Да

pub_id Идентификатор издателя CHAR(3) FK publishers (pub_id)

pages Количество страниц INTEGER Да

price Цена за 1 экземпляр DECIMAL(5,2) Да

sales Общее количество INTEGER Дапроданных экземпляров

pubdate Дата публикации DATE Да

contract Не совпадает с null, SMALLINT

если автор/авторыподписал/подписали контракт

Рис. 2.24. Содержимое таблицы titles

title_id title_name type pub_id pages price sales pubdate contract

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

T01 1977! history P01 107 21,99 566 2000-08-01 1

T02 200 Years of German Humor history P03 14 19,95 9566 1998-04-01 1

T03 Ask Your System Administrator computer P02 1226 39,95 25667 2000-09-01 1

T04 But I Did It Unconsciously psychology P01 510 12,99 13001 1999-05-31 1

T05 Exchange of Platitudes psychology P01 201 6,95 201440 2001-01-01 1

T06 How About Never? biography P01 473 19,95 11320 2000-07-31 1

T07 I Blame My Mother biography P03 333 23,95 1500200 1999-10-01 1

T08 Just Wait Until After School children P01 86 10 4095 2001-06-01 1

T09 Kiss My Boo-Boo children P01 22 13,95 5000 2002-05-31 1

T10 Not Without My Faberge Egg biography P01 NULL NULL NULL NULL 0

T11 Perhaps It’s a Glandular Problem psychology P01 826 7,99 94123 2000-11-30 1

T12 Spontaneous, Not Annoying biography P01 507 12,99 100001 2000-08-31 1

T13 What Are The Civilian Applications? history P03 802 29,99 10467 1999-05-31 1

Структура таблицы titles приведенав табл. 2.4, а ее содержимое показано нарис. 2.24.

Page 67: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

67

Таблица title_authors

Объектные типы «Авторы», которым соот-ветствует таблица authors, и «Книги», кото-рым соответствует таблица titles, имеютсвязь «многие-ко-многим», потому что, содной стороны, один автор способен напи-сать много книг, а с другой – одна книгаможет быть написана несколькими автора-ми. Чтобы реализовать эту связь, необхо-дима, как мы теперь знаем, стыковочнаятаблица. Таблица title_authors являетсястыковочной таблицей, соединяющей таб-лицы titles и authors (см. раздел «Связи» вэтой главе). В стыковочной таблице столб-цы title_id и au_id вместе формируют со-ставной первичный ключ, а по отдельностикаждый из них является внешним ключом,указывающим соответственно на таблицыtitles и authors. Неключевые столбцы со-держат столбец au_order, показывающийочередность имени автора на обложке кни-ги (для книг с единственным автором зна-чение этого столбца всегда равно 1), и стол-бец royalty_share, показывающий долю ав-тора в общем гонораре за книгу (для книг сединственным автором значение этогостолбца всегда равно 1). Табл. 2.5 отражаетструктуру таблицы title_authors, а рис.2.25 показывает ее содержание.

Таблица 2.5. Структура таблицы title_authors

Имя столбца Описание Тип данных null Ключи(Да/Нет)

title_id Уникальный идентификатор книги CHAR(3) PK, FK titles (title_id)

au_id Идентификатор автора CHAR(3) PK, FK authors (au_id)

au_order Очередность следования SMALLINT

имени автора на обложке

royalty_share Доля автора DECIMAL(5,2)

в общем авторском гонораре

title_id au_id au_order royalty_share

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

T01 A01 1 1.00

T02 A01 1 1.00

T03 A05 1 1.00

T04 A03 1 0.60

T04 A04 2 0.40

T05 A04 1 1.00

T06 A02 1 1.00

T07 A02 1 0.50

T07 A04 2 0.50

T08 A06 1 1.00

T09 A06 1 1.00

T10 A02 1 1.00

T11 A03 2 0.30

T11 A04 3 0.30

T11 A06 1 0.40

T12 A02 1 1.00

T13 A01 1 1.00

Рис. 2.25. Содержимое таблицы title_authors

Наша типовая база данных

Page 68: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

68 Реляционная модель

Таблица royalties

В таблице royalties хранятся данныео проценте отчислений от продажи книгив виде авторского гонорара, выплачивае-мого всем авторам (отметим, не каждомуавтору), включая авансовый платеж, вып-лачиваемый всем авторам (подчеркнем, некаждому автору) произвольной книги.Первичным ключом таблицы royalties яв-ляется столбец title_id. Таблица royaltiesимеет связь «один-к-одному»с таблицей titles, поэтому первичныйключ таблицы royalties является одно-временно и ее внешним ключом, которыйуказывает на первичный ключ таблицыtitles.Табл. 2.6 отображает структуру таблицыroyalties, а рис. 2.26 – ее содержание.

Таблица 2.6. Структура таблицы royalties

Имя столбца Описание Тип данных null (Да/Нет) Ключи

title_id Уникальный идентификатор книги CHAR(3) PK,FK titles(title_id)

advance Предоплата автору (авторам) DECIMAL(9,2) Да

royalty_rate Доля общего доходас продаж книги, выплачиваемаяв виде авторского гонорара DECIMAL(5,2)

title_id Advance royalty_rate

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

T01 10000 0,05

T02 1000 0,06

T03 15000 0,07

T04 20000 0,08

T05 100000 0,09

T06 20000 0,08

T07 1000000 0,11

T08 0 0,04

T09 0 0,05

T10 NULL NULL

T11 100000 0,07

T12 50000 0,09

T13 20000 0,06

Рис. 2.26. Содержимое таблицы royalties

Page 69: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

Основы SQL

Вы, наверное, заметили, что в предыдущейглаве мы почти не упоминали об SQL. Иэтому есть причина, которую символичес-ки можно объяснить так:SQL ≠ Реляционная модель.Дело в том, что язык SQL основан на ре-ляционной модели, но не является пря-молинейной попыткой ее внедрения. На-пример, одно из отклонений от кано-нической реляционной модели состоитв том, что первичные ключи в SQL, в отли-чие от модели, не являются обязательны-ми. Из этого следует, что с точки зренияSQL таблицы, у которых нет ключей, мо-гут иметь строки, являющиеся точной ко-пией друг друга. Значит, какие-то данныетаких таблиц могут стать недоступными.Но подробно разбираться с несоответстви-ями между SQL и реляционной модельюмы не будем, а порекомендуем ознако-миться со статьями И. Ф. Кодда(E. F. Codd), Криса Дейта (Chris Date) илиФабьена Паскаля (Fabian Pascal), опубли-кованными в Internet. Однако подчерк-нем: результатом этих несоответствийявилось то, что именно пользователиСУБД, а не сама СУБД должны строитьреляционные модели. Еще одним резуль-татом является то, что термины модели

33333и SQL, как это видно из табл. 2.1, не полно-стью взаимозаменяемы.

Теперь, когда с оговорками покончено,приступим к изучению SQL. Итак, любаяпрограмма SQL – это некая последова-тельность команд SQL, выполняемых вопределенном порядке. Чтобы написатьтакую программу, следует выучить пра-вила, управляющие синтаксисом SQL.В этой главе мы познакомимся с правила-ми, которые управляют формированиемпроизвольной команды SQL, типами дан-ных и значениями null.

Синтаксис SQLНа рис. 3.1 показан пример команды SQL«в разрезе». Постарайтесь немного от-влечься от семантики этой команды. Делов том, что мы использовали ее только длятого, чтобы объяснить синтаксис SQL.Начнем с определений.Комментарий. Всякий комментарий – этонеобязательный текст, который вы печа-таете на отдельной строке программы,чтобы объяснить эту программу. Ком-ментарий должен начинаться с двух дефи-сов. Когда СУБД обнаруживает их, она иг-норирует то, что стоит за ними, то есть

Page 70: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

70 Основы SQL

сам комментарий. Комментарии занима-ют целую строку.

Команда SQL. Любая команда SQL – этодопустимая комбинация лексем, которуюпредваряет какое-нибудь ключевое слово.Здесь под лексемой понимается основнаянеделимая частица языка SQL в том зна-чении, что грамматически никакую лексе-му нельзя разделить на более мелкие со-ставные элементы. Лексемами являютсяключевые слова, идентификаторы, опера-торы, литералы и другие символы (всеони будут рассмотрены позднее).

Предложения. Любая команда SQL вклю-чает не менее одного предложения. В са-мом общем случае всякое предложениеSQL – это фрагмент команды SQL, кото-рый начинается с какого-нибудь ключево-го слова, является обязательным или не-обязательным и должен быть записанв определенном порядке. В рассматривае-мом примере мы имеем четыре предло-жения, а именно: SELECT, FROM, WHERE, ORDER.

Ключевые слова. Произвольное ключевоеслово, иногда называемое зарезервирован-ным, – это такое слово, которое в языкеSQL имеет определенное значение и при-

Рис. 3.1. Пример команды SQL с комментарием

Комментарий

Команда SQL Предложения

Ключевые слова

Имена

Завершающая точка с запятой

��Retrieve authors from New York

SELECT au_fname, au_lname

FROM authors

WHERE state = 'NY'

ORDER BY au_lname;

менение которого в SQL строго регламен-тировано. Следует иметь в виду, что ис-пользование любого ключевого слова внеконтекста (например, в качестве иден-тификатора) будет считаться ошибкой.В табл. 3.1 перечислены ключевые словаSQL, а в табл. 3.2 – потенциальные словаSQL (которые пока не являются офици-ально зарезервированными, но однаждымогут ими стать).Идентификаторы. Произвольный иденти-фикатор – это такое слово, которое вы(или разработчик базы данных) применя-ете для того, чтобы именовать объектыпроизвольной базы данных, в том числетаблицы, столбцы, псевдоимена (псевдо-нимы) и представления. Идентификаторне может быть ключевым словом, и егодлина не может превышать 128 знаков.Знаком в SQL может быть любой символалфавита, включая символы латинскогоалфавита и латинские идеограммы (одна-ко обратите внимание на советы, приве-денные в конце настоящего раздела).В нашем примере именами, в частности,являются au_fname, au_lname, authors иstate.

Завершающая точка с запятой. Записькаждой команды SQL должна заканчивать-ся точкой с запятой1.

Page 71: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

71

Таблица 3.1. Ключевые слова SQL

ABSOLUTE COMMIT ELSE INSERT Null

ACTION CONNECT END INT ONLY

ADD CONNECTION END-EXEC INTEGER OPEN

ALL CONSTRAINT ESCAPE INTERSECT OPTION

ALLOCATE CONSTRAINTS EXCEPT INTERVAL OR

ALTER CONTINUE EXCEPTION INTO ORDER

AND CONVERT EXEC IS OUTER

ANY CORRESPONDING EXECUTE ISOLATION OUTPUT

ARE COUNT EXISTS JOIN OVERLAPS

AS CREATE EXTERNAL KEY PAD

ASC CROSS EXTRACT LANGUAGE PARTIAL

ASSERTION CURRENT FALSE LAST POSITION

AT CURRENT_DATE FETCH LEADING PRECISION

AUTHORIZATION CURRENT_TIME FIRST LEFT PREPARE

AVG CURRENT_TIMESTAMP FLOAT LEVEL PRESERVE

BEGIN CURRENT_USER FOR LIKE PRIMARY

BETWEEN CURSOR FOREIGN LOCAL PRIOR

BIT DATE FOUND LOWER PRIVILEGES

BIT_LENGTH DAY FROM MATCH PROCEDURE

BOTH DEALLOCATE FULL MAX PUBLIC

BY DEC GET MIN READ

CASCADE DECIMAL GLOBAL MINUTE REAL

CASCADED DECLARE GO MODULE REFERENCES

CASE DEFAULT GOT0 MONTH RELATIVE

CAST DEFERRABLE GRANT NAMES RESTRICT

CATALOG DEFERRED GROUP NATIONAL REVOKE

CHAR DELETE HAVING NATURAL RIGHT

CHARACTER DESC HOUR NCHAR ROLLBACK

CHAR_LENGTH DESCRIBE IDENTITY NEXT ROWS

CHARACTER_LENGTH DESCRIPTOR IMMEDIATE NO SCHEMA

CHECK DIAGNOSTICS IN NOT SCROLL

CLOSE DISCONNECT INDICATOR NULL SECOND

COALESCE DISTINCT INITIALLY NULLIF SECTION

COLLATE DOMAIN INNER NUMERIC SELECT

COLLATION DOUBLE INPUT OCTET_LENGTH SESSION

COLUMN DROP INSENSITIVE OF SESSION_USER

SET SUM TRAILING UPPER WHENEVER

SIZE SYSTEM_USER TRANSACTION USAGE WHERE

SMALLINT TABLE TRANSLATE USER WITH

Синтаксис SQL

Page 72: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

72 Основы SQL

Таблица 3.2. Потенциальные ключевые слова SQL

AFTER EQUALS OLD RETURN TEST

ALIAS GENERAL OPERATION RETURNS THERE

ASYNC IF OPERATORS ROLE TRIGGER

BEFORE IGNORE OTHERS ROUTINE TYPE

BOOLEAN LEAVE PARAMETERS ROW UNDER

BREADTH LESS PENDANT SAVEPOINT VARIABLE

COMPLETION LIMIT PREORDER SEARCH VIRTUAL

CALL LOOP PRIVATE SENSITIVE VISIBLE

CYCLE MODIFY PROTECTED SEQUENCE WAIT

DATA NEW RECURSIVE SIGNAL WHILE

DEPTH NONE REF SIMILAR WITHOUT

DICTIONARY OBJECT REFERENCING SQLEXCEPTION

EACH OFF REPLACE SQLWARNING

ELSEIF OID RESIGNAL STRUCTURE

Таблица 3.1. Ключевые слова SQL (окончание)

SOME TEMPORARY TRANSLATION USING WORK

SPACE THEN TRIM VALUE WRITE

SQL TIME TRUE VALUES YEAR

SQLCODE TIMESTAMP UNION VARCHAR ZONE

SQLERROR TIMEZONE_HOUR UNIQUE VARYING

SQLSTATE TIMEZONE_MINUTE UNKNOWN VIEW

SUBSTRING TO UPDATE WHEN

Page 73: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

73

SQL – это язык со свободным форматомпредложений. Любая его команда может:

быть напечатана как в верхнем, так и внижнем регистре (например, ключе-вые слова SELECT и select считаютсяидентичными);

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

быть напечатана на одной строке с лю-быми другими командами;

начинаться на любой позиции гори-зонтальной разметки экрана/листа.

Однако вам следует придерживаться опре-деленного стиля написания команд SQL (см.рис. 3.2), например использовать верхнийрегистр для ключевых слов и нижний ре-гистр для идентификаторов. Кроме того,можно начинать каждое предложение с но-вой строки с соответствующим отступом(см. подраздел «Дополнительные соглаше-ния» в этой главе). Вопрос стиля нельзя иг-норировать, потому что его отсутствие при-водит к следующим ошибкам:

неправильная орфография при напи-сании какого-нибудь идентификатораили команды;

отсутствие завершающей точки с за-пятой;

неправильный порядок расположенияпредложений в теле команды;

1 Некоторые современные СУБД не требуют ста-вить точку с запятой в конце команды, хотя и незапрещают это (например, MS SQL Server). –Прим. науч. ред.

отсутствие кавычек у строковых кон-стант (литералов) и констант даты и вре-мени;

наличие кавычек у цифровых кон-стант;

неправильное совместное применениеимен таблиц и имен столбцов (напри-мер, для нашей типовой учебной базытакой ошибкой было бы использова-ние SELECT royalty_share FROM authors;вместо SELECT royalty_share FROM ti-tle_authors;.

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

Поскольку употреблять ключевые словав качестве идентификаторов запрещено, вывполне можете встраивать любые ключевыеслова внутрь идентификаторов как состав-ную часть слова. Например: group и maxнельзя использовать как идентификаторы(они являются зарезервированными слова-ми – см. табл. 3.1), а идентификаторыgroups и max_price вполне допустимы.

Синтаксис SQL

Рис. 3.2. Правил форматирования команд SQLнемного. Команда, приведенная на этом рисунке,эквивалентна представленной на рис. 3.1

select au_fname

, AU_LNAME

FROM

аuthors WhErE state

= 'NY' order

bY

AU_lname

;

Page 74: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

74 Основы SQL

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

name’. Тем не менее подчеркнем еще раз:лучше применять не пробелы, а знак под-черкивания (last_name) или идентифика-тор, набранный заглавными и прописны-ми буквами (LastName).

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

Коммерческие СУБД налагают собственныеограничения на длину идентификатора и наалфавит. Поэтому имеет смысл проанали-зировать документацию по вашей СУБД,используя ключи поиска «идентификато-ры» и «имена».

Более того, различные СУБД имеют своисобственные дополнительные ключевыеслова, которые, очевидно, не могут бытьидентификаторами в «родной» СУБД (новполне могут быть идентификаторами поканонам SQL и, возможно, в других СУБД).Вот почему неплохо было бы организоватьпоиск в документации по вашей СУБДс использованием ключей поиска «ключе-вые слова» и «зарезервированные слова».

Имейте в виду, что Microsoft SQL Server,Oracle, MySQL и PostgreSQL разрешаютвстроенные сопроводительные и много-строчные комментарии, которые необходи-мо помещать между символами /* и */.Организуйте поиск в документации по клю-чам «комментарии». Имейте в виду, чтокомментарии MySQL могут начинаться сознака #.

Таблица 3.3. Типы выражений

Выражение Пример

Case (Выбор) CASE WHEN n <> 0 THEN x/n ELSE 0 END

Cast (Преобразование типов) CAST(pubdate AS CHARACTER)

Datatime (Дата или время) start_time + ‘01:30’

Interval (Интервал между датамиили между отметками времени суток) INTERVAL '7' DAY*2

Numeric (Числовой) (sales*price)/12

String (Строковый) 'Dear ' || au_fname || ','

Page 75: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

75

Типы данныхВ разделе «Таблицы, столбцы и строки»главы 2 уже говорилось о том, что доменлюбого столбца накладывает определен-ные ограничения на то, какие значенияразрешено хранить в этом столбце. В язы-ке SQL для определения домена служитпонятие типа данных.Итак, тип данных имеет следующие ха-рактеристики:

любой столбец в какой угодно таблицепроизвольной базы данных может со-держать данные какого-нибудь, но толь-ко одного типа;любой тип данных из тех, что применя-ются в любой таблице произвольнойбазы, должен относиться к одной из ка-тегорий, перечисленных в табл. 3.4;тип данных налагает ограничения на тезначения, которые разрешается хранитьв любом столбце, и определяет те опе-рации, которые с этими значениямиможно выполнять. Например, столбец,для которого определен тип данныхinteger (целые числа), может содер-жать в качестве своего значения любоецелое число, удовлетворяющее огра-ничениям конкретной СУБД, и дляэтого значения СУБД будет поддержи-вать обычные арифметические опера-ции сложения, вычитания, умноженияи деления (а также другие операции изтех, что определены над полем целыхчисел). Но в этот столбец нельзя запи-сать нечисловое значение вроде ‘shaden-freude’, и никакая СУБД не будет дляэтого столбца поддерживать символь-ные операции наподобие преобразова-ния символов в верхний регистр (ка-питализация);

Таблица 3.4. Категории типов данных

Тип данных Хранит данные

Character string Строки символов

Bit string Строки битов

Exact numeric Целые и действительныечисла с плавающейдесятичной точкой

Approximate numeric Числа с плавающей точкой

Datetime Значения даты и времени

Interval Интервалы даты и времени

Типы данных

Page 76: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

76 Основы SQL

тип данных любого столбца произволь-ной базы определяет порядок сорти-ровки этого столбца. Так, целые числа1, 2 и 10, записанные в некий столбецс типом данных integer, будут отсорти-рованы в арифметическом порядке: 1, 2,10. Те же целые числа, но записанные вкакой-нибудь другой столбец как стро-ки символов '1', '2' и '10', будут отсор-тированы по лексикографическомупринципу: '1', '10' и '2'. Имейте в виду:лексикографическое упорядочивание сна-чала исследует первые знаки упорядочи-ваемых значений, а потом разбиваетэти значения на упорядоченные группы,соответствующие первым знакам. Еслисреди этих групп есть такие, которыесостоят из более чем одного элемента,выполняется сортировка уже в этихгруппах, но по второму знаку. Так про-должается до тех пор, пока во всех груп-пах окажется или по одному члену, иливсе члены будут равны. Таким образом,при лексикографическом упорядочива-нии '10' стоит перед '2', поскольку пер-вый символ числа 10 идет раньше, чемпервый символ числа 2;значения литералов хранятся в столб-цах в соответствии с типами данныхэтих столбцов. При этом считается,что литерал – это буквальная константа(ее еще называют константой в явномпредставлении), выражающая строгосебя, а не какой-либо результат произ-вольного выражения (каковым можетбыть, например, арифметическая фор-мула). В качестве примеров литераловможно привести 40 и 12.34 – числовые,'40' и 'ennui' – символьные, DATE '2002-05-10' и TIME '09:45:00' – литералы датыи времени (datetime).

Чтобы определить или изменить тип дан-ных любого столбца, применяйте командыCREATE TABLE и ALTER TABLE. Подробнееоб этом см. в главе 10.

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

Поставщики коммерческих СУБД решаютмногие вопросы практического примене-ния типизации данных. И можно утверж-дать, что типы данных канонической СУБДSQL не всегда совпадают с теми типамиданных, которые практикует конкретнаяСУБД. Причем совпадение не гарантирова-но даже в том случае, когда тип данныхязыка SQL и тип данных некой СУБД на-зываются одинаково. Имея в виду этотфакт, в следующих разделах, посвящен-ных типизации данных, мы будем даватьсоветы, разъясняющие эквивалентностьили похожесть типов данных языка SQL иконкретных СУБД. Необходимо еще отме-тить, что СУБД оперируют большим коли-чеством типов данных, чем язык. Это сде-лано для того, чтобы хранить некоторыеспециальные значения, например булевы(Boolean) или денежные (monetary). Еслиу вас возникла потребность разобратьсясо стандартными типами данных и с темитипами данных, которые попадают в рас-ширение стандартных типов данных, сле-дует проанализировать документацию повашей СУБД с использованием ключа по-иска «типы данных».

Подробнее о порядке сортировки читайтев разделе «Сортировка строк с помощьюпредложения ORDER BY» главы 4.

Page 77: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

77

Строковые типы данныхСтроковые, или текстовые, типы данных(Character string) используются для пред-ставления текста. Любая строка символов,или просто строка, отличается следую-щими характеристиками:

является упорядоченной последователь-ностью некоторого количества (возмож-но, нулевого) знаков;

Таблица 3.5. Строковые типы данных

Тип Описание

CHARACTER Строка фиксированной длины. Представляет собой фиксированноечисло символов. Любая строка, хранимая в столбце, определенномкак CHARACTER(длина), может состоять из любого числа символов,не превышающего того числа, которое мы обозначили здесь какдлина и которое должно быть целым, не меньше 1. Максимальнодопустимая длина строки определяется конкретной СУБД. Когдавы записываете в столбец, определенный как CHARACTER(длина),какую-нибудь строку, фактическая длина которой меньше той,которая определена для данного столбца, СУБД автоматическидополняет фактическую длину до полной пробелами (например,строка ‘Jack’ записалась бы в столбец CHARACTER (6) как строка‘Jack ’). Имейте в виду, что CHARACTER и CHAR – синонимы

CHARACTER VARYING Строка переменной длины. Представляет собой любое числосимволов, не превышающее некоего фиксированного числа.Любая строка, хранимая в столбце, определенном какCHARACTER VARYING(длина), может состоять из любого числа символов,не превышающего того числа, которое мы обозначили здесь какдлина и которое должно быть целым, не меньше 1. Максимальнодопустимая длина строки определяется конкретной СУБД.Когда вы записываете в столбец, определенный какCHARACTER VARYING(длина), какую-нибудь строку, фактическая длинакоторой меньше определенной для данного столбца, СУБД,в отличие от случая с CHARACTER, не дополняет автоматическифактическую длину до полной пробелами, а хранит ее в том виде,в каком она есть (например, строка ‘Jack’ записалась бы в столбецCHARACTER VARYING(6) как строка ‘Jack’). Имейте в виду,что CHARACTER VARYING, CHAR VARYING и CHARVAR – синонимы

NATIONAL CHARACTER Этот тип данных совпадает с типом CHARACTER, только в столбце этоготипа можно хранить лишь стандартизованные многобайтовые илидвухбайтовые знаки (Unicode) (см. врезку в данном разделе).В командах SQL строки в формате NATIONAL CHARACTER следуетзаписывать так, как строки в формате CHARACTER, но перед открыва-ющей кавычкой надо ставить знак N, например: N‘a*b’. Имейтев виду, что NATIONAL CHARACTER, NATIONAL CHAR и NCHAR – синонимы

NATIONAL CHARACTER VARYING Этот тип данных совпадает с типом CHARACTER VARYING, тольков столбце этого типа можно хранить лишь стандартизованныемногобайтовые или двухбайтовые знаки (Unicode) (см. врезкув данном разделе и пояснение по типу данных NATIONAL CHARACTER).Имейте в виду, что NATIONAL CHARACTER VARYING, NATIONAL CHAR VARYINGи NCHAR VARYING – синонимы

ее длина может быть как фиксирован-ной, так и переменной;учитывает регистр (например, при сор-тировке 'A' стоит перед 'a');в командах SQL любой строковый лите-рал должен быть заключен в одинар-ные кавычки;должна относиться к одному из типовданных, перечисленных в табл. 3.5.

Строковые типы данных

Page 78: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

78 Основы SQL

Чтобы в произвольную строку вставитьодинарную кавычку, введите подряд двасимвола одинарной кавычки. Например,если вы напечатаете it’’s, получитсястрока it’s. Двойная кавычка является от-дельным знаком и специального обраще-ния, как одинарная кавычка, не требует.

Фактическая длина произвольной строкиизмеряется количеством знаков в этой стро-ке и равна целому числу в интервале от 0 дочисла, подставленного вместо слова, кото-рое мы обозначили как длина при опреде-лении типа столбца. Строка, которая вооб-ще не содержит символов, то есть две оди-нарных кавычки (‘’) без пробела междуними, называется пустой, или строкой нуле-вой длины. Такая строка относится к типуданных VARCHAR нулевой длины.

Коммерческие СУБД обрабатывают строкификсированной длины быстрее, чем стро-ки переменной длины.

Строковыми типами данных в рассматри-ваемых коммерческих СУБД являются:

Microsoft Access – text, memo;

Microsoft SQL Server – varchar, text,nchar, nvarchar, ntext;

Oracle – char, varchar, varchar2,nchar, nvarchar, nvarchar2;

MySQL – char, varchar, nchar,

nvarchar, text, tinytext, medium-

text, longtext;

PostgreSQL – char, varchar, text.

Имейте в виду, что СУБД Oracle восприни-мает любую пустую строку как значение null(см. раздел «Значение null» в этой главе).

Двухбайтовая система кодировкисимволов Unicode

Любой компьютер хранит знаки (бук-вы, цифры, знаки пунктуации, симво-лы, в том числе управляющие) в видечисловых значений. Говоря формаль-ным языком, любая система кодиро-вания – это взаимнооднозначное ото-бражение множества знаков на некоеподмножество натуральных чисел. Приэтом существенно, что мировые языкии операционные системы имеют раз-ные национальные системы кодирова-ния. Например, стандартные американ-ские строковые константы применяютсистему кодирования ASCII, присваива-ют значения 256 = 28 различным знакам.Это немного. Это даже мало, потомучто такого количества знаков едва хва-тает на весь латинский алфавит.

Так вот, Unicode – это единое множе-ство чисел (16-разрядных двоичныхили двухбайтовых), которое представ-ляет знаки почти всех мировых языков.Unicode может закодировать 65 536 = 216

знаков. Консорциум Unicode разрабо-тал одноименный стандарт и следит заего применением. Действующие систе-мы кодирования на основе Unicodeпредставлены как в бумажном вариан-те, так и в Internet. Подробную инфор-мацию об этом стандарте можно найтина сайте www.unicode.org.

Page 79: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

79

Битовые типы данныхБитовые типы данных (Bit String) служатдля того, чтобы представлять двоичныечисла. Любая строка битов отличается сле-дующими характеристиками:

является упорядоченной последователь-ностью, состоящей из какого-то числа,возможно из 0, битов;каждый бит имеет значение или 0, или 1;ее обычно применяют для хранения такназываемых больших бинарных объектов(Binary Large Object – BLOB), в част-ности файлов приложений, оцифрован-ных звуков и изображений;в командах SQL любую строку битовследует заключать в одинарные кавычки;относится к одному из типов, перечис-ленных в табл. 3.6.

Ни одна СУБД не пытается транслироватьстроки б итов, а оставляет их для при-ложений.

Опытному программисту может пригодить-ся то, что кроме двоичной (основание сис-темы счисления равно 2) можно применятьи шестнадцатеричную форму (основаниесистемы счисления равно 16) или гексо-форму. Шестнадцатеричная система счис-ления применяет в качестве цифр десяте-ричные цифры от 0 до 9 и еще латинскиебуквы от A до F включительно (регистр неимеет значения). Один гексознак эквивален-тен 4 битам. В командах SQL строки битовв шестнадцатеричной форме должны иметьлатинскую букву X перед первой кавычкой.Например, бинарная строка B'01001011'соответствует гексостроке X'4B'.

Таблица 3.6. Битовые типы данных

Тип Описание

BIT Строка фиксированной длины.Представляет собой фиксирован-ное число битов. Любая строка,хранимая в любом столбце, опре-деленном как BIT(длина), можетсостоять из любого числа битов,не превышающего того числа, ко-торое мы обозначили здесь какдлина и которое должно быть це-лым, не меньше 1. Максимальнодопустимая длина строки опреде-ляется конкретной СУБД. Когдавы записываете в столбец, опре-деленный как BIT(длина), какую-нибудь строку, фактическая длинакоторой меньше определенной дляданного столбца, СУБД, в отличиеот того, что имело место для типаданных CHARACTER, скорее всего,выдаст сообщение об ошибке.В командах SQL строки битов за-писываются так же, как строкиCHARACTER, но в строке BIT передпервой кавычкой должна стоятьлатинская буква «B».Например, B'01001011' – это строкатипа BIT(8)

BIT VARYING Строка переменной длины.Представляет собой любоечисло символов, не превышаю-щее некоего фиксированного чис-ла. Любая строка, хранимаяв любом столбце, определенномкак BIT VARYING(длина), может со-стоять из любого числа символов,не превышающего того числа,которое мы обозначили здесь какдлина и которое должно быть це-лым, не меньше 1. Максимальнодопустимая длина строки опреде-ляется конкретной СУБД. Когда вызаписываете в столбец, определен-ный как BIT VARYING(длина), какую-нибудь строку, фактическая длинакоторой меньше определенной дляданного столбца, СУБД, аналогич-но тому, как это происходит длятипа CHARACTER VARYING, хранит этустроку, как она есть, не дополняяее пробелами. Например, строкаB‘0101’ записалась бы в любойстолбец BIT VARYING(8) как строкаB‘0101’

Битовые типы данных

Page 80: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

80 Основы SQL

Битовыми типами данных в рассматривае-мых коммерческих СУБД являются:

Microsoft Access – yes, no, binary,OLE object;

Microsoft SQL Server – binary, varbi-nary, image;

Oracle – raw, long raw, blob, bfile;

MySQL – blob, tinyblob, medium-blob, longblob;

PostgreSQL – bit, varbit.

Точные числовыетипы данных

Точные числовые типы данных (Exactnumeric) применяются для представленияточных числовых значений. Произволь-ное точное числовое значение имеет сле-дующие характеристики:

может быть положительным, отрица-тельным и нулем;

является или целым, или рациональ-ным числом, причем:

– целым числом называется такое ра-циональное число, которое не со-держит дробной составляющей (нетзнаков после десятичной точки);

– рациональным числом называетсяконечная десятичная дробь (естьзнаки после десятичной точки);

имеет произвольные, но фиксирован-ные точность и масштаб, где:

– точностью называется число знача-щих цифр в записи числа (то естьобщее количество цифр в десятич-ной записи числа без учета десятич-ной точки);

– масштабом называется число цифрсправа от десятичной точки (очевид-но, что для получения целого числанеобходимо и достаточно устано-вить масштаб равным 0, а также то,что масштаб не может быть большеточности);

относится к одному из типов, перечис-ленных в табл. 3.7 (отдельные примерыприведены в примечаниях этого разде-ла).

Page 81: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

81

Таблица 3.7. Точные числовые типы данных

Тип Описание

NUMERIC Представляет произвольное рацио-нальное число, хранимое в столбце,который определяется какNUMERIC(точность [ ,масштаб]). Нату-ральное число (исключая 0), кото-рое надо подставить как точность,ограничено сверху только требова-ниями конкретной СУБД. Натураль-ное число (включая 0), котороенадо подставить как масштаб, неможет превосходить точности. Есливы указываете этот тип данных,опустите масштаб, система поумолчанию назначит его равным 0,что сделает соответствующие числацелыми (Integer)

DECIMAL Аналогичен типу NUMERIC, и некото-рые СУБД определяют эти типы какэквивалентные. Разница, в зависи-мости от конкретной СУБД, можетсостоять в том, что СУБД можетвыбрать большее значение точнос-ти, чем указал пользователь в вы-ражении DECIMAL(точность [ ,масш-таб]). Значит, задавая точность, вызадаете только нижнюю границудля ее фактического значения.Имейте в виду, что DECIMAL и DEC –синонимы

INTEGER Представляет произвольное целоечисло. Минимально допустимоеи максимально допустимое значе-ния для столбца, чей тип данныхопределен как INTEGER, зависяттолько от тех ограничений, которыеналагает конкретная СУБД. При за-дании типа данных столбца какINTEGER не требуется вводить ника-ких аргументов. Имейте в виду,что INTEGER и INT– синонимы

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

Таблица 3.8. Варианты точности и масштабадля рационального числа с фиксированнойдесятичной точкой 123.89

Спецификация Хранитсястолбца

NUMERIC(5) 124

NUMERIC(5,0) 124

NUMERIC(5,1) 123.9

NUMERIC(5,2) 123.89

NUMERIC(4,0) 124

NUMERIC(4,1) 123.9

NUMERIC(4,2) Выходит за пределы точности

NUMERIC(2,0) Выходит за пределы точности

Точные числовые типы данных

В табл. 3.8 показано, как хранится произ-вольное число 123.89 в столбцах с типомданных NUMERIC и с различными величи-нами точности и масштаба.

Не заключайте числовые значения в ка-вычки.

Если числа не участвуют в арифметическихвычислениях, их следует хранить как стро-ки. Например, телефонные номера и почто-вые индексы надо хранить как строки сим-волов. Дело в том, что так можно предот-вратить безвозвратные потери данных.Например, если бы вы хранили почтовыйиндекс '02116' не как строку, а как чис-ло, то потеряли бы первый ноль.

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

Page 82: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

82 Основы SQL

Точными числовыми типами данных врассматриваемых коммерческих СУБД яв-ляются:

Microsoft Access – decimal, integer,byte, long integer;

Microsoft SQL Server – numeric, de-cimal, integer, smallint, bigint,tinyint;

Oracle – numeric, decimal, integer,smallint, number;

MySQL – numeric, decimal, integer,smallint, bigint, mediumint, tiny-int;

PostgreSQL – numeric, decimal, inte-ger, smallint, bigint.

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

может представлять собой положитель-ное или отрицательное число либо нуль;

представляет собой рациональное при-ближение некого действительного чис-ла с плавающей точкой;

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

выражено в экспоненциальном пред-ставлении, то есть:

– равно некому рациональному чис-лу, умноженному на 10 в какой-тоцелой степени;

– записано с помощью символа экспо-ненты E в форме αEβ, где:

– рациональное число α, называемоемантиссой, выражает значащие циф-ры в десятичной записи числа;

– целое число β, называемое порядком,выражает степень десятки;

– и мантисса, и порядок могут иметьлюбой знак, например 2.5E2 == 2.5×102 = 250 (мантисса равна 2.5, апорядок – 2), но –2.5E-2 = –2.5××10–2 = –0.025;

Page 83: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

83

имеет фиксированную точность, но неимеет никакого масштаба как таково-го, где:

– учтено, что знак и величина экспо-ненты автоматически определяютмасштаб;

– точность – это число бинарных би-тов, которое используется для того,чтобы хранить мантиссу;

– для преобразования десятеричнойточности в бинарную нужно умно-жить десятеричную точность на3.32193;

– для преобразования бинарной точ-ности в десятеричную следует умно-жить бинарную точность на 0.30103(24 бита дают 7 знаков точности,а 53 бита – 15 знаков точности);

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

Не заключайте числовые значения в ка-вычки.

Действительными числовыми типами дан-ных в рассматриваемых коммерческих СУБДявляются:

Microsoft Access – single, double;

Microsoft SQL Server – float, real;

Oracle – float, real, double

precision, number;

MySQL – float, real, double preci-sion;

PostgreSQL – real, double precision.

Таблица 3.9. Действительные числовыетипы данных

Тип Описание

FLOAT Представляет собой произ-вольное рациональное при-ближение действительногочисла с плавающей точкой.Любое число с плавающейточкой хранится в столбце,который следует указыватькак FLOAT(precision). Счита-ется, что вместо precisionнадо подставить значениеточности, но выраженное нев количестве значащих деся-тичных цифр, а в количествебитов. Точность не должнабыть меньше 1. Максималь-но допустимая величина точ-ности определяется теми ог-раничениями, которыенакладывает конкретнаяСУБД

REAL Этот тип данных совпадаетпо своему определениюс типом FLOAT, только здесьточность вводить не надо,так как ее автоматическиопределяет сама СУБД, тоесть при указании данноготипа не надо вводить ника-ких аргументов. Числа типаREAL

часто называют числамиодинарной точности с пла-вающей точкой

DOUBLE PRECISION Этот тип данных совпадаетпо своему определениюс типом FLOAT, только здесьточность вводить не надо, таккак ее автоматически опреде-ляет сама СУБД. Вот почемуточность DOUBLE PRECISIONвдвое превышает точностьREAL (числа DOUBLE PRECISIONеще называют числами двой-ной точности с плавающейточкой). Это, в частности, оз-начает, что при указании дан-ного типа не надо вводить ни-каких аргументов

Действительные числовые типы данных

Page 84: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

84 Основы SQL

Календарные типы данныхДля отображения даты и времени сутокследует применять календарные типы дан-ных (Datetime). Значения, относящиесяк одному из календарных типов, отлича-ются следующими характеристиками:

время рассчитывается по отношениюк скоординированному всемирномувремени (UTC; Universal CoordinatedTime), которое раньше называлось вре-менем по Гринвичу (Greenwich MeanTime). Это время соответствует следу-ющему требованию стандарта SQL-92:каждому сеансу SQL по умолчаниюсопоставлен некий сдвиг по времениот UTC, чтобы он учитывался при оп-ределении начала и конца каждого се-анса (например, нормальный сдвиг

Таблица 3.10. Календарные типы данных

Тип данных Описание

DATE Тип данных для отображения даты. Каждое значение данных этого типа хра-нится в столбце, который следует описать как DATE, имеет три целочислен-ных поля (YEAR, MONTH, DAY), формат yyyy-mm-dd и общую длину, равную 10.Например, 2002-06-14 означает 14 июня 2002 года. При указании этого типаданных не требуется вводить никаких аргументов

TIME Тип данных для отображения времени суток. Каждое значение данныхэтого типа хранится в столбце, которому следует присвоить тип TIME, име-ет три целочисленных поля (HOUR, MINUTE, SECOND), формат hh:mm:ss и об-щую длину, равную 8 знаков, включая двоеточия. Например, 22:06:57 оз-начает 22 часа, 6 минут, 57 секунд. Для этого типа данных не требуетсявводить никаких аргументов, но вы можете ввести значение одного нео-бязательного аргумента TIME(точность), чтобы задать доли секунды. Вмес-то необязательного аргумента точность следует вводить натуральныечисла, имея в виду необходимое число десятичных разрядов в записидробной части секунды. Ясно, что точность не может быть меньше 0.Максимально допустимое значение точности зависит от выбора конкрет-ной СУБД, но минимальное значение среди максимально допустимыхзначений точности по множеству тех СУБД, которые рассматриваютсяв настоящей книге, равно 6. Поля HOUR и MINUTE представляют собой нату-ральные числа, а поле SECOND – рациональное число в виде десятичнойдроби. Формат значений произвольного столбца, чей тип данных опреде-лен как TIME, – это hh:mm:ss.ssss..., где hh обозначает 2 знака поля HOUR,mm – 2 знака поля MINUTE, ss.ssss... – знаки в записи значения поляSECOND (длина значения столбца TIME равна 8 знакам, включая двоеточия и,возможно, еще одно двоеточие и какое-то число знаков в дробной частисекунды). Вот пример значения поля TIME с дробной частью секунды:22:06:57.13333. В табл. 3.11 перечислены области допустимых значенийрассмотренных полей

географической зоны от UTC для Сан-Франциско (Калифорния) равен –8 ча-сов);дата формируется в соответствии с пра-вилами Григорианского календаря (всекоммерческие СУБД, рассматриваемыев настоящем издании, не признают дат,рассчитанных по другим календарям);расчет значений времени основан на24-часовом формате, который иногданазывают военным (применяйте обо-значение 13:00, а не 1:00 PM);смысловые составляющие даты разде-ляются дефисами (-), а смысловые со-ставляющие времени – двоеточием (:);любое из рассматриваемых значенийотносится к одному из типов, приведен-ных в табл. 3.10.

Page 85: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

85

Таблица 3.10. Календарные типы данных (окончание)

Тип данных Описание

TIMESTAMP Представляет собой комбинацию значения данных типа DATE и значе-ния данных типа TIME, разделенных пробелом. Формат данного типатаков: yyyy-mm-dd hh:mm:ss. Общая длина любого значения этого типаравна 19 знаков, например, 2002-06-14 22:06:57 соответствует рас-сматриваемому формату и означает 22 часа, 6 минут, 57 секунд,14 июня, 2002 года. Создавая столбец типа TIMESTAMP, вы можете за-дать один необязательный параметр – TIMESTAMP(точность). Вместонеобязательного аргумента точность следует вводить натуральныечисла, имея в виду необходимое вам число десятичных разрядов в за-писи дробной части секунды. Очевидно, что точность не может бытьменьше 0. Максимально допустимое значение точности зависит от вы-бора конкретной СУБД, но минимальное значение среди максимальнодопустимых значений точности по множеству тех СУБД, которые рас-сматриваются в настоящей книге, равно 6. Тогда формат изменится наyyyy-mm-dd hh:mm:ss.ssss..., длина значения столбца TIMESTAMP равна20 знакам, включая двоеточия и, возможно, еще одно двоеточие и ка-кое-то число знаков в дробной части секунды

TIME WITH TIME ZONE Во всем эквивалентен типу TIME, только его формат включает допол-нительное поле TIME_ZONE_OFFSET, чтобы показывать отклонение отUTC в часах. Это поле особенное. Дело в том, что его формат со-впадает с форматом не поля, а целого интервального типа данных –INTERVAL HOUR TO MINUTE (интервалы длиной, выраженной в часах, с воз-можным добавком, выраженным в минутах) – и может содержатьзначения, указанные в табл. 3.11 (см. следующий раздел). Чтобыввести любое значение в столбец, чей тип данных TIME WITH TIME ZONE,вам для присвоения какого-нибудь значения вашей временной зоне(то есть вашему часовому поясу) надо присоединить к предложениюдля формата типа данных TIME предложение AT TIME ZONEtime_zone_offset. Вот пример задания значения столбца TIME WITH TIMEZONE: 22:06:57 AT TIME ZONE -08:00. Это означает 22 часа, 06 минут,57 секунд местного времени, которому соответствует сдвиг от UTC,равный -8 часов 00 минут. Вы можете модифицировать формат TIMEпредложением AT LOCAL. В этом случае вы сообщите системе, чтоваша временная зона соответствует той, которая установлена длятекущего сеанса как зона по умолчанию. Если предложение AT опу-щено, считается, что для всех значений времени данного столбцаэтого типа данных по умолчанию задано AT LOCAL

TIMESTAMP WITH TIME ZONE Во всем эквивалентен типу TIMESTAMP, только его формат содержит до-полнительное поле TIME_ZONE_OFFSET, чтобы показывать отклонение отUTC в часах. Синтаксис здесь тот же, что и для типа данных TIME WITHTIME ZONE, только теперь вам надо еще включать в значение столбцакакую-нибудь дату, например: 2002-06-14 22:06:57 AT TIME ZONE –08:00

Календарные типы данных

Page 86: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

86 Основы SQL

О том, как определить системное время,рассказывается в разделе «Извлечение зна-чений текущих даты и времени» главы 5.

Можно сравнивать значения даты и време-ни, если в их форматы включены одни и теже поля. О том, как это сделать, рассказы-вается в главе 4 (раздел «Фильтрация строкс помощью предложения WHERE») и вглаве 5 (раздел «Операции с данными датыи времени»).

Поле SECOND может принимать значениядо 61.999... включительно, а не 59.999,что, казалось бы, естественнее. Это сде-лано для того, чтобы в значение какой-нибудь конкретной минуты (хотя эта на-добность возникает редко и только в спе-циальных случаях) можно было вводитьтак называемые секунды перескока, кото-рые, в свою очередь, нужны для того,чтобы синхронизировать земные часыс так называемым звездным временем(существует определенная погрешность,поскольку на оборот Земли вокруг оси тре-буется примерно (а не точно) 24 часа, а наоборот Земли вокруг Солнца – 365 суток).Поля типа DATETIME и их допустимыезначения приведены в табл. 3.11.

Чтобы получить произвольную константу(литерал) даты и времени, напечатайтесначала имя конкретного типа данныхдаты и времени, потом пробел, затем зна-чение нужной вам константы в одинарныхкавычках, например: DATE ‘yyyy-mm-dd’,или TIME ‘hh:mm:ss’, или TIMESTAMP‘yyyy-mm-dd hh:mm:ss’.

SQL-92 не воспринимает даты обратногоисторического отсчета (то есть до новойэры/до рождества Христова, в английскомобозначении – B.C.E/B.C), но ваша СУБДвполне может воспринимать и обрабаты-вать их.

Таблица 3.11. Поля типа DATETIMEи их допустимые значения

Поле Интервал допустимыхзначений

YEAR от 0001 до 9999

MONTH от 01 до 12

DAY от 01 до 31

HOUR от 00 до 23

MINUTE от 00 до 59

SECOND от 00 до 61.999...(см. советы)

TIME_ZONE_OFFSET от -12:59 до +13:00

Page 87: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

87

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

В применении типа данных TIME WITHTIME ZONE нет никакого резона, посколькучасовые пояса сами по себе не имеют ни-какого смысла, если только они не связа-ны с конкретной датой. Дело в том, чтосдвиги часовых поясов от UTC зависят отвремени года, в том числе и от так назы-ваемого сезонного времени. Поэтому, еслинадо учитывать сдвиг от UTC, используйтетип данных TIMESTAMP WITH TIME ZONE.

Календарными типами данных в рассматри-ваемых коммерческих СУБД являются:

Microsoft Access – date/time;

Microsoft SQL Server – datetime,smalldatetime;

Oracle – date, timestamp;

MySQL – date, time, datetime, time-stamp;

PostgreSQL – date, time, timestamp.

Коммерческие СУБД позволяют вводитьданные в следующих форматах:

месяц-день-год;

день-месяц-год;

и некоторых других.

Те же СУБД позволяют применять при вво-де 24-часовой или 12-часовой формат (вре-мя AM/PM). При этом надо иметь в виду, чтоиногда формат ввода может отличаться отформата отображения.

В СУБД Microsoft Access заключайте лите-ралы даты и времени в решетки #, а нев кавычки, и опускайте префикс в видеимени типа данных.

В СУБД Microsoft SQL Server при записилитералов даты и времени опускайте пре-фикс в виде имени типа данных.

Календарные типы данных

Page 88: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

88 Основы SQL

Интервальныетипы данных

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

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

хранит количественную меру промежут-ка времени между двумя значениямидаты или/и времени суток так, что есливы вычтете из одного значения другое,то получите этот самый интервал (на-пример, между 09:00 и 13:00 интервалсоставляет 04:30, то есть 4 часа 30 минут);применяется для того, чтобы организо-вывать приращение какого-нибудь зна-чения даты или/и времени (см. в главе 5раздел «Операции с данными даты ивремени»);его формат содержит те же самые поля,что и тип DATETIME (то есть YEAR, HOUR,SECOND и т.д.), и те же самые разделите-ли, что и типы даты и времени, но чис-ленные значения, которые вводятсяв эти поля, могут предваряться или зна-ком «+» (вперед), или знаком «-» (на-зад), чтобы показать соответствие на-правлений конкретного интервалаи оси времени;

имеет отношение к следующим двумуровням измерения времени:– интервал Year-month (год-месяц) вы-

ражен в годах и полном количествемесяцев;

– интервал Day-time (дневное время)выражен в днях, часах, минутахи секундах;

его квалификатор может состоять изединственного поля или из несколькихполей, причем:

– однополевой квалификатор указы-вается как YEAR, или MONTH, или DAY,или HOUR, или SECOND, чтобы про-извольный однополевой столбец,определенный как INTERVAL HOUR,смог бы содержать интервалы напо-добие «4 часа» или «25 часов»;

– многополевой квалификатор обо-значается в виде start_field TOend_field, чтобы произвольный стол-бец, определяемый как, скажем,INTERVAL DAY TO MINUTE, мог бы содер-жать следующие интервалы: «2 дня,5 часов, 10 минут», где:

– вместо start_field будет проставле-но или поле YEAR, или поле MONTH, илиполе DAY, или поле HOUR, или полеMINUTE;

– вместо end_field будет проставленоили поле YEAR, или поле MONTH, илиполе DAY, или поле HOUR, или полеMINUTE, или поле SECOND, чтобыend_field определяло заведомо не-большую единицу измерения вре-мени по сравнению с start_field;

формат любого однополевого столбцаможет включать значение точности,определяющее длину (то есть количест-во позиций для знаков) единственногополя (например, INTERVAL HOUR(2)),причем:

Page 89: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

89

– если значение точности опущено,система по умолчанию считает егоравным 2;

– если единственное поле – это SECOND,его формат может дополнительновключать еще и значение дробнойточности, записываемое при фор-матировании поля через запятую пос-ле первого значения точности, и оп-ределенное количество десятичныхдолей секунды справа от десятичнойточки (по умолчанию эта дробнаяточность считается равной 6), напри-мер INTERVAL SECOND (5,2);

формат любого многопольного столб-ца может быть точным как для start_-field, так и для end_field (если толькоend_field – это не SECOND, но тогдау end_field может быть дробная точ-ность), например INTERVAL DAY(3) TOMINUTE или INTERVAL MINUTE(2) TO SE-COND(4);

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

Чтобы получить интервальный литерал, на-печатайте INTERVAL, пробел, интервальноезначение в одинарных кавычках, например:INTERVAL '15-3' для интервала, равного15 годам и 3 месяцам в будущем, илиINTERVAL '-22:06:5.5' для интервала22 часа, 6 минут и 5,5 секунды тому назад.

Интервальными типами данных в рассмат-риваемых коммерческих СУБД являются:

Microsoft Access – не поддерживает;

Microsoft SQL Server – не поддерживает;

Oracle – Interval;

MySQL – не поддерживает;

PostgreSQL – Interval.

Таблица 3.12. Интервальные типы данных

Тип Описание

Year-month Эти интервалы содержат или не-кое значение в годах, или в меся-цах, или в годах и месяцах. Допус-тимыми типами столбцов длятаких интервалов являются:INTERVAL YEAR,INTERVAL YEAR(точность),INTERVAL MONTH,INTERVAL MONTH(точность),INTERVAL YEAR TO MONTH,INTERVAL YEAR(точность) TO MONTH

Day-time Эти интервалы могут содержатьзначения, выраженные в некойкомбинации дней, часов, минутисекунд. Некоторые из допустимыхтипов столбцов для такихинтервалов:INTERVAL MINUTE, INTERVALDAY(точность),INTERVAL DAY TO HOUR,INTERVAL DAY(точность) TO SECOND,INTERVAL MINUTE(точность) TO SECOND(дробная точность)

Интервальные типы данных

Page 90: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

90 Основы SQL

Значение nullЧтобы как-то отобразить отсутствующиеили неизвестные данные, вы можете вос-пользоваться значением null. Оно имеетследующие характеристики:

в командах SQL ключевое слово NULLпредставляет значение null;

значение null применяют вместо такогозначения, которое неизвестно, никогдане будет известно или будет известнопозже либо вообще неприемлемо покакой-то причине;

значение null – это не ноль, не пустаястрока ('') и не строка пробелов (в от-ношении Oracle утверждение о пустыхстроках неверно; см. примечание с по-меткой DBMS в конце этого раздела).Например, если какое-то значение стол-бца price равно NULL, это свидетельству-ет о том, что цена пока не известна илине определена, а не о том, что некийтовар вообще не имеет никакой ценыили его цена равна нулю;значение null не принадлежит ни к ка-кому типу данных, и поэтому его мож-но записать в любой столбец, за ис-ключением тех, которые определеныкак NOT NULL (см. раздел «Запрет значе-ния null с помощью ограничения NOTNULL» в главе 10);значение null можно при необходимос-ти найти и идентифицировать предло-жением IS NULL (см. раздел «Проверкана значение null c помощью оператораIS NULL» в главе 4);значения null не равны друг другу. По-этому вы не сможете определить, соот-ветствует ли какое-нибудь значение nullдругому значению в вашей базе. Инте-ресно, что именно эта ситуация позволя-

ет построить модель трехзначной логи-ки (см. раздел «Комбинирование усло-вий с помощью операторов AND, OR иNOT» в главе 4);хотя ни одно значение null не равноникакому другому значению null, пред-ложение DISTINCT воспринимает всезначения null в любом произвольно вы-бранном столбце как копии одногои того же значения (см. раздел «Удале-ние повторяющихся строк с помощьюключевого слова DISTINCT» в главе 4);при сортировке произвольного столбца,содержащего NULL, эти значения в зави-симости от СУБД, на которой вы рабо-таете, будут либо больше, либо меньшелюбого из значений этого столбца, неявляющихся значениями null (см. раздел«Сортировка строк с помощью предло-жения ORDER BY» в главе 4);значения null «размножаются» в процес-се вычислений. Дело в том, что резуль-татом многих выражений, операций ифункций, в которых есть хотя бы однозначение null, будет NULL, например:(12*NULL)/4 = NULL (см. главу 5);функции агрегатов данных, напримерSUM(), AVG() и MAX(), игнорируют NULL впроцессе вычислений (см. главу 6);если столбец, по которому осуществля-ется группировка предложением GROUPBY, содержит значения null, все они бу-дут помещены в одну отдельную груп-пу (см. раздел «Группирование строк сиспользованием предложения GROUPBY» в главе 6);значения null влияют на результатыобъединений (см. раздел «Использова-ние объединений» в главе 7);значения null могут создавать пробле-мы в подзапросах (см. раздел «Значе-ния NULL в подзапросах» в главе 8).

Page 91: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

91

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

Дополнительная информация о значенияхnull приводится в разделах «Проверка назначение null с использованием функцииCOALESCE()» и «Сравнение выражений спомощью функции NULLIF()» главы 5.

Любой столбец, в который разрешено вво-дить NULL, называется обнуляемым.

Термин нулевое значение по отношениюк значению null является некорректными может привести к ошибкам. Помните, чтолюбое значение null показывает, главнымобразом, отсутствие значения.

Не употребляйте ключевое слово NULLв кавычках. Иначе ваша СУБД интерпрети-рует его как строку символов 'NULL', а некак значение null.

Значение null можно получить даже изстолбца, который не является обнуляемым(то есть является необнуляемым). Рассмот-рим, например, рис. 3.3. Здесь столбецau_id таблицы authors не является обну-ляемым, но команда SELECT возвращаетNULL в качестве максимального au_id.

Формат представления значений null, воз-никающих как результат какой-либо опера-ции, отличается в различных коммерческихСУБД. Эти значения могут быть отображе-ны как NULL, (NULL), <NULL> или простопробел. Некоторые СУБД предложат вамсамостоятельно выбрать формат отобра-жения значений null.

Последняя версия СУБД Oracle распознаетпустую строку ('') как значение null. В бу-дущем эта трактовка вполне может изме-ниться. Вот почему Oracle рекомендует непринимать пустые строки за значения null.Кроме того, отождествление пустых строк изначений null может создать проблемы припереносе между разными СУБД. Возьмемдля примера нашу типовую базу. Столбецau_fname таблицы authors определен какNOT NULL (то есть необнуляемый). В то жевремя в среде СУБД Oracle имя автораKellsey (значение первичного ключа равноA06) занесено в виде одиночного пробела(' '), но в некоторых других СУБД имя это-го же автора будет уже храниться в видепустой строки (''). Подробнее об учебнойбазе данных читайте в разделе «Наша ти-повая база данных» главы 2.

SELECT MAX(au_id)

FROM authors

WHERE au_lname = ‘XXX’

MAX(au_id)

----------

NULL

Рис. 3.3. Пример получения значения null изстолбца, для которого эти значения недопустимы

Значение null

Page 92: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

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

FROM tables

[JOIN joins]

[WHERE search_condition]

[GROUP BY grouping_columns]

[HAVING search_condition]

[ORDER BY sort_columns]

Поскольку каждое предложение здесь име-ет особый смысл, разберем их по порядку:

предложения SELECT, FROM, ORDER BYи WHERE – в этой главе;предложения GROUP BY и HAVING – в главе 6;предложение JOIN – в главе 7.

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

(см. во введении подраздел «Дополнитель-ные соглашения»).

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

Отметим еще одно: хотя команда SELECTпредставляет собой мощный инструмент,она неопасна. Дело в том, что с ее помощьювы не сможете ни добавлять, ни удалять, ниизменять данные и объекты любой базыданных (представляющие опасность инст-рументы описываются в главе 9).

Page 93: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

93

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

Выбор одного столбца из таблицы

Напечатайте следующее (см. листинг 4.1 ирис. 4.1):

SELECT column

FROM table;

Подразумевается, что:

идентификатор column будет заменен ре-альным именем какого-нибудь столбца;

идентификатор table будет замененименем таблицы, которая содержитстолбец column.

Выбор нескольких произвольныхстолбцов в одной таблице

Следует напечатать (см. листинг 4.2 ирис. 4.2):SELECT columns

FROM table;

Подразумевается, что:

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

Выбор столбцов с помощью предложений SELECT и FROM

Листинг 4.1. Выбрать и перечислить названиягородов, где живут авторы, занесенные в нашутиповую базу. Результаты см. на рис. 4.1

SELECT city

FROM authors;

city

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

Bronx

Boulder

San Francisco

San Francisco

New York

Palo Alto

Sarasota

Рис. 4.1. Результат работы программы,приведенной в листинге 4.1

au_fname au_lname city state

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

Sarah Buchman Bronx NY

Wendy Heydemark Boulder CO

Hallie Hull San Francisco CA

Klee Hull San Francisco CA

Christian Kells New York NY

Kellsey Palo Alto CA

Paddy O’Furniture Sarasota FL

Рис. 4.2. Результат работы программы,приведенной в листинге 4.2

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

SELECT au_fname, au_lname, city, state

FROM authors;

Page 94: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

94 Выбор данных из произвольной таблицы

идентификатор table будет заменен име-нем таблицы, которая содержит столбцыcolumns.

В результате выполнения последней ко-манды выбранные столбцы будут рас-печатаны в том порядке, в каком они пе-речислены в columns, а не в том порядке,в котором они определены в таблицеtable.

Выбор всех столбцов в одной таблице

Напечатайте следующее (см. листинг 4.3 ирис. 4.3):SELECT *

FROM table;

Подразумевается, что вместо table вы под-ставите имя какой-нибудь реальной табли-цы.

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

au_id au_fname au_lname phone address city state zip

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

A01 Sarah Buchman 718-496-7223 75 West 205 St Bronx NY 10468

A02 Wendy Heydemark 303-986-7020 2922 Baseline Rd Boulder CO 80303

A03 Hallie Hull 415-549-4278 3800 Waldo Ave, #14F San Francisco CA 94123

A04 Klee Hull 415-549-4278 3800 Waldo Ave, #14F San Francisco CA 94123

A05 Christian Kells 212-771-4680 114 Horatio St New York NY 10014

A06 Kellsey 650-836-7128 390 Serra Mall Palo Alto CA 94305

A07 Paddy O’Furniture 941-925-0752 1442 Main St Sarasota FL 34236

Рис. 4.3. Результат работы программы, приведенной в листинге 4.3

Листинг 4.3. Выбрать и перечислить все столбцытаблицы authors. Результаты выполнениялистинга см. на рис. 4.3

SELECT *

FROM authors;

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

Свойство замкнутости таблиц (о которомупоминалось в советах из раздела «Табли-цы, столбцы и строки» главы 2) гарантиру-ет, что результатом любой командыSELECT будет некая таблица.

Результат, показанный на рис. 4.1, содержитдублированные строки, потому что в нашейтиповой базе есть два автора, живущихв Сан-Франциско. Чтобы удалить дублиро-ванные строки, обратитесь к разделу «Уда-ление повторяющихся строк с помощьюключевого слова DISTINCT» в этой главе.

Page 95: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

95

Те строки, которые вы получаете в резуль-тате исполнения наших запросов, могутбыть упорядочены не так, как на соответ-ствующих рисунках. Чтобы разобраться, вчем здесь дело, обратитесь к разделу«Сортировка строк с помощью предложе-ния ORDER BY» в этой главе.

Для указания значения null в какой-либотаблице или в каком-либо результате мыбудем применять ключевое слово NULL(см. раздел «Значение null» в главе 3, ли-стинг 4.4 и рис. 4.4).

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

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

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

Любая операция, которая выбирает опреде-ленные столбцы (то есть не все) из какой-нибудь таблицы, называется проекцией.

Листинг 4.4. Назвать город, штат и странукаждого издателя

SELECT city, state, country

FROM publishers;

city state country

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

New York NY USA

San Francisco CA USA

Hamburg NULL Germany

Berkeley CA USA

Рис. 4.4. Результат выполнения команды,представленной в листинге 4.4. Обратитевнимание, что столбец state для Германиине имеет смысла (в этой стране не штаты, аземли). Поэтому в столбце state напротивГермании стоит ключевое слово NULL, котороев данном случае является идентификаторомзначения null, отличающегося от какого-нибудь«невидимого» значения, например пустой строкиили строки пробелов

Выбор столбцов с помощью предложений SELECT и FROM

Page 96: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

96 Выбор данных из произвольной таблицы

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

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

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

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

– SELECT au_fname AS "First name";– SELECT au_fname "First name".

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

Создание псевдонимов столбцов

Следует напечатать:SELECT column1 AS alias1,

column2 AS alias2,

column3 AS alias3,

columnN AS aliasN

FROM table;

Предполагается, что:column1, column2, column3, …, columnN бу-дут заменены реальными (исходными,«родными») именами столбцов;alias1, alias2, alias3, …, aliasN будутзаменены соответствующими псевдо-нимами;table будет заменено именем той таб-лицы, которая содержит столбцы с на-званиями column1, column2, column3, …,columnN.

Page 97: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

97

В листинге 4.5 показаны возможные син-таксические вариации предложения AS,а на рис. 4.5 – результат работы програм-мы, приведенной в этом листинге.Мы постараемся не опускать без надобно-сти ключевое слово AS и заключать псев-донимы в двойные кавычки всегда, еслине возникнет потребности в чем-либодругом. Смысл этого в единообразии и,следовательно, в большей прозрачностиматериала. Дополнительное преимуще-ство заключается в независимости нашихпрограмм от среды СУБД (это называетсяпереносимостью программ; см. советы вконце данного раздела). То есть програм-ма, которая представлена в листинге 4.5,должна быть записана так:SELECT au_fname AS "First name",

au_lname AS "Last name",

city AS "City",

state,

zip AS "Postal code"

FROM authors;

Предложение AS применяют еще и длятого, чтобы именовать производные стол-бцы (то есть те столбцы, значения которыхопределяются выражениями; подробнее опроизводных столбцах см. в разделе «Со-здание производных столбцов» в главе 5).

Листинг 4.5. Ключевое слово AS обозначаетпроизвольный псевдоним любого столбца дляотображения результатов. Представленнаякоманда демонстрирует альтернативныесинтаксические конструкции предложения AS.Имеет смысл выбрать какую-то одну из этихконструкций и придерживаться ее

SELECT au_fname AS "First name",

au_lname AS "Last name",

city AS City,

state,

zip AS "Postal code"

FROM authors;

First name Last name City state Postal code

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

Sarah Buchman Bronx NY 10468

Wendy Heydemark Boulder CO 80303

Hallie Hull San Francisco CA 94123

Klee Hull San Francisco CA 94123

Christian Kells New York NY 10014

Kellsey Palo Alto CA 94305

Paddy O’Furniture Sarasota FL 34236

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

Создание псевдонимов столбцов с помощью предложения AS

Page 98: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

98 Выбор данных из произвольной таблицы

С помощью предложения AS также можносоздавать псевдонимы таблиц (подробнееоб этом см. в разделе «Создание псевдо-нимов таблиц с помощью предложенияAS» в главе 7).

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

Зарезервированные слова тоже можно при-менять в качестве псевдонимов, но тогда ихнадо заключать в кавычки. Например, запросSELECT SUM(sales) AS "Sum" FROM

titles; использует в качестве псевдонимастолбца зарезервированное слово SUM. Под-робнее о таких словах мы говорили в раз-деле «Синтаксис SQL» главы 3.

В Microsoft Access и PostgreSQL ключевоеслово AS является обязательным при созда-нии псевдонимов столбцов или таблиц.Если, работая в среде Oracle или PostgreSQL,вам нужно будет заключить псевдоним вкавычки, придется использовать толькодвойные кавычки (одинарные в этом слу-чае недопустимы).

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

Имейте в виду, что Oracle автоматическиобрежет справа любой псевдоним так, что-бы его длина была равна количеству зна-ков, определенному при создании таблицыдля значений того столбца, которому выназначаете этот псевдоним. Например,если бы вы задали тип данных для столбцакак CHAR(5) и собрались бы назначить емупсевдоним Postal code, то этот псевдонимбыл бы распечатан как Posta.

Следует отметить, что разные СУБД могутиметь собственные ограничения на приме-нение в псевдонимах спецсимволов, сим-волов пунктуации и пробелов. Поэтомуесть смысл проанализировать документа-цию по вашей СУБД с использованиемключей поиска SELECT или AS.

Page 99: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

99

Удаление повторяющихсястрок с помощьюключевого слова DISTINCTСтруктура реляционных баз данных тако-ва, что столбцы очень часто содержатодинаковые значения в разных строках.Поэтому вполне естественно желать отпроизвольного запроса такого результата,в котором каждая строка была бы уникаль-ной. Например, если бы мы запустили зап-рос (см. листинг 4.6), который должен пере-числить штаты, где живут авторы, занесен-ные в нашу типовую базу, результат был быточно таким, как на рис. 4.6. Мы видим, чтоэтот результат содержит ненужные дубли-каты. Для устранения этого неудобства слу-жит ключевое слово DISTINCT, с помощьюкоторого можно убрать лишние значенияиз результата запроса.

Удаление дубликатов строк

Вам следует напечатать:

SELECT DISTINCT columns

FROM table;

Предполагается, что:вместо columns вы подставите одно илинесколько реальных имен столбцов,перечисленных через запятую;вместо table подставите реальное имятой таблицы, из которой выбираютсяэти столбцы.

Реализация этого подхода продемонстри-рована в листинге 4.7 и на рис. 4.7.

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

Удаление повторяющихся строк с помощью ключевого слова DISTINCT

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

SELECT DISTINCT state

FROM authors;

Листинг 4.6. Перечислить штаты, где живутавторы, занесенные в нашу типовую базу данных.Результат исполнения запроса показан на рис. 4.6

SELECT state

FROM authors;

state

——---

NY

CO

CA

CA

NY

CA

FL

Рис. 4.6. Результат исполнения запроса,представленного в листинге 4.6.Можно видеть дубликаты штатов CA и NY

state

——---

NY

CO

CA

FL

Рис. 4.7. Результат исполнения запроса,представленного в листинге 4.7. Здесь уже нетни дубликатов CA, ни дубликатов NY

Page 100: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

100 Выбор данных из произвольной таблицы

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

Несмотря на то что значения null никогда небывают равны друг другу (поскольку счита-ются неизвестными), предложениеDISTINCT, напротив, считает их дубликата-ми. Вот почему команда SELECT DISTINCTвернет только одно значение null, независи-мо от того, сколько значений null она встре-тит (см. раздел «Значение null» в главе 3).

Синтаксис команды SELECT содержит инеобязательное ключевое слово ALL. Одна-ко часто оно не встречается. Дело в том, чтоэто слово указывает на действие, котороеявляется действием по умолчанию (то естьбыло бы выполнено и без всяких указаний):отобразить все строки, в том числе дубли-каты. То есть запрос SELECT columns FROMtable; эквивалентен запросу SELECT ALLcolumns FROM table;, а синтаксическая ди-аграмма такова: SELECT [ALL | DISTINCT]columns FROM table;.

Предложение DISTINCT можно применятьвместе с функциями агрегирования (име-ются в виду функции, аргументами кото-рых являются множества значений данных;подробнее об этой функции предложенияDISTINCT читайте в разделе «Исключениеповторных значений с помощью предложе-ния DISTINCT» в главе 6).

Если в таблице произвольной базы данныхпервичный ключ определен правильно, то,как нам теперь известно, каждая строкаэтой таблицы будет уникальной. Поэтомузапросы SELECT DISTINCT * FROM table;и SELECT * FROM table; вернули бы длятакой таблицы (ее имя здесь обозначенокак table) один и тот же результат.

Листинг 4.8. Перечислить города и штаты, вкоторых живут авторы, занесенные в нашу типовуюбазу данных. Результат представлен на рис. 4.8

SELECT city, state

FROM authors;

city state

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

Bronx NY

Boulder CO

San Francisco CA

San Francisco CA

New York NY

Palo Alto CA

Sarasota FL

Рис. 4.8. Результат запроса, приведенногов листинге 4.8, содержит один дубликат строкидля Сан-Франциско (Калифорния)

Рис. 4.9. Результат запроса, представленногов листинге 4.9. Обратите внимание, чтоуникальным здесь считается сочетание«город-штат», а не значение какого-либо столбца.По этому критерию здесь нет дубликатов строк

city state

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

Bronx NY

Boulder CO

San Francisco CA

New York NY

Palo Alto CA

Sarasota FL

Листинг 4.9. Перечислить различные пары(город, штат), чтобы адрес каждого автора,занесенного в нашу базу, содержал такую парув качестве элемента

SELECT DISTINCT city, state

FROM authors;

Page 101: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

101

Сортировка строкс помощью предложенияORDER BYПри выполнении запроса СУБД выдаетстроки в случайном порядке, так как ре-ляционная модель определяет, что поря-док строк не имеет никакого значения длятабличных операций. Зато вы сами може-те расположить строки с использованиемпредложения ORDER BY и отсортировать ихпо какому-нибудь столбцу или группестолбцов либо в восходящем (от самогонижнего к самому верхнему) либо в нис-ходящем (от самого верхнего к самомунижнему) порядке. О значении «верха иниза» подробнее говорится в тексте врез-ки этого раздела. Имейте в виду, что пред-ложение ORDER BY всегда является послед-ним предложением команды SELECT.

Выполнение сортировки по одномупроизвольному столбцу

Следует напечатать:SELECT columns

FROM table

ORDER BY sort_column [ASC | DESC];

Предполагается, что:

вместо columns вы подставите имя одно-го столбца или, через запятую, именанескольких столбцов, которые надовыбрать;вместо sort_column вы подставите имястолбца, по которому хотите отсорти-ровать результат запроса;вместо table вы подставите имя табли-цы, содержащей и столбцы columns,и столбец sort_column, который, чтоважно, совсем не должен быть средистолбцов columns;определите восходящий порядок сор-тировки опцией ASC, или нисходящий

Сортировка строк с помощью предложения ORDER BY

Порядок сортировки

Смысл сортировки по возрастаниюи убыванию прозрачен для чисел и дат,а смысл сортировки строк символовдовольно запутан. Для сортировкисимволов любая СУБД применяет такназываемую упорядочивающую пос-ледовательность, или схему сличениязнаков. Эта схема задает некое отноше-ние предшествования ( a < b обозначает«a предшествует b»), но выбор алфави-та зависит от того, какой язык вы при-меняете. Например, у европейских язы-ков – латинский алфавит, у иврита –древнееврейский, а у китайского –иероглифы. Так вот, схема сличениязнаков алфавита кроме упорядочива-ния его символов должна решить про-блему чувствительности к регистру('A' < 'a'?), проблему диакритическогознака ударения ('A' < 'А'?), проблемучувствительности к длине строки (длямногобайтовых знаков или знаковUnicode) и некоторые другие вопросы,в частности особенности строенияязыка, диалекты и т.п. Имейте в виду,что в стандарте SQL не заданы ни ал-фавиты, ни схемы сличения символов.Поэтому каждая СУБД по умолчаниюприменяет собственную стратегиюсортировки и собственную схему сли-чения символов. Обычно СУБД предо-ставляют в распоряжение пользовате-ля команды и инструментарий, с по-мощью которых можно посмотреть натекущий алфавит и на текущую схемусличения. Например, в среде MicrosoftSQL Server вы можете запустить ко-манду exec sp_helpsort. А для получе-ния исчерпывающей информации наэту тему вам следует проанализиро-вать документацию по вашей СУБДс использованием ключей поиска «сли-чение» и «порядок сортировки».

` `

Page 102: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

102 Выбор данных из произвольной таблицы

порядок сортировки опцией DESC, или незададите никакого порядка сортировки,и тогда по умолчанию будет назначенпорядок по возрастанию ASC (см. лис-тинги 4.10, 4.11 и рис. 4.10, 4.11).

Сортировка по нескольким столбцам

Необходимо напечатать:SELECT columns

FROM table

ORDER BY sort_column1 [ASC | DESC],

sort_column2 [ASC | DESC],

sort_column3 [ASC | DESC],

sort_columnN [ASC | DESC];

Предполагается, что:

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

вместо sort_column1, sort_column2,sort_column3, … , sort_columnN вы под-ставите в соответствии с правиламисинтаксиса имена тех столбцов, по ко-торым хотите отсортировать результатзапроса;

вместо table вы подставите имя тойтаблицы, которая содержит и столбцыcolumns, и все столбцы sort_column1,sort_column2, sort_column3, …, sort_-columnN, которые, что важно, вовсе недолжны быть среди столбцов columns;

для каждого из столбцов sort_column1,sort_column2, sort_column3, …, sort_-columnN вы определите восходящий по-рядок сортировки опцией ASC или нис-ходящий порядок сортировки опциейDESC либо не укажете никакого порядка

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

SELECT au_fname, au_lname, city, state

FROM authors

ORDER BY au_lname ASC;

au_fname au_lname city state

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

Sarah Buchman Bronx NY

Wendy Heydemark Boulder CO

Hallie Hull San Francisco CA

Klee Hull San Francisco CA

Christian Kells New York NY

Kellsey Palo Alto CA

Paddy O’Furniture Sarasota FL

Рис. 4.10. Результат исполнения запроса,представленного в листинге 4.10. Фамилииотсортированы в порядке возрастания

Page 103: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

103

Листинг 4.11. Перечислить имена, фамилии,города и штаты проживания авторов, которыезанесены в нашу типовую базу данных,в нисходящем (то есть в обратном алфавитном)порядке. Здесь ключевое слово DESC являетсяобязательным. Результат исполнения этогозапроса см. на рис. 4.11

SELECT au_fname, au_lname, city, state

FROM authors

ORDER BY au_fname DESC;

au_fname au_lname city state

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

Wendy Heydemark Boulder CO

Sarah Buchman Bronx NY

Paddy O’Furniture Sarasota FL

Klee Hull San Francisco CA

Hallie Hull San Francisco CA

Christian Kells New York NY

Kellsey Palo Alto CA

Рис. 4.11. Результат исполнения запроса,представленного в листинге 4.11. Сортировкавыполнена по столбцу имен в нисходящемпорядке. Обратите внимание, что у авторапо фамилии Келлси (Kellsey) нет имени и онопредставлено пустой строкой (‘’). Поэтому Келлсистоит последним (вспомните о значении null).Он был бы первым, если бы сортировкапроводилась тоже по именам, но в восходящем(то есть прямом алфавитном) порядке

сортировки, и тогда по умолчанию бу-дет назначен восходящий порядок ASC(см. листинг 4.12 и рис. 4.12);в соответствии с определенным вышепорядком СУБД сначала отсортируетвсе строки по sort_column1;потом СУБД отсортирует те строки,которые после предыдущего шага име-ют равные значения sort_column1, поsort_column2;наконец, СУБД отсортирует те строки,которые после предыдущего шага име-ют равные значения sort_column(N-1),по sort_columnN.

Если столбец/столбцы, по которому/кото-рым проводится сортировка, принадле-жит/принадлежат множеству столбцов вы-борки, обозначенному нами как columns,SQL позволяет указать в предложении за-проса ORDER BY этот/эти столбец/столбцы непо имени, а по расположению во множествеcolumns.

Выполнение сортировкипо нескольким столбцамс указанием их местоположения

Необходимо напечатать:SELECT columns

FROM table

ORDER BY sort_num1 [ASC | DESC],

sort_num2 [ASC | DESC],

sort_num3 [ASC | DESC],

sort_numN [ASC | DESC];

Предполагается, что:

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

Сортировка строк с помощью предложения ORDER BY

Page 104: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

104 Выбор данных из произвольной таблицы

вместо sort_num1, sort_num2, sort_num3,…, sort_numN вы подставите натураль-ные числа, расположенные в интер-вале от 1 до числа, равного мощностимножества columns, и соответствую-щие позициям во множестве columns,которые занимают столбцы sort_num1,sort_num2, sort_num3, …, sort_numN;вместо table вы подставите имя табли-цы, содержащей и столбцы columns,и все столбцы sort_num1, sort_num2,sort_num3, …, sort_numN, которые, чтоважно, должны быть среди столбцовcolumns;для каждого из столбцов sort_num1,sort_num2, sort_num3, …, sort_numN вы ис-пользуете восходящий порядок сорти-ровки опцией ASC, или нисходящий по-рядок сортировки опцией DESC, или неукажете никакого порядка сортировки,и тогда по умолчанию будет назначенвосходящий порядок ASC (см. листинг4.13 и рис. 4.13);СУБД сначала отсортирует в соответ-ствии с определенным выше порядкомвсе строки по sort_num1;потом СУБД отсортирует строки, ко-торые после предыдущего шага име-ют равные значения sort_num1, поsort_num2;наконец, СУБД отсортирует те строки,которые после предыдущего шага име-ют равные значения sort_num(N-1), поsort_numN.

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

SELECT au_fname, au_lname, city, state

FROM authors

ORDER BY state ASC,

city DESC;

au_fname au_lname city state

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

Hallie Hull San Francisco CA

Klee Hull San Francisco CA

Kellsey Palo Alto CA

Wendy Heydemark Boulder CO

Paddy O’Furniture Sarasota FL

Christian Kells New York NY

Sarah Buchman Bronx NY

Рис. 4.12. Результат исполнения запроса,представленного в листинге 4.12

Page 105: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

105

Стандарт SQL требует, чтобы при сорти-ровке значений null они трактовались иликак превосходящие (аналог максимума) вседругие значения, не являющиеся значения-ми null, или как уступающие (аналог мини-мума) всем другим значениям, не являю-щимся значениями null. И поскольку требо-вание стандарта SQL не является однознач-ным, одни СУБД трактуют значения null какнаивысшие, а другие – как самые низшие(см. примечание DBMS в этом разде-ле, раздел «Значение null» главы 3, лис-тинг 4.14 и рис. 4.14).

Можно осуществлять сортировку по столб-цам, не входящим в состав выбираемыхстолбцов, перечисленных в предложенииSELECT (см. листинг 4.15 и рис. 4.15). Од-нако, если столбцы, по которым осуществ-ляется сортировка, определены их относи-тельным местоположением, они должныбыть перечислены в предложении SELECT(см. листинг 4.13 и рис. 4.13).

В предложении ORDER BY вы можете ука-зать псевдонимы столбцов вместо их имен(см. листинг 4.16, рис. 4.16 и раздел «Со-здание псевдонимов столбцов с помощьюпредложения AS» в данной главе).

Хотя это и нелепо, можно указать одини тот же столбец в предложении ORDER BYнесколько раз.

Листинг 4.13. Перечислить имена, фамилии, городаи штаты проживания авторов, занесенных в нашутиповую базу данных, так, чтобы результат былсначала отсортирован в восходящем (алфавитном)порядке по столбцу штата (столбец 4 впредложении SELECT), а потом в нисходящем(обратном алфавитном) порядке по столбцуфамилии (столбец 2 в предложении SELECT).Результат исполнения этого запроса см. на рис. 4.13

SELECT au_fname, au_lname, city, state

FROM authors

ORDER BY 4 ASC, 2 DESC;

au_fname au_lname city state

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

Kellsey Palo Alto CA

Hallie Hull San Francisco CA

Klee Hull San Francisco CA

Wendy Heydemark Boulder CO

Paddy O’Furniture Sarasota FL

Christian Kells New York NY

Sarah Buchman Bronx NY

Рис. 4.13. Результат исполнения запроса,представленного в листинге 4.13

Сортировка строк с помощью предложения ORDER BY

Page 106: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

106 Выбор данных из произвольной таблицы

Если значения в столбцах, по которым осу-ществляется сортировка предложением OR-DER BY, повторяются, то строки, соответству-ющие этой комбинации значений, будут пе-речислены при выдаче результата в каком-нибудь случайном порядке. Хотя некоторыеиз наших примеров соответствуют как разэтому случаю (см. рис. 4.10, 4.12, 4.13),вы обязательно должны выбирать доста-точно много столбцов в предложенииORDER BY, чтобы комбинация значений встолбцах сортировки была уникальной.Это особенно важно тогда, когда результатзапроса должен быть предъявлен какому-нибудь конечному пользователю.

В соответствии со стандартом ANSI пред-ложение ORDER BY является частью объ-явления CURSOR некоего курсора, а не ко-манды SELECT. Но курсоры как таковые, бу-дучи объектами, определяемыми внутриприкладных программ, не рассматриваютсяв этой книге. Однако хотелось бы подчерк-нуть, что наиболее популярные направле-ния практического применения SQL позво-ляют использовать предложение ORDER BYв любой команде SELECT (хотя бы потому,что СУБД строит курсор автоматически иневидимо для вас).

Вы можете выполнять сортировку и по ре-зультатам выражений. В главе 5 как разрассказывается, как создавать выражения,применяя функции и операторы (см. лис-тинг 4.17 и рис. 4.17).

В предложении ORDER BY вполне допусти-мо применять одновременно и имена стол-бцов, и относительные места расположе-ния столбцов, и выражения.

Листинг 4.14. Значения null, находящиеся встолбце, по которому проводится сортировка,в зависимости от применяемой вами СУБД будутперечислены или самыми первыми, или самымипоследними. Результат исполнения данногозапроса см. на рис. 4.14

SELECT pub_id, state, country

FROM publishers

ORDER BY state ASC;

pub_id state country

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

P03 NULL Germany

P02 CA USA

P04 CA USA

P01 NY USA

Рис. 4.14. Результат исполнения запроса,представленного в листинге 4.14. Мы видим,что этот результат отсортирован по столбцу штатав восходящем (то есть в алфавитном) порядке.СУБД, в среде которой исполнен этот запрос,трактует любое значение null как низшее из всехвозможных (как самую первую букву алфавита).Другая СУБД, которая трактует значение null каквысшее из всех возможных (как самуюпоследнюю букву алфавита), поставила быту же самую строку самой последней

Page 107: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

107

Листинг 4.15. Обратите внимание, что столбецпочтового индекса (zip) не входит в составстолбцов, которые надо выбрать. Результатисполнения этого запроса см. на рис. 4.15

SELECT city, state

FROM authors

ORDER BY zip ASC;

city state

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

New York NY

Bronx NY

Sarasota FL

Boulder CO

San Francisco CA

San Francisco CA

Palo Alto CA

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

Сортировка строк с помощью предложения ORDER BY

Листинг 4.16. Использовать в предложении ORDERBY не имена столбцов, а их псевдонимы.Результат исполнения запроса см. на рис. 4.16

SELECT au_fname AS "First name",

au_lname AS "Last name",

state

FROM authors

ORDER BY state ASC,

"Last name" ASC,

"First name" ASC;

First name Last name state

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

Hallie Hull CA

Klee Hull CA

Kellsey CA

Wendy Heydemark CO

Paddy O’Furniture FL

Sarah Buchman NY

Christian Kells NY

Рис. 4.16. Результат исполнения запроса,представленного в листинге 4.16

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

Сортировка по столбцам, заданным их отно-сительным местом расположения, полезнав запросах UNION (подробнее об этом мы по-говорим в разделе «Комбинирование строкс помощью оператора UNION» главы 7).

Page 108: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

108 Выбор данных из произвольной таблицы

Листинг 4.17. Выполнить сортировку повыражению. Результат исполнения запросасм. на рис. 4.17

SELECT title_id,

price,

sales,

price * sales AS "Revenue"

FROM titles

ORDER BY "Revenue" DESC;

title_id price sales Revenue

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

T07 23.95 1500200 35929790.00

T05 6.95 201440 1400008.00

T12 12.99 100001 1299012.99

T03 39.95 25667 1025396.65

T11 7.99 94123 752042.77

T13 29.99 10467 313905.33

T06 19.95 11320 225834.00

T02 19.95 9566 190841.70

T04 12.99 13001 168882.99

T09 13.95 5000 69750.00

T08 10.00 4095 40950.00

T01 21.99 566 12446.34

T10 NULL NULL NULL

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

Коммерческие СУБД Microsoft Access, Mic-rosoft SQL Server и PostgreSQL при сорти-ровке воспринимают значение null как низ-шее из всех возможных, а Oracle и MySQL –как высшее из всех возможных.

Коммерческие СУБД накладывают ограниче-ния на тип столбцов в предложении ORDERBY. Так, в Microsoft SQL Server нельзя сор-тировать по столбцам ntext, text

и image, а в Oracle нельзя сортировать постолбцам blob, clob, nclob и bfile. Дляполучения исчерпывающей информацииоб этих ограничениях следует проанали-зировать документацию по вашей СУБДс использованием ключей поиска SELECTи ORDER BY.

В среде Microsoft Access нельзя применятьпсевдонимы столбцов в предложенииORDER BY. Например, чтобы исполнить вAccess запрос, который представлен в ли-стинге 4.17, вам пришлось бы или пере-печатать предложение ORDER BY в следую-щем виде: ORDR BY price * sales DESC,или обозначить столбец, по которому осу-ществляется сортировка, его относитель-ным местом расположения: ORDER BY 4DESC.

Page 109: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

109

Фильтрация строкс помощью предложенияWHEREДо сих пор в результат выполнения каж-дого запроса включались все строки, ка-кие только есть в соответствующей табли-це. Если не все из них вам нужны, мож-но применить предложение WHERE и от-фильтровать лишнее из результата. Не-обходимо подчеркнуть, что именно спо-собность фильтровать результаты ха-рактеризует команду SELECT как оченьмощную. В предложении WHERE вам сле-дует указать какое-нибудь условие отбо-ра, включающее одно или несколько та-ких условий, которым должны удовлет-ворять строки произвольной таблицы, что-бы быть пропущенными через фильтр.Здесь под условием поиска, или предика-том, как его еще называют, понимаетсялогическое выражение, которое можетпринимать три значения, а именно: «исти-на», «ложь» и «неизвестно» (по-английски,соответственно, true, false и unknown).При этом существенно, что значение «не-известно» появилось в этой схеме благо-даря значению null (подробнее об этоммы поговорим в следующем разделе).Фильтрование предложением WHERE осу-ществляется таким образом, что те итолько те строки, для которых заданноеусловие принимает значение истины,включаются в результат исполнения за-проса, а все остальные строки из этогорезультата исключаются.Чтобы составить различные типы условий(см. табл. 4.1), SQL предоставляет в рас-поряжение программиста целый ряд опе-раторов, где под произвольным операто-ром понимается набор символов или клю-чевое слово, определяющие конкретныедействия, которые необходимо совершитьнад некими значениями или какими-либо

Таблица 4.1. Типы условий

Условие Операторы SQL

Сравнение =, <>, <, <=, >, >=

Сопоставление с образцом LIKE

Фильтрация диапазона BETWEEN

Фильтрация списка IN

Проверка на значение null IS NULL

Фильтрация строк с помощью предложения WHERE

Page 110: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

110 Выбор данных из произвольной таблицы

другими элементами (подробнее об опера-торах мы поговорим в главе 5). В этом раз-деле мы рассмотрим условия сравнения(см. табл. 4.1), а обо всех других условияхпоговорим в конце главы. С помощью ло-гических операторов AND, OR и NOT вы мо-жете комбинировать и инвертировать лю-бые условия (см. следующий раздел).

Любой оператор сравнения (см. табл. 4.2)должен сравнивать два значения и в резуль-тате этого сравнения принять одно из трехвозможных значений: «истина», «ложь» или«неизвестно». При этом тип данных, ккоторому относятся сравниваемые значе-ния, должен определить то, как именноэти значения сравниваются:

строки символов сравниваются лекси-кографически, когда символ > означа-ет «следует за…», а символ < – «пред-шествует» (см. разделы «Типы данных»в главе 3 и «Сортировка строк с помо-щью предложения ORDER BY» в дан-ной главе);

числа сравниваются арифметически,когда символ > означает «больше», асимвол < – «меньше»;

данные даты и времени сравниваютсяхронологически, когда символ > означа-ет «позже», а символ < – «раньше» (приэтом два сравниваемых значения датыи времени должны иметь один и тот жеформат, то есть состоять из одних и техже полей year, month, day и т.д.).

Имейте в виду, что сравнивать следует дан-ные даты и времени только одного форма-та. Если вы попытаетесь сравнить разно-форматные данные, ваша СУБД может:

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

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

Таблица 4.2. Операторы сравнения

Оператор Описание

= Равно

<> Не равно

< Меньше

<= Не больше

> Больше

>= Не меньше

Page 111: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

111

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

Фильтрация строк с помощьюпроизвольного сравнения

Напечатайте:SELECT columns

FROM table

WHERE test_column op value;

Предполагается, что:

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

вместо table вы подставите имя тойтаблицы, которая содержит столбцыcolumns;

формируя условие поиска, вы:

– вместо test_column подставите имякакого-нибудь одного столбца в таб-лице table, причем этот столбец со-всем не должен входить в составстолбцов columns;

– вместо op подставите какой-нибудьиз операторов сравнения, перечис-ленных в табл. 4.2;

– вместо value подставите какое-нибудьзначение, которое надо сравнить созначением столбца test_column

(см. листинги и рис. 4.18– 4.20).

Если в команде SELECT принимают учас-тие и предложение ORDER BY, и предложе-ние WHERE, следует печатать предложениеWHERE перед предложением ORDER BY.

Листинг 4.18. Распечатать список авторов,занесенных в нашу типовую базу, чтобы фамилиякаждого из них отличалась от Хал (Hull). Результатисполнения этого запроса см. на рис. 4.18

SELECT au_id, au_fname, au_lname

FROM authors

WHERE au_lname <> 'Hull';

Листинг 4.19. Распечатать список названий изкниг, занесенных в нашу типовую базу данных,для которых контракт пока не подписан. Результатисполнения этого запроса см. на рис. 4.19

SELECT title_name, contract

FROM titles

WHERE contract = 0;

au_id au_fname au_lname

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

A01 Sarah Buchman

A02 Wendy Heydemark

A05 Christian Kells

A06 Kellsey

A07 Paddy O’Furniture

Рис. 4.18. Результат исполнения запроса,представленного в листинге 4.18

title_name contract

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

Not Without My Faberge Egg 0

Рис. 4.19. Результат исполнения запроса,представленного в листинге 4.19

Фильтрация строк с помощью предложения WHERE

Page 112: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

112 Выбор данных из произвольной таблицы

Значение null никогда ни с чем не совпада-ет, в том числе с любым другим значениемnull. Поэтому строки, в которых есть значе-ния null, через фильтр никогда не пройдути среди строк результата не появятся. Что-бы извлечь строки, содержащие значенияnull, обратитесь к разделу «Проверка назначение null с помощью оператора ISNULL» в данной главе. Общую информа-цию о значениях null вы можете почерпнутьиз раздела «Значение null» в главе 3.

Как правая, так и левая часть сравнения(а сравнение – это часть предложенияWHERE, которая не является ключевым сло-вом WHERE) может иметь гораздо болеесложную структуру по сравнению с той, чтопоказана в начале текущего подраздела.Наиболее общая форма произвольногосравнения такова: expr1 op expr2, гдеи expr1 и expr2 – выражения. А произволь-ное выражение – это, в свою очередь, про-извольная, не запрещенная синтаксисом,комбинация имен столбцов, литералов,функций и операторов, которая на любойстроке принимает единственное значение(короче говоря, выражение – это функциястроки; см. листинг 4.21 и рис. 4.21). Под-робнее о выражениях мы поговорим в гла-ве 5.

В предложении WHERE нельзя применятьагрегатные функции, в частности SUM()или COUNT(). Подробнее об этом мы по-говорим в главе 6.

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

Листинг 4.20. Распечатать названия книг,выпущенных не ранее 2001 года. Результатисполнения см. на рис. 4.20

SELECT title_name, pubdate

FROM titles

WHERE pubdate >= DATE '2001-01-01';

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

SELECT title_name,

price * sales AS 'Revenue'

FROM titles

WHERE price * sales > 1000000;

title_name pubdate

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

Exchange of Platitudes 2001-01-01

Just Wait Until After School 2001-06-01

Kiss My Boo-Boo 2002-05-31

Рис. 4.20. Результат исполнения запроса,представленного в листинге 4.20

title_name Revenue

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

Ask Your System Administrator 1025396.65

Exchange of Platitudes 1400008.00

I Blame My Mother 35929790.00

Spontaneous, Not Annoying 1299012.99

Рис. 4.21. Результат исполнения запроса,представленного в листинге 4.21

Page 113: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

113

Хотя стандарт SQL гласит, что регистр име-ет значение внутри закавыченных строксимволов, в конечном счете только схемасличения вашей СУБД будет определять,чувствительны ли к регистру сравнениястрок символов ('A' = 'a') или нет ('A' ≠'a'). Отметим, что по умолчанию MicrosoftAccess, Microsoft SQL Server и MySQL выда-ют нечувствительные к регистру сравнениястрок символов, а Oracle и PostgreSQL – на-оборот, чувствительные к регистру срав-нения строк символов.

В среде Microsoft Access, печатая литера-лы даты и времени, следует опускать клю-чевое слово DATE, а сами литералы нужнозаключать не в кавычки, а в символы #. По-этому, чтобы выполнить в этой среде за-прос, представленный в листинге 4.20, за-мените дату в предложении WHERE следу-ющей #2001-01-01#.

В среде Microsoft SQL Server, печатая ли-тералы даты и времени, следует опускатьключевое слово DATE. Поэтому, чтобы вы-полнить в этой среде запрос, представлен-ный в листинге 4.20, замените дату в пред-ложении WHERE на '2001-01-01'.

Чтобы в среде PostgreSQL сравнить значе-ние произвольного столбца, где типом дан-ных определен NUMERIC или DECIMAL, снеким действительным числом (то есть срациональным числом с плавающей деся-тичной точкой), вам придется преобразо-вать это число в тип NUMERIC или DECIMAL(целые и рациональные числа с фиксиро-ванной точкой). Подробно эта тема рас-крыта в разделе «Преобразование типовданных с помощью функции CAST()» гла-вы 5.

Фильтрация строк с помощью предложения WHERE

Page 114: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

114 Выбор данных из произвольной таблицы

Комбинирование условийс помощью операторовAND, OR и NOTПрактика показывает, что определять не-сколько условий в одном предложенииWHERE приходится довольно часто. В част-ности, у вас возникнет такая необходи-мость, когда вы захотите отобрать строки,чтобы они удовлетворяли некоему огра-ничению, основанному на значениях сра-зу нескольких столбцов. В этом и другихподобных случаях вы можете применитьоператоры AND и OR, чтобы скомпоноватьнесколько условий в одно объединенноеусловие. Операторы AND и OR вместе с опе-ратором NOT называются логическими, илиБулевыми, операторами. Они предназначе-ны для работы с так называемыми значе-ниями истины, которых существует всеготри: «истина», «ложь» и «неизвестно».Если вы программировали на других язы-ках (или изучали логику высказываний,называемую пропозициональной), то зна-комы с системой двухзначной логики, обо-значаемой как система 2VL. Такая логикаименно потому и называется двухзначной,что в ее системе результатом любого выра-жения всегда является только одно из двух:или «истина», или «ложь». То есть 2VLпредполагает абсолютное знание, когдапро любое высказывание точно известно,истинно оно или ложно. Но базы данныхмоделируют реальный мир, наши знанияо котором всегда были и будут неполны-ми. Вот почему в базах данных появилисьзначения null. Они моделируют неопреде-ленность реального мира, выраженнуюв неизвестных значениях (см. раздел «Зна-чение null» в главе 3).Итак, поскольку система 2VL не можетсмоделировать реальный недостаток зна-ний, язык SQL применяет трехзначную

логику (3VL). Основное отличие трехзнач-ной логики от двухзначной состоитв том, что результатом любого логическо-го выражения в рамках трехзначной ло-гики может быть одно, но уже не из двух,а из трех значений: или «истина», или«ложь», или «неизвестно». При этом взапросах трехзначная логика работает так,что, если и только если на произвольнойстроке выражение истинно, эта строкавключается в результат исполнения запро-са. В двух других случаях она из результа-та запроса исключается. Однако строки созначениями null тоже можно извлечь. Что-бы понять, как это делается, обратитеськ разделу «Проверка на значение null с по-мощью оператора IS NULL» в этой главе.

Оператор AND

Перечислим основные характеристикиоператора AND:

объединяет два условия и принимаетзначение true (истина) тогда и толькотогда, когда каждое из этих условийпринимает значение true;в табл. 4.3, называемой таблицей истин-ности, перечислены возможные вариан-ты объединения двух условий операто-ром AND, где:

– крайний левый столбец показываетмеру истинности первого условия;

– верхняя строка показывает меру ис-тинности второго условия;

– каждая клетка отображает результатобъединения оператором AND этихусловий;

с помощью нужного количества опе-раторов AND можно объединить любоеколичество условий, и для того, чтобыпроизвольная строка была включенав итоговый результат, каждое из этих ус-ловий должно принять значение true;

Page 115: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

115

коммутативен (то есть его результат независит от порядка записи первогои второго условий – предложение WHEREcondition1 AND condition2 эквивалентнопредложению WHERE condition2 AND con-dition1);

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

Примеры использования оператора ANDприведены в листингах 4.22–4.23 и нарис. 4.22–4.23.

Оператор OR

Перечислим отличительные черты опера-тора OR:

объединяет два условия и принимаетзначение true (истина) тогда и толькотогда, когда хотя бы одно из этих усло-вий принимает значение true;

таблица истинности оператора OR пред-ставлена в табл. 4.4;

с помощью соответствующего количе-ства операторов OR можно объединитьлюбое количество условий так, чтобырезультирующее условие приняло зна-чение true тогда и только тогда, когдахотя бы одно составляющее условиеприняло значение true;

коммутативен (то есть его результатне зависит от порядка записи первогои второго условий точно так же, каку оператора AND);

Листинг 4.22. Распечатать названиякниг-биографий, которые продаются дешевле20 долларов за каждую. Результат исполненияэтого запроса см. на рис. 4.22

SELECT title_name, type, price

FROM titles

WHERE type = 'biography' AND price <

20;

Таблица 4.3. Таблица истинности AND

AND True False Unknown

True True False Unknown

False False False False

Unknown Unknown False Unknown

title_name type price

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

How About Never? biography 19.95

Spontaneous, Not Annoying biography 12.99

Рис. 4.22. Результат исполнения запроса,представленного в листинге 4.22

Комбинирование условий с помощью операторов AND, OR и NOT

Page 116: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

116 Выбор данных из произвольной таблицы

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

Примеры использования оператора ORприведены в листингах 4.24–4.25 и нарис. 4.24–4.25. Обратите внимание, чтолистинг 4.25 демонстрирует влияние нарезультат запроса того факта, что в усло-виях предложения WHERE, объединенныхоператором OR, есть значения null. Вывполне могли бы ожидать в качестве ре-зультата запроса, представленного в лис-тинге 4.25, распечатки всех строк таблицыpublishers. Но на рис. 4.25 нет строкис первичным ключом P03. Это произошлопотому, что отсутствующая строка содер-жит значение null в столбце state (напом-ним еще раз: значение null стоит тампотому, что в Германии нет штатов). По-скольку, как мы теперь знаем, на этомсамом значении null оба условия предло-жения WHERE принимают значение unknownи, следовательно, ни одно из них не рав-но true, та строка, для которой это имеетместо, должна быть исключена из резуль-тата запроса. Чтобы как следует разоб-раться в этой логике, внимательно прочи-тайте раздел «Проверка на значение nullс помощью оператора IS NULL» в даннойглаве.

Оператор NOT

Перечислим основные характеристикиоператора NOT:

в отличие от операторов AND и OR, несвязывает двух условий, а инвертируетодно-единственное условие;таблица истинности оператора NOT

представлена в табл. 4.5;

Таблица 4.4. Таблица истинности OR

OR True False Unknown

True True True True

False True False Unknown

Unknown True Unknown Unknown

Листинг 4.23. Перечислить из нашей типовой базыданных имена, фамилии и штаты проживанияавторов, чьи фамилии начинаются с H–Z. Результатисполнения этого запроса см. на рис. 4.23

SELECT au_fname, au_lname, state

FROM authors

WHEREau_lname >= 'H'

ANDau_lname <= 'Zz'

ANDstate <> 'CA';

au_fname au_lname state

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

Wendy Heydemark CO

Christian Kells NY

Paddy O’Furniture FL

Рис. 4.23. Результат исполнения запроса,представленного в листинге 4.23. Рассматриваяэтот результат, вспомните, что результат сравнениястрок символов зависит от схемы сличения вашейСУБД (см. раздел «Сортировка строк с помощьюпредложения ORDER BY» в данной главе)

Таблица 4.5. Таблица истинности NOT

Условие Инверсия(Condition) (NOT Condition)

True False

False True

Unknown Unknown

Page 117: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

117

Листинг 4.25. Распечатать идентификаторы,названия, штаты и страны тех издательств,которые или находятся в штате Калифорния, илине находятся в штате Калифорния. Этот, напервый взгляд, бессмысленный запрос былспециально придуман для того, чтобыпродемонстрировать последствия попаданиязначений null в условия предложения WHERE,соединенные оператором OR. Результатисполнения запроса см. на рис. 4.25

SELECT pub_id, pub_name, state, country

FROM publishers

WHERE (state = 'CA')

OR (state <> 'CA');

Листинг 4.24. Распечатать из нашей типовой базыданных имена и фамилии авторов, которые живутили в штате Нью-Йорк, или в штате Колорадо, илив городе Сан-Франциско. Результат исполненияданного запроса см. на рис. 4.24

SELECT au_fname, au_lname, city, state

FROM authors

WHERE (state = 'NY')

OR (state = 'CO')

OR (city = 'San Francisco');

au_fname au_lname city state

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

Sarah Buchman Bronx NY

Wendy Heydemark Boulder CO

Hallie Hull San Francisco CA

Klee Hull San Francisco CA

Christian Kells New York NY

Рис. 4.24. Результат исполнения запроса,представленного в листинге 4.24

pub_id pub_name state country

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

P01 Abatis Publishers NY USA

P02 Core Dump Books CA USA

P04 Tenterhooks Press CA USA

Рис. 4.25. Результат исполнения запроса,представленного в листинге 4.25

в сравнениях следует ставить операторNOT перед именем столбца или выраже-нием, значение которого предполага-ется инвертировать, но не перед соот-ветствующим оператором (например,предложение WHERE NOT state = 'CA'скомпоновано правильно, а предложе-ние WHERE state NOT = 'CA' – неверно,хотя и читается «естественнее»);всегда действует только на одно условие,поэтому, чтобы инвертировать несколь-ко условий, придется повторить длякаждого из них отдельный операторNOT. Например, чтобы перечислить всеназвания книг, которые одновременно

не являются биографиями и оценивают-ся не ниже 20 долларов, вам пришлосьбы напечатать:

SELECT title_id, type, price

FROM titles

WHERE NOT type = 'biography'

AND NOT price < 20;

что было бы правильным решением по-ставленной задачи. Но если бы вы напе-чатали:SELECT title_name, type, price

FROM titles

WHERE NOT type = 'biography'

AND price < 20;

Комбинирование условий с помощью операторов AND, OR и NOT

Page 118: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

118 Выбор данных из произвольной таблицы

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

применять или не применять операторNOT в сравнениях – это дело вкуса и сти-ля. Например, предложения WHERE NOTstate = 'CA' и WHERE state <> 'CA' экви-валентны;инвертируемое условие разрешаетсязаключать в круглые скобки.

Примеры использования оператора NOTприведены в листингах 4.26–4.27 и нарис. 4.26–4.27.

Одновременное использованиеоператоров AND, OR и NOT

SQL позволяет строить сложные условияпосредством совместного использованияоператоров AND, OR, NOT в одном логичес-ком выражении. При этом ваша СУБД бу-дет определять очередность примененияэтих операторов на основе правил пред-шествования SQL. Об этих правилах под-робно рассказывается в разделе «Опреде-ление последовательности вычисления»главы 5. Сейчас вы должны уяснить, чтопри вычислении значения сложного логи-ческого выражения, содержащего не-сколько разных логических операторов,при прочих равных условиях первымприменяется оператор NOT, затем операторAND, а последним – оператор OR. Мы ис-пользовали выражение «… при прочихравных условиях…», так как можно изме-нить порядок вычисления операторов,просто расставив круглые скобки. Общееправило таково, что сначала надо вычис-лить выражения во всех скобках, а потомвыражение, составленное из этих скобок,

Листинг 4.26. Перечислить из нашей типовойбазы данных фамилии и имена авторов, которыене живут в Калифорнии. Результат исполненияэтого запроса см. на рис. 4.26

SELECT au_fname, au_lname, state

FROM authors

WHERE NOT (state = 'CA');

Листинг 4.27. Распечатать названия книг, ценакоторых не ниже 20 долларов и объем продажкоторых превышает 15 000 копий. Результатисполнения этого запроса см. на рис. 4.27

SELECT title_name, sales, price

FROM titles

WHERE NOT (price < 20)

AND (sales > 15000);

au_fname au_lname state

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

Sarah Buchman NY

Wendy Heydemark CO

Christian Kells NY

Paddy O’Furniture FL

Рисунок 4.26. Результат исполнения запроса,представленного в листинге 4.26

title_name sales price

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

Ask Your System Administrator 25667 39.95

I Blame My Mother 1500200 23.95

Рис. 4.27. Результат исполнения запроса,представленного в листинге 4.27

Page 119: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

119

объединенных логическими операторами.Например, на основе правил предшест-вования, которые приняты вашей СУБДпо умолчанию, сложное условие x AND NOTy OR z эквивалентно сложному условию (xAND (NOT y)) OR z. В этой связи хотелось быподчеркнуть, что для того, чтобы после-довательность применения операторовбыла максимально прозрачной, разумновсегда применять скобки и не полагатьсяна правила предшествования, действую-щие по умолчанию. Например, если бынужно было распечатать из нашей базыназвания книг, тема которых или исто-рия, или биография, и чтобы они приэтом стоили не более 20 долларов каждая,то запрос, который представлен у нас влистинге 4.28, этой задачи не решил бы.Все дело в том, что по умолчанию (то естьбез скобок) оператор AND вычисляетсяраньше, чем оператор OR, поэтому весьзапрос листинга 4.28 будет выполнен в та-кой последовательности:

1. Найти и выбрать все книги-биогра-фии, которые стоят менее 20 долларов.

2. Найти и выбрать все книги по истории(независимо от их цены).

3. В качестве результата распечатать обамножества строк со столбцами title_id,type, price (уникальный идентификаторкниги, ее тема, цена; см. рис. 4.28).

Чтобы заставить запрос выбирать строкив соответствии с поставленной задачей,оператор OR должен срабатывать раньшеоператора AND. В соответствии с этой иде-ей легко «починить» запрос листинга 4.28расстановкой скобок. Исправленный за-прос, представленный в листинге 4.29,выполняется в такой последовательности:

1. Найти и выбрать все книги-биографиии все книги по истории (независимо отих цены).

Листинг 4.28. Если бы мы захотели перечислитькниги из нашей базы, темой который являетсяили история, или биография и цена которых ниже20 долларов, то этот запрос не сработал бы.Причина состоит в том, что по умолчаниюоператор AND выполняется раньше оператора OR.Результат исполнения этого запроса см. на рис.4.28

SELECT title_id, type, price

FROM titles

WHERE type = 'history'

OR type = 'biography'

AND price < 20;

title_id type price

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

T01 history 21.99

T02 history 19.95

T06 biography 19.95

T12 biography 12.99

T13 history 29.99

Рис. 4.28. Результат исполнения запроса,представленного в листинге 4.28. Мы видим,что в распечатку включены две книги, ценакоторых превышает 20 долларов. Следовательно,данный запрос не решает поставленной задачии его надо откорректировать

Комбинирование условий с помощью операторов AND, OR и NOT

Page 120: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

120 Выбор данных из произвольной таблицы

2. Из всего множества книг, прошедшихфильтр на предыдущем шаге, выбратьтолько те, которые стоят менее 20 дол-ларов.

3. В качестве результата распечатать по-следнее отобранное множество строксо столбцами title_id, type, price (уни-кальный идентификатор книги, ее тема,цена; см. рис. 4.29).

Хотя во всех примерах данного разделаоператоры AND, OR и NOT работают исклю-чительно с условиями сравнения, они мо-гут работать и с условиями любого друго-го типа.

Одна очень распространенная ошибка со-стоит в том, чтобы при формировании пред-ложения WHERE перед оператором OR ста-вить условие, а после оператора OR – неусловие, а только его часть в виде литералаили функции. Пример этой ошибки: WHEREstate = 'NY' OR 'CA'. Правильное реше-ние: WHERE state = 'NY' OR state = 'CA'.

Дословный перевод вполне осмысленныхутверждений языка общения на язык SQLведет к бессмысленным и даже невернымкомандам SQL. Например, директива «Пере-числить все книги по ценам менее 10 дол-ларов за копию и более 30 долларов закопию» имеет четкий прикладной смысл.Но примененный в ней союз «и», будучипереведен на SQL, превратит весь запросв бессмыслицу:

SELECT title_name, price

FROM titles

WHERE price<10 AND price>30;

Очевидно, что результатом этого запроса(синтаксически абсолютно корректного)является пустое множество строк, посколь-ку трудно представить себе такую книгу,которая одновременно (добавим к этому –в одной и той же стране и у одного и того

Листинг 4.29. Чтобы «исправить» запрос,представленный в листинге 4.28, были введеныкруглые скобки, чтобы заставить СУБД выполнитьоператор OR раньше, чем оператор AND. Результатисполнения этого запроса см. на рис. 4.29

SELECT title_id, type, price

FROM titles

WHERE (type = 'history'

OR (type = 'biography')

AND price < 20;

title_id type price

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

T02 history 19.95

T06 biography 19.95

T12 biography 12.99

Рис. 4.29. Результат исполненияоткорректированного запроса, представленногов листинге 4.29. Задача решена

Page 121: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

121

же продавца) стоила бы и менее 10 долла-ров, и более 30 долларов. Но применениеоператора AND требует от СУБД именно та-кого понимания. В то же время, если поду-мать над смыслом директивы на русскомязыке, становится ясно, что при переводе еена SQL условия отбора книг по критерию«цены» надо соединять не оператором AND(потому что по-русски было бы не «ценаи цена», а «книги и книги»), а операторомOR, так как именно он выбирает книги,удовлетворяющие хотя бы одному из кри-териев, налагаемых на цену книг, а не обо-им критериям одновременно (в русскомварианте смысл критерия «книги и книги»≠ «книги по цене 1 или по цене 2»): WHEREprice<10 OR price>30. Если мы заменимэтим предложением соответствующеепредложение WHERE в приведенном запро-се, то он заработает правильно.

С помощью операторов одно и то же ус-ловие можно выразить разными способа-ми (см. табл. 4.6). Первые две эквивалент-ности называются логическими законамиМоргана, а последняя эквивалентность –двойным отрицанием.

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

NOT ((p AND q) OR (NOT p AND r)) =

= NOT (p AND q) AND NOT (NOT p AND r) =

= (NOT p OR NOT q) AND (p OR NOT r)

Если вы работаете в среде MySQL, имейтев виду, что False AND Unknown будет рав-но Unknown, а не False, как это, казалосьбы, следует из табл. 4.3.

Таблица 4.6. Логическая эквивалентность условий

Условие Эквивалентное усло-вие

NOT (p AND q) (NOT p) OR (NOT q)

NOT (p OR q) (NOT p) AND (NOT q)

NOT (NOT p) P

Комбинирование условий с помощью операторов AND, OR и NOT

Page 122: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

122 Выбор данных из произвольной таблицы

Сравнение по шаблонуоператором LIKEВсе приведенные нами запросы были при-мерами того, как выбирать строки на ос-нове знания точных значений какого-ни-будь столбца или столбцов. Но, если точ-ные значения не известны или известнылишь частично, рассмотренные выше при-емы не сработают. В этом случае вам при-годится оператор LIKE, который позволяетрешать задачи выборки на основе непол-ной информации (кто-то говорит вам:«Имя этого автора начинается на Кел…»)и выбирать строки, значения которых по-хожи друг на друга в каком-либо смысле(вам нужно ответить на вопрос: «Кто изавторов, занесенных в нашу базу, живетв районе Сан-Франциско?»). Перечислимосновные характеристики оператора LIKE:

работает только со строками символов,но не с числами и не со значениямидаты и времени;всегда применяет какой-нибудь шаблонзначений, которому должны соответ-ствовать отбираемые строки. Шабло-ном называется произвольная закавы-ченная строка, содержащая какую-либокомбинацию буквенных символов, со-ответствие которым должно быть абсо-лютным, и произвольную комби-нацию метасимволов. Здесь под ме-тасимволами понимаются специаль-ные операторы, которые работают какподстановочные знаки и нужны длятого, чтобы описывать структуру дан-ных. В шаблоне значений такой ме-тасимвол задает определенную струк-туру того элемента данных, которыйдолжен быть подставлен на его местов этот шаблон для проверки соответ-ствия. В табл. 4.7 перечислены допус-тимые операторы-метасимволы, а в

Таблица 4.7. Метасимвольные операторы

Оператор-метасимвол Соответствует

% Строке, состоящейиз любого количествапроизвольныхсимволов

_ Любому одномусимволу

Page 123: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

123

табл. 4.8 – некоторые ходовые примерышаблонов значений;проверка на совпадение с шаблономзначений может учитывать или не учи-тывать регистр букв (см. примечаниеDBMS в разделе «Фильтрация строкс помощью предложения WHERE» вэтой главе);вы всегда можете инвертировать лю-бое условие LIKE, поставив перед опера-тором LIKE оператор NOT (то есть опера-тор NOT LIKE – это инверсия оператораLIKE);вы можете объединять условия LIKEс любым количеством других условийлогическими операторами AND и OR;

Таблица 4.8. Примеры применения метасимволов в шаблонах

Шаблон Соответствует

'A%' Соответствует любому строковому значению длиной не меньше одного символа,начинающемуся с заглавной английской (это важно) «A», включая просто однузаглавную букву «A». Пример строковых значений, соответствующих данному шаб-лону: ‘Anonymous’, ‘AC/DC’

'%s' Любому строковому значению длиной не меньше одного символа, заканчивающе-муся строчной буквой «s», включая просто одну строчную букву «s». Имейтев виду, что строковое значение, состоящее из «s» и следующих за ней пробелов,не отвечает данному шаблону. Вот еще пара строковых значений: 'DMBSes','Victoria Falls'

'%in%' Любому строковому значению длиной не меньше двух символов, содержащемусочетание строчных букв «in». Этому шаблону соответствуют строковые значения'in', 'inch', 'Pine', 'linchpin', 'lynchpin'

'____' Любому строковому значению длины, равному 4. Этому шаблону соответствуют'АБВГ', 'Я ем' и 'Jack'

'Qua__' Любому строковому значению длиной 5 символов, начинающемуся с Qua.… Это-му шаблону соответствуют 'Quack', 'Quaff' и 'Quake'

'_re_' Любому строковому значению длины, равному 4 символам; его вторым и третьимзначениями является пара строчных английских букв «re». Под этот шаблон подхо-дят следующие строковые значения: 'Tree', 'area' и 'fret'

'_re%' Любому строковому значению длиной не меньше 3 символов; в нем на второми третьем местах стоят английские строчные буквы «re». Под этот шаблон подходятследующие строковые значения: 'Tree', 'area', 'fret', 'fretful' и 'freeze'

'%re_' Любому строковому значению длиной не меньше 3 символов; в нем на второми третьем местах, считая с конца, стоят английские строчные буквы «e» и «r» соот-ветственно. Под этот шаблон подходят следующие строковые значения: 'Tree','area', 'fret', 'red' и 'Blood red'

когда некий шаблон значений совсем несодержит метасимволов, оператор LIKEработает как оператор сравнения (=) –см. табл. 4.2, а оператор NOT LIKE –соответственно как оператор сравнения(<>). К примеру, предложение WHEREcity LIKE 'New York' эквивалентно пред-ложению WHERE city = 'New York'.

Фильтрация строкпо произвольному шаблону значений

Напечатайте:SELECT columns

FROM table

WHERE test_column [NOT] LIKE

'pattern';

Сравнение по шаблону оператором LIKE

Page 124: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

124 Выбор данных из произвольной таблицы

Подразумевается, что:

идентификатор columns будет замененодним или несколькими, разделенны-ми запятыми, реальными именами ка-ких-нибудь столбцов;

идентификатор table будет замененименем той таблицы, которая содер-жит эти столбцы columns;

вы, формируя условие поиска, сделае-те следующее:

– вместо test_column подставите имякакого-нибудь одного столбца в таб-лице table, причем этот столбец со-всем не должен входить в составстолбцов columns;

– вместо pattern подставите шаблонзначений, которые СУБД будет со-поставлять со значениями в столб-це test_column;

шаблон значений – это строка сим-волов наподобие тех, что приведеныв табл. 4.8;

если нужно отобрать строки, которыене соответствуют выбранному шабло-ну значений, воспользуйтесь не опе-ратором LIKE, а оператором NOT LIKE(см. листинги и рис. 4.30–4.33).

Одной из особенностей SQL является то,что можно включать в шаблон значенийметасимволы не только как таковые (тоесть не только как операторы и подстано-вочные, специальные знаки), но и какобыкновенные символы. Чтобы СУБД пе-рестала воспринимать произвольный ме-тасимвол в качестве оператора и специаль-но го символа, ей надо об этом объявить спомощью другого оператора и специально-го символа, называемого переключающимсимволом. Посредством переключающегосимвола вы сможете в нужный моментобъяснить СУБД, чтобы она воспринимала

Листинг 4.30. Распечатать из нашей базы данныхимена и фамилии авторов, чьи фамилииначинаются на Kel. Результат исполнения запросасм. на рис. 4.30

SELECT au_fname, au_lname

FROM authors

WHERE au_lname LIKE "Kel%";

au_fname au_lname

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

Christian Kells

Kellsey

Рис. 4.30. Результат исполнения запроса,представленного в листинге 4.30

Page 125: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

125

тот или иной метасимвол, скажем, знакпроцентов или знак подчеркивания, когдабудет определять, соответствует или нетнекая строка заданному шаблону значений.Чтобы освободить нужный вам метасимволот его специального значения, следует в оп-ределенном месте тела команды напеча-тать переключающий символ и сразу пос-ле него без пробела – нужный вам мета-символ. А для того, чтобы обозначить этотсамый переключающий символ, надо при-менить ключевое слово ESCAPE. Если, на-пример, в качестве переключающегосимвола используется восклицательныйзнак, то сочетание знаков !% в шаблонезначений будет означать буквально %,и более ничего. Такие метасимволы назы-ваются переключенными. При этом непе-реключенные метасимволы (которые неявляются переключенными) сохраняютсвое специальное значение. Символ, опре-деленный вами как переключающий, неможет быть составным элементом шабло-на. Например, если надо организовать по-иск подстроки '50% OFF!', придется выб-рать в качестве переключающего символане восклицательный знак, а что-то другое.Наконец, шаблоны значений, использую-щие переключенные символы, принятотоже называть переключенными.

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

Включение в шаблон значенийзнака метасимвола

Напечатайте:SELECT columns

FROM table

WHERE test_column [NOT] LIKE

'pattern'

ESCAPE 'escape_char';

Листинг 4.31. Распечатать из нашей базы данныхимена и фамилии авторов, в фамилиях которыхна местах 3-го и 4-го знаков есть буква «l».Результат исполнения этого запросасм. на рис. 4.31

SELECT au_fname, au_lname

FROM authors

WHERE au_lname LIKE '__ll%';

Листинг 4.32. Распечатать имена и фамилииавторов, учитываемых в нашей базе данных,которые живут в районе залива Сан-Франциско(почтовый индекс этого места начинаетсяс цифр 94…). Результат исполнения запросасм. на рис. 4.32

SELECT au_fname, au_lname, city, state,

zip

FROM authors

WHERE zip LIKE '94___';

au_fname au_lname city state zip

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

Hallie Hull San Francisco CA 94123

Klee Hull San Francisco CA 94123

Kellsey Palo Alto CA 94305

Рис. 4.32. Результат исполнения запроса,представленного в листинге 4.32

au_fname au_lname

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

Hallie Hull

Klee Hull

Christian Kells

Kellsey

Рис. 4.31. Результат исполнения запроса,представленного в листинге 4.31

Сравнение по шаблону оператором LIKE

Page 126: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

126 Выбор данных из произвольной таблицы

Предполагается, что:

за исключением предложения ESCAPE,весь синтаксис именно такой, как у ко-манды SELECT в подразделе «Фильтра-ция строк по произвольному шаблонузначений» настоящей главы;вы сделаете следующее (см. листинг4.34 и рис. 4.34):– замените escape_char каким-нибудь

одним знаком, который и будетпереключающим символом в этомзапросе;

– не будете включать переключающийсимвол, который пока обозначен какescape_char, в шаблон значений.

Вместо test_column вы вполне можетеподставить произвольное выражение.

Оператор NOT можно ставить и передоператором LIKE, и перед шаблономtest_column. Это значит, что операторNOT, который может находиться передоператором LIKE, работает независимо отоператора NOT, который может стоять пе-ред шаблоном значений (посмотрите, од-нако, подраздел «Оператор NOT» из пред-шествующего раздела). Например, пред-ложение WHERE phone NOT LIKE '212-%'эквивалентно предложению WHERE NOT

phone LIKE '212-%'. В этой связи вывполне могли бы написать следующее не-лепое по сути, но синтаксически правиль-ное предложение WHERE NOT phone NOTLIKE '212-%', задача которого – отобратьавторов, чей телефон начинается на 212.

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

Листинг 4.33. Перечислить из нашей базы данныхавторов, живущих за пределами территориальнойобласти, которая соответствует междугороднимтелефонным кодам 212, 415 и 303. Обратитевнимание на три разных способа исключитьненужные телефонные номера с помощьюшаблона. Советуем воспользоваться первымспособом, потому что проверка соответствийоднознаковому (_) шаблону выполняется намногобыстрее, чем многознаковому (%). Результатисполнения запроса см. на рис. 4.33

SELECT au_fname, au_lname, phone

FROM authors

WHERE phone NOT LIKE '212-___-____'

AND phone NOT LIKE '415-___-%'

AND phone NOT LIKE '303-%';

au_fname au_lname phone

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

Sarah Buchman 718-496-7223

Kellsey 650-836-7128

Paddy O’Furniture 941-925-0752

Рис. 4.33. Результат исполнения запроса,представленного в листинге 4.33

Таблица 4.9. Переключенныеи непереключенные шаблоны значений

Шаблон Соответствует

‘100%’ Непереключенный шаблон.Соответствует строке символовдлиной не менее 3 символов,начинающейся со 100…

‘100!%’ Переключенный шаблон. Соответ-ствует строго подстроке ‘100%’

‘_op’ Непереключенный шаблон.Соответствует строке длинойстрого 3 символа, на второми третьем местах которой стоятбуквы латинского алфавита «o»и «p» соответственно

‘!_op’ Переключенный шаблон. Соответ-ствует строго подстроке ‘_op’

Page 127: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

127

СУБД Microsoft Access не поддерживаетпредложение ESCAPE. Если же вы работа-ете в этой среде и намерены переключитькакой-нибудь метасимвол шаблона, вамследует заключить этот метасимвол в квад-ратные скобки. Например, чтобы запуститьв Access запрос, представленный в лис-тинге 4.34, нужно переписать предложе-ние WHERE следующим образом: WHEREtitle_name LIKE '%[%]%'.

Некоторые коммерческие СУБД, в томчисле Microsoft SQL Server, поддержива-ют так называемые регулярные выраже-ния стандарта POSIX, когда метасимволв виде квадратных скобок с указаннымв них диапазоном значений соответству-ет любому символу из этого диапазона,а метасимвол в виде квадратных скобок,в которых сначала стоит символ крыш-ки, а потом некий диапазон, соответ-ствует любому символу не из диапазона(в математике говорят «из дополненияк диапазону»). В табл. 4.10 приведеныпримеры таких шаблонов. Но имейте ввиду, что синтаксис регулярных выраже-ний зависит от конкретной СУБД, поэто-му, если вы хотите их применять, орга-низуйте сначала поиск в документациипо своей СУБД с использованием ключаWHERE или LIKE.

Отдельные СУБД допускают применениеоператора LIKE для поиска числовыхзначений, а также значений даты и вре-мени.

Листинг 4.34. Перечислить из нашей типовой базыназвания книг, которые содержат знак процентов(именно как знак %, а не как оператор-метасимвол).Заметьте, что СУБД воспринимает только тот знакпроцентов в шаблоне значений, который следует запереключающим символом (в данном случае завосклицательным знаком), а все остальные знакипроцентов в том же самом шаблоне (которыйтеперь стал переключенным) сохраняютфункциональность операторов-метасимволов.Результат исполнения запроса см. на рис. 4.34

SELECT title_name

FROM titles

WHERE title_name LIKE '%!%%' ESCAPE

'!';

Таблица 4.10. Примеры шаблонов [] и [^]

Шаблон Соответствует

‘[a-c]’ ‘bat’, ‘cat’, но не ‘fat’

‘[bcf]at’ ‘bat’, ‘cat’, ‘fat’, но не ‘eat’

‘[^c]at’ ‘bat’, ‘fat’, но не ‘cat’

‘se[^n]%’ Всем строкам длинойне менее двух символов;они начинаются на «se»,но не заканчиваются на «n»

title_name

——————————————------------------------————-

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

Сравнение по шаблону оператором LIKE

Page 128: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

128 Выбор данных из произвольной таблицы

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

работает со строками символов, числа-ми и значениями даты и времени;его диапазон состоит из двух час-тей (символически обозначаемых какlow_value и high_value), разделенныхоператором AND:– меньшее значение low_value, опреде-

ляющее нижнюю границу диапазо-на;

– большее значение high_value, опре-деляющее верхнюю границу диапа-зона, и такое, что оно не меньшенижней границы;

это очень удобное, сжатое и экономич-ное предложение, но вы можете ис-ключить его из предложения WHERE, по-строив с помощью оператора AND экви-валентное, но более длинное предло-жение WHERE, например: предложениеWHERE test_column BETWEEN low_value ANDhigh_value эквивалентно предложениюWHERE (test_column >= low_value) AND(test_column <= high_value);обозначает замкнутый диапазон, верх-няя и нижняя границы которого вклю-чаются в него. Но вы можете опреде-лить и открытый диапазон, исключаю-щий границы. Для этого, правда, вамнадо отказаться от оператора BETWEEN ииспользовать в предложении WHERE опе-раторы сравнения (<) и (>), например:WHERE (test_column > low_value) AND(test_column < high_value);

имейте в виду, что сравнения строкмогут быть (или не быть) чувствитель-ными к регистру букв. Это зависит оттого, с какой СУБД вы работаете (см.примечание DBMS в разделе «Фильт-рация строк с помощью предложенияWHERE» этой главы);оператор BETWEEN можно инвертиро-вать оператором NOT в оператор NOTBETWEEN;условия, выраженные предложениямиBETWEEN, можно объединять с другимиусловиями с использованием операто-ров AND и OR.

Фильтрация строк по любомудиапазону

Напечатайте:SELECT columns

FROM table

WHERE test_column [NOT] BETWEENlow_value AND high_value;

Подразумевается, что:

идентификатор columns будет замененодним или несколькими, разделенны-ми запятыми, реальными именами ка-ких-нибудь столбцов;идентификатор table будет замененименем той таблицы, которая содер-жит столбцы columns;формируя условие поиска, вы сделаетеследующее:– вместо test_column подставите имя ка-

кого-нибудь одного столбца в табли-це table, причем он совсем не долженвходить в состав столбцов columns;

– вместо low_value и high_value под-ставите границы диапазона значений,которые СУБД будет сопоставлять созначениями в столбце test_column,при этом большее значение high_-value, определяющее верхнюю гра-

Page 129: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

129

ницу диапазона, должно быть неменьше low_value, определяющегонижнюю границу диапазона;

оба граничных значения, определяю-щие диапазон, должны относиться кодному и тому же типу данных, кото-рый, в свою очередь, должен совпадатьили быть совместимым с типом дан-ных, назначенным столбцу test_column;если нужно отобрать строки, которыене соответствуют выбранному диапа-зону значений, вы укажете не операторBETWEEN, а оператор NOT BETWEEN (см. ли-стинги и рис. 4.35–4.37).

Вместо test_column вы вполне можетеподставить произвольное выражение.

Оператор NOT можно ставить и перед опера-тором BETWEEN, и перед столбцомtest_column. Это значит, что оператор NOT,стоящий перед оператором BETWEEN, рабо-тает независимо от оператора NOT, которыйнаходится перед столбцом test_column (по-смотрите, тем не менее, подраздел «Опера-тор NOT» и советы в разделе «Сравнение пошаблону оператором LIKE» в данной главе).

Задача определения любого символьногодиапазона требует немалых интеллектуаль-ных усилий. Допустим, что нам требуетсяорганизовать поиск авторов, чьи фамилииначинаются с буквы «F». ПредложениеWHERE last_name BETWEEN 'F' AND 'G' несработает. Когда мы так говорим, то имеемв виду, что оно, будучи вполне корректнымсинтаксически, даст неверный результат.В данном же случае в результат вошел быавтор, в фамилии которого есть буква «G»(подчеркнем, есть буква «G», а не начинает-ся с буквы «G», ведь интервал – замкну-тый), в дополнение ко всем тем авторам,фамилии которых начинаются с буквы «F».Правильный способ указать интервал длярешения подобных задач – отметить верх-нюю границу двумя буквами (почти всегда),первой и последней, например: WHERE

last_name BETWEEN 'F' AND 'Fz'.

Листинг 4.35. Перечислить из нашей базы всехавторов, у которых почтовый индекс не попадаетв интервал от 20 000 до 89 999. Результатисполнения запроса см. на рис. 4.35

SELECT au_fname, au_lname, zip

FROM authors

WHERE zip NOT BETWEEN '20000' AND '89999';

Листинг 4.36. Перечислить названия книг изнашей базы, цена которых составляет от 10до 19,95 долларов включительно. Результатисполнения этого запроса см. на рис. 4.36

SELECT title_id, price

FROM titles

WHERE price BETWEEN 10 AND 19.95;

au_fname au_lname zip

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

Sarah Buchman 10468

Hallie Hull 94123

Klee Hull 94123

Christian Kells 10014

Kellsey 94305

Рис. 4.35. Результат исполнения запроса,представленного в листинге 4.35

title_id price

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

T02 19.95

T04 12.99

T06 19.95

T08 10.00

T09 13.95

T12 12.99

Рис. 4.36. Результат исполнения запроса,представленного в листинге 4.36

Сравнение с диапазоном с помощью оператора BETWEEN

Page 130: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

130 Выбор данных из произвольной таблицы

В листинге 4.38 показано, как переписатьзапрос, представленный в листинге 4.36,для открытого диапазона, исключающегозначения 10 и 19,95 доллара. Результатработы откорректированного запроса см.на рис. 4.38.

В среде СУБД PostgreSQL из примеров влистингах 4.36 и 4.38 вам следует преоб-разовать числа с плавающей десятичнойточкой в тип DECIMAL (см. раздел «Преоб-разование типов данных с помощью фун-кции CAST()» в главе 5). Преобразуйте ли-тералы с плавающей точкой следующимобразом: CAST(19.95 AS DECIMAL).

В среде СУБД Microsoft Access, печатаялитералы даты и времени в предложенииWHERE, вам следует опускать ключевое сло-во DATE, а также заключать каждый такойлитерал не в кавычки, а в знаки решетки (#).Чтобы, например, выполнить запрос, пред-ставленный в листинге 4.37, замените датыв предложении WHERE следующими #2000-01-01# и #2000-12-31#.

В среде СУБД Microsoft SQL Server, печатаялитералы даты и времени в предложенииWHERE, вам следует опускать ключевое сло-во DATE. Чтобы, например, выполнить за-прос, представленный в листинге 4.37, за-мените даты в предложении WHERE следу-ющими '2000-01-01' и '2000-12-31'.

Имейте в виду, что некоторые СУБД допус-кают, чтобы значение, подставляемое вме-сто low_value, превосходило (не предше-ствовало) значение, подставляемое вместоhigh_value. Чтобы разобраться в этомвопросе как следует, организуйте поискв документации по своей СУБД с исполь-зованием ключей поиска WHERE илиBETWEEN.

Листинг 4.37. Перечислить названия книг,которые вышли из печати в 2000 году.Результат исполнения запроса см. на рис. 4.37

SELECT title_id, pubdate

FROM titles

WHERE pubdate BETWEEN DATE '2000-01-01'

AND DATE '2000-12-31';

Листинг 4.38. Перечислить названия книг, ценакоторых составляет от 10 до 19,95 доллара.Результат исполнения запроса см. на рис. 4.38

SELECT title_id, price

FROM titles

WHERE (price > 10)

AND (price < 19.95);

title_id pubdate

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

T01 2000-08-01

T03 2000-09-01

T06 2000-07-31

T11 2000-11-30

T12 2000-08-31

Рис. 4.37. Результат исполнения запроса,представленного в листинге 4.37

title_id price

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

T04 12.99

T09 13.95

T12 12.99

Рис. 4.38. Результат исполнения запроса,представленного в листинге 4.38

Page 131: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

131

Фильтрацияс помощью оператора IN

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

работает со строками символов, числа-ми и значениями даты и времени;

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

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

test_column IN (value1, value2, value3)эквивалентно предложению WHERE (test_-column = value1) OR (test_column = value2)OR (test_column = value3);

имейте в виду, что сравнения строкмогут быть (или не быть) чувствитель-ными к регистру букв (это зависит оттого, с какой СУБД вы работаете; см.примечание DBMS в разделе «Фильтра-ция строк с помощью предложенияWHERE» в данной главе);

его можно преобразовать операторомNOT в оператор NOT IN;

условия, выраженные предложениямиIN, можно объединять с другими усло-виями операторами AND и OR.

Фильтрация строкпо произвольному списку

Напечатайте:SELECT columns

FROM table

WHERE test_column [NOT] IN

(value1, value2,…);

Подразумевается, что:

идентификатор columns будет замененодним или несколькими, разделенны-ми запятыми, реальными именами ка-ких-нибудь столбцов;

идентификатор table будет заменен име-нем таблицы, которая содержит столб-цы columns;вы, формируя условие поиска, выпол-ните следующее:

– вместо test_column подставите имякакого-нибудь столбца в таблицеtable, причем этот столбец совсемне должен входить в состав столб-цов columns;

– вместо value1, value2,… подставитеодно или несколько разделенных за-пятыми значений, которые СУБДбудет сопоставлять со значениямив столбце с именем test_column,при этом значения списка value1,value2,… могут быть перечислены влюбом порядке и их тип должен илисовпадать, или быть совместимым стипом столбца test_column;

если нужно отобрать строки, которыене соответствуют выбранному спискузначений, вы укажете не оператор IN,а оператор NOT IN (см. листинги ирис. 4.39–4.41).

Фильтрация с помощью оператора IN

Page 132: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

132 Выбор данных из произвольной таблицы

Вместо test_column вы вполне можетеподставить произвольное выражение.

Оператор NOT можно ставить и перед опе-ратором IN, и перед test_column. Этозначит, что оператор NOT, стоящий передоператором IN, работает независимо отоператора NOT, расположенного передстолбцом test_column, по которому про-водится фильтрование (см. подраздел«Оператор NOT» и советы в разделе «Срав-нение по шаблону оператором LIKE» в на-стоящей главе).

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

Порядок вычисления сложных условий го-раздо проще понять, если вместо много-численных операторов OR применять одиноператор IN (см. раздел «Комбинированиеусловий с помощью операторов AND, OR иNOT» в данной главе).

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

Оператор NOT IN эквивалентен объедине-нию операторами AND нескольких усло-вий-неравенств. Например, следующийзапрос эквивалентен запросу из листинга4.39:

SELECT au_fname, au_Lname, state

FROM authors

WHERE state <> 'NY'

AND state <> 'NJ'

AND state <> 'CA';

Листинг 4.39. Перечислить авторов, учитываемыхв нашей базе, которые живут не в штате Нью-Йорк,не в штате Нью-Джерси, не в штате Калифорния.Результат исполнения этого запросасм. на рис. 4.39

SELECT au_fname, au_lname, state

FROM authors

WHERE state NOT IN ('NY', 'NJ', 'CA');

Листинг 4.40. Перечислить названия книг изнашей типовой базы, за которые аванс,выплаченный автору каждой книги, составил или0 долларов, или 1000 долларов, или 5000долларов. Результат исполнения этого запроса см.на рис. 4.40

SELECT title_id, advance

FROM royalties

WHERE advance IN

(0.00, 1000.00, 5000.00);

au_fname au_lname state

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

Wendy Heydemark CO

Paddy O’Furniture FL

Рис. 4.39. Результат исполнения запроса,представленного в листинге 4.39

title_id advance

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

T02 1000.00

T08 0.00

T09 0.00

Рис. 4.40. Результат исполнения запроса,представленного в листинге 4.40

Page 133: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

133

В среде СУБД PostgreSQL при работес запросом из листинга 4.40 следует пре-образовать числа с плавающей десятичнойточкой в тип данных DECIMAL (см. раздел«Преобразование типов данных с помо-щью функции CAST()» в главе 5). Моди-фицируйте литералы с плавающей точкойследующим образом:

WHERE advance IN

(CAST(0.00 AS DECIMAL),

CAST(1000.00 AS DECIMAL),

CAST(5000.00 AS DECIMAL))

В среде СУБД Microsoft Access, печатаялитералы даты и времени в предложенииWHERE, следует опускать ключевое словоDATE и окружать каждый такой литерал некавычками, а знаками решетки (#). Чтобы,например, запустить запрос, представлен-ный в листинге 4.41, измените предложе-ние WHERE следующим образом:

WHERE pubdate IN

(#2000-01-01#,

#2001-01-01#,

#2002-01-01#)

В среде СУБД Microsoft SQL Server, печатаялитералы даты и времени в предложенииWHERE, следует опускать ключевое словоDATE. Чтобы, например, запустить запрос,представленный в листинге 4.41, измени-те предложение WHERE следующим обра-зом:

WHERE pubdate IN

('2000-01-01',

'2001-01-01',

'2002-01-01')

Листинг 4.41. Перечислить названия (точнее,однозначные идентификаторы и датыпубликации) книг из нашей базы,опубликованных 1 января 2000, 2001 и 2002годов. Результат исполнения этого запроса см. нарис. 4.41

SELECT title_id, pubdate

FROM titles

WHERE pubdate IN

(DATE '2000-01-01',

DATE '2001-01-01',

DATE '2002-01-01');

title_id pubdate

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

T05 2001-01-01

Рис. 4.41. Результат исполнения запроса,представленного в листинге 4.41

Фильтрация с помощью оператора IN

Page 134: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

134 Выбор данных из произвольной таблицы

Проверка на значение nullс помощью оператораIS NULLВ разделе «Значение null» главы 3 мы го-ворили о том, что значения null замеща-ют отсутствующие или неизвестные зна-чения. Это создает определенную пробле-му с обнаружением этих значений, пото-му что ни опция LIKE, ни опция BETWEEN, ниопция IN, ни другие опции предложенияWHERE не могут обнаружить значений null,поскольку неизвестное значение не можетудовлетворить каким бы то ни было изве-стным условиям. Между тем находить этизначения иногда необходимо. Например,обратите внимание, что строка P03 табли-цы publishers (издатели) как раз имеетзначение null в столбце state (штат) про-сто потому, что в Германии нет штатов(см. листинги и рис. 4.43 и 4.44). Но длятого, чтобы «отловить» это значение null,мы не смогли бы применить так называе-мые дополняющие сравнения (то есть ин-вертированные), так как искомое значениеnull является неопределенным.

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

Перечислим основные характеристикиоператора IS NULL:

работает со столбцами любых типовданных;

его можно инвертировать в оператор ISNOT NULL;

можно объединять условия IS NULLс другими условиями операторами ANDи OR.

Листинг 4.42. Перечислить места нахождения всехиздателей, учитываемых в нашей типовой базе.Результат исполнения этого запроса см. на рис. 4.42

SELECT pub_id, city, state, country

FROM publishers;

pub_id city state country

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

P01 New York NY USA

P02 San Francisco CA USA

P03 Hamburg NULL Germany

P04 Berkeley CA USA

Рис. 4.42. Результат исполнения запроса,представленного в листинге 4.42

Листинг 4.43. Перечислить идентификаторы иадреса издателей, учитываемых в нашей типовойбазе данных, которые находятся в штатеКалифорния. Результат исполнения этого запросасм. на рис. 4.43

SELECT pub_id, city, state, country

FROM publishers

WHERE state = 'CA';

pub_id city state country

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

P02 San Francisco CA USA

P04 Berkeley CA USA

Рис. 4.43. Результат исполнения запроса,представленного в листинге 4.43

Page 135: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

135

Выбор строк, содержащих значения null

Напечатайте:SELECT columns

FROM table

WHERE test_column IS [NOT] NULL;

Подразумевается, что:

идентификатор columns будет замененодним или несколькими, разделенны-ми запятыми, реальными именами ка-ких-нибудь столбцов;

идентификатор table будет замененименем таблицы, которая содержитстолбцы columns;

вы, формируя условие поиска, вместоtest_column подставите имя какого-ни-будь одного столбца в таблице table,причем он совсем не должен входитьв состав столбцов columns, но именносреди его значений вы будете искатьзначения null;

если вы захотите отобрать строки, ко-торые содержат в столбце test_columnзначения, не являющиеся значениямиnull, вы укажете не оператор IS NULL,а оператор IS NOT NULL (см. листингии рис. 4.45–4.46).

Вместо test_column вы вполне можетеподставить произвольное выражение.

Оператор NOT можно ставить и перед клю-чевым словом NULL, и перед test_column.Это значит, что оператор NOT, стоящий пе-ред ключевым словом NULL, работает не-зависимо от оператора NOT, расположен-ного перед столбцом test_column, покоторому проводится фильтрование (см.советы в разделе «Сравнение по шаблонуоператором LIKE» в данной главе).

Листинг 4.44. Этот запрос является неудачнойпопыткой (правильное решение той же задачипоказано в листинге 4.45) выбрать издателей,учитываемых в нашей типовой базе данных,которые расположены вне штата Калифорния.Результат исполнения этого запроса см. на рис. 4.44

SELECT pub_id, city, state, country

FROM publishers

WHERE state <> 'CA';

pub_id city state country

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

P01 New York NY USA

Рис. 4.44. Результат исполнения запроса,представленного в листинге 4.44. Мы видим, что,кроме тех издателей, которые находятся в штатеКалифорния, данный результат игнорируетстроку P03, хотя соответствующий издательрасположен не в Калифорнии, а в Германии.Строго говоря, условия state = 'CA' и state <>'CA' не являются взаимодополняющими, потомучто значения null не соответствуют ни одномузначению, независимо от типа. Следовательно,их нельзя обнаружить ни одним из типов условий,которые мы рассматривали

Проверка на значение null с помощью оператора IS NULL

Page 136: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

136 Выбор данных из произвольной таблицы

Листинг 4.46. Выбрать книги-биографии, датыпубликации (прошлые или будущие) которыхизвестны. Результат исполнения этого запросасм. на рис. 4.46

SELECT title_id, type, pubdate

FROM titles

WHERE type = 'biography'

AND pubdate IS NOT NULL;

Из результата произвольного запроса вамудастся исключить предложением WHEREнекую строку, содержащую значение null,только при условии, что столбец этой стро-ки, который содержит значение null, ока-жется столбцом, объявленным в предложе-нии WHERE как test_column. Но если зна-чение null находится в другом столбце,строка окажется в результате запроса.К примеру, следующий запрос выберет всестроки таблицы publishers (см. рис. 4.42),так как значение null, которое находитсяв столбце state, ни с чем не сравнивает-ся, поскольку задачу столбца test_columnздесь выполняет столбец state:

SELECT pub_id, city, state, country

FROM publishers

WHERE country <> 'Canada';

Повторим еще раз, что никакая пустаястрока (‘’) не равна значению null. Напри-мер, в нашей типовой базе данных в таб-лице authors есть столбец au_fname

(имя), который, в свою очередь, содержитпустую строку (имеется в виду строка кактип данных) в строке (имеется в видустрока как структурный элемент таблицы),соответствующей автору A06 с фамилиейKellsey. Так вот, условием, применяемым впредложении WHERE для того, чтобы отыс-кать автора, у которого вместо имени пус-тая строка, является WHERE au_fname = '',а не WHERE au_fname IS NULL. Тем не ме-нее обязательно ознакомьтесь с примеча-ниями DBMS, относящимися к Oracle, вразделе «Значение null» главы 3.

Чтобы предотвратить появление значенийnull в произвольном столбце таблицы, об-ратитесь к разделу «Запрет значения nullс помощью ограничения NOT NULL» в гла-ве 10.

Листинг 4.45. Перечислить всех издателей, которыенаходятся вне штата Калифорния. Результатисполнения этого запроса см. на рис. 4.45

SELECT pub_id, city, state, country

FROM publishers

WHERE state <> 'CA'

OR state IS NULL;

pub_id city state country

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

P01 New York NY USA

P03 Hamburg NULL Germany

Рис. 4.45. Результат исполнения запроса,представленного в листинге 4.45. Теперь мывидим, что издатель P03 есть в результате

title_id type pubdate

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

T06 biography 2000-07-31

T07 biography 1999-10-01

T12 biography 2000-08-31

Рис. 4.46. Результат исполнения запроса,представленного в листинге 4.46. Если бы неусловие IS NOT NULL, этот результат содержалбы и книгу с названием, определяемымпервичным ключом T10

Page 137: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

Операторы и функции нужны для того,чтобы вычислять различные выраженияпо данным, которые извлекаются из столб-цов или/и которые определяются вашейСУБД. С помощью операторов и функ-ций вы можете выполнять:

арифметические операции (например,всем сотрудникам фирмы сократитьзарплату на 10%);строковые операции (например, объе-динить личные данные и печатать по-чтовые адреса на почтовых конвертах);операции с данными даты и времени(например, вычислить интервалы меж-ду парами дат);разнообразные системные операции(например, выяснить у СУБД, сколькосейчас времени).

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

Операторы и функции 55555В разделе «Синтаксис SQL» главы 3 мы ужеговорили о том, что любое выражение –это синтаксически не запрещенная комби-нация символов и лексем, которая на лю-бом наборе значений, подставляемых вме-сто этих символов и лексем, принимаетодно-единственное значение определенноготипа или значение null. К примеру, в ариф-метической операции умножения price*2оператором является «*», а «price» и «2» –это операнды.

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

Page 138: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

138 Операторы и функции

Созданиепроизводных столбцовОператоры и функции нужны для того,чтобы с их помощью создавать так назы-ваемые производные столбцы. Любойпроизводный столбец – это результат вы-числительного процесса, получаемый входе исполнения какого-то запроса SELECT,содержащего в качестве одного из своихпредложений такое, которое отличаетсяот простой ссылки на один какой-нибудьстолбец. Производные столбцы служатисключительно для визуализации и никог-да не становятся постоянными столбцамитаблиц.Очень часто значения производного столб-ца вычисляются по значениям сущест-вующих столбцов базы данных, но вы впринципе можете создать любой производ-ный столбец с помощью какого-нибудьвыражения с константами (например, стро-ки символов, числа или даты) или какого-нибудь системного значения (скажем, сис-темного времени). В частности, запрос,представленный в листинге 5.1, реализуетодно тривиальное арифметическое вы-числение. И этому запросу не требуетсявообще никакого предложения FROM, таккак он не использует никаких данных (ре-зультат исполнения этого запроса см. нарис. 5.1). Здесь неплохо было бы освежитьв памяти материал из раздела «Таблицы,столбцы и строки» главы 2, где говори-лось о свойстве замкнутости реляцион-ной модели, которое, в свою очередь,состоит в том, что результатом любойоперации должна быть обязательно ка-кая-нибудь таблица. На первый взгляд,наблюдается противоречие с этим свой-ством. Но на самом деле никакого проти-воречия нет, просто результатом явля-ется простейшая таблица, состоящая изодного столбца и одной строки (то есть

Листинг 5.1. Здесь представлено одно выражениес константами в предложении SELECT. Данныйзапрос не требует никакого предложения FROM,поскольку никаких данных из таблицы невыбирается. Результат исполнения этого запросапоказан на рис. 5.1

SELECT 2 + 3;

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

2 + 3

-----

5

Листинг 5.2. Выбрать один столбец и одновыражение с константами. Результат исполненияэтого запроса см. на рис. 5.2

SELECT au_id, 2 + 3

FROM authors;

au_id 2 + 3

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

A01 5

A02 5

A03 5

A04 5

A05 5

A06 5

A07 5

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

Page 139: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

139

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

Если вы не присвоите производному столб-цу имя с помощью предложения AS, это сде-лает ваша СУБД (см. листинг 5.3 и рис. 5.3,а также раздел «Создание псевдонимовстолбцов с помощью предложения AS»в главе 4). Несмотря на возможность при-сваивать названия производным столбцампо умолчанию, мы постараемся давать имимена явным образом.

В среде Oracle предложение FROM являет-ся обязательной составляющей командыSELECT. Поэтому, если вы хотите выбратьэтой командой какое-нибудь выражениес константами, вам придется использоватьпредложение FROM, но с псевдотаблицейDUAL, которую ваша СУБД автоматическисоздает как раз на такой случай (имеетсмысл организовать поиск в документациипо вашей СУБД Oracle с использованиемключа DUAL). Чтобы в среде Oracle выпол-нить запрос из листинга 5.1, нужно доба-вить в него предложение FROM, котороеякобы выбирает нужную константу изпсевдотаблицы DUAL. Вот так:

SELECT 2 + 3

FROM DUAL;

В среде СУБД PostgreSQL, выполняя за-прос из листинга 5.3, следует преобразо-вать числа с плавающей десятичной точ-кой в тип данных DECIMAL (см. раздел«Преобразование типов данных с помо-щью функции CAST()» в этой главе). Из-мените формулу в производном столбце,содержащем значение с плавающей точ-кой, следующим образом:

Price * CAST((1 – 0.10) AS DECIMAL)

Листинг 5.3. Перечислить идентификаторы книгвместе с ценами, сниженными на 10%. Имейте ввиду, что, если из предложения SELECT удалитьоба предложения AS, соответствующиепроизводные столбцы будут названы поумолчанию. Результат исполнения этого запросасм. на рис. 5.3

SELECT title_id,

price,

0.10 AS "Discount",

price * (1 - 0.10) AS "New price"

FROM titles;

title_id price Discount New price

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

T01 21.99 0.10 19.79

T02 19.95 0.10 17.95

T03 39.95 0.10 35.96

T04 12.99 0.10 11.69

T05 6.95 0.10 6.25

T06 19.95 0.10 17.95

T07 23.95 0.10 21.56

T08 10.00 0.10 9.00

T09 13.95 0.10 12.56

T10 NULL 0.10 NULL

T11 7.99 0.10 7.19

T12 12.99 0.10 11.69

T13 29.99 0.10 26.99

Рис. 5.3. Результат исполнения запроса,представленного в листинге 5.3

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

Page 140: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

140 Операторы и функции

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

Произвольный бинарный (двухместный)арифметический оператор – это такойоператор, который выполняет какую-ни-будь математическую операцию над дву-мя числовыми операндами. Такими опе-раторами, конечно, являются обычныематематические операторы сложения (+),вычитания (–), умножения (*) и деления(/). Более наглядно все арифметическиеоператоры SQL представлены в табл. 5.1,где под expr понимается произвольноечисловое выражение.

Изменение знака произвольного числа

Напечатайте –expr.Предполагается, что вместо expr вы под-ставите какое-нибудь числовое выраже-ние (см. листинг 5.4 и рис. 5.4).

Сложение, вычитание,умножение или деление

Напечатайте:

expr1 + expr2 – если хотите сложить двачисловых выражения;

expr1 - expr2 – если хотите вычесть изодного числового выражения другоечисловое выражение;

Листинг 5.4. Изменить знак числа с помощьюоператора отрицания. Результат исполнения этогозапроса см. на рис. 5.4

SELECT title_id,

-advance AS "Advance"

FROM royalties;

Таблица 5.1. Арифметические операторы

Оператор Операция

-expr Инвертирует знак числовоговыражения

+expr Оставляет знак числовоговыражения без изменения

expr1 + expr2 Суммирует два числовыхвыражения

expr1 - expr2 Вычитает из одного числово-го выражения другое число-вое выражение

expr1 * expr2 Перемножает два числовыхвыражения

expr1 / expr2 Делит одно числовое выра-жение на другое числовоевыражение

title_id Advance

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

T01 -10000.00

T02 -1000.00

T03 -15000.00

T04 -20000.00

T05 -100000.00

T06 -20000.00

T07 -1000000.00

T08 0.00

T09 0.00

T10 NULL

T11 -100000.00

T12 -50000.00

T13 -20000.00

Рис. 5.4. Результат исполнения запроса,представленного в листинге 5.4. Обратитевнимание, что у нуля знака нет (он не может бытьположительным или отрицательным)

Page 141: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

141

expr1 * expr2 – если хотите перемно-жить два числовых выражения;expr1 / expr2 – если хотите разделитьодно числовое выражение на другоечисловое выражение.

Предполагается, что вместо expr1, expr2 выподставите какие-нибудь числовые выра-жения (см. листинг 5.5 и рис. 5.5).

Результат любой арифметической опера-ции равен null, если в ней присутствует зна-чение null.

Если вы хотите объединить несколько опе-раторов в одно выражение, вам могут по-надобиться круглые скобки (см. раздел«Определение последовательности вычис-лений » в этой главе).

Унарные операторы часто называют одно-местными операторами, а бинарные опе-раторы – двухместными.

СУБД предоставляют в распоряжение про-граммиста не только арифметические опе-раторы, но и функции, и дополнительныеарифметические операторы. Они включаютстатистические, финансовые, некоторыетак называемые научные, тригонометричес-кие функции и функции преобразования(организуйте поиск в документации по сво-ей СУБД с использованием ключей «опера-тор» и «функция»).

Если вы включите в одно арифметическоевыражение операнды разных числовых ти-пов данных, СУБД преобразует (или, как го-ворят, приведет) все операнды к типу дан-ных самого сложного из них и выдаст ре-зультат всего выражения как элемент дан-ных именно этого типа. Этот процессназывается повышением. Например, есливы сложите числа INTEGER (целые числа)и FLOAT (рациональные числа с плавающейточкой), СУБД преобразует целые числав рациональные с плавающей точкой (при-ведет целые числа к рациональным с пла-вающей точкой) и выдаст сумму как ра-циональное число с плавающей точкой. Но

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

SELECT title_id,

price * sales AS "Revenue"

FROM titles

WHERE type = 'biography'

ORDER BY price * sales DESC;

title_id Revenue

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

T07 35929790.00

T12 1299012.99

T06 225834.00

T10 NULL

Рис. 5.5. Результат исполнения запроса,представленного в листинге 5.5

Арифметические операции

Page 142: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

142 Операторы и функции

в некоторых случаях вам понадобится пре-образовывать один тип данных в другой типданных явным образом, то есть не полага-ясь на СУБД. Этот вопрос обсуждается в раз-деле «Преобразование типов данных с по-мощью функции CAST()» данной главы.

При делении целого на целое (то есть типINTEGER на тип INTEGER) следует бытьпредельно осторожным. В зависимости оттого, с какой именно СУБД вы работаете,дробная часть результата такого деленияможет быть не усечена совсем, усеченачастично или усечена полностью (то естьотброшена). Например, вы могли бы пред-полагать, что два производных столбца,которые порождаются запросом из листин-га 5.6, содержат одни и те же результаты,поскольку в обоих случаях значения столб-ца pages (тип INTEGER) делится на однои то же число. Однако в первом случае де-лителем является 10 (тип INTEGER), а вовтором – 10.00 (тип FLOAT). Так вот, СУБДMicrosoft Access, Oracle и MySQL выдадутрезультат, которого вы ожидаете (см. рис.5.6а), а СУБД Microsoft SQL Server иPostgreSQL усекут дробную часть частного(см. рис. 5.6б).

Листинг 5.6. В этом запросе первый из двухпроизводных столбцов получается делениемзначений столбца pages (то есть числа страницв книге) на целое число 10 (тип данных INTEGER).Второй же производный столбец получен делениемзначений того же самого столбца, но нарациональное число с плавающей десятичнойточкой 10.00 (тип данных FLOAT). Вы могли быожидать, что значения в обоих производныхстолбцах будут совпадать. На самом деле всезависит от того, с какой конкретно СУБД выработаете. Разные результаты исполнения этогозапроса, зависящие от СУБД, см. на рис. 5.6а и 5.6б

SELECT title_id,

pages,

pages/10 AS "pages/10",

pages/10.0 AS "pages/10.0"

FROM titles;

title_id pages pages/10 pages/10.0

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

T01 107 10.7 10.7

T02 14 1.4 1.4

T03 1226 122.6 122.6

T04 510 51.0 51.0

T05 201 20.1 20.1

T06 473 47.3 47.3

T07 333 33.3 33.3

T08 86 8.6 8.6

T09 22 2.2 2.2

T10 NULL NULL NULL

T11 826 82.6 82.6

T12 507 50.7 50.7

T13 802 80.2 80.2

title_id pages pages/10 pages/10.0

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

T01 107 10 10.7

T02 14 1 1.4

T03 1226 122 122.6

T04 510 51 51.0

T05 201 20 20.1

T06 473 47 47.3

T07 333 33 33.3

T08 86 8 8.6

T09 22 2 2.2

T10 NULL NULL NULL

T11 826 82 82.6

T12 507 50 50.7

T13 802 80 80.2

Рис. 5.6а. Результат исполнения запроса,представленного в листинге 5.6, в среде СУБДMicrosoft Access, Oracle и MySQL. Обратитевнимание, что деление целого на целое даетрациональное число с плавающей десятичнойточкой

Рис. 5.6б. Результат исполнения запроса,представленного в листинге 5.6, в среде СУБДMicrosoft SQL Server и PostgreSQL. Обратитевнимание, что деление целого на целое дает целое

Page 143: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

143

ОпределениепоследовательностивычисленияЕсли в каком-нибудь выражении прини-мают участие несколько операторов, ихприоритет в процессе вычисления такоговыражения определяется правилами пред-шествования, или очередностью вычис-ления операторов (эти правила задаютсястандартом SQL, с одной стороны, и допол-няются и корректируются вашей СУБД,с другой стороны). Эти правила оченьпросты. Операторы выполняются в поряд-ке убывания их очередности, то есть чемвыше очередность оператора, тем раньшеэтот оператор исполняется (вычисляется).В частности, очередность арифметичес-ких операторов (+, -, *, / и т.п.) выше, чемочередность операторов сравнения (<, =, >и т.п.), у которых очередность выше, чему логических операторов (NOT, AND, OR).Отсюда следует, что выражение a or b * c>= d эквивалентно выражению a or ((b *c) >= d). Другими словами, операторыс низкой очередностью менее обязываю-щие, чем операторы с высокой очереднос-тью. В табл. 5.2 операторы перечисленыв порядке убывания их очередности, начи-ная с высшей. Операторы, оказавшиесяв одной строке этой таблицы, имеют однуи ту же очередность.

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

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

SELECT 2 + 3 * 4 AS "2+3*4",

(2 + 3) * 4 AS "(2+3)*4",

6 / 2 * 3 AS "6/2*3",

6 / (2 * 3) AS "6/(2*3)";

Таблица 5.2. Порядок вычисления операторов (отвысшей очередности к низшей)

Оператор Описание

+, - Унарные операторытождества и отрицания

*, / Бинарные арифметичес-кие операторы умноже-ния и деления

+, - Бинарные арифметичес-кие операторы сложенияи вычитания

=, <>, <, <=, >, >= Операторы сравнения

NOT Логический операторNOT

AND Логический операторAND

OR Логический оператор OR

2+3*4 (2+3)*4 6/2*3 6/(2*3)

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

14 20 9.00 1.00

Рис. 5.7. Результат исполнения запроса,представленного в листинге 5.7

Определение последовательности вычисления

Page 144: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

144 Операторы и функции

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

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

В табл. 5.2 нет нескольких стандартныхоператоров SQL (например, IN и EXISTS)и нескольких нестандартных операторов,зависящих от выбора конкретной СУБД.Чтобы окончательно прояснить вопросо порядке вычисления, который применяетваша СУБД, организуйте поиск в ее доку-ментации по ключу «правила предшество-вания» или «очередность» (precedence).

Чтобы запустить запрос из листинга 5.7в среде СУБД Oracle, вам придется доба-вить в предложение SELECT специальнуютаблицу DUAL. Подробнее на эту тему см.в примечании DBMS в разделе «Созданиепроизводных столбцов» этой главы.

Page 145: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

145

Объединение строкс помощью оператора ||Чтобы объединить, или конкатенировать,связать строки, применяют оператор кон-катенации ||. Перечислим его основныехарактеристики:

знаком оператора конкатенации явля-ется символ ||, состоящий из двух по-следовательных символов |;конкатенация не вставляет пробел меж-ду строками;конкатенация – это бинарный (двух-местный) оператор, который объеди-няет две строки в одну, например: вы-ражение 'formal' || 'dehyde' даетв результате строку 'formaldehyde';для получения сложной строки опера-тор конкатенации можно применятьнесколько раз, например: выражение'a' || 'b' || 'c' || 'd' дает в резуль-тате строку 'abcd';конкатенация любой строки с пустойстрокой оставляет первую строку без из-менения, например: выражение 'a' || ''|| 'b' дает в результате строку 'ab';результатом любой операции конкате-нации, в которой одним из ее операн-дов служит значение null, является зна-чение null, например: выражение 'a' ||NULL || 'b' дает в результате NULL (од-нако советуем прочитать об исключе-нии, которое относится к СУБД Oracle,в примечании с пометкой DBMS этогораздела);

чтобы связать произвольную строкус произвольной нестрокой (например,с каким-нибудь значением даты и вре-мени или числом), вам придется снача-ла преобразовать эту самую нестрокув некую строку, если только СУБД несделает это автоматически (см. раздел«Преобразование типов данных с помо-щью функции CAST()» в этой главе).

Объединение двух строк

Напечатайте string1 || string2.

Предполагается, что:

вы замените string1 и string2 темистроками, которые хотите конкатени-ровать (объединить);

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

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

С помощью оператора конкатенации мож-но объединять строки битов, например:B'0100' || B'1011', что дает в результа-те B'01001011'.

Объединение строк с помощью оператора ||

Page 146: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

146 Операторы и функции

Чтобы удалить ненужные пробелы из произ-вольной объединенной строки, можно при-менить функцию TRIM(). В разделе «Типыданных» главы 3 мы говорили, что значе-ния CHAR дополняются длинными хвостами,которые иногда создают уродливые цепив конкатенированных строках. Так вот, функ-ция TRIM() удалит ненужные пробелы. На-пример, на рис. 5.8 вы можете видеть, чтоперед именем Kellsey стоит ненужный про-бел. Следовательно, здесь можно восполь-зоваться функцией TRIM(), и она его убе-рет.Подробнее этот материал освещается вразделе «Удаление пробелов с помощьюфункции TRIM()» этой главе.

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

SELECT au_fname || ' ' || au_lname

AS "Author name"

FROM authors

ORDER BY au_lname ASC, au_fname ASC;

Author name

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

Sarah Buchman

Wendy Heydemark

Hallie Hull

Klee Hull

Christian Kells

Kellsey

Paddy O’Furniture

Рис. 5.8. Результат исполнения запроса,представленного в листинге 5.8

Page 147: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

147

Листинг 5.9. Перечислить в порядке убыванияобъемы продаж (то есть общее количествокогда-либо проданных копий) книг-биографий,учитываемых в нашей типовой базе данных.Обратите внимание, здесь нам понадобилосьпреобразовать значения объемов продаж из типаданных INTEGER (целые числа) в символьный типданных CHAR(7). Результат исполнения этогозапроса см. на рис. 5.9

SELECT CAST(sales AS CHAR(7))

|| ' copies sold of title '

|| title_id

AS "Biography sales"

FROM titles

WHERE type = 'biography'

AND sales IS NOT NULL

ORDER BY sales DESC;

Biography sales

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

1500200 copies sold of title T07

100001 copies sold of title T12

11320 copies sold of title T06

Рис. 5.9. Результат исполнения запроса,представленного в листинге 5.9

Листинг 5.10. Перечислить в порядке убываниядаты публикации книг, учитываемых в нашейтиповой базе данных. Обратите внимание, нампришлось конвертировать значения pubdate изтипа данных даты и времени в некий символьныйтип. Результат исполнения этого запроса см.на рис. 5.10

SELECT 'Title '

|| title_id

|| ' published on '

|| CAST(pubdate AS CHAR(10))

AS "Biography publication dates"

FROM titles

WHERE type = 'biography'

AND pubdate IS NOT NULL

ORDER BY pubdate DESC;

Biography publication dates

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

Title T12 published on 2000-08-31

Title T06 published on 2000-07-31

Title T07 published on 1999-10-01

Рис. 5.10. Результат исполнения запроса,представленного в листинге 5.10

Объединение строк с помощью оператора ||

Page 148: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

148 Операторы и функции

В СУБД Microsoft Access оператором конка-тенации является +, а функцией преобразо-вания типов данных – Format(string).Чтобы в этой среде выполнить запросы излистингов 5.8–5.11, внесите следующиеизменения в выражения с операторамиконкатенации и с функциями конверсиитипов:

au_fname + ' ' + au_lname – длялистинга 5.8;

FORMAT(sales)

→ + ' copies sold of title '

→ + title_id – для листинга 5.9;

'Title '

→ + title_id

→ + ' published on '

→ + FORMAT(pubdate) – длялистинга 5.10;

au_fname + ' ' + au_lname

→ = 'Klee Hull'; – для листинга5 . 1 1 .

В СУБД Microsoft SQL Server операторомконкатенации является «плюс» (+). Чтобы вэтой среде выполнить запросы из листин-гов 5.8–5.11, внесите следующие измененияв выражения с операторами конкатенации:

au_fname + ' ' + au_lname – длялистинга 5.8;

CAST(sales AS CHAR(7))

→ + ' copies sold of title '

→ + title_id – для листинга 5.9;

'Title '

→ + title_id

→ + ' published on '

→ + CAST(pubdate AS CHAR(10)) –

для листинга 5.10;

au_fname + ' ' + au_lname

→ = 'Klee Hull'; – для листинга5 . 1 1 .

В СУБД MySQL оператор || означает логи-ческое OR, а функцией конкатенации являет-ся CONCAT(), на вход которой можно пере-дать любое количество аргументов и кото-рая автоматически конвертирует любые дан-ные, не являющиеся строками символов, встроки символов так, что функция CAST()

становится ненужной. Чтобы в среде MySQLвыполнить запросы, представленные в лис-тингах 5.8–5.11, внесите следующие изме-ненияв выражения с операторами конкатена-ции:

CONCAT(au_fname + ' ' + au_lname)

– для листинга 5.8;

CONCAT(sales,

→ ' copies sold of title '

→ title_id) – для листинга 5.9;

CONCAT('Title ',

→ title_id,

→ ' published on ',

→ pubdate) – для листинга 5.10;

CONCAT(au_fname,' ', au_lname)

→ = 'Klee Hull'; – для листинга5.11.

СУБД Oracle трактует любую пустую строкукак значение null. Поэтому выражение 'a'|| NULL || 'b' в этой среде означает'ab' (см. примечания с пометкой DBMS вразделе «Значение null» главы 3).

При конкатенации СУБД Oracle, MySQL иPostgreSQL автоматически и неявно (тоесть не требуя вашего вмешательства) кон-вертируют все нестроки в строки. Поэтомузапросы из листингов 5.9 и 5.10 будут ра-ботать в среде этих СУБД, даже если выопустите в них функцию CAST(). Чтобыразобраться с этим вопросом до конца,организуйте поиск в документации по сво-ей СУБД с использованием ключей «конка-тенация» (concatenation) и «преобразова-ние» (conversion).

Page 149: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

149

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

всякая подстрока – это некая последо-вательность символов исходной стро-ки, включая пустую и исходную стро-ки целиком, в которых символы распо-ложены подряд;

функция SUBSTRING() выделяет частьпроизвольной строки определенной дли-ны, выраженной количеством знаковв подстроке, начиная от указанной по-зиции в исходной строке;

любая подстрока пустой строки естьпустая строка;

если какой-либо аргумент функцииSUBSTRING() является значением null, навыходе функции будет тоже значениеnull (об исключении из этого правила,связанного с СУБД Oracle, говорится впримечании с пометкой DBMS в этомразделе).

Порядок выборапроизвольной подстроки

Напечатайте:SUBSTRING(string FROM start [FOR length])

Предполагается, что:

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

таблицы, который должен содержать

Выбор произвольной подстроки с помощью функции SUBSTRING()

Листинг 5.11. Перечислить всех авторов,учитываемых в нашей типовой базе данных, чьеполное имя – Klee Hull. Результат исполнениязапроса см. на рис. 5.11

SELECT au_id, au_fname, au_lname FROM authors

WHERE au_fname || ' ' || au_lname = 'Klee Hull';

au_id au_fname au_lname

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

A04 Klee Hull

Рис. 5.11. Результат исполнения запроса,представленного в листинге 5.11

Page 150: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

150 Операторы и функции

строки символов (на такую исход-ную строку следует указывать заго-ловком этого столбца);

– произвольным строковым литера-лом;

– результатом операции или функ-ции, у которых на выходе должнабыть строка символов;

вместо start вы подставите целое чис-ло, которое обозначает место первогосимвола подстроки в исходной строке;вместо length вы подставите целое чис-ло, которое равно общему числу зна-ков в выделяемой подстроке, имея приэтом в виду, что, если опустить пред-ложение FOR length, функция SUB-STRING() выдаст в качестве подстрокивесь хвост исходной строки, начинаяс указанного (с помощью start) перво-го символа и заканчивая последнимсимволом исходной строки string (см.листинги и рис. 5.12–5.14).

Вы можете применять функцию выделенияподстроки SUBSTRING() везде, где исполь-зуется какое-либо выражение, в частностив предложениях SELECT, WHERE, ORDER BY.

С помощью функции выделения под-строки SUBSTRING() можно выделятьподстроки из строк битов, например: SUB-STRING(B'01001011', 5, 4) даст в ре-зультате B'1011'.

В СУБД Microsoft Access функцией выде-ления подстроки является Mid(string,start[,length]), а для выполнения кон-катенации строки надо использовать опе-ратор +. Поэтому для выполнения в этойсреде запросов, представленных в листин-гах 5.12–5.14, вам придется внести в нихследующие изменения:

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

SELECT pub_id,

SUBSTRING(pub_id FROM 1 FOR 1)

AS "Alpha part",

SUBSTRING(pub_id FROM 2)

AS "Num part"

FROM publishers;

pub_id Alpha part Num part

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

P01 P 01

P02 P 02

P03 P 03

P04 P 04

Рис. 5.12. Результат исполнения запроса,представленного в листинге 5.12

Page 151: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

151Выбор произвольной подстроки с помощью функции SUBSTRING()

Листинг 5.13. Распечатать из нашей базы данныхпервую букву (инициал) имени, точку, пробел ифамилию всех авторов, живущих или в штатеНью-Йорк, или в штате Колорадо. Результатисполнения запроса см. на рис. 5.13

SELECT SUBSTRING(au_fname FROM 1 FOR

1)

|| '. '

|| au_lname

AS "Author name",

state

FROM authors

WHERE state IN ('NY', 'CO');

Author name state

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

S. Buchman NY

W. Heydemark CO

C. Kells NY

Рис. 5.13. Результат исполнения запроса,представленного в листинге 5.13

Mid(pub_id, 1, 1)

Mid(pub_id, 2) – для запроса, пред-ставленного в листинге 5.12;

Mid(au_fname, 1, 1) + '. ' +

→ au_lname – для запроса, представ-ленного в листинге 5.13;

Mid(phone, 1, 3)='415' – для за-проса, представленного в листинге5.14.

В СУБД Microsoft SQL Server функциейвыделения подстроки является SUB-

STRING(string, start,length), а дляобъединения строк надо применять опера-тор +. Чтобы в этой среде выполнить зап-росы, представленные в листингах 5.12–5.14, вам придется внести в них следующиеизменения:

SUBSTRING(pub_id, 1, 1)

SUBSTRING(pub_id, 2 LEN(pub_id)-1)–для запроса, представленного в лис-тинге 5.12;

SUBSTRING(au_fname, 1, 1)

→ + '. '

→ + au_lname – для запроса, пред-ставленного в листинге 5.13;

SUBSTRING(phone, 1, 3)='415' – для за-проса, представленного в листинге 5.14.

В СУБД Oracle функцией выделения под-строки является SUBSTR(string, start,

[length]). Поэтому, чтобы в этой средевыполнить запросы из листингов 5.12–5.14, вам придется внести в них следую-щие изменения:

SUBSTR(pub_id, 1, 1)

SUBSTR(pub_id, 2) – для запроса,представленного в листинге 5.12;

SUBSTRING(au_fname, 1, 1)

→ a|| '. '

→ a|| au_lname – для запроса, пред-ставленного в листинге 5.13;

Листинг 5.14. Перечислить имена и фамилииучитываемых в нашей типовой базе данных авторов,у которых телефонный номер начинается на 415.Результат исполнения запроса см. на рис. 5.14

SELECT au_fname, au_lname, phone

FROM authors

WHERE SUBSTRING(phone FROM 1 FOR

3)='415';

au_fname au_lname phone

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

Hallie Hull 415-549-4278

Klee Hull 415-549-4278

Рис. 5.14. Результат исполнения запроса,представленного в листинге 5.14

Page 152: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

152 Операторы и функции

SUBSTR(phone, 1, 3)='415' – для за-проса, представленного в листинге 5.14.

Если вы работаете в среде СУБД MySQL, тодля выполнения запроса из листинга 5.13вам придется применить функциюCONCAT() и изменить выражение с опера-тором конкатенации следующим образом:

CONCAT(

→ SUBSTRING(au_fname, FROM 1 FOR

1),

→ '. ',

→ au_lname)

Советуем еще раз обратиться к разделу«Объединение строк с помощью опера-тора ||» в этой главе.

СУБД Oracle воспринимает любую пустуюстроку как значение null. Например, функциявыделения подстроки SUBSTR(NULL, 1, 2)выдаст на выходе значение '' (пустая стро-ка). Прочитайте еще раз примечания с помет-кой DBMS в разделе «Значение null» главы 3.

СУБД может автоматически и неявно (тоесть не спрашивая вашего решения) огра-ничить аргументы start и/или length,которые являются или слишком маленьки-ми, или слишком большими значениями,и заменить их разумными с точки зренияСУБД значениями. Например, если вместоstart поставить какое-нибудь отрицатель-ное число, СУБД может заменить значениеstart единицей, а если вместо length ис-пользовать слишком большое число, вы-водящее подстроку за рамки исходнойстроки, СУБД может заменить его длинойвсей строки. Чтобы как следует разобрать-ся в этом вопросе, организуйте поиск в до-кументации по вашей СУБД с использова-нием ключа «substring» или «substr».

Page 153: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

153

Переключение регистрасимволов строкис использованиемфункций UPPER()и LOWER()Функцию UPPER() применяют для конвер-тации произвольной строки символовнижнего регистра в строку символов верх-него регистра, чтобы поменялся толькорегистр символов (буквы должны быть теже самые, но заглавные). Функция LOWER()используется для конвертации произволь-ной строки символов верхнего регистрав строку символов нижнего регистра, чтобыпоменялся только регистр символов (буквыдолжны быть те же самые, но строчные).Основные характеристики функций UPPER()и LOWER() таковы:

произвольный символ, который можетнаходиться как в верхнем (А), так и внижнем (а) регистре, называется регист-ровым;регистровыми символами являются толь-ко буквы, следовательно, переключениерегистра затрагивает только буквы, ацифры, знаки пунктуации и пробелыпри этом остаются неизменными;функции UPPER() и LOWER() обычно при-меняют для того, чтобы форматироватьрезультаты и производить сравнения безучета регистра символов в каком-ни-будь предложении WHERE (функцияUPPER()/LOWER() меняет регистр толькотех букв, которые были в нижнем/верх-нем регистре, а регистр тех букв, кото-рые и так были в верхнем/нижнем реги-стре, остается прежним);на пустые строки изменение регистране оказывает никакого влияния;если аргумент любой из функций UP-PER() и LOWER()является значением null,

на выходе этой функции будет тожезначение null (но обратите внимание наисключение из этого правила, котороесвязано с СУБД Oracle и о котором го-ворится в примечании DBMS этого раз-дела).

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

Напечатайте:

UPPER(string) – чтобы переключится наверхний регистр;LOWER(string) – чтобы переключитьсяна нижний регистр.

Предполагается, что вместо string вы под-ставите явно или укажете на ту исходнуюстроку, у которой хотите переключитьрегистр и которая может быть любымстроковым выражением, например (см. ли-стинги и рис. 5.15–5.16):

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

Функции переключения регистра UPPER()и LOWER() можно применять везде, где ис-пользуется какое-либо выражение, в част-ности в предложениях SELECT, WHERE,ORDER BY.

Функции переключения регистра UPPER() иLOWER() не оказывают никакого воздействияна такие наборы символов (в том числе ал-фавиты), для которых концепция регистра непредусмотрена (например, алфавит иврита).

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

Page 154: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

154 Операторы и функции

Листинг 5.15. Показать имена авторов,учитываемых в нашей типовой базе данных,в нижнем регистре, а их фамилии – в верхнемрегистре. Результат исполнения запроса см.на рис. 5.15

SELECT LOWER(au_fname) AS "Lower",

UPPER(au_lname) AS "Upper"

FROM authors;

Листинг 5.16. Перечислить названия книг,учитываемых а нашей типовой базе данных,которые содержат сочетание (латинских) букв«MO», независимо от регистра. Чтобы этот запроссработал, все буквы шаблона LIKE должны бытьв верхнем регистре. Результат исполнениязапроса см. на рис. 5.16

SELECT title_name

FROM titles

WHERE UPPER(title_name) LIKE '%MO%';

Lower Upper

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

sarah BUCHMAN

wendy HEYDEMARK

hallie HULL

klee HULL

christian KELLS

KELLSEY

paddy O’FURNITURE

Рис. 5.15. Результат исполнения запроса,представленного в листинге 5.15

title_name

————————————---------------

200 Years of German Humor

I Blame My Mother

Рис. 5.16. Результат исполнения запроса,представленного в листинге 5.16

Функции переключения регистра UPPER() иLOWER() влияют на символы, находящиесяпод каким-нибудь диакритическим знаком(скажем, ударения), например: UPPER('o')дает 'O'.

В СУБД Microsoft Access функциями пе-реключения регистра являются UCase(string) и LCase(string). Поэтому,чтобы выполнить в этой среде запросы излистингов 5.15 и 5.16, вам придется вне-сти в текст предложений, отвечающих запереключение регистра, следующие изме-нения:

LCase(au_fname) и UCase(au_lname)–для запроса, представленного в лис-тинге 5.15;

UCase(title_name) LIKE '%MO%' –для запроса, представленного в лис-тинге 5.16.

СУБД Oracle воспринимает любую пустуюстроку как значение null. Поэтому и функ-ция UPPER(NULL), и функцияLOWER(NULL) выдадут на выходе значение'' (пустая строка). Советуем еще раз об-ратиться к примечаниям с пометкой DBMSв разделе «Значение null» главы 3.

СУБД может предоставлять дополнитель-ные возможности форматирования строки,такие как инверсия регистра или конверсиястрок в предложения или заголовки. Что-бы как следует разобраться с этим вопро-сом, проанализируйте документацию повашей СУБД с использованием ключа «сим-вольные функции» (character functions).

``

Page 155: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

155

Удаление пробеловс помощью функции TRIM()Функцию TRIM() применяют для того, что-бы отсекать нежелательные символы в кон-це строк. Основные характеристики функ-ции TRIM() таковы:

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

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

обычно ее применяют для того, чтобыформатировать результаты запросов ипроизводить сравнения предложениемWHERE;

эта функция особенно полезна в томслучае, когда надо удалить длинныецепочки пробелов на хвостах значенийтипа данных CHAR (в разделе «Типы дан-ных» главы 3 мы отмечали, что СУБДавтоматически дополняют значениятипа данных CHAR хвостами пробелов,чтобы создать строки символов тойдлины, которая для них указана);

на пустые строки она не влияет;

если какой-либо ее аргумент являетсязначением null, на выходе функции бу-дет тоже значение null (советуем обра-тить внимание на исключение из этогоправила, которое связано с СУБД Ora-cle, – см. примечание DBMS в этом раз-деле).

Как удалить пробелы в строке

Напечатайте:TRIM([[LEADING | TRAILING | BOTH]

FROM] string)

Предполагается, что:

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

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

– произвольным строковым лите-ралом;

– результатом некой операции илифункции, на выходе которых долж-на быть строка символов;

вы укажете опцию LEADING в том слу-чае, если необходимо отсечь головныепробелы;вы укажете опцию TRAILING в том слу-чае, если необходимо отсечь хвостовыепробелы;вы укажете опцию BOTH в том случае,если необходимо удалить одновремен-но и хвостовые, и головные пробелы;вы можете не указывать опции LEADING,TRAILING и BOTH, и тогда СУБД назначитпо умолчанию опцию BOTH (см. лис-тинг 5.17 и рис. 5.17).

Удаление пробелов с помощью функции TRIM()

Page 156: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

156 Операторы и функции

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

Напечатайте:

TRIM([LEADING | TRAILING | BOTH]

'trim_chars' FROM string)

Предполагается, что (см. листинги и рис.5.18 и 5.19):

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

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

– произвольный строковый литерал;– результат некой операции или функ-

ции, у которых на выходе должнабыть строка символов;

вы укажете опцию LEADING в том слу-чае, если необходимо отсечь головныесимволы;вы укажете опцию TRAILING в том слу-чае, если необходимо удалить хвосто-вые символы;вы укажете опцию BOTH в том случае,если необходимо убрать одновремен-но и хвостовые, и головные символы;вы можете не указывать опции LEADING,TRAILING и BOTH, и тогда СУБД назначитпо умолчанию опцию BOTH.

Листинг 5.17. Последовательно отсечь иголовные, и хвостовые пробелы у строки ' AAA'.Символы < (меньше) и > (больше) обозначаютдлину усеченных строк. Результат исполнениязапроса см. на рис 5.17

SELECT

'<' || ' AAA ' || '>'

AS "Untrimmed",

'<' || TRIM(LEADING FROM ' AAA ') || '>'

AS "Leading",

'<' || TRIM(TRAILING FROM ' AAA ') || '>'

AS "Trailing",

'<' || TRIM(' AAA ') || '>'

AS "Both";

Untrimmed Leading Trailing Both

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

< AAA > <AAA > < AAA> <AAA>

Рис. 5.17. Результат исполнения запроса,представленного в листинге 5.17

Page 157: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

157

Функцию TRIM() можно использоватьтам, где применяют какое-либо выраже-ние, в частности в предложениях SELECT,WHERE, ORDER BY.

В запросе из листинга 5.8 мы объединилив один производный столбец имена и фа-милии авторов, учитываемых в нашей ти-повой базе данных. Результат исполненияэтого запроса представлен на рис. 5.8. Мывидим «лишний» пробел перед фамилиейKellsey. Этот пробел оказывается лишнимтолько в строке, соответствующей Келлси.Он должен разделять имена и фамилииавторов, но у Келлси нет имени, и СУБДпоставила перед этой фамилией пробел,который и стал лишним. Так вот, чтобыубрать этот пробел, можно воспользовать-ся функцией TRIM(). С этой целью вамследует изменить текст выражения из лис-тинга 5.8, которое содержит операторыконкатенации, следующим образом:

TRIM(au_fname || ' ' || au_lname)

В СУБД Microsoft Access функциями отсе-чения (их еще называют функциями трим-мирования) являются:

LTrim(string) – для отсечения голов-ных пробелов;

RTrim(string) – для отсечения хвос-товых пробелов;

Trim(string) – для удаления и голов-ных, и хвостовых пробелов одновре-менно.

Чтобы в среде этой СУБД от произвольнойстроки отсечь символы, не являющиесяпробелами, следует использовать функциюReplace(string, find, replacement

[ ,start] [ ,count] [ ,compare]),которая заменит указанные символы пус-тыми строками.

Для того чтобы объединить строки в средеСУБД Microsoft Access, применяйте опера-тор +. В частности, если нужно выполнитьзапросы из листингов 5.17 и 5.18, в текстэтих запросов придется внести соответ-ствующие изменения:

Листинг 5.18. Удалить первую заглавнуюлатинскую букву «H» в тех фамилиях авторов,учитываемых в нашей типовой базе данных,которые начинаются именно с нее. Результатисполнения запроса см. на рис. 5.18

SELECT au_lname,

TRIM(LEADING 'H' FROM au_lname)

AS "Trimmed name"

FROM authors;

au_lname Trimmed name

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

Buchman Buchman

Heydemark eydemark

Hull ull

Hull ull

Kells Kells

Kellsey Kellsey

O’Furniture O’Furniture

Рис. 5.18. Результат исполнения запроса,представленного в листинге 5.18

Листинг 5.19. Игнорируя головные и хвостовыепробелы, перечислить трехзначныеидентификаторы названий книг, учитываемых внашей типовой базе данных, которые начинаютсяс буквенно-цифрового сочетания T1. Результатисполнения запроса представлен на рис. 5.19

SELECT title_id

FROM titles

WHERE TRIM(title_id) LIKE 'T1_';

title_id

--------

T10

T11

T12

T13

Рис. 5.19. Результат исполнения запроса,представленного в листинге 5.19

Удаление пробелов с помощью функции TRIM()

Page 158: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

158 Операторы и функции

'<' + ' AAA ' + '>''<' + LTRIM(' AAA ') + '>'

'<' + RTRIM(' AAA ') + '>'

'<' + TRIM(' AAA ') + '>' – для за-проса, приведенного в листинге 5.17;

Replace(au_lname, 'H', '', 1, 1) –для запроса, представленного в листин-ге 5.18.

В СУБД Microsoft SQL Server функциямиотсечения (их еще называют функциямитриммирования) являются:

LTRIM(string) – для отсечения голов-ных пробелов;

RTRIM(string) – для удаления хвосто-вых пробелов.

Чтобы объединить строки в среде этойСУБД, применяйте оператор +. В частности,если нужно запустить запрос из листинга5.17, в текст этого запроса придется внестисоответствующие изменения:

'<' + ' AAA ' + '>'

'<' + LTRIM(' AAA ') + '>''<' + RTRIM(' AAA ') + '>'

'<' + LTRIM(RTRIM(' AAA ')) + '>'

Функции отсечения LTRIM(string) иRTRIM (string) в среде СУБД Microsoft SQLServer удаляют именно пробелы, а не про-извольные символы, обозначенные какtrim_chars. Поэтому, чтобы удалить произ-вольные символы и запустить на выполнениезапросы из листингов 5.18 и 5.19, вам придет-ся встраивать в текст этих запросов и комби-нировать какие-нибудь символьные функции,в том числе характерные только для СУБДMicrosoft SQL Server, например CHARINDEX(),LEN(), PATINDEX(), REPLACE(), STUFF(),SUBSTRING. В частности, в тексты выраженийусечения соответствующих запросов можновнести такие изменения:

REPLACE(

→ SUBSTRING(au_lname, 1, 1), 'H', '')→ SUBSTRING(au_lname, 2,

→ LEN(au_lname)) – для листинга 5.18;

LTRIM(RTRIM(title_id)) LIKE 'T1_'

– для листинга 5.19.

Чтобы в среде СУБД Oracle исполнить за-прос из листинга 5.17, в текст этогозапроса придется добавить предложениеFROM DUAL. Советуем перечитать примеча-ние с пометкой DBMS в разделе «Созданиепроизводных столбцов» этой главы.Имейте в виду, что Oracle не разрешаетподставлять вместо trim_chars сразу не-сколько символов.

Чтобы в среде СУБД MySQL исполнитьзапрос, представленный в листинге 5.17,в текст этого запроса придется встроитьфункцию конкатенации CONCAT() (см.раздел «Объединение строк с помощьюоператора ||» в этой главе). Исправьте вы-ражения конкатенации запроса из листин-га 5.17 следующим образом:

CONCAT('<',' AAA ','>')

CONCAT('<',

→ TRIM(LEADING FROM ' AAA '),

→ '>')CONCAT('<',

→ TRIM(TRAILING FROM ' AAA '),

→ '>')CONCAT('<',TRIM(' AAA '),'>')

СУБД Oracle воспринимает любую пустуюстроку как значение null. Поэтому функцияTRIM(NULL) выдаст на выходе значение ''(пустая строка). В этой связи посмотритееще раз примечания с пометкой DBMSв разделе «Значение null» главы 3.

Возможно, СУБД предоставляет в вашераспоряжение так называемые функции-заполнители, которые должны вставлятьсимволы пробелов или какие-либо другиесимволы в свободные места символьныхстрок. Например, в СУБД Oracle такимифункциями-заполнителями являютсяLPAD() и RPAD(). Для того чтобы деталь-но разобраться с функциями-заполнителя-ми, организуйте поиск в документации посвоей СУБД с использованием ключа «сим-вольные функции» (character functions).

Page 159: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

159

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

на ее выходе всегда будет или какое-нибудь натуральное число, или ноль(тип INTEGER);эта функция подсчитывает символы,а не байты: произвольный символ, со-стоящий из нескольких байтов, илипроизвольный символ Unicode пред-ставляют для этой функции всего одинсимвол (как считать байты, описывает-ся в примечаниях этого раздела);длина любой пустой строки равнанулю;если аргументом этой функции являет-ся значение null, на ее выходе тоже бу-дет значение null (обращаем ваше вни-мание на исключение из этого правила,которое связано с СУБД Oracle, – см.примечание с пометкой DBMS в этомразделе).

Вычисление длины произвольной строки

Напечатайте CHARACTER_LENGTH(string).Предполагается, что вместо string выподставите явно или укажете на ту исход-ную строку (см. листинги и рис. 5.20 и5.21), для которой хотите вычислить дли-ну, причем вместо string будете подстав-лять какие-нибудь строковые выраженияпо своему выбору, например:

Листинг 5.20. Вычислить длину имен авторов,учитываемых в нашей типовой базе данных.Результат исполнения запроса см. на рис. 5.20

SELECT au_fname,

CHARACTER_LENGTH(au_fname) AS "Len"

FROM authors;

au_fname Len

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

Sarah 5

Wendy 5

Hallie 6

Klee 4

Christian 9

0

Paddy 5

Рис. 5.20. Результат исполнения запроса,представленного в листинге 5.20

Определение длины строки с помощью функции CHARACTER_LENGTH()

Page 160: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

160 Операторы и функции

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

Вы можете применять функцию CHARAC-TER_LENGTH() везде, где используется ка-кое-либо выражение, в частности в предло-жениях SELECT, WHERE, ORDER BY.

Функции CHAR_LENGTH() иCHARACTER_LENGTH() являются синонимами.

Стандарт SQL определяет следующие стро-ковые функции:

BIT_LENGTH(expr) – выводит числобитов в произвольном выражении,например BIT_LENGTH(B'01001011')равно 8;

OCTET_LENGTH(expr) – выводит числобайтов в произвольном выражении,например OCTET_LENGTH(B'01001011')равно 1, а OCTET_LENGTH('ABC') – 3.

Восьмеричная длина (то есть длина в бай-тах) равна округленному до ближайшегоцелого частному от деления двоичной дли-ны (то есть длины в битах) на натуральноечисло 8. Подробнее о тех функциях, кото-рые предоставляют конкретные СУБД дляопределения восьмеричной и двоичнойдлины, рассказывается в примечании с по-меткой DBMS этого раздела.

В СУБД Microsoft Access и Microsoft SQLServer функцией определения длины стро-ки является LEN(string). Поэтому для за-пуска в среде этих СУБД запросов из лис-тингов 5.20 и 5.21 вам придется внестив выражения с функциями определениядлины строки следующие изменения:

LEN(au_fname) – для листинга 5.20;

LEN(title_name) – для листинга 5.21.

В СУБД Oracle функцией определениядлины строки является LENGTH(string).Поэтому для запуска в среде этой СУБДзапросов из листингов 5.20 и 5.21 придет-ся внести в выражения с функциями оп-ределения длины строки следующие из-менения:

LENGTH(au_fname) – для листинга 5.20;

LENGTH(title_name) – для листинга 5.21.

Функции побитового и побайтового под-счета длины строки сильно меняютсяпри переходе от одной СУБД к другой.Так, Microsoft Access предлагает Len(),Microsoft SQL Server – DATALENGTH(),Oracle – LENGTHB(), MySQL –BIT_COUNT() и OCTET_LENGTH(), нако-нец, PostgreSQL – OCTET_LENGTH().

СУБД Oracle трактует любую пустую стро-ку как значение null. ПоэтомуLENGTH(NULL) дает ''. Однако в предпос-ледней строке рис. 5.20 мы видим не 0, а1. Это происходит потому, что вместо име-ни соответствующего автора в СУБД Oracleстоит пробел ' ', а не пустая строка ''(см. примечание с пометкой DBMS в раз-деле «Значение null» главы 3).

Page 161: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

161

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

SELECT title_name,

CHARACTER_LENGTH(title_name) AS "Len"

FROM titles

WHERE CHARACTER_LENGTH(title_name) < 30

ORDER BY CHARACTER_LENGTH(title_name) ASC;

title_name Len

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

1977! 5

Kiss My Boo-Boo 15

How About Never? 16

I Blame My Mother 17

Exchange of Platitudes 22

200 Years of German Humor 25

Spontaneous, Not Annoying 25

But I Did It Unconsciously 26

Not Without My Faberge Egg 26

Just Wait Until After School 28

Ask Your System Administrator 29

Рис. 5.21. Результат исполнения запроса,представленного в листинге 5.21

Поиск подстроки с использованием функции POSITION()

Поиск подстрокис использованием функцииPOSITION()Чтобы найти любую заданную подстрокувнутри произвольной заданной строки,применяют функцию POSITION(). Основ-ные характеристики функции POSITION()таковы:

она всегда дает на выходе целое не-отрицательное число (тип INTEGER), ко-торое указывает на позицию в тойстроке, в которой производится поиск,первого знака первого вхождения ис-комой подстроки;если та строка, в которой производитсяпоиск, не содержит искомой подстроки,функция POSITION() выдаст ноль;строковые сравнения, определяющиеработу этой функции, в зависимости отвыбора конкретной СУБД могут бытькак чувствительны, так и нечувстви-тельны к регистру сравниваемых симво-лов (см. примечания с пометкой DBMSв разделе «Фильтрация строк с помо-щью предложения WHERE» главы 4);позиция любой подстроки в любой пу-стой строке есть ноль;если хотя бы одним аргументом этойфункции является значение null, на еевыходе тоже будет значение null (об-ратите внимание на исключение из это-го правила, которое связано с СУБДOracle, – см. далее в этом разделе при-мечание с пометкой DBMS).

Page 162: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

162 Операторы и функции

Порядок поиска произвольнойподстроки

Напечатайте POSITION(substring IN string).

Предполагается, что:

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

вместо substring подставите явно илиукажете на ту подстроку, которую со-бираетесь искать в исходной строке;

вместо substring и string будете под-ставлять какие-нибудь строковые вы-ражения по своему выбору, например:

– значение любого столбца произволь-ной таблицы, который должен содер-жать строки символов (на такуюстроку или подстроку следует указы-вать заголовком этого столбца);

– произвольный строковый литерал;

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

Еще раз подчеркнем, что функция POSI-TION() всегда выдает минимальное значениепозиции (целое неотрицательное числоили ноль, тип INTEGER), на которой в исход-ной строке стоит первый символ искомойподстроки. Объясним смысл минимума:если искомая подстрока встречается в ис-ходной строке несколько раз, таких пози-ций первого символа подстроки будет тоженесколько, и среди этих натуральных чи-сел, соответствующих позициям первыхсимволов, берется минимальное. Если иско-мая подстрока не будет найдена в исходнойстроке, функция POSITION() выдаст ноль(см. листинги и рис. 5.22 и 5.23).

Page 163: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

163

Функцию POSITION() можно использо-вать везде, где применяют какое-либо вы-ражение, в частности в предложенияхSELECT, WHERE, ORDER BY.

В СУБД Microsoft Access функцией опреде-ления подстроки является InStr(start_-position, string, substring). Поэто-му для выполнения в среде этой СУБД зап-росов из листингов 5.22 и 5.23 в выраже-ния с функциями определения позицииподстроки придется внести следующие из-менения:

InStr(1, au_fname, 'e') иInStr(1, au_lname, 'ma') – длялистинга 5.22;

InStr(1, title_name, 'u') – длялистинга 5.23.

В СУБД Microsoft SQL Server функциейопределения подстроки является CHAR-INDEX(substring, string). Поэтому длявыполнения в среде этой СУБД запросовиз листингов 5.22 и 5.23 в выражения сфункциями определения позиции под-строки придется внести следующие изме-нения:

CHARINDEX('e', au_fname) иCHARINDEX('ma', au_lname) – длялистинга 5.22;

CHARINDEX('u', title_name) – длялистинга 5.23.

В СУБД Oracle функцией определения под-строки является INSTR(string,

substring). Поэтому для выполнения всреде этой СУБД запросов из листингов5.22 и 5.23 в соответствующие выражения сфункциями определения позиции подстро-ки придется внести следующие изменения:

INSTR(au_fname, 'e') иINSTR(au_lname, 'ma') – для листин-га 5.22;

INSTR(title_name, 'u') – длялистинга 5.23.

Листинг 5.22. Указать для всех авторов,учитываемых в нашей типовой базе данных,позицию подстроки e (латинская строчная буква)в строке, являющейся именем автора, и позициюподстроки ma (латинские строчные буквы) встроке, являющейся фамилией этого автора.Результат исполнения запроса см. на рис. 5.22

SELECT

au_fname,

POSITION('e' IN au_fname) AS "Pos

e",

au_lname,

POSITION('ma' IN au_lname) AS "Pos

ma"

FROM authors;

au_fname Pos e au_lname Pos ma

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

Sarah 0 Buchman 5

Wendy 2 Heydemark 6

Hallie 6 Hull 0

Klee 3 Hull 0

Christian 0 Kells 0

0 Kellsey 0

Paddy 0 O’Furniture 0

Рис. 5.22. Результат исполнения запроса,представленного в листинге 5.22

Поиск подстроки с использованием функции POSITION()

Page 164: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

164 Операторы и функции

СУБД Oracle трактует любую пустую стро-ку как значение null. Поэтому POSITION(NULL) дает '' (см. примечания с помет-кой DBMS в разделе «Значение null» гла-вы 3).

Чтобы найти вхождения произвольнойподстроки в произвольную строку, отлич-ные от первого, можно комбинировать ивстраивать функцию локализации подстро-ки POSITION(). Однако коммерческие СУБДпредоставляют в ваше распоряжение болеемощные и удобные инструменты решенияэтой задачи. Так, Microsoft Access предлагаетInStr(), Microsoft SQL Server –CHARINDEX(), Oracle – INSTR(), MySQL –LOCATE().

Листинг 5.23. Перечислить названия книг,учитываемых в нашей типовой базе данных,которые содержат не более 10 символов, считаяслева направо, и латинскую строчную букву «u».Сортировка названий осуществляется поуменьшению (нисходящий порядок) величинызначения позиции вхождения подстроки (то естьлатинской строчной буквы «u»). Результатисполнения запроса см. на рис. 5.23

SELECT title_name,

POSITION('u' IN title_name) AS "Pos"

FROM titles

WHERE POSITION('u' IN title_name)

BETWEEN 1 AND 10

ORDER BY POSITION('u' IN title_name) DESC;

title_name Pos

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

Not Without My Faberge Egg 10

Spontaneous, Not Annoying 10

How About Never? 8

Ask Your System Administrator 7

But I Did It Unconsciously 2

Just Wait Until After School 2

Рис. 5.23. Результат исполнения запроса,представленного в листинге 5.23

Page 165: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

165

Операции с даннымидаты и времени

Как мы уже отмечали, соответствие ком-мерческих СУБД, которые рассматриваютсяв настоящей книге, требованиям, предъ-являемым стандартом SQL к операторами функциям над данными даты и времении интервалами, весьма относительное. ЭтиСУБД имеют свои собственные дополни-тельные операторы и функции для выпол-нения, в частности, арифметических дей-ствий с данными интервальных типов иданными даты и времени. Поэтому насто-ящий раздел с самого начала задумывал-ся как краткий справочник по соответ-ствию коммерческих СУБД стандарту SQL.Хотим напомнить, что об интервальныхтипах данных и о типах данных даты ивремени мы говорили в разделах «Типыданных» и «Интервальные типы данных»главы 3.

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

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

Таблица 5.3. Операторы, операндами которых могутбыть интервалы и данные даты и времени

Операция Результат

(Дата и время) – (Дата и время) Интервал

(Дата и время) + Интервал Дата и вре-мя

(Дата и время) - Интервал Дата и время

Интервал + (Дата и время) Дата и время

Интервал + Интервал Интервал

Интервал - Интервал Интервал

Интервал * Число Интервал

Интервал / Число Интервал

Число * Интервал Интервал

Операции с данными даты и времени

Page 166: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

166 Операторы и функции

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

Легко заметить, что некоторые операциинад интервалами и данными даты и вре-мени не определены. Например, сложе-ние двух дат не имеет никакого смысла.

В табл. 5.3 перечислены допустимые сточки зрения стандартного SQL операто-ры, которые могут работать с интервала-ми и данными даты и времени. Врезка вданном разделе объясняет, как и почемувы можете применять один и тот же опе-ратор, чтобы выполнять принципиальноразные операции.Рассмотрим еще одну функцию SQL –EXTRACT(). Она выделяет конкретное полезначения даты и времени или интервалаи на выходе выдает его в виде числа. Этуфункцию обычно применяют в выраже-ниях сравнения или/и при форматирова-нии результатов запросов.

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

НапечатайтеEXTRACT(field FROM

datetime_or_interval) .

Предполагается, что:

вместо datetime_or_interval вы подста-вите явно или укажете на то значениедаты и времени либо на интервал, изкоторого хотите выделить часть, притом что datetime_or_interval можетбыть:

– выражением даты и времени или ин-тервальным выражением, например:заголовок некоего столбца, которыйдолжен содержать значения даты ивремени или интервалы;

Переопределение операторов

Напомним, что операторы +, -, *, /обычно используются для операций счислами. В некоторых СУБД компа-нии Microsoft оператор + применяет-ся еще и для конкатенации строк. Пе-регрузкой произвольного заданногооператора называется такое его состо-яние, когда он должен выполнять бо-лее одной функции в зависимости отзадаваемых условий. Чаще всего эти-ми условиями для перегружаемых опе-раторов являются их операнды. Таквот, каждый из операторов +, -, * и /даже в рамках стандартного SQL ведетсебя по-разному, в зависимости оттого, являются ли его операндамичисла или интервалы и/или значениядаты и времени. Имейте в виду, чтокроме стандартного SQL перегружатьне только операторы, но и функцииможет ваша собственная СУБД. Здесьпод перегрузкой функций понимаетсяназначение произвольной функции бо-лее одной задачи в зависимости от рядапривносимых условий. Чаще всегоэтими условиями являются типы дан-ных аргументов этой функции. Такаяфункция называется перегружаемой.Примером перегружаемой функциислужит CONCAT() СУБД MySQL, кото-рая в качестве аргументов принимаеткак строки, так и нестроки. CONCAT()вынуждена дополнительно конвер-тировать нестроки в строки, чего онане делает, когда ее аргументами явля-ются только строки (см. примечаниес пометкой DBMS в разделе «Объеди-нение строк с помощью оператора ||»этой главы).

Page 167: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

167

– литералом даты и времени или ин-тервальным литералом;

– результатом какой-нибудь операцииили функции, на выходе которыхдолжны быть значения даты и вре-мени или интервалы;

вместо field вы подставите ту частьdatetime_or_interval, которую хотитевыделить, при том что field может быть(см. табл. 3.11) одним из следующихполей:

– YEAR, MONTH, DAY, HOUR, MINUTE, SECOND;

– TIMEZONE_HOUR, TIMEZONE_MINUTE.

Кроме того, если field является полемSECOND, функция EXTRACT() на выходе вы-даст значение типа NUMERIC, а во всех ос-тальных случаях – значение типа INTEGER(см. листинг 5.24 и рис. 5.24).

Вы можете применять функцию EXTRACT()везде, где используется какое-либо выраже-ние, в частности в предложениях SELECT,WHERE, ORDER BY.

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

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

SELECT

title_id,

pubdate

FROM titles

WHERE EXTRACT(YEAR FROM pubdate)

BETWEEN 2001 AND 2002

AND EXTRACT(MONTH FROM pubdate)

BETWEEN 1 AND 6

ORDER BY pubdate DESC;

title_id pubdate

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

T09 2002-05-31

T08 2001-06-01

T05 2001-01-01

Рис. 5.24. Результат исполнения запроса,представленного в листинге 5.24

Операции с данными даты и времени

Page 168: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

168 Операторы и функции

В СУБД Microsoft Access и Microsoft SQLServer функцией выделения являетсяDATEPART(datepart, date). Поэтому,для выполнения в среде этих СУБД запро-са из листинга 5.24 в соответствующиевыражения с функциями выделения при-дется внести следующие изменения:

DATEPART("yyyy", pubdate);

DATEPART("m", pubdate).

СУБД Oracle, MySQL и PostgreSQL могутпринимать в качестве аргумента fieldфункции EXTRACT() помимо указанныхвыше значений этого аргумента еще и дру-гие типы данных.

Коммерческие СУБД предоставляют в вашераспоряжение функции, которые прибав-ляют интервалы к датам, например:

DATEDIFF() – для Microsoft Access иMicrosoft SQL Server;

ADD_MONTHS() – для Oracle;

DATE_ADD() и DATE_SUB() – для MySQL.

Сложная арифметика с данными даты ивремени и с интервалами вполне обычноедело в программировании на SQL, и всекоммерческие СУБД предоставляют в рас-поряжение программиста, кроме стандарт-ных инструментов SQL, дополнительныефункции, работающие на интервалах и да-тах. В этой связи имеет смысл организо-вать поиск в документации по вашей СУБДс использованием ключей «date and timefunctions» и «datetime functions» (то естьфункции даты и времени).

Page 169: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

169

Извлечение значенийтекущих даты и времениЧтобы извлечь текущие дату и время изсистемных часов компьютера, на которомработает конкретная СУБД, применяютфункции CURRENT_DATE, CURRENT_TIME, CUR-RENT_TIMESTAMP.

Порядок извлечениятекущих даты и времени

Напечатайте:

CURRENT_DATE – чтобы вывести дату;

CURRENT_TIME – чтобы вывести время;

CURRENT_TIMESTAMP – чтобы вывести от-метку времени.

На выходе функции в первом случае будетзначение, относящееся к типу данных DATE,во втором случае – к типу данных TIME,а в третьем – к типу данных TIMESTAMP (см.раздел «Типы данных» в главе 3, а также ли-стинги и рис. 5.25 и 5.26).

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

Каждая из функций CURRENT_TIME() иCURRENT_TIMESTAMP() принимает в качест-ве аргумента некую точность, обозна-чающую количество десятичных разрядовв записи дробной части секунды, котороеследует, по вашему мнению, включитьв значение времени. Например, функцияCURRENT_TIME(6) даст на выходе такоезначение текущего времени, что в полеSECOND доли секунды будут представленыточностью равной 6. Подробнее о смыслеи практическом применении понятия точ-ности читайте в разделе «Типы данных»главы 3.

Извлечение значений текущих даты и времени

Листинг 5.25. Распечатать текущие дату, время,отметку времени. Результат исполнения запросасм. на рис. 5.25

SELECT

CURRENT_DATE AS "Date",

CURRENT_TIME AS "Time",

CURRENT_TIMESTAMP AS "Timestamp";

Date Time Timestamp

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

2002-05-19 21:02:04 2002-05-19 21:02:04

Рис. 5.25. Результат исполнения запроса,представленного в листинге 5.25

Листинг 5.26. Перечислить книги, учитываемыев нашей типовой базе данных, дата публикациикоторых или неизвестна совсем, или попадаетв интервал 90 дней, считая назад от текущей даты(та дата, которая в данном запросе считается«текущей», получена в запросе из листинга 5.25).Перечень должен быть упорядочен по удалениюдаты публикации книги от текущей даты(нисходящий порядок). Результат исполнениязапроса см. на рис. 5.26

SELECT title_id, pubdate

FROM titles

WHERE pubdate

BETWEEN CURRENT_TIMESTAMP

- INTERVAL 90 DAY

AND CURRENT_TIMESTAMP

+ INTERVAL 90 DAY

OR pubdate IS NULL

ORDER BY pubdate DESC;

title_id pubdate

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

T09 2002-05-31

T10 NULL

Рис. 5.26. Результат исполнения запроса,представленного в листинге 5.26

Page 170: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

170 Операторы и функции

CURRENT_DATE(), функция CURRENT_-

TIME() в этих версиях все равно не под-держивается. Кроме того, реализация запро-са, представленного в листинге 5.25, в средеСУБД Oracle требует предложения FROM

DUAL (см. примечание DBMS в разделе «Со-здание производных столбцов» этой главы).Таким образом, чтобы запустить запрос, ко-торый представлен в листинге 5.25, в средеСУБД Oracle, вам придется изменить предло-жение SELECT следующим образом:

SELECT SYSDATE AS "Date"

FROM DUAL;

Однако это еще не все. Дело в том, чтофункция SYSDATE выводит системныедату и время, но не отображает это время,если только не получит соответствующегоуказания от отформатированной функцииTO_CHAR(), например:

SELECT TO_CHAR(SYSDATE,

→ 'YYYY-MM-DD HH24:MI:SS')

FROM DUAL;

Чтобы в среде СУБД Oracle выполнить за-прос из листинга 5.26, в текст предложе-ния BETWEEN этого запроса придется вне-сти следующие изменения:

BETWEEN SYSDATE – 90

AND SYSDATE + 90

Чтобы в среде СУБД PostgreSQL выпол-нить запрос из листинга 5.26, в текстпредложения BETWEEN этого запроса вампридется внести следующие изменения:

BETWEEN CURRENT_TIMESTAMP – 90

AND CURRENT_TIMESTAMP + 90

Для получения подробной информациио системных функциях даты и времени ва-шей СУБД организуйте поиск в ее докумен-тации по ключам «date and time functions»(функции даты и времени) и «system func-tions» (системные функции).

В СУБД Microsoft Access есть три систем-ные функции даты/времени: Date(),Time() и Now(). Чтобы выполнить в сре-де этой СУБД запросы из листингов 5.25 и5.26, вам следует:

внести изменения в текст выраженийдаты и времени запроса, представлен-ного в листинге 5.25:

– заменить CURRENT_DATE AS

"Date" на DATE() AS "Date";

– заменить CURRENT_TIME AS

"Time" на Time() AS "Time";

– заменить CURRENT_TIMESTAMP AS"Timestamp" на Now() AS

"Timestamp";

внести изменения в текст предложенияBETWEEN этого запроса, представлен-ного в листинге 5.26:

BETWEEN NOW() – 90

AND NOW() + 90.

В СУБД Microsoft SQL Server системной фун-кцией даты/времени является CURRENT_-TIMESTAMP (или ее синоним – функцияGETDATE()). Ни функция CURRENT_DATE(),ни функция CURRENT_TIME() данной СУБДне поддерживаются. Чтобы выполнить за-прос из листинга 5.25 в среде СУБДMicrosoft SQL Server, вам придется убратьиз текста запроса выражения с функциямиCURRENT_DATE() и CURRENT_TIME(). А длявыполнения запроса из листинга 5.26 втекст предложения BETWEEN этого запросавам придется внести следующие изменения:

BETWEEN CURRENT_TIMESTAMP – 90

AND CURRENT_TIMESTAMP + 90.

В СУБД Oracle системной функцией да-ты/времени является SYSDATE. И хотяOracle 9i и более поздних версий поддер-живает функции CURRENT_TIMESTAMP() и

Page 171: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

171

Отображение информациио пользователеЧтобы идентифицировать пользователя,действующего в системе данного базово-го сервера СУБД, применяют функциюCURRENT_USER.

Вывод информациио текущем пользователе

Напечатайте CURRENT_USER (см. листинги рис. 5.27; эта функция выдает на выходеимя текущего пользователя базы данных).

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

Листинг 5.27. Напечатать имя текущегопользователя

SELECT CURRENT_USER AS "User";

User

———----

cfehily

Рис. 5.27. Результат выполнения листинга 5.27

Отображение информации о пользователе

Page 172: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

172 Операторы и функции

Кроме функции CURRENT_USER, стандартSQL определяет функции получения именипользователя SESSION_USER иSYSTEM_USER. Различия между ними следу-ющие: во-первых, каждая функция понима-ет под пользователем не тот или не совсемтот объект, по сравнению с остальнымидвумя функциями; во-вторых, каждая фун-кция подразумевает свой собственныйкруг полномочий. Так, функцияCURRENT_USER указывает на регистрацион-ный идентификатор пользователя базыданных, под чьим набором полномочий втекущий момент исполняются запросы(например, этот пользователь можетиметь право в рамках своих полномочийзапускать только, допустим, командыSELECT, и никакие другие). В то же времяфункция SESSION_USER укажет на иденти-фикатор полномочий текущего сеанса ра-боты с базой данных (и эти полномочиямогут не совпадать с полномочиями теку-щего пользователя). Наконец, функцияSYSTEM_USER укажет на регистрационныйномер (учетную запись) пользователяв базовой операционной системе. Очевид-но, что эти три пользовательских значениясущественно зависят от выбора конкрет-ной СУБД и могут не совпадать. За получе-нием подробной информации о пользова-телях, сессиях (сеансах) и полномочиях об-ратитесь к документации по вашей СУБДи организуйте в ней поиск по ключам«authorization», «session», «user», «role» (тоесть «полномочия», «сеанс», «пользова-тель» и «роль»).

Чтобы в среде СУБД Microsoft Access вы-полнить запрос из листинга 5.27, вам при-дется изменить текст предложения SELECTследующим образом:

SELECT CureentUser AS "User";

Чтобы в среде СУБД Oracle выполнить зап-рос, который представлен в листинге 5.27,вам придется изменить текст предложенияSELECT следующим образом:

SELECT USER AS "User" FROM DUAL;

Чтобы в среде СУБД MySQL выполнитьзапрос, который представлен в листинге5.27, вам придется изменить текст предло-жения SELECT следующим образом:

SELECT USER() AS "User";

Кроме уже рассмотренной пользовательс-кой функции СУБД Microsoft SQL Server под-держивает SESSION_USER и SYSTEM_USER.

Кроме уже рассмотренной пользовательс-кой функции СУБД MySQL поддерживаетSESSION_USER() и SYSTEM_USER().

Еще одна функция СУБД Oracle – SYS-_CONTEXT() – дает на выходе пользова-тельские атрибуты сеанса.

СУБД PostgreSQL поддерживает SESSI-ON_USER.

За получением подробной информации осистемных функциях обратитесь к доку-ментации по вашей СУБД и организуйтев ней поиск по ключам «user» или «systemfunction» («пользователь» или «системнаяфункция»).

Page 173: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

173

Преобразование типовданных с помощьюфункции CAST()В большинстве случаев ваша СУБД ав-томатически произведет преобразование(или, как еще говорят, приведение) типовданных. Тем самым СУБД позволит, незадумываясь о типах данных, применять,скажем, одновременно числа и датыв символьных выражениях типа конкате-нации или, допустим, без проблем ис-пользовать разнотипные числа в смешан-ных арифметических выражениях, пото-му что все эти числа будут преобразова-ны в один «старший» или самый сложныйтип данных (подробнее об этом см. в при-мечании с пометкой DBMS в разделе«Арифметические операции» этой главы).Но иногда ваша СУБД не будет произво-дить преобразование типов данных авто-матически. В этом случае вам следует вос-пользоваться функцией CAST() и принуди-тельно конвертировать некое выражениеиз одного типа данных в другой (см. раз-дел «Типы данных» в главе 3).

Основные характеристики функции CAST()таковы:

все преобразования типов данных, ко-торые проводит ваша СУБД, делятсяна две категории (иногда конверсиятипов данных запрещена, например:тип FLOAT нельзя преобразовать в типTIMESTAMP):

– неявные, или автоматические, проис-ходят без участия функции CAST(),которую в этом случае указывать ненадо;

– явные, для совершения которых не-обходимо указать функцию CAST();

Преобразование типов данных с помощью функции CAST()

Page 174: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

174 Операторы и функции

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

вы можете преобразовать любой исход-ный символьный тип данных в другойтип данных, но только если соответ-ствующая строка символов представ-ляет собой в целевом типе данных ка-кое-нибудь незапрещенное буквенноевыражение;некоторые преобразования числовыхтипов в зависимости от конкретнойСУБД или округляют, или усекают дан-ные, как это происходит при конверсиииз типа DECIMAL в тип INTEGER;преобразование из типа VARCHAR в типCHAR может вызвать усечение строк;иногда преобразование типов можетсгенерировать ошибку, если целевойтип не имеет достаточно места длятого, чтобы вместить преобразованноезначение (например, преобразованиеиз типа FLOAT в тип SMALLINT даст сбой,если преобразуемое число с плаваю-щей десятичной точкой не попадаетв интервал, который СУБД отводитдля чисел типа SMALLINT, а именно: от–32 768 до +32 768);

любое преобразование из типа NUMERICв тип DECIMAL может потребовать явногоуказания функции CAST() из-за опаснос-ти потери точности, которая могла быпроизойти при неявном преобразо-вании;

Листинг 5.28. Этот запрос должен преобразоватьцены книг, учитываемых в нашей типовой базеданных, из типа данных DECIMAL в типы данныхINTEGER и CHAR(8). При этом символы < и >используются для того, чтобы показать границыстрок типа CHAR(8). В результате вы получитеили то, что показано на рис. 5.28a, или то, чтопоказано на рис. 5.28б, в зависимости от того,округляет или усекает ваша СУБД целые числа

SELECT

price

AS "price(DECIMAL)",

CAST(price AS INTEGER)

AS "price(INTEGER)",

'<' || CAST(price AS CHAR(8)) || '>'

AS "price(CHAR(8))"

FROM titles;

price(DECIMAL) price(INTEGER) price(CHAR(8))

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

21.99 21 <21.99 >

19.95 19 <19.95 >

39.95 39 <39.95 >

12.99 12 <12.99 >

6.95 6 <6.95 >

19.95 19 <19.95 >

23.95 23 <23.95 >

10.00 10 <10.00 >

13.95 13 <13.95 >

NULL NULL NULL

7.99 7 <7.99 >

12.99 12 <12.99 >

29.99 29 <29.99 >

Рис. 5.28a. Результат исполнения запроса,представленного в листинге 5.28, в случае, еслиСУБД усекает десятичные числа (то есть, строгоговоря, Exact Numeric, целые и рациональныечисла с фиксированной десятичной точкой) приконвертации их в целые

Page 175: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

175

при любом преобразовании из типаDATE в тип TIMESTAMP часть результатаэтого преобразования, которая соответ-ствует времени, будет равна 00:00:00(это полночь);

если какой-либо аргумент функцииCAST() является значением null, ее ре-зультат тоже будет значением null (ис-ключение составляет СУБД Oracle, см.далее в этом разделе примечание с по-меткой DBMS).

Преобразование значенийодного типа данных к другому

Напечатайте CAST(expr AS data_type).

Предполагается, что (см. листинги 5.28,5.29 и рис. 5.28a, 5.28б, 5.29):

вместо expr вы подставите выражение,которое хотите преобразовать в другойтип данных;

вместо data_type вы подставите целевойтип данных, имея в виду следующее:тип data_type должен быть одним из ти-пов данных, представленных в табл. 3.5,3.6, 3.7, 3.9, 3.10, 3.12, которые могут принеобходимости включать аргументыдлины, точности и масштаба (множестводопустимых значений data_type включа-ет, например, CHAR(10), VARCHAR(25), NU-MERIC(5,2), INTEGER, FLOAT, DATE);

если тип данных, обозначенный какexpr, окажется несовместимым с типомданных data_type, СУБД выдаст ошибку.

Функцию CAST() можно применять там,где используется какое-либо выражение,в частности в предложениях SELECT,WHERE, ORDER BY.

price(DECIMAL)price(INTEGER)price(CHAR(8))

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

21.99 22 <21.99 >

19.95 20 <19.95 >

39.95 40 <39.95 >

12.99 13 <12.99 >

6.95 7 <6.95 >

19.95 20 <19.95 >

23.95 24 <23.95 >

10.00 10 <10.00 >

13.95 14 <13.95 >

NULL NULL NULL

7.99 8 <7.99 >

12.99 13 <12.99 >

29.99 30 <29.99 >

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

Преобразование типов данных с помощью функции CAST()

Page 176: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

176 Операторы и функции

Расширяющим называется любое прео-бразование одного произвольного типаданных в другой тип данных при условии,что потери данных или их искажения непроизойдет. Например, преобразование изтипа данных SMALLINT в тип данныхINTEGER является расширяющим, так какпо своему формату тип INTEGER способенвместить любое число, представленноетипом SMALLINT. Преобразование данных,не являющееся расширяющим, называетсясужающим. Оно несет в себе риск потериили/и искажения данных. Примером сужа-ющего преобразования может служитьпреобразование, противоположное толькочто рассмотренному расширяющему преоб-разованию. Преобразование из типа данныхINTEGER в тип данных SMALLINT можетпривести к тому, что некоторые числа,представленные значениями типа данныхINTEGER, просто не поместятся в форматетипа данных SMALLINT, следовательно, бу-дут искажены. Все расширяющие преобра-зования разрешены во всех СУБД, но по-пытка выполнить какое-нибудь сужающеепреобразование может вынудить СУБД вы-дать предупреждение о возможной ошиб-ке или сообщение об ошибке.

СУБД Microsoft Access вместо одной функ-ции преобразования типов CAST() вклю-чает целое семейство аналогичных функ-ций. Например, функции CStr(expr),CInt(expr) и CDec(expr) приведут про-извольное выражение, представленноекак expr, к некой строке, некоему целомучислу или некоему числу с фиксирован-ной десятичной точкой. Кроме того, вэтой среде вы можете применять следую-щие функции:

Space(number) – чтобы добавить встроку пробелы в числе, обозначенномкак number;Left(string, length) – чтобы усечьстроку string до длины length.

Учитывая все вышесказанное и то, чтооператором конкатенации строк в СУБДMicrosoft Access является оператор +, призапуске в среде СУБД Microsoft Access за-просов из листингов 5.28 и 5.29 вам при-дется внести в выражения следующие из-менения:

CInt(price)

'<' + CStr(price) + '>' – длялистинга 5.28;

CStr(sales)

→ + Space(8 – Len(CStr(sales)))

→ + ' copies sold of '

→ + Left(title_name, 20) – длялистинга 5.29.

В СУБД Microsoft SQL Server для конкате-нации строк следует применять оператор +.Поэтому, чтобы запустить в среде этойСУБД запросы из листингов 5.28 и 5.29,вам придется внести соответствующие из-менения в текст предложений с оператора-ми конкатенации:

'<' + CAST(price AS CHAR(8)) + '>'

–для листинга 5.28;

CAST(sales AS CHAR(8))

→ + ' copies sold of '

→ + CAST(title_name AS CHAR(20)) –для листинга 5.29.

СУБД Oracle не разрешает преобразовы-вать символьную строку произвольноготипа в тип CHAR(length), если длина це-левого типа, указанная как length, мень-ше длины исходной строки. То есть в средеСУБД Oracle не удастся применить функциюCAST() для усечения строк. С этой цельювоспользуйтесь функцией SUBSTR().О том, как это делается, прочитайте в при-мечании DBMS в разделе «Выбор произ-вольной подстроки с помощью функцииSUBSTRING()» этой главы.

Таким образом, если в среде СУБД Oracleзапустить запрос из листинга 5.29, вам

Page 177: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

177

придется внести соответствующие измене-ния в текст выражения с функцией CAST():

CAST(sales AS CHAR(8))

→ || ' copies sold of '

→ || SUBSTR(title_name, 1, 20)

В среде СУБД MySQL множество допусти-мых целевых типов данных функцииCAST() ограничено бинарными, целочис-ленными типами данных и типами данныхдаты и времени (только при указании це-левого типа данных вместо типа INTEGERследует ставить SIGNED). Также в средеMySQL вы можете применять следующиефункции:

RPAD(string, length, padstring) –чтобы добавлять пробелы в строки;

LEFT(string, length) – чтобы усе-кать строки.

Наконец, в среде СУБД MySQL для конка-тенации строк следует применять функ-цию CONCAT().

Чтобы в среде СУБД MySQL выполнитьзапросы из листингов 5.28 и 5.29, вам при-дется внести соответствующие измененияв выражения с функцией CAST():

CAST(price AS SIGNED)

CONCAT('<', RPAD(price, 8, ' '),

→ '>') – для листинга 5.28;

CONCAT(

→ RPAD(sales, 8, ' ')

→ ' copies sold of ',

→ LEFT(title_name, 20)) – для лис-тинга 5.29.

В среде СУБД PostgreSQL для усечениястрок следует применять функцию SUB-STRING(), а для преобразования произ-вольного числа в строку – функцию TO_-CHAR(number, format).

Листинг 5.29. Перечислить в порядке убывания(нисходящий порядок) общие объемы продажвместе с некоторыми частями названий заданнойдлины тех книг, учитываемых в нашей типовойбазе данных, темой которых является илиистория, или биография. Здесь преобразование втип CHAR(20) укорачивает эти названия доразумных пределов, делая итоговый переченьболее читабельным. Результат исполнениязапроса см. на рис. 5.29

SELECT

CAST(sales AS CHAR(8))

|| ' copies sold of '

|| CAST(title_name AS CHAR(20))

AS "History and biography sales"

FROM titles

WHERE sales IS NOT NULL

AND type IN ('history', 'biography')

ORDER BY sales DESC;

History and biography sales

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

1500200 copies sold of I Blame My Mother

100001 copies sold of Spontaneous, Not Ann

11320 copies sold of How About Never?

10467 copies sold of What Are The Civilia

9566 copies sold of 200 Years of German

566 copies sold of 1977!

Рис. 5.29. Результат исполнения запроса,представленного в листинге 5.29

Преобразование типов данных с помощью функции CAST()

Page 178: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

178 Операторы и функции

Если в среде СУБД PostgreSQL вы хотитезапустить запросы, представленные в ли-стингах 5.28 и 5.29, то в выражения с фун-кцией CAST() придется внести соответ-ствующие изменения:

CAST(price AS INTEGER)

'<' || TO_CHAR(price, '99.99 ')

||

→ '>' – для листинга 5.28;

SUBSTRING(title_name FROM 1→ FOR 20) – для листинга 5.29.

СУБД Oracle трактует любую пустую стро-ку как значение null. Поэтому CAST(NULLAS CHAR) дает на выходе '' (пустая стро-ка). Обратите внимание на примечание спометкой DBMS в разделе «Значение null»главы 3.

Чтобы в среде СУБД PostgreSQL сравнитьлюбое значение столбца, которому присво-ен тип данных NUMERIC или DECIMAL (тоесть целые и рациональные числа с фик-сированной десятичной точкой), с любымдействительным числом (то есть с числом,которое в программировании принято на-зывать действительным, а на самом деле –с целым или рациональным числом с пла-вающей точкой, так как действительныечисла есть абстракция), необходимо кон-вертировать это число или в тип NUMERIC,

или в тип DECIMAL. Например, следующаякоманда в среде СУБД PostgreSQL не сра-ботает, так как столбец price относитсяк типу DECIMAL(5,2):

SELECT price

FROM titles

WHERE price < 20.00;

Обозначенную проблему можно решитьс помощью такого запроса:

SELECT price

FROM titles

WHERE price < CAST(20.00 AS

DECIMAL);

Кроме рассмотренных функций коммер-ческие СУБД включают и другие функциипреобразования и форматирования:

CONVERT() – в СУБД Microsoft SQLServer и MySQL;

TO_CHAR(), TO_DATE(), TO_TIME-

STAMP() и TO_NUMBER() – в СУБДOracle и PostgreSQL.

Чтобы полностью разобраться в этом во-просе, организуйте поиск в документациипо вашей СУБД с использованием клю-чей «conversion», «cast» или «formattingfunctions» (преобразование, приведениеили функции форматирования).

Page 179: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

179

Вычисление условныхзначений с помощьювыражения CASEДо появления стандарта SQL-92 програм-мисты часто жаловались на то, что недо-статок условных конструкций в SQL вы-нуждает обращаться к базовому языкувсякий раз, когда возникает необходи-мость предпринять некие действия на ос-нове истинностного значения условия (тоесть когда надо принять во внимание всезначения условия, а именно: true, false,unknown, что означает «истина», «ложь»,«неизвестно»). В качестве ответа на это за-мечание в стандарт SQL-92 были введенывыражение CASE и его упрощенные экви-валенты COALESCE() и NULLIF(). В этомразделе мы поговорим о функции CASE(), авсе остальные аналогичные конструкциибудут рассмотрены в конце главы.Основные характеристики выраженияCASE:

если вы когда-либо программировали,то заметите, конечно, что выражениеCASE представляет собой SQL-эквива-лент командных конструкций if-then-else или switch, активно применяемыхв процедурных языках. Разница лишьв том, что CASE – это выражение, а некоманда;по сути, выражение CASE вычисляет не-сколько логических условий подряд допоявления первого значения true (исти-на) и выдает одно какое-нибудь значе-ние, соответствующее этому условию,оказавшемуся истинным;выражение CASE позволяет, не внося ни-каких фактических изменений в дан-ные таблиц произвольной базы данных,

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

основное предназначение выраженияCASE состоит в том, чтобы заменятькоды или аббревиатуры (любимыепрограммистами и разработчиками базданных за то, что их легче хранитьи ими легче управлять по сравнениюс пространными текстовыми объяснени-ями) более читабельными значениями(например, если столбец marital_statusсодержит целочисленные коды 1, 2, 3,4 состояний «холост», «женат», «разве-ден» и «вдовец», то непрофессионалы впрограммировании баз данных, ктобудет работать с вашей базой, навер-ное, предпочтут воспользоваться пояс-нениями, а не кодами, похожими накриптограмму);

выражение CASE имеет два формата:

– в простом формате сравнивает про-извольно заданное выражение с про-стыми выражениями из какого-либоперечня и выдает общий результатсравнений;

– в поисковом формате вычисляет ло-гические выражения определенногонабора логических (Булевых) выра-жений и по результатам этих вычис-лений выдает общий результат;

если ни одно из проверяемых условийне окажется истинным (то есть значе-ние true так и не появится), в качестверезультата по умолчанию выражениеCASE выдаст необязательное предложе-ние ELSE.

Вычисление условных значений с помощью выражения CASE

Page 180: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

180 Операторы и функции

Использование выражения CASEв простом формате

Напечатайте:CASE comparison_value

WHEN value1 THEN result1

WHEN value2 THEN result2

WHEN valueN THEN resultN

[ELSE default_result]

END

Предполагается, что:

вместо value1, value2, …, valueN вы под-ставите выражения, не содержащие опе-ратор сравнения;вместо result1, result2, …, resultN выподставите выражения, являющиесякандидатами на результат всего выра-жения CASE в простом формате;вместо выражения comparison_valueподставите (явно или через указаниена заголовок столбца некой таблицы)выражение, которому должно соответ-ствовать каждое выражение из группыvalue1, value2, …, valueN, чтобы соот-ветствующий кандидат стал результа-том;вместо default_result вы подставитевыражение, которое по вашему реше-нию быть должно результатом вы-ражения CASE в том случае, когда ниодно значение из группы value1, value2,…, valueN не будет соответствовать зна-чению comparison_value (если опцияELSE default_result будет опущена,СУБД выберет опцию ELSE NULL);вы выберете все выражения, участвую-щие в выражении CASE в простом фор-мате, чтобы они обязательно были илиодного и того же типа данных или та-ких типов данных, которые можно не-явно преобразовать в один и тот жетип данных.

Выражение CASE в простом формате сравни-вает каждое выражение из группы va-lue1, value2, …, valueN с выражениемcomparison_value: сначала value1, потомvalue2… и т.д. до тех пор, пока не будет до-стигнуто соответствие (то есть то тех пор,пока в результате сравнения не будет полу-чено логическое значение truth). Когда жесоответствие будет достигнуто на некоемусловии valuea (1<a< N), выражение CASEвыдаст в качестве результата значение re-sulta. Если весь набор значений value1,value2, …, valueN будет просмотрен, а соот-ветствие ни на одном значении достигнутоне будет, выражение CASE выдаст в качестверезультата default_result (или значениеnull; см. листинг 5.30 и рис. 5.30).

Использование выражения CASEв поисковом формате

Напечатайте:CASE

WHEN condition1 THEN result1

WHEN condition2 THEN result2

WHEN conditionN THEN resultN

[ELSE default_result]

END

Предполагается, что:

вы замените condition1, condition2, …,conditionN поисковыми условиями,каждое из которых может содержатьоператоры сравнения и несколько логи-ческих выражений, связанных операто-рами AND и OR, и принимать значенияtrue, false, unknown (см. раздел «Фильт-рация строк c помощью предложенияWHERE» в главе 4);

вместо result1, result2, …, resultN выподставите выражения, являющиесякандидатами на результат всего выра-жения CASE в поисковом формате;

Page 181: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

181

вместо default_result вы подставите,если захотите, то выражение, котороепо вашему решению должно быть ре-зультатом выражения CASE в поисковомформате, если ни одно условие из груп-пы condition1, condition2, …, condi-tionN не даст значения true (если опцияELSE default_result будет опущена,СУБД будет считать выбранной опциюELSE NULL);

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

Выражение CASE в поисковом формате вы-числяет каждое из логических выраженийcondition1, condition2, …, conditionN: сна-чала condition1, потом condition2… и т.д.до тех пор, пока в результате не будет по-лучено логическое значение truth. Когдаже это значение будет достигнуто на неко-ем условии conditiona, при котором 1<a<N,выражение CASE выдаст в качестве результа-та значение resulta. Если весь набор усло-вий condition1, condition2, …, conditionNбудет просмотрен, а значение truth достиг-нуто не будет, выражение CASE выдаст в ка-честве результата default_result (или зна-чение null; см. листинг 5.31 и рис. 5.31).

Имейте в виду, что в зависимости от СУБДвыражение CASE в поисковом форматеможет по достижении первого результатакак продолжить работу и рассмотреть всеостальные предложения WHEN и соответ-ствующие условия, так и остановиться.В связи с этим вам следует предотвратитьнежелательные побочные эффекты типаделения на ноль при вычислении выраже-ний, до которых дело не должно былодойти, как вам могло показаться, исходя изпредположения, что выражение CASE ра-ботает до первого результата.

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

SELECT

title_id,

type,

price,

CASE type

WHEN 'history'

THEN price * 1.10

WHEN 'psychology'

THEN price * 1.20

ELSE price

END

AS "New price"

FROM titles

ORDER BY type ASC, title_id ASC;

title_id type price New price

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

T06 biography 19.95 19.95

T07 biography 23.95 23.95

T10 biography NULL NULL

T12 biography 12.99 12.99

T08 children 10.00 10.00

T09 children 13.95 13.95

T03 computer 39.95 39.95

T01 history 21.99 24.19

T02 history 19.95 21.95

T13 history 29.99 32.99

T04 psychology 12.99 15.59

T05 psychology 6.95 8.34

T11 psychology 7.99 9.59

Рис. 5.30. Результат исполнения запроса,представленного в листинге 5.30

Вычисление условных значений с помощью выражения CASE

Page 182: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

182 Операторы и функции

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

Выражение CASE поможет избежать оши-бок, связанных с делением на ноль:

CASE

WHEN n <> 0 THEN expr/n

ELSE 0

END

Выражение CASE в простом формате мож-но представлять как упрощенную записьследующего выражения CASE в поисковомформате:

CASE

WHEN comparison_value = value1

THEN result1

WHEN comparison_value = value2

THEN result2

WHEN comparison_value = valueN

THEN resultN

[ELSE default_result]

END

CУБД Microsoft Access не поддерживаетвыражение CASE, поэтому для запускав этой среде запросов, представленныхв листингах 5.30 и 5.31, вам придетсявместо выражения CASE использоватьфункцию Switch(condition1, result1,

condition2, result2, …, conditionN,

resultN) и, соответственно, заменитьв текстах указанных запросов выраженияCASE следующими:

Switch(

→ type IS NULL, NULL,

→ type = 'history' , price * 1.10,

→ type = 'psychology' , price * 1.20,

→ type IN ('biography',

→ type = 'children' , 'computer'),

price) – для листинга 5.30;

Switch(

→ sales IS NULL,

→ 'Unknown' ,

→ sales <= 1000,

→ 'Not more than 1,000',

→ sales <= 10000,

→ 'Between 1,001 and 10,000',

→ sales <= 100000,

→ 'Between 1,001 and 100,000',

→ sales <= 1000000,

→ 'Between 1,001 and 1,000,000',

→ sales > 1000000,

→ 'Over 1,000,000') – для лис-тинга 5.31.

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

SELECT

title_id,

CASE

WHEN sales IS NULL

THEN 'Unknown'

WHEN sales <= 1000

THEN 'Not more than 1,000'

WHEN sales <= 10000

THEN 'Between 1,001 and 10,000'

WHEN sales <= 100000

THEN 'Between 10,001 and 100,000'

WHEN sales <= 1000000

THEN 'Between 100,001 and

1,000,000'

ELSE 'Over 1,000,000'

END

AS "Sales category"

FROM titles

ORDER BY sales ASC;

Page 183: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

183

СУБД Oracle 9i поддерживает выражениеCASE в простом формате, поэтому в сре-де Oracle 9i запрос из листинга 5.30 бу-дет выполнен без проблем в том виде,как он есть. Но если вы работаете в средеСУБД Oracle 8i, то для запуска запроса излистинга 5.30 придется вносить в негоизменения: или заменить выражениеCASE в простом формате подходящимвыражением CASE в поисковом формате(см. выше примечание в этом разделе),или использовать функцию DECODE

(comparison_value, value1, result1,

value2, result2, …, default_-

result) следующим образом:

DECODE(type,

→ NULL, NULL,

→ 'history', price * 1.10,

→ 'psychology', price * 1.20,

→ price)

Если вы работаете в среде СУБД Post-greSQL, то для исполнения запроса, пред-ставленного в листинге 5.30, придетсяпреобразовать числа с плавающей точкойв тип данных DECIMAL. Подробнее о том,как выполнять подобные преобразования,читайте в разделе «Преобразование типовданных с помощью функции CAST()» этойглавы. В текст выражений для вычисленияновых цен, относящихся к рассматривае-мому запросу, придется внести следующиеизменения:

price * CAST((1.10) AS DECIMAL);

price * CAST((1.20) AS DECIMAL).

title_id Sales category

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

T10 Unknown

T01 Not more than 1,000

T08 Between 1,001 and 10,000

T09 Between 1,001 and 10,000

T02 Between 1,001 and 10,000

T13 Between 10,001 and 100,000

T06 Baetween 10,001 and 100,000

T04 Between 10,001 and 100,000

T03 Between 10,001 and 100,000

T11 Between 10,001 and 100,000

T12 Between 100,001 and 1,000,000

T05 Between 100,001 and 1,000,000

T07 Over 1,000,000

Рис. 5.31. Результат исполнения запроса,представленного в листинге 5.31

Вычисление условных значений с помощью выражения CASE

Page 184: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

184 Операторы и функции

Проверка на значения nullс использованием функцииCOALESCE()Функция COALESCE() выдает первое выра-жение, не являющееся значением null,которое она встретит при просмотре сво-их аргументов слева направо. ФункциюCOALESCE() чаще всего применяют длятого, чтобы в результатах запросов вместозначений null показывать какое-нибудьспециальное обозначение. Дело в том, чтонекоторых пользователей значение nullсмущает. Интересно отметить, что функ-ция COALESCE(expr1, expr2, expr3) – это неболее чем упрощенная форма следующе-го выражения CASE в поисковом формате:CASE

WHEN expr1 IS NOT NULL THEN expr1

WHEN expr2 IS NOT NULL THEN expr2ELSE expr3

END

Отображение первого найденногозначения, не являющегосязначением null

Напечатайте COALESCE(expr1, expr2,…).Предполагается, что:

вместо expr1, expr2,… вы подставите не-обходимое количество выражений, раз-деленных запятыми;вы выберете все выражения expr1,expr2,…, чтобы они обязательно былиодного и того же типа данных или та-ких типов данных, которые можно не-явно преобразовать в один и тот же типданных.

Функция COALESCE() выдаст первое выраже-ние, не являющееся значением null, котороеона встретит при просмотре и вычислениисвоих аргументов в порядке следования ихслева направо. Если все выражения expr1,expr2,… окажутся значениями null, функцияCOALESCE() тоже выдаст значение null (см.листинг 5.32 и рис. 5.32).

Функцию COALESCE() можно использо-вать там, где применяется какое-либо вы-ражение, в частности в предложенияхSELECT, WHERE, ORDER BY.

СУБД Microsoft Access не поддерживаетфункцию COALESCE(), поэтому для запускав этой среде запроса из листинга 5.32 вме-сто функции COALESCE() придется ис-пользовать функцию Switch(condition1,result1, condition2, result2, …,conditionN, resultN) и, соответственно,заменить в тексте указанного запроса функ-цию COALESCE() следующим выражением:

Switch(state IS NOT NULL, state,

→ state IS NULL, 'N/A')

СУБД Oracle 9i поддерживает функциюCOALESCE(), поэтому в среде Oracle 9i за-прос из листинга 5.32 будет выполнен безпроблем в том виде, как он есть. Но есливы работаете в среде СУБД Oracle 8i, то длязапуска запроса из листинга 5.32 придетсявносить в него изменения: вместо функцииCOALESCE() применить функциюNVL(expr1, expr2), которая принимаеттолько два аргумента (если надо заменитьтакое выражение, где у функцииCOALESCE() больше двух аргументов, всреде СУБД Oracle 8i следует использоватьвыражение CASE).

В запросе 5.32 можно применить функциюNVL(), например:

NVL(state, 'N/A')

Page 185: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

185

Листинг 5.32. Распечатать идентификаторыи места нахождения издателей, учитываемыхв нашей типовой базе данных, но так, чтобыпри наличии у какого-нибудь издателя в столбцеstate (штат) значения null в распечатке вместонего стояло N/A. Результат исполнения запросасм. на рис. 5.32

SELECT

pub_id,

city,

COALESCE(state, 'N/A') AS "state",

country

FROM publishers;

pub_id city state country

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

P01 New York NY USA

P02 San Francisco CA USA

P03 Hamburg N/A Germany

P04 Berkeley CA USA

Рис. 5.32. Результат исполнения запроса,представленного в листинге 5.32

Сравнение выражений с помощью функции NULLIF()

Сравнение выражений спомощью функции NULLIF()Функция NULLIF() сравнивает два выраже-ния, являющиеся ее аргументами, и выда-ет на выходе:

значение null, если эти выражения рав-ны (то есть совпадают в каком-либозаранее определенном смысле);первое из этих двух выражений, еслиони не равны.

Самый распространенный вариант исполь-зования функции NULLIF(): значение nullвводится в то место, куда должно быть вве-дено другое значение, которое на данныймомент или/и неизвестно, или/и недоступ-но для распознавания, или/и утеряно.Некоторые программисты не любят при-менять значение null в качестве слова-за-менителя. Поэтому вместо значения nullони предпочитают, скажем, число –1 илистроку 'N/A' (выражения not applicable илиnot available переводятся как «не годится»,«нет в наличии» или «не определено»).Здесь важно понимать, что все СУБД име-ют очень четкие инструкции о том, какобращаться со значениями null. Именнопоэтому весьма желательно преобразоватьотсутствующие значения, которые долж-ны быть определены пользователем, в зна-чения null. Если бы вам, к примеру, пона-добилось вычислить среднее значение ка-кого-нибудь столбца, состоящего из дан-ных, определяемых пользователем, то приусловии, что пользователь вводил –1 вся-кий раз, когда ему было неизвестно вер-ное значение, а вы находили бы среднеесреди значений единицы с отрицательнымзнаком, получился бы неверный результат.Перед вычислением среднего отрица-тельные числа надо как-то убрать. Вотздесь вам и пригодится функция NULLIF(),

Page 186: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

186 Операторы и функции

которая преобразует –1 в значения null,пропущенные СУБД при расчете среднегоарифметического.

В некотором смысле функция NULLIF() естьне более чем сокращенное обозначение од-ного выражения CASE в поисковом формате,а именно: функция NULLIF(expr1, expr2) эк-вивалентна выражениюCASE

WHEN expr1 = expr2 THEN NULL

ELSE expr1

END

Отображение значения null в случаеэквивалентности двух выражений

Напечатайте NULLIF(expr1, expr2).

Предполагается, что:

вместо expr1 и expr2 вы подставите вы-ражения, которые хотите сравнить;

вместо expr2 вы не будете подставлятьлитерал NULL.

Функция NULLIF() сравнивает значенияexpr1 и expr2, являющиеся ее аргументами,и выдает на выходе (см. листинг 5.33 и рис.5.33):

значение null, если эти выражения со-впадают;

значение expr1, если эти выражения несовпадают.

Листинг 5.33. Столбец contract таблицы titlesнашей типовой базы данных содержит ноль,если для книги, которой соответствует строка,не существует формального контракта. Поменятьнули на значения null. Ненулевые значенияне должны быть изменены этим запросом.Результат исполнения запроса см. на рис. 5.33

SELECT

title_id,

contract,

NULLIF(contract, 0) AS "Null

contract"

FROM titles;

title_id contract Null contract

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

T01 1 1

T02 1 1

T03 1 1

T04 1 1

T05 1 1

T06 1 1

T07 1 1

T08 1 1

T09 1 1

T10 0 NULL

T11 1 1

T12 1 1

T13 1 1

Рис. 5.33. Результат исполнения запроса,представленного в листинге 5.33

Page 187: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

187

Функцию NULLIF() можно использоватьвезде, где применяется какое-либо выра-жение, в частности в предложениях SE-LECT, WHERE, ORDER BY.

СУБД Microsoft Access не поддерживаетфункцию NULLIF(), поэтому для выпол-нения в этой среде запроса из листин-га 5.33 вам придется вместо функцииNULLIF() применить функцию IIf(expr1

= expr2, NULL, expr1) и, соответствен-но, заменить в тексте указанного запросафункцию NULLIF() следующей:

IIf(contract = 0, NULL, contract)

СУБД Oracle 9i поддерживает функциюNULLIF(), поэтому в этой среде запрос излистинга 5.33 будет выполнен без проблемв том виде, как он есть. Но если вы работа-ете в среде СУБД Oracle 8i, то для запусказапроса из листинга 5.33 придется вноситьв него изменения: вместо функцииNULLIF() применить CASE.

В запросе 5.33 можно использовать CASE,например:

CASE

WHEN contract = 0 THEN NULL

ELSE contract

END

Сравнение выражений с помощью функции NULLIF()

Page 188: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

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

всех строк произвольной таблицы;только строк, заданных неким предло-жением WHERE;

только строк, созданных неким пред-ложением GROUP BY.

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

В данной главе помимо собственно агре-гатных функций SQL отдельно рассматри-ваются два предложения команды SELECT:

GROUP BY – группирует строки;HAVING – фильтрует группы строк.

Суммированиеи группировка данных

Page 189: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

189

Использованиеагрегатных функцийСтандартные функции агрегатов языкаSQL перечислены в табл. 6.1. Приведем ихосновные характеристики:

под expr подразумевается не только наи-менование (заголовок) некоего столбцаопределенной таблицы, но и какой-нибудь литерал, произвольная функ-ция или комбинация заголовков стол-бцов, литералов, функций и связыва-ющих все это в единое целое опера-торов;

области определения агрегатных функ-ций совпадают не всегда;

функции SUM() и AVG() определены толь-ко для числовых типов данных;

функции MIN() и MAX() определены длясимвольных и числовых типов дан-ных, а также для типов данных датыи времени;

функции COUNT(expr) и COUNT(*) опреде-лены для всех типов данных;

все агрегатные функции, за исключени-ем COUNT(*), игнорируют значения null,но вы всегда можете посредством функ-ции COALESCE() подставить в аргументлюбой агрегатной функции вместо nullпроизвольное значение (см. раздел «Про-верка на значения null с использованиемфункции COALESCE()» в главе 5);

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

Таблица 6.1. Агрегатные функции

Функция Результат

MIN(expr) Минимальное значение в expr

MAX(expr) Максимальное значение вexpr

SUM(expr) Сумма всех значений в expr

AVG(expr) Среднее всех значений в expr

COUNT(expr) Число всех значений, не яв-ляющихся значениями null, вexpr

COUNT(*) Число строк в произвольнойтаблице или произвольном на-боре строк

Использование агрегатных функций

Page 190: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

190 Суммирование и группировка данных

для того чтобы агрегировать (то естьсобрать в одном агрегате) различныезначения, следует применить предложе-ние DISTINCT (см. раздел «Исключениеповторных значений с помощью пред-ложения DISTINCT» в этой главе);агрегатные функции часто применяютвместе с предложением GROUP BY (см.раздел «Группирование строк с исполь-зованием предложения GROUP BY» вэтой главе);чтобы ограничить множество строк,которые могут принять участие в агре-гатных вычислениях, следует использо-вать предложение WHERE (см. раздел«Фильтрация строк с помощью предло-жения WHERE» в главе 4);заголовки по умолчанию для столбцовпод агрегатные выражения определяют-ся выбором конкретной СУБД, поэто-му, чтобы не полагаться на эти заголов-ки, следует применить предложение ASи назвать результирующий столбецпринудительно (см. раздел «Созданиепсевдонимов столбцов с помощьюпредложения AS» в главе 4);никакое агрегатное выражение не можетпоявиться в предложении WHERE. Напри-мер, если бы вам понадобилось найтисреди книг, учитываемых в нашей ти-повой базе данных, такую, объем про-даж которой был бы максимален средивсех книг нашей базы, демонстрируемоеприменение агрегатной функции MAX()было бы неверным:SELECT title_id

FROM titles

WHERE sales = MAX(sales);

любое предложение SELECT должно со-держать или неагрегатные выражения(скалярные выражения, то есть те, ко-торые работают со своими аргумента-ми строка за строкой), или агрегатные

выражения, но не те и другие одновре-менно. Например, если бы вам пона-добилось найти среди книг, учитыва-емых в нашей типовой базе данных,такую, объем продаж которой был бымаксимальным среди всех книг нашейбазы, демонстрируемое применение аг-регатной функции MAX() было бы не-верным:

SELECT title_id, MAX(sales)

FROM titles;

единственным исключением из преды-дущего правила является то, что в од-ном предложении SELECT можно приме-нять и агрегатные, и неагрегатные вы-ражения, но только для тех столбцов,по которым проводится группировка,причем в этой же самой команде SELECT(см. раздел «Группирование строк с ис-пользованием предложения GROUP BY»в этой же главе). Например, следующийзапрос вполне допустим:SELECT type, SUM(sales)

FROM titles

GROUP BY type;

в одном и том же предложении SELECTможно применять более одного агре-гатного выражения, например:

SELECT MIN(sales), MAX(sales)

FROM titles;

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

SELECT SUM(AVG(sales))

FROM titles;

агрегатные выражения можно применятьв подзапросах (см. главу 8). Приведемвполне допустимый запрос, который на-ходит среди книг, учитываемых в нашей

Page 191: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

191

типовой базе данных, ту, объем продажкоторой максимален:SELECT title_id, price

FROM titles

WHERE sales =

(SELECT MAX(sales) FROM titles);

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

AVG(SELECT price FROM titles)

Имейте в виду, что СУБД Oracle позволя-ет вкладывать агрегатные выражения другв друга, но только в запросах с группиров-кой GROUP BY. Например, следующий зап-рос вычисляет средний максимальныйобъем продаж по всем типам (темам) книг,учитываемых в нашей типовой базе данных.При выполнении этого запроса СУБД Oracleсначала вычисляет внутренний (вложенный)агрегат MAX(sales) для групп строк,определенных столбцом type, по которомуосуществляется группировка, а потом еще разагрегирует полученные результаты вложен-ной агрегатной функции:

SELECT AVG(MAX(sales))

FROM titles

GROUP BY type;

СУБД MySQL 4.0 и более ранних версий неподдерживает подзапросы.

Коммерческие СУБД предоставляют, помиморассмотренных, не входящие в стандарт SQLагрегатные функции, например для вычисле-ния дополнительных статистических показа-телей, скажем, среднеквадратичного откло-нения (корень квадратный из дисперсии).Если вы хотите как следует разобраться сдополнительными агрегатными функциямивашей СУБД, организуйте поиск в ее доку-ментации по ключам «aggregate functions»и «group functions» (агрегатные функции игрупповые функции).

Использование агрегатных функций

Page 192: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

192 Суммирование и группировка данных

Поиск минимумапосредствомфункции MIN()Чтобы найти минимум по произвольно-му множеству значений, применяют агре-гатную функцию MIN().

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

Напечатайте MIN(expr).Предполагается, что:

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

Листинг 6.1 и рис. 6.1 демонстрируют тексткоманд и результаты сразу несколькихзапросов, применяющих агрегатную фун-кцию MIN(). Первый из этих запросов вы-дает название самой дешевой книги извсех, учитываемых в нашей типовой базеданных. Второй запрос выдает самую ран-нюю дату публикации по всем книгам,учитываемым в нашей типовой базе дан-ных. Третий запрос находит среди всехкниг в базе данных, темой которых явля-ется история, ту, чей объем в страницахминимален, и выдает это самое числостраниц.

Листинг 6.1. Несколько запросов с применениемфункции MIN(). Результаты выполнения листингасм. на рис. 6.1

SELECT MIN(price) AS "Min price"

FROM titles;

SELECT MIN(pubdate) AS "Earliest pubdate"

FROM titles;

SELECT MIN(pages) AS "Min history pages"

FROM titles

WHERE type = 'history';

Min price

---------

6.95

Earliest pubdate

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

1998-04-01

Min history pages

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

14

Рис. 6.1. Результат исполнения запросов,представленных в листинге 6.1

Page 193: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

193

Функцию MIN() допустимо применять толь-ко к данным, имеющим следующие типы:символьные, числовые и дата и время.

Если функцию MIN() применить к символь-ным данным, она найдет среди них такоезначение, которое займет самое последнееместо в упорядочивающей последовательно-сти (схеме сличения символов) конкретнойСУБД (см. раздел «Сортировка строк с по-мощью предложения ORDER BY» в главе 4).

Применять предложение DISTINCT в соче-тании с агрегатной функцией MIN() бес-смысленно (см. раздел «Исключение по-вторных значений с помощью предложе-ния DISTINCT» в этой главе).

В разных СУБД строковые сравнения могуткак зависеть, так и не зависеть от регистрасимволов (см. примечание с пометкой DBMSв разделе «Фильтрация строк с помощьюпредложения WHERE» главы 4).

При сравнении на равенство двух строк,относящихся к типу VARCHAR, ваша СУБДможет дополнить пробелами справа ту изних, которая короче, и потом сравнить двестроки уже равной длины посимвольно.В таком случае, например, строка 'Jack'и строка 'Jack ' окажутся равными. Что-бы понять, как эти тонкости отразятся наработе функции MIN() и ответить на во-прос о том, какую из строк 'Jack' или'Jack ' (с двумя пробелами на конце)выберет функция MIN(), обратитесь к до-кументации по вашей СУБД или поэкспери-ментируйте сами.

Поиск минимума посредством функции MIN()

Page 194: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

194 Суммирование и группировка данных

Поиск максимумас использованиемфункции MAX()Чтобы найти максимум по произвольно-му множеству значений, применяют функ-цию MAX().

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

Напечатайте MAX(expr).Предполагается, что:

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

Листинг 6.2 и рис. 6.2 демонстрируют тексткоманд и результаты сразу несколькихзапросов, применяющих агрегатную фун-кцию MAX(). Первый из этих запросов вы-дает ту из фамилий авторов, учитываемыхв нашей типовой базе данных, которая яв-ляется последней по алфавиту. Второйзапрос выдает цены самой дешевой и са-мой дорогой книг из всех, учитываемых внашей типовой базе данных, плюс разбросцен. Третий запрос выводит максималь-ный валовой доход, полученный на книге,учитываемой в нашей типовой базе дан-ных, тема которой – история (валовой до-ход равен произведению цены книги и ееобъема продаж).

Листинг 6.2. Несколько запросов с применениемфункции MAX(). Результаты выполнения см. нарис. 6.2

SELECT MAX(au_lname) AS "Max last name"

FROM authors;

SELECT

MIN(price) AS "Min price",

MAX(price) AS "Max price",

MAX(price) - MIN(price) AS "Range"

FROM titles;

SELECT MAX(price * sales)

AS "Max history revenue"

FROM titles

WHERE type = 'history';

Max last name

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

O’Furniture

Min price Max price Range

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

6.95 39.95 33.00

Max history revenue

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

313905.33

Рис. 6.2. Результат исполнения запросов,представленных в листинге 6.2

Page 195: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

195

Функцию MAX() допустимо применять толь-ко к данным, имеющим следующие типы:символьные, числовые и дата и время.

Если функцию MAX() применить к символь-ным данным, она найдет среди них значе-ние, которое займет самое высокое местов упорядочивающей последовательности(схеме сличения символов) конкретнойСУБД (см. раздел «Сортировка строк с по-мощью предложения ORDER BY» в главе 4).

Применять предложение DISTINCT в соче-тании с агрегатной функцией MAX() бес-смысленно (см. раздел «Исключение по-вторных значений с помощью предложе-ния DISTINCT» в этой главе).

В разных СУБД строковые сравнения могуткак зависеть, так и не зависеть от регистрасимволов (см. соответствующее приме-чание с пометкой DBMS в разделе «Филь-трация строк с помощью предложенияWHERE» в главе 4).

При сравнении на равенство двух строк,относящихся к типу VARCHAR, ваша СУБДможет дополнить справа пробелами ту изних, которая короче, и потом сравнить двестроки уже равной длины посимвольно.В таком случае, например, строка 'Jack'и строка 'Jack ' (с двумя пробелами наконце) окажутся равными. Чтобы понять,как эти тонкости отразятся на работе функ-ции MAX(), и ответить на вопрос о том,какую из строк 'Jack' или 'Jack ' вы-берет функция MAX(), обратитесь к доку-ментации по вашей СУБД или поэкспери-ментируйте сами.

Поиск максимума с использованием функции MAX()

Page 196: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

196 Суммирование и группировка данных

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

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

Напечатайте SUM(expr).Предполагается, что вы:

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

Листинг 6.3 и рис. 6.3 демонстрируюттекст команд и результаты сразу несколь-ких запросов, применяющих агрегатнуюфункцию SUM(). Первый из этих запросоввыдает сумму авансовых платежей, вы-плаченных всем авторам, учитываемымв нашей типовой базе данных. Второйзапрос выводит общий объем продажкниг, опубликованных в 2000 году, а тре-тий запрос – итоговую цену (суммарнаяцена), итоговый объем продаж (суммаобъемов по всем книгам) и итоговый ва-ловой доход (сумма по всем книгам про-изведений цен на объем продаж). Заметь-те, между прочим, одну математическуюзаморочку, которая состоит в том, чтосумма произведений вовсе не должнабыть равна произведению сумм (мульти-пликативная и аддитивная операции некоммутативны).

Листинг 6.3. Несколько запросов с применениемфункции SUM(). Результаты выполнения см. нарис. 6.3

SELECT SUM(advance) AS "Total advances"

FROM royalties;

SELECT SUM(sales)

AS "Total sales (2000 books)"

FROM titles

WHERE pubdate

BETWEEN DATE '2000-01-01'

AND DATE '2000-12-31';

SELECT

SUM(price) AS "Total price",

SUM(sales) AS "Total sales",

SUM(price * sales) AS "Total revenue"

FROM titles;

Total advances

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

1336000.00

Total sales (2000 books)

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

232677

Total price Total sales Total revenue

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

220.65 1975446 41428860.77

Рис. 6.3. Результат исполнения запросов,представленных в листинге 6.3

Page 197: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

197

Функция SUM() обрабатывает данныетолько числовых типов.

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

В среде СУБД Microsoft Access при записилитералов даты и времени не употребля-ется ключевое слово DATE, а сами литера-лы заключаются не в кавычки, а в знакирешетки (#). Поэтому, чтобы в среде Mic-rosoft Access запустить те запросы, кото-рые представлены в листинге 6.3, вампридется заменить литералы даты и вре-мени во втором запросе на соответствен-но #2000-01-01# и #2000-12-31#.

В среде СУБД Microsoft SQL Server при за-писи литералов даты и времени не употреб-ляется ключевое слово DATE. Поэтому, что-бы в среде Microsoft SQL Server запуститьзапросы, которые представлены в листинге6.3, вам придется заменить литералы датыи времени во втором запросе на соответ-ственно '2000-01-01' и '2000-12-31'.

Вычисление суммы с помощью функции SUM()

Page 198: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

198 Суммирование и группировка данных

Порядок расчета среднегозначения с помощьюфункции AVG()Чтобы вычислить среднее, или средне-арифметическое, значение произвольногомножества значений, применяют агрегат-ную функцию AVG(). При этом под сред-неарифметическим значением несколь-ких величин имеется в виду результат де-ления суммы этих величин на их число.Для того чтобы вычислить среднее значе-ние произвольного множества значений,напечатайте SUM(expr).Предполагается, что:

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

Листинг 6.4 и рис. 6.4 демонстрируют тексткоманд и результаты сразу несколькихзапросов, применяющих агрегатную фун-кцию AVG(). Первый из этих запросов вы-дает ту величину, какой равнялась бысредняя цена одной книги, взятая по всемумножеству книг, учитываемых в нашейбазе, если бы цены этих книг выросливдвое по сравнению с действующими зна-чениями. Второй запрос выдает сред-ний и общий объемы продаж, вычислен-ные по множеству книг, учитываемых внашей базе, и книг, написанных по бизне-су. Обратите внимание, что оба результа-та равны значению null, а не нулю, пото-му что в нашей базе нет книг по бизнесу,а функция SUM() на пустом множестве даетне ноль, а null. Третий из этих запросов ис-пользует некий субзапрос (см. главу 8)

Листинг 6.4. Несколько запросов с применениемфункции AVG(). Результаты выполнения см. нарис. 6.4

SELECT AVG(price * 2) AS "AVG(price *

2)"

FROM titles;

SELECT AVG(sales) AS "AVG(sales)",

SUM(sales) AS "SUM(sales)"

FROM titles

WHERE type = 'business';

SELECT title_id, sales

FROM titles

WHERE sales >

(SELECT AVG(sales) FROM titles)

ORDER BY sales DESC;

AVG(price * 2)

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

36.775000

AVG(sales) SUM(sales)

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

NULL NULL

title_id sales

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

T07 1500200

T05 201440

Рис. 6.4. Результат исполнения запросов,представленных в листинге 6.4

Page 199: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

199

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

Функция AVG() работает с данными толь-ко числовых типов.

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

Если бы вы применили для обозначениянеизвестных или отсутствующих значенийне null, а, скажем, –1, подстановка отрица-тельных величин в функцию AVG() приве-ла бы к неверному значению среднего. По-этому, если вы заменяете значения null, то,прежде чем проводить какие-либо вычисле-ния, примените функцию NULLIF(), преоб-разуйте отсутствующие значения в значе-ния null, тем самым исключив их из вычис-лений (см. раздел «Сравнение выраженийс помощью функции NULLIF()» в главе 5).

СУБД MySQL 4.0 и более ранних версий неподдерживает подзапросы. Поэтому в сре-де этой СУБД вы не сможете запуститьтретий запрос из тех, что представлены влистинге 6.4.

Порядок расчета среднего значения с помощью функции AVG()

Page 200: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

200 Суммирование и группировка данных

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

COUNT(expr) – выдает число таких строк,в которых аргумент expr, называемыйкритерием подсчета, не является значе-нием null;COUNT(*) – выдает общее число строкв произвольном множестве, включаязначения null и дубликаты.

Подсчет строк, не содержащихзначение null

Напечатайте COUNT(expr).Предполагается, что:

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

Подсчет всех строк, включая те,которые содержат значение null

Напечатайте COUNT(*). Эта функция вы-даст натуральное число или ноль (типINTEGER).Листинг 6.5 и рис. 6.5 демонстрируют тексткоманд и результаты сразу трех запросов,применяющих агрегатные функцииCOUNT(expr) и COUNT(*). Все три запроса счи-тают строки таблицы titles и идентичныдруг другу во всем, за исключением пред-ложения WHERE. Обратите внимание: счетчи-ки строк в первом запросе различны. Этопроизошло потому, что столбец price со-

Листинг 6.5. Несколько запросов с применениемфункций COUNT(expr) и COUNT(*). Результатывыполнения см. на рис. 6.5

SELECT

COUNT(title_id) AS

"COUNT(title_id)",

COUNT(price) AS "COUNT(price)",

COUNT(*) AS "COUNT(*)"

FROM titles;

SELECT

COUNT(title_id) AS

"COUNT(title_id)",

COUNT(price) AS "COUNT(price)",

COUNT(*) AS "COUNT(*)"

FROM titles

WHERE price IS NOT NULL;

SELECT

COUNT(title_id) AS

"COUNT(title_id)",

COUNT(price) AS "COUNT(price)",

COUNT(*) AS "COUNT(*)"

FROM titles

WHERE price IS NULL;COUNT(title_id) COUNT(price) COUNT(*)

—---------—————— ————------—— ———----—

13 12 13

COUNT(title_id) COUNT(price) COUNT(*)

---------——————— —————------— ———----—

12 12 12

COUNT(title_id) COUNT(price) COUNT(*)

———-———--------— ------—————— ———----—

1 0 1

Рис. 6.5. Результат исполнения запросов,представленных в листинге 6.5

Page 201: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

201

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

Функции COUNT(expr) и COUNT(*) рабо-тают на всех типах данных.

Ни функция COUNT(expr), ни функцияCOUNT(*) никогда не дают на выходе зна-чение null.

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

Подсчет строк с помощью функции COUNT()

Page 202: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

202 Суммирование и группировка данных

Исключение повторныхзначений с помощьюпредложения DISTINCTЧтобы исключить участие дубликатов строкв вычислении некоторых агрегатных функ-ций, применяют предложение DISTINCT (см.раздел «Удаление повторяющихся строк спомощью ключевого слова DISTINCT» вглаве 4).Начнем с того, что в самом общем видеSQL-синтаксис любой агрегатной функ-ции выглядит так:agg_func([ALL | DISTINCT] expr)

Предполагается, что:

вместо agg_func вы подставите какую-нибудь агрегатную функцию (напри-мер, MIN(), MAX(), SUM(), AVG() или CO-UNT());вместо выражения expr, называемогокритерием, вы подставите заголовоккакого-нибудь столбца, какой-нибудьлитерал или какое-нибудь выражение;будете иметь в виду, что:

– опция ALL выбирается СУБД поумолчанию, применяет выбраннуюагрегатную функцию ко всем стро-кам агрегата, независимо от значе-ний критерия на них, и поэтому еепочти никогда явно в текст запросане вставляют;

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

С функциями SUM(), AVG() и COUNT(expr)предложение DISTINCT работает так: оноисключает дубликаты строк (которые со-держатся в агрегате) из процесса вычисле-ния агрегатной функции. Применять этопредложение с агрегатными функциями

Листинг 6.6. Несколько агрегатных запросов сприменением предложения DISTINCT. Результатысм. на рис. 6.6

SELECT

COUNT(*) AS "COUNT(*)",

COUNT(price) AS "COUNT(price)",

SUM(price) AS "SUM(price)",

AVG(price) AS "AVG(price)"

FROM titles;

SELECT

COUNT(DISTINCT price)

AS "COUNT(DISTINCT)",

SUM(DISTINCT price)

AS "SUM(DISTINCT)",

AVG(DISTINCT price)

AS "AVG(DISTINCT)"

FROM titles;

COUNT(*) COUNT(price) SUM(price) AVG(price)

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

13 12 220.65 18.3875

COUNT(DISTINCT) SUM(DISTINCT) AVG(DISTINCT)

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

10 187.71 18.7710

Рис. 6.6. Результат исполнения запросов,представленных в листинге 6.6

Page 203: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

203

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

Вычисление суммы на произвольноммножестве различных значений

Напечатайте SUM(DISTINCT expr).Предполагается, что:

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

Вычисление среднего значенияна произвольном множестверазличных значений

Напечатайте AVG(DISTINCT expr).Предполагается, что:

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

Подсчет строк,не содержащих значение null

Напечатайте COUNT(DISTINCT expr).Предполагается, что:

критерий expr – это заголовок какого-нибудь столбца, литерал или числовоевыражение;

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

Имейте в виду, что предложение DISTINCTв составе предложения SELECT не обяза-тельно даст тот же результат, что и пред-ложение DISTINCT в составе агрегатнойфункции. В листинге 6.7 представлены тризапроса, которые считают идентификато-ры авторов, учитываемых в нашей типовойбазе данных, в таблице title_authors,но каждый по-своему (на рис. 6.7 показа-ны результаты исполнения этих запросов):

первый запрос считает все идентифи-каторы авторов, имеющиеся в наличии;

второй запрос, несмотря на наличиепредложения DISTINCT, выдает тот жерезультат, что и первый запрос, по-скольку еще до того, как предложениеDISTINCT начало работать, функцияCOUNT(expr) уже выполнила все вы-числения и выдала результат в виде од-ной-единственной строки, которую этопредложение изменить не может;

в третьем запросе, в отличие от второ-го, сначала к идентификаторам авторовприменяется предложение DISTINCT,и только после этого производится под-счет этих идентификаторов.

Имейте в виду, что, если в одном предло-жении SELECT вы будете одновременноприменять и агрегаты, пропущенные черезпредложение DISTINCT, и агрегаты, не об-работанные предложением DISTINCT, ре-зультаты всего запроса наверняка будутошибочны. Запросы, представленные в ли-стинге и на рис. 6.8, как раз демонстриру-ют все возможные комбинации включения/исключения предложения DISTINCT длясумм и счетчиков:

первая комбинация – предложение DI-STINCT не включено ни в сумму, нив счетчик;

вторая комбинация – предложение DI-STINCT включено в сумму, но не вклю-чено в счетчик;

Исключение повторных значений с помощью предложения DISTINCT

Page 204: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

204 Суммирование и группировка данных

третья комбинация – предложение DI-STINCT не включено в сумму, но вклю-чено в счетчик;

четвертая комбинация – предложениеDISTINCT включено и в сумму, и в счетчик.

Так вот, из этих четырех запросов толькопервый (нет ни одного включения предло-жения DISTINCT) и последний (оба вклю-чения) математически осмысленны, потомучто, по крайней мере, считают некую суммуи число слагаемых этой самой суммы. Этолегко проверить с помощью агрегатныхфункций AVG(price) и AVG(DISTINCTprice). Дело в том, что в смешанных тре-тьем и втором случаях вы не сможете опре-делить эти величины делением суммы насчетчик.

СУБД Microsoft Access не поддерживаетагрегатные функции с предложением DI-STINCT в качестве аргумента. Например,следующий запрос в среде Microsoft Accessбыл бы некорректен:

SELECT SUM(DISTINCT price)

FROM titles;

Однако вы могли бы имитировать такуюсумму с предложением DISTINCT в каче-стве аргумента посредством подзапроса(см. примечания в разделе «Принципы ра-боты с подзапросами» главы 8), например:

SELECT SUM(price)

FROM (SELECT DISTINCT price

FROM titles);

К тому же этот обходной маневр в сре-де Access не позволит вам запутатьсяво включениях/исключениях предложенияDISTINCT в агрегатные функции, как этобыло в третьем и втором запросах из техчетырех, что представлены в листинге 6.8.

Имейте в виду, что СУБД MySQL поддержи-вает агрегатную функцию COUNT(DISTINCTexpr), но не поддерживает ниSUM(DISTINCT expr), ни AVG(DISTINCTexpr). Поэтому запросы, представлен-ные в листингах 6.6 и 6.8, в среде MySQL

работать не будут. Работая в СУБДMicrosoft SQL Server, надо иметь в виду,что, если вы применяете предложениеDISTINCT, вместо expr можно подставитьтолько заголовок столбца, а, скажем,арифметическое выражение – нельзя. На-пример, следующий запрос был бы не-корректен в СУБД Microsoft SQL Server:

SELECT COUNT(DISTINCT price * sales)

FROM titles;

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

SELECT COUNT(au_id)

AS "COUNT(au_id)"

FROM title_authors;

SELECT DISTINCT COUNT(au_id)

AS "DISTINCT COUNT(au_id)"

FROM title_authors;

SELECT COUNT(DISTINCT au_id)

AS "COUNT(DISTINCT au_id)"

FROM title_authors;

COUNT(au_id)

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

17

DISTINCT COUNT(au_id)

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

17

COUNT(DISTINCT au_id)

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

6

Рис. 6.7. Результат исполнения запросов,представленных в листинге 6.7

Page 205: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

205

Листинг 6.8. Примеры запросов со смешаннымприменением агрегатов, обработанных и необработанных предложением DISTINCT в одноми том же предложении SELECT. Результатыподобных запросов могут быть ошибочными(см. рис. 6.8)

SELECT

COUNT(price)

AS "COUNT(price)",

SUM(price)

AS "SUM(price)"

FROM titles;

SELECT

COUNT(price)

AS "COUNT(price)",

SUM(DISTINCT price)

AS "SUM(DISTINCT price)"

FROM titles;

SELECT

COUNT(DISTINCT price)

AS "COUNT(DISTINCT price)",

SUM(price)

AS "SUM(price)"

FROM titles;

SELECT

COUNT(DISTINCT price)

AS "COUNT(DISTINCT price)",

SUM(DISTINCT price)

AS "SUM(DISTINCT price)"

FROM titles;

COUNT(price)SUM(price)

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

12 220.65

COUNT(price)SUM(DISTINCT price)

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

12 187.71

COUNT(DISTINCT price)SUM(price)

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

10 220.65

COUNT(DISTINCT price) SUN(DISTINCT price)

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

10 187.71

Рис. 6.8. Результаты запросов, представленныхв листинге 6.8. Мы видим, что счетчики и суммынадо вычислять одинаково. В противном случаезначения средних, получаемые как частноеот деления суммы на счетчик, будут неверными,как во втором (187.71/12) и третьем (220.65/10)запросах. В то же время первый (220.65/12)и четвертый (187.71/10) запросы дают результаты,имеющие определенный смысл

Исключение повторных значений с помощью предложения DISTINCT

Page 206: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

206 Суммирование и группировка данных

Группирование строкс использованиемпредложения GROUP BYДо сих пор мы рассматривали примене-ние агрегатных функций ограниченно, тоесть всего лишь итожили все значения ка-кого-нибудь столбца или все те значе-ния, которые удовлетворяли какому-ни-будь условию поиска предложения WHERE.Однако SQL предоставляет нам возмож-ность разбивать любую таблицу на логи-ческие группы (категории) и вычислятьагрегатные статистические функции накаждой из этих групп. Для этого и служитпредложение GROUP BY.Лучше всего пояснить эту абстрактнуюидею примером. Например, запрос, пред-ставленный в листинге 6.9, применяетпредложение GROUP BY для того, чтобыподсчитать те книги, которые написал,в том числе в соавторстве, каждый автор,учитываемый в нашей типовой базе дан-ных. В соответствующем предложенииSELECT столбец au_id идентифицирует ав-торов, а производный столбец num_books,называемый агрегатным, подсчитыва-ет книги каждого автора. Так вот, рольпредложения GROUP BY в этом запросесостоит в том, чтобы заставить СУБДвычислить столбец независимых значе-ний num_books, по одному на каждого на-шего автора (то есть на каждый иденти-фикатор au_id), а не одно-единственноезначение на всю таблицу. Фактическизапрос выполняет разбиение/группировкукниг по авторам. Это значит, что, по-скольку столбец au-id, будучи первич-ным ключом, однозначно идентифициру-ет наших авторов, группировка строктаблицы title_authors проводится постолбцу au_id, который в этом случаеназывается группирующим. Основные

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

SELECT

au_id,

COUNT(*) AS "num_books"

FROM title_authors

GROUP BY au_id;

au_id num_books

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

A01 3

A02 4

A03 2

A04 4

A05 1

A06 3

Рис. 6.9. Результат исполнения запроса,представленного в листинге 6.9

Page 207: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

207

характеристики предложения GROUP BYтаковы:

располагается в команде SELECT послепредложения WHERE, но перед предложе-нием ORDER BY;группирующими столбцами могут бытьзаголовки (имена) столбцов или заго-ловки производных столбцов;

любой столбец, представленный в пред-ложении SELECT, но не являющийсяагрегатным, должен содержаться ив предложении GROUP BY. Например, сле-дующий запрос некорректен:

SELECT type, pub_id, COUNT(*)

FROM titles

GROUP BY type;

потому, что:

– имени столбца pub_id нет в предло-жении GROUP BY;

– группирующий столбец единствен-ный и предложение GROUP BY можетвыдавать только одну строку на каж-дое значение столбца type, следова-тельно, не может выдать несколькозначений столбца pub_id, которыенадо связать со значением столбцаtype;

если предложение SELECT содержит некоесложное выражение, не являющееся аг-регатным (сложным в данном случаеявляется любое выражение, отличное отпросто заголовка столбца), соответству-ющее выражение в предложении GROUP BYдолжно совпадать с этим сложным выра-жением из предложения SELECT букваль-но, а не по смыслу;

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

группе, которая определена последней,то есть по последнему группирующемустолбцу);если произвольный группирующийстолбец содержит значение null, то стро-ка, в которую попадает это значение,после выполнения данного запроса ста-новится отдельной группой;если группирующий столбец содержитнесколько значений null, все они поме-щаются в одну группу, что не означаетравенства значений null, сколько бы ихни было;устранять строки из результата запросаследует предложением WHERE, причем доприменения предложения GROUP BY;применять псевдонимы столбцов впредложении GROUP BY нельзя, но приме-нять псевдонимы таблиц в качествеквалификаторов можно (см. раздел«Создание псевдонимов столбцов с по-мощью предложения AS» в главе 4);без предложения ORDER BY группы, выда-ваемые предложением GROUP BY, не будутупорядочены, и для сортировки резуль-тата запроса (в порядке убывания книг),представленного в листинге 6.9, вам при-дется добавить предложение ORDER BY"num_books" DESC.

Порядок группирования строк

Напечатайте:SELECT columns

FROM table[WHERE search_condition]

GROUP BY grouping_columns[HAVING search_condition]

[ORDER BY sort_columns];

Предполагается, что:

вместо table вы подставите имя той таб-лицы, строки которой хотите сгруппи-ровать;

Группирование строк с использованием предложения GROUP BY

Page 208: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

208 Суммирование и группировка данных

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

– оба набора столбцов принадлежалитаблице table, исключая агрегатныестолбцы;

– columns, включая агрегатные столб-цы, соответствовали тем столбцам,которые вы хотите видеть в резуль-тате исполнения запроса;

– grouping_columns соответствовали на-бору группирующих столбцов;

– все столбцы набора столбцов columns,не являющиеся агрегатными, былипредставлены в наборе группирую-щих столбцов grouping_columns;

– порядок следования заголовков стол-бцов в grouping_columns определялуровни группировки от высшего досамого нижнего.

Предложение GROUP BY реализуется следу-ющим образом:

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

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

Если предложение SELECT включает пред-ложение WHERE, СУБД приступит к группи-ровке строк только после того, как приме-нит условие поиска search_condition ковсем строкам таблицы table.Если предложение SELECT включает и пред-ложение WHERE, и предложение ORDER BY,столбцы условия sort_columns должны бытьнабраны из столбцов columns (см. разделы«Фильтрация строк с помощью предложе-ния WHERE» и «Сортировка строк с помо-щью предложения ORDER BY» в главе 4).Наконец, предложение HAVING, фильтрую-щее уже сгруппированные строки, будетрассмотрено в следующем разделе.Листинг 6.10 и рис. 6.10 в одном и том жезапросе, включающем предложение GROUPBY, наглядно демонстрируют разницу меж-ду агрегатными функциями COUNT(expr)и COUNT(*). Обратите внимание, что табли-ца publishers содержит одно значение null(для издателя P03 в Германии). Теперьвспомните: в разделе «Подсчет строк спомощью функции COUNT()» этой гла-вы мы говорили, что агрегатная функцияCOUNT(expr) считает только те строки, зна-чение критерия expr на которых не яв-ляется значением null, и что функцияCOUNT(*) считает все строки агрегата.В ходе исполнения рассматриваемого за-проса предложение GROUP BY распознаетзначение null и создает предназначеннуюспециально для него группу. После этогосчетчик COUNT(*) находит значение nullи вычисляет его для группы, специальнопредусмотренной для значений null. По-этому для значения null столбца stateсчетчик (то есть значение агрегатногостолбца) COUNT(*) равен 1. В то же времясчетчик COUNT(state) находит это значе-ние null в той самой группе, но не вычисля-ет его потому, что эта агрегатная функциятак устроена. Таким образом, значение

Page 209: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

209

счетчика COUNT(state) для значения nullстолбца state равно 0.Имейте в виду, что, если некий столбец,не являющийся агрегатным, содержит зна-чения null, применение агрегатной функ-ции COUNT(*) вместо агрегатной функцииCOUNT(expr) может привести к невернымрезультатам. Запрос, представленный влистинге 6.11 и на рис. 6.11, как раз и де-монстрирует этот случай – он должен рас-печатать итоговую статистику продажс разбивкой по типам книг. Но значениеобъема продаж для одной из биографийесть null. Поэтому, как мы теперь знаем,счетчики COUNT(sales) и COUNT(*) различа-ются на 1. И хотя расчет среднего значе-ния в пятом столбце с псевдонимом SUM/COUNT(sales) математически состоятеленв том смысле, что равен некой сумме, де-ленной на число ее слагаемых, расчет сред-него значения в шестом столбце с псевдо-нимом SUM/COUNT(*) математически несосто-ятелен. Эту несостоятельность мы прове-рили агрегатной функцией AVG(sales),значения которой с разбивкой по типамкниг распечатаны в последнем столбце(см. листинг 6.8).В листинге 6.12 и на рис. 6.12 представленодин простой запрос, включающий пред-ложение GROUP BY, который вычисляети распечатывает общий объем продаж,средний объем продаж и общее числокниг по нашей типовой базе данных с раз-бивкой по типам этих книг. Запрос, пред-ставленный в листинге 6.13 и на рис. 6.13,является модификацией предыдущего за-проса, суть которой в том, что мы доба-вили предложение WHERE, чтобы еще догруппировки исключить из рассмотрениявсе книги с ценой ниже $13, и предложе-ние ORDER BY, чтобы отсортировать резуль-тат в порядке уменьшения общих объемовпродаж.

Листинг 6.10. Этот запрос демонстрирует разницумежду агрегатными функциями COUNT(expr)и COUNT(*) в одном и том же предложенииSELECT, включающем предложение GROUP BY.Результат исполнения запроса см. на рис. 6.10

SELECT

state,

COUNT(state)AS "COUNT(state)",

COUNT(*) AS "COUNT(*)"

FROM publishers

GROUP BY state;

state COUNT(state) COUNT(*)

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

NULL 0 1

CA 2 2

NY 1 1

Рис. 6.10. Результат исполнения запроса,представленного в листинге 6.10

Группирование строк с использованием предложения GROUP BY

Page 210: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

210 Суммирование и группировка данных

type SUM(sales) COUNT(sales) COUNT(*) SUM/COUNT(sales) SUM/COUNT(*) AVG(sales)

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

biography 1611521 3 4 537173.67 402880.25 537173.67

children 9095 2 2 4547.50 4547.50 4547.50

computer 25667 1 1 25667.00 25667.00 25667.00

history 20599 3 3 6866.33 6866.33 6866.33

psychology 308564 3 3 102854.67 102854.67 102854.67

Рис. 6.11. Результат исполнения запроса, представленного в листинге 6.11

Листинг 6.14 и рис. 6.14 демонстрируютприменение нескольких группирующихстолбцов для того, чтобы подсчитать чис-ло книг каждого типа, которые напечаталкаждый издатель.В листинге 6.15 и на рис. 6.15 показананекая модификация запроса, представ-ленного в листинге 5.31 и на рис. 5.31 (см.главу 5). Здесь мы не распечатывали спи-сок книг нашей базы с разбивкой по кате-гориям объемов продаж, а использовалипредложение GROUP BY, чтобы показать чис-ло книг в каждой из этих категорий.

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

Листинг 6.11. Для получения математическидостоверных результатов следует применятьне функцию COUNT(*), а функцию COUNT(expr).В противном случае ошибка неизбежна, когдаexpr содержит значения null. Результатисполнения запроса см. на рис. 6.11

SELECT

type,

SUM(sales) AS "SUM(sales)",

COUNT(sales) A S

"COUNT(sales)",

COUNT(*) AS "COUNT(*)",

SUM(sales)/COUNT(sales)

AS "SUM/COUNT(sales)",

SUM(sales)/COUNT(*)

AS "SUM/COUNT(*)",

AVG(sales) AS "AVG(sales)"

FROM titles

GROUP BY type;

Page 211: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

211

Листинг 6.13. Здесь в текст запроса из листинга 6.12добавлены предложения WHERE и ORDER BY, чтобыне учитывать книги дешевле $13 и упорядочитьрезультат по уменьшению общего объема продаж.Результат исполнения запроса см. на рис. 6.13

SELECT

type,

SUM(sales) AS "SUM(sales)",

AVG(sales) AS "AVG(sales)",

COUNT(sales) AS "COUNT(sales)"

FROM titles

WHERE price >= 13

GROUP BY type

ORDER BY "SUM(sales)" DESC;

type SUM(sales) AVG(sales) COUNT(sales)

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

biography 1511520 755760.00 2

computer 25667 25667.00 1

history 25559 6866.33 3

psychology 5000 5000.00 1

Рис. 6.13. Результат исполнения запроса,представленного в листинге 6.13

Листинг 6.12. Этот простой запрос с применениемпредложения GROUP BY вычисляет несколькоитоговых статистических показателей длякаждого типа книг. Результат исполнения запросасм. на рис. 6.12

SELECT

type,

SUM(sales) AS "SUM(sales)",

AVG(sales) AS "AVG(sales)",

COUNT(sales) AS "COUNT(sales)"

FROM titles

GROUP BY type;

type SUM(sales) AVG(sales) COUNT(sales)

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

biography 1611521 537173.67 3

children 9095 4547.50 2

computer 25667 25667.00 1

history 25559 6866.33 3

psychology 308564 102854.67 3

Рис. 6.12. Результат исполнения запроса,представленного в листинге 6.12

Группирование строк с использованием предложения GROUP BY

Page 212: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

212 Суммирование и группировка данных

Листинг 6.14. Перечислить книги каждого типас разбивкой по издателям в убывающем порядке(нижний уровень группировки) внутривозрастающего порядка идентификаторовиздателей (верхний уровень группировки).Результат исполнения запроса см. на рис. 6.14

SELECT

pub_id,

type,

COUNT(*) AS "COUNT(*)"

FROM titles

GROUP BY pub_id, type

ORDER BY pub_id ASC, "COUNT(*)" DESC;

pub_id type COUNT(*)

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

P01 biography 3

P01 history 1

P02 computer 1

P03 history 2

P03 biography 1

P04 psychology 3

P04 children 2

Рис. 6.14. Результат исполнения запроса,представленного в листинге 6.14

Листинг 6.15. Перечислить книги в каждойкатегории объемов продаж в порядке ихуменьшения. Результат исполнения запросасм. на рис. 6.15

SELECT CASE

WHEN sales IS NULLTHEN 'Unknown'

WHEN sales <= 1000THEN 'Not more than 1,000'

WHEN sales <= 10000THEN 'Between 1,001 and 10,000'

WHEN sales <= 100000THEN 'Between 10,001 and 100,000'

WHEN sales <= 1000000THEN 'Between 100,001 and 1,000,000'

ELSE 'Over 1,000,000'END

AS "Sales category",COUNT(*) AS "Num titles"

FROM titles GROUP BY

CASE WHEN sales IS NULL

THEN 'Unknown' WHEN sales <= 1000

THEN 'Not more than 1,000' WHEN sales <= 10000

THEN 'Between 1,001 and 10,000' WHEN sales <= 100000

THEN 'Between 10,001 and 100,000' WHEN sales <= 1000000

THEN 'Between 100,001 and 1,000,000' ELSE 'Over 1,000,000'

END ORDER BY MIN(sales) ASC;

Sales category Num titles

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

Unknown 1

Not more than 1,000 1

Between 1,001 and 10,000 3

Between 10,001 and 100,000 5

Between 100,001 and 1,000,000 2

Over 1,000,000 1

Рис. 6.15. Результат исполнения запроса,представленного в листинге 6.15

Page 213: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

213

Если в некоем запросе предложение GROUPBY применяется без агрегатной функции, онообрабатывается именно так, как предложениеDISTINCT (см. листинг 6.16 и рис. 6.16). Под-робнее об этом предложении читайтев разделе «Удаление повторяющихся строкс помощью ключевого слова DISTINCT»главы 4.

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

Никогда не полагайтесь на то, что предло-жение GROUP BY упорядочит строки резуль-тата. Мы настоятельно рекомендуем всегдаприменять предложение ORDER BY вместе спредложением GROUP BY (несмотря на то,что из нескольких примеров мы для кратко-сти исключили предложение ORDER BY). Бо-лее того, некоторые коммерческие СУБД тре-буют применять ORDER BY вместе с GROUP BY.

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

Листинг 6.16. Обратите внимание, что обапредставленных здесь запроса дают один и тот жерезультат, который показан на рис. 6.16

SELECT type

FROM titles

GROUP BY type;

SELECT DISTINCT type

FROM titles;

type

----------

biography

children

computer

history

psychology

Рис. 6.16. Результат исполнения запросов,представленных в листинге 6.16

Группирование строк с использованием предложения GROUP BY

Page 214: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

214 Суммирование и группировка данных

Если вы работаете в среде СУБД MicrosoftAccess, то для запуска запроса из листин-га 6.15 выражение CASE придется заме-нить функцией SWITCH() (см. примечаниес пометкой DBMS в разделе «Вычислениеусловных значений с помощью выраженияCASE» главы 5).

СУБД MySQL не поддерживает предложе-ния CASE в составе предложения GROUPBY. Поэтому запрос, представленный в ли-стинге 6.15, в MySQL работать не будет.

Имейте в виду, что некоторые коммерческиеСУБД, например MySQL и PostgreSQL, раз-решают использовать псевдонимы столбцовв предложении GROUP BY.

Листинг 6.17. Показать средние продажис разбивкой по цене в порядке ее уменьшения.Результат исполнения запроса см. на рис. 6.17

SELECT price, AVG(sales) AS "AVG(sales)"

FROM titles

WHERE price IS NOT NULL

GROUP BY price

ORDER BY price ASC;

price AVG(sales)

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

6.95 201440.0

7.99 94123.0

10.00 4095.0

12.99 56501.0

13.95 5000.0

19.95 10443.0

21.99 566.0

23.95 1500200.0

29.99 10467.0

39.95 25667.0

Рис. 6.17. Результат исполнения запроса,представленного в листинге 6.17. Мы видим,что, если игнорировать явную статистическуюаномалию, связанную с ценой $23,95, очевиднаслабая обратная зависимость объема продажот цены

Page 215: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

215

Фильтрация группс помощью предложенияHAVINGВ определенном смысле предложение HA-VING накладывает ограничения на предло-жение GROUP BY подобно тому, как предло-жение WHERE взаимодействует с командойSELECT. Перечислим основные характерис-тики предложения HAVING:

в команде SELECT оно помещается пос-ле предложения GROUP BY, но передORDER BY;именно так, как предложение WHEREограничивает число строк, показывае-мых командой SELECT, предложениеHAVING ограничивает число групп, пока-зываемых предложением GROUP BY;СУБД применяет условие поиска пред-ложения WHERE до того, как осуществля-ется группировка, а условие поискапредложения HAVING выполняется послеэтого;синтаксис его аналогичен синтаксисупредложения WHERE, только HAVING мо-жет содержать агрегатные функции;может сослаться на любые составляю-щие списка предложения SELECT.

Очередность, с которой СУБД применяетпредложения WHERE, GROUP BY и HAVING, та-кова:

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

2. Предложение GROUP BY группирует вы-ход предложения WHERE.

3. Предложение HAVING фильтрует строкисгруппированного результата.

Фильтрация групп

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

содержать агрегатные функции;во всем, кроме включения в свой со-став агрегатных функций, быть анало-гичным условию поиска предложенияWHERE, рассмотренному в разделе «Филь-трация строк с помощью предложенияWHERE» главы 4;объединять несколько условий поискалогическими операторами AND и ORв сложное условие поиска предложе-ния HAVING, инвертируя, если надо, неко-торые из этих составляющих логичес-ким оператором NOT.

Предложение HAVING обрабатывается такимобразом, что его условие поиска применя-ется к строкам выхода, который произво-дит группировка. И только те группы, ко-торые соответствуют условию поиска, про-ходят в результат. При этом вы можетеприменять предложение HAVING толькок столбцам, появляющимся в предложе-нии GROUP BY или в какой-нибудь агрегатнойфункции.Для иллюстрации всего вышесказанногорассмотрим несколько запросов. В запро-се из листинга 6.18 мы модифицировализапрос, который был рассмотрен в лис-тинге 6.9. Суть модификации заключает-ся в следующем: вместо того чтобы пере-числять с разбивкой по авторам книги,которые были написаны, в том числе

Фильтрация групп с помощью предложения HAVING

Page 216: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

216 Суммирование и группировка данных

в соавторстве, каждым из авторов, учиты-ваемых в нашей типовой базе данных, мыприменили предложение HAVING с цельюперечислить только тех авторов, кто на-писал, в том числе в соавторстве, не менеетрех книг.В запросе из листинга 6.19 условие поискапредложения HAVING одновременно являетсяодним из агрегатных выражений предложе-ния SELECT. Этим мы демонстрируем, чтоусловие поиска предложения HAVING можетвключать агрегатные функции. Как видноиз листинга 6.20 и рис. 6.20, запрос, пред-ставленный в листинге 6.19, будет выпол-няться даже тогда, когда вы удалите агре-гатную функцию AVG() из перечня SELECTи оставите функцию, которая входит в со-став условия поиска предложения HAVING,единственной применяемой в запросе.Запрос, представленный в листинге 6.21 ина рис. 6.21, с помощью нескольких груп-пирующих столбцов подсчитывает коли-чество книг каждого типа, которые опуб-ликовал каждый издатель. При этом усло-вие поиска предложения HAVING исключаетте группы второго уровня группировки,в которых данный издатель (первый уро-вень группировки) имеет не более однойопубликованной книги произвольногозаданного типа (второй уровень группи-ровки). Запрос выдает некое подмноже-ство того результата, который был ужеполучен с помощью запроса, представ-ленного в листинге 6.14.В запросе из листинга 6.22 предложениеWHERE, как мы видим, сначала исключаетиз рассмотрения все книги, кроме тех, чтовыпущены издателями P03 и P04. Послеэтого предложение GROUP BY группируетвыход предложения WHERE по столбцу type.Наконец, предложение HAVING фильтруетстроки в сформированных группах.

Листинг 6.19. Перечислить книги,средний валовой доход по которым превышает1 миллион долларов, и средний валовой доходс разбивкой по их типам. Результат исполнениязапроса см. на рис. 6.19

SELECT

type,

COUNT(price) AS "COUNT(price)",

AVG(price * sales) AS "AVG revenue"

FROM titles

GROUP BY type

HAVING AVG(price * sales) > 1000000;

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

SELECT

au_id,

COUNT(*) AS "num_books"

FROM title_authors

GROUP BY au_id

HAVING COUNT(*) >= 3;

au_id num_books

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

A01 3

A02 4

A04 4

A06 3

Рис. 6.18. Результат исполнения запроса,представленного в листинге 6.18

type COUNT(price) AVG revenue

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

biography 3 12484879.00

computer 1 1025396.65

Рис. 6.19. Результат исполнения запроса,представленного в листинге 6.19

Page 217: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

217

Листинг 6.20. Запрос получен удалениемиз предложения SELECT команды листинга 6.19агрегатной функции AVG(price*sales).На рис. 6.20 мы видим, что модифицированныйзапрос работает

SELECT

type,

COUNT(price) AS "COUNT(price)"

FROM titles

GROUP BY type

HAVING AVG(price * sales) > 1000000;

type COUNT(price)

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

biography 3

computer 1

Рис. 6.20. Результат исполнения запроса,представленного в листинге 6.20

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

SELECT

pub_id,

type,

COUNT(*) AS "COUNT(*)"

FROM titles

GROUP BY pub_id, type

HAVING COUNT(*) > 1

ORDER BY pub_id ASC, "COUNT(*)" DESC;

pub_id type COUNT(*)

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

P01 biography 3

P03 history 2

P04 psychology 3

P04 children 2

Рис. 6.21. Результат исполнения запроса,представленного в листинге 6.21

Фильтрация групп с помощью предложения HAVING

Page 218: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

218 Суммирование и группировка данных

Листинг 6.22. Перечислить для издателей P03и P04 общий объем продаж выпущенных книгс разбивкой по их типам. Учесть только те книги,итоговый объем продаж которых превышает10 000 копий при средней цене одной копии менее$20. Результат исполнения запроса см. на рис. 6.22

SELECT

type,

SUM(sales) AS "SUM(sales)",

AVG(price) AS "AVG(price)"

FROM titles

WHERE pub_id IN ('P03', 'P04')

GROUP BY type

HAVING SUM(sales) > 10000

AND AVG(price) < 20;

type SUM(sales) AVG(price)

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

psychology 308564 9.31

Рис. 6.22. Результат исполнения запроса,представленного в листинге 6.22

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

SELECT pub_id, SUM(sales)

FROM titlesWHERE pub_id IN ('P03', 'P04')

GROUP BY pub_idHAVING SUM(sales) > 1000;

SELECT pub_id, SUM(sales)FROM titles

GROUP BY pub_idHAVING SUM(sales) > 1000

AND pub_id IN ('P03', 'P04');

Page 219: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

Все запросы, которые мы рассматрива-ли до сих пор, обладали одной общей чер-той. Каждый из них выбирал данные из ка-кой-нибудь одной таблицы. В этой главемы объясним, как с помощью объедине-ний выбирать строки одновременно из не-скольких таблиц. Эта задача очень важнав практической работе с базами данных.Для начала припомните, что в разделе «Свя-зи» главы 2 мы определили, что связь – этообъединение, устанавливаемое между об-щими столбцами двух разных таблиц. Таквот, объединение – это такая табличнаяоперация, на вход которой подаются дверазные таблицы и которая применяет свя-занные столбцы этих таблиц для того, что-бы объединить их строки в некую одну об-щую таблицу. В принципе с помощью це-почки объединений вы можете сгруппиро-вать любое число таблиц.В чем же заключается важность объедине-ний? Дело в том, что основная информа-ция любой базы данных хранится не в ее

77777Выбор данныхиз нескольких таблиц

таблицах, а, образно выражаясь, междуними. Конечно, имеются в виду связи меж-ду множествами строк. Если взять, к при-меру, нашу типовую базу данных, таблицыauthors, publishers и titles содержат важнуюинформацию, спору нет. Но имен-но связи между этими таблицами дают воз-можность отвечать на вопросы о том, ктои что написал, кто и что опубликовал,кому были посланы чеки в счет выплатыавторского гонорара и т.д. Следовательно,именно связи позволяют анализироватьданные как систему. И только анализируяданные о произвольном объекте как еди-ное целове (то есть работая с модельюданных), мы можем получить о нем адек-ватное представление.Учитывая все вышесказанное, в этой гла-ве мы рассмотрим различные типы допу-стимых в рамках стандарта SQL объеди-нений, объясним их назначение и то, какследует строить команды SELECT, которыеиспользуют эти объединения.

Page 220: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

220 Выбор данных из нескольких таблиц

Уточнение имен столбцовВспомните, в разделе «Таблицы, столбцыи строки» главы 2 мы отмечали, что име-на столбцов в пределах одной таблицыдолжны быть уникальными для любойтаблицы, но могут повторяться в другихтаблицах. Например, таблицы нашей ти-повой базы данных authors и publishersсодержат столбцы city. Поэтому, еслипри построении запроса рассматриваетсяболее одной таблицы, имена столбцовмогут перестать быть их однозначнымиидентификаторами.Для того чтобы определить те столбцы,имена которых перестали быть их одно-значными идентификаторами из-за пере-хода к нескольким таблицам, используюттак называемые уточненные имена. Лю-бое уточненное имя является составным.В простейшем случае для получения со-ставного имени произвольного столбцанадо напечатать имя его таблицы, точку(без пробела) и имя этого столбца в этойтаблице. Очевидно, что в любой приклад-ной системе, применяющей только однубазу данных, такие уточненные имена бу-дут однозначно идентифицировать своистолбцы просто потому, что таблицы дол-жны иметь в рамках одной базы уникаль-ные имена. Более сложные уточненныеимена применяются в сложных сетевыхиерархических СУБД (см. примечаниеDBMS в этом разделе).

Уточнение имени произвольного столбца

Напечатайте table.column.Предполагается, что:

вместо column вы подставите имя столб-ца, которое хотите уточнить;вместо table вы подставите имя табли-цы, где находится столбец, имя кото-рого вы хотите уточнить (см. листинг7.1 и рис. 7.1).

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

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

Еще одна веская причина применять толь-ко уточненные имена состоит в том, чтобыобезопасить себя от того, что какие-либобудущие коррекции структуры какой-ни-будь из таблиц вашей базы внесут не-однозначность идентификации. Например,если бы, корректируя таблицуpublishers нашей типовой базы, кто-ни-будь добавилв нее столбец zip (почтовый индекс), толюбая неуточненная ссылка (то есть ссыл-ка на неуточненное имя столбца) на стол-бец zip в произвольном запросе, которыйвыбирал бы строки из таблиц authors(эта таблица содержит столбец zip с са-мого начала) и publishers, не была быоднозначной.

Page 221: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

221

Отдельные коммерческие СУБД могут потре-бовать дополнительных префиксов-уточ-нителей в зависимости от того, в какомместе иерархической структуры даннойСУБД хранится ваш запрос. Может так слу-читься, что вам придется обозначать ка-кую-нибудь таблицу именем сервера, име-нем базы и именем владельца. Поэтому втех запросах SQL, которые требуют длин-ных уточненных имен, полезно применятьпсевдонимы таблиц (об этом подробнорассказывается в следующем разделе).Например, любое полностью уточненноеимя таблицы в среде СУБД Microsoft SQLServer выглядит так:

server_name.db_name.owner_name.

→ table_name

Предполагается, что:

вместо server_name вы подставитеимя вашего сервера;

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

вместо owner_name вы подставите имявладельца базы данных;

вместо table_name вы подставите имятаблицы.

Oracle 8i требует, чтобы объединения стро-ились по синтаксису предложения WHERE(см. раздел «Создание объединений с по-мощью синтаксиса JOIN и WHERE» в этойглаве). Oracle 9i и более поздних версийподдерживает синтаксис JOIN, следова-тельно, запрос, который представлен в ли-стинге 7.1, будет функционировать без из-менений. Но для того, чтобы запустить этотзапрос в Oracle 8i, вам придется его моди-фицировать следующим образом:

SELECT au_id, authors.city

FROM authors, publishers

WHERE authors.city =

→ publishers.city;

Листинг 7.1. В этом запросе уточненные именаопределяют, какой из столбцов city,присутствующих в таблицах authorsи publishers, используется в запросе.Результат исполнения запроса см. на рис. 7.1

SELECT au_id, authors.city

FROM authors

INNER JOIN publishers

ON authors.city = publishers.city;

au_id city

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

A03 San Francisco

A04 San Francisco

A05 New York

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

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

SELECT

au_fname,

au_lname

FROM authors;

SELECT

authors.au_fname,

authors.au_lname

FROM authors;

Уточнение имен столбцов

Page 222: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

222 Выбор данных из нескольких таблиц

Создание псевдонимовтаблиц с помощьюпредложения ASСоздавать псевдонимы таблиц с использо-ванием предложения AS следует так же, какпсевдонимы столбцов (см. раздел «Созда-ние псевдонимов столбцов с помощьюпредложения AS» в главе 4). Полезностьпсевдонимов таблиц заключается в том,что они:

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

Создание произвольного псевдонимапроизвольной таблицы

В предложении FROM или JOIN напечатайтеследующее:table [AS] alias

Предполагается, что:

вы замените table именем той таблицы,для которой намерены создать псевдо-ним;вы замените alias собственно псев-донимом, который должен быть од-ним словом, содержащим только или/ибуквы, или/и цифры, или/и знаки под-черкивания (пробелы, знаки пунктуа-ции или специальные знаки не допус-каются).

Хотя предложение повышает читабель-ность текстов программ, ключевое сло-во AS печатать не обязательно (см. лис-тинг 7.2 и рис. 7.2).

При создании псевдонимов таблиц мыопускаем ключевое слово AS. Дело в том,что это необходимо для максимальной со-вместимости представленных в книге за-просов с рассматриваемыми в ней коммер-ческими СУБД (см. примечание DBMSв этом разделе).

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

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

Имейте в виду, что, если псевдоним какой-нибудь таблицы создан, он заменяет фак-тическое имя этой таблицы в своем запро-се. Это значит, что, если некой таблицеприсвоен псевдоним, надо использоватьего во всех уточненных ссылках (то есть вуточненных именах столбцов). Например,следующий запрос недопустим:

SELECT authors.au_id

FROM authors a;

Каждый псевдоним таблицы должен бытьуникальным в своей команде SQL.

В каждом самообъединении от псевдони-мов таблиц требуется указать на одну и туже таблицу более одного раза. О самообъ-единениях читайте далее в разделе «Созда-ние самообъединения».

Page 223: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

223

Чтобы приписать псевдонимы представле-ниям, тоже можно применить предложе-ние AS (о представлениях читайте в главе12).

Применять ключевые и зарезервированныеслова в качестве псевдонимов таблиц нельзя(см. раздел «Синтаксис SQL» в главе 3).

В среде СУБД Oracle, создавая псевдонимлюбой таблицы, необходимо опускать клю-чевое слово AS.

Oracle 8i требует, чтобы объединения стро-ились по синтаксису предложения WHERE(см. раздел «Создание объединений с по-мощью синтаксиса JOIN и WHERE» в этойглаве). Oracle 9i и более поздних версийподдерживает синтаксис JOIN, следова-тельно, запрос, который представлен влистинге 7.2, сработает так же, без изме-нений. Но для того, чтобы проверить вы-полнение этого запроса в Oracle 8i, вампридется его модифицировать следующимобразом:

SELECT

a.au_fname, a.au_lname, a.city

FROM authors a, publishers p

WHERE a.city = p.city;

Листинг 7.2. Псевдонимы таблиц делаюттексты запросов короче и читабельнее. Обратитевнимание, что применять псевдонимы запросовв предложении SELECT можно до того, как онифактически описаны в этом предложении.Результат исполнения запроса показан на рис. 7.2.

SELECT au_fname, au_lname, a.city

FROM authors a

INNER JOIN publishers p

ON a.city = p.city;

au_fname au_lname city

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

Hallie Hull San Francisco

Klee Hull San Francisco

Christian Kells New York

Рис. 7.2. Результат исполнения запроса,представленного в листинге 7.2.

Создание псевдонимов таблиц с помощью предложения AS

Page 224: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

224 Выбор данных из нескольких таблиц

ИспользованиеобъединенийЛюбой запрос, который выбирает данныеиз более чем одной таблицы, выполняеткакое-либо объединение. В следующихразделах мы подробно расскажем о раз-личных типах объединений (см. табл. 7.1),о том, зачем нужен каждый из этих типов,и о том, как на практике создавать коман-ды SELECT, которые будут применять объе-динения.

Перечислим основные характеристикиобъединений:

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

таблицы всегда объединяются построч-но при выполнении всевозможных ус-ловий, определенных в вашем запросе;

те строки, которые не соответствуютзаданным условиям, могут быть каквключены в объединение, так и исклю-чены из него, в зависимости от типаэтого объединения;

значения связанных столбцов обычнопроверяют на равенство (=), но вы так-же можете сравнивать эти значения,применяя другие операторы сравнения(<>, <, <=, >, >=);

объединением по равенству называетсятакое объединение, в условии которогоприменяется оператор равенства (=),чтобы группировать строки, имеющиеравные значения в определенных стол-бцах, называемых связанными;

объединение по равенству – это част-ный случай более общего тэта-объ-единения, при котором значения свя-занных столбцов сравнивают с приме-нением не только оператора равенства,но и, возможно, любого другого опера-тора сравнения;связанные (или связывающие, как ихеще называют) столбцы любого объ-единения чаще всего оказываются свя-занными ключевыми столбцами, новы сами, строя свое объединение, мо-жете связать любые столбцы, если ихтипы данных совместимы (все это неимеет отношения к так называемомуперекрестному объединению, при ко-тором не должно быть никаких выде-ленных связанных столбцов);чтобы обезопасить себя от построениябессмысленных объединений, напримеробъединения на столбцах titles.priceи royalties.advance, выбирайте связываю-щие столбцы так, чтобы они были опре-делены на одном домене (одно частовстречающееся условие объединениявключает какой-нибудь внешний ключодной дочерней таблицы и связанный сэтим внешним ключом первичный ключдругой материнской таблицы; см. разде-лы «Первичные ключи» и «Внешниеключи» в главе 2);если какой-нибудь ключ является со-ставным (то есть содержит несколькостолбцов), можете сделать связываю-щими все столбцы этого ключа;связанные (связывающие) столбцы необязательно должны иметь одно и тоже имя (за исключением случая есте-ственного объединения);чтобы выполнить слияние более двухтаблиц, объединения таблиц можновкладывать друг в друга, выстраиватьв цепочку и комбинировать, но при

Page 225: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

225

Таблица 7.1. Типы объединений

Тип объединения Комментарий

Группирует строки таблиц по правилу «каждая с каждой». Перваястрока первой таблицы объединяется с первой строкой второйтаблицы (правый бок/стык с левым боком/стыком), потом перваястрока первой таблицы объединяется со второй строкой второй таб-лицы, и т.д. до тех пор, пока в первой таблице не закончатся строки

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

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

В этом объединении сначала проводится сравнение значений свя-занных столбцов. Но в результат объединения включаются все стро-ки левой таблицы, а не только те, для которых в правой таблице на-шлись строки с удовлетворяющими сравнению значениямисвязанных столбцов. Если некой строке слева не нашлось ни однойстроки справа, СУБД объединяет ее с искусственной строкой, состо-ящей из значений null

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

Включает и все строки левой таблицы, и все строки правой табли-цы. Если у какой-либо строки (слева или справа) нет пары по свя-занным столбцам, СУБД объединяет ее с искусственной строкой, со-стоящей из значений null

Объединение произвольной таблицы с самой собой

Полноеили перекрестноеобъединение(CROSS JOIN)

Естественное объединение(NATURAL JOIN)

Внутреннее объединение(INNER JOIN)

Левое внешнееобъединение(LEFT OUTER JOIN)

Правое внешнееобъединение(RIGHT OUTER JOIN)

Полноевнешнее объединение(FULL OUTER JOIN)

Тривиальное объединение,самообъединение(SELF JOIN)

Использование объдинений

Page 226: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

226 Выбор данных из нескольких таблиц

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

хотя SQL не ограничивает количествотех таблиц (или объединений), которыемогут появиться в одном запросе, напрактике или ваша СУБД будет иметьвстроенные ограничения, или админи-стратор базы данных наложит такиеограничения, которые, как правило,окажутся гораздо ниже встроенных ог-раничений СУБД (маловероятно, чтовам когда-нибудь понадобится объеди-нить в одном запросе пять или шестьтаблиц, скорее, их будет две или три);

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

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

только если этим объединением будетперекрестное или какое-нибудь внеш-нее объединение, за исключением то-го случая, когда предложение WHEREявно исключает значения null из рас-смотрения (подробнее о значениях nullчитайте в разделе «Значение null» вглаве 3);

объединения существуют только в мо-мент выполнения своего запроса и неявляются составными элементами нибаз данных, ни СУБД;

так как типы данных связывающихстолбцов должны быть совместимыв том смысле, что СУБД могла бы принеобходимости преобразовать данныеэтих столбцов в какой-нибудь общийтип и сравнить, вам следует иметь в видуследующее (см. раздел «Типы данных»в главе 3):

– для большинства коммерческихСУБД совместимыми являются чис-ловые типы данных (INTEGER, NUMERICи FLOAT), символьные типы данных(CHAR, VARCHAR) и типы данных датыи времени (DATE, TIMESTAMP);

– поскольку преобразования данныхтребуют значительной дополнитель-ной вычислительной работы и силь-но замедляют процесс исполнениязапроса, для максимальной произво-дительности необходимо назначатьсвязывающими столбцами в двухтаблицах те столбцы, типы данныхкоторых не просто попарно совмес-тимы, а идентичны, включая одина-ковое решение вопроса о допустимо-сти/недопустимости значений null;

Page 227: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

227

чтобы запросы исполнялись быстрее,следует индексировать связывающиестолбцы (см. главу 11);представления можно объединять с таб-лицами и с другими представлениями;чтобы создать любое объединение, мо-жете применить или синтаксис JOIN, илисинтаксис WHERE (см. следующий раздел);в конце этой главы рассказывается обоперациях SQL на наборах строк (см.табл. 7.2), которые, не являясь объеди-нениями, позволяют объединять ре-зультаты двух разных запросов в одинобщий результат.

Таблица 7.2. Операции по обработке наборов строк

Операция Результат

UNION Все строки результатов обоихзапросов. Дубликаты удалены

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

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

Использование объединений

Page 228: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

228 Выбор данных из нескольких таблиц

Создание объединенийс помощью синтаксисаJOIN и WHEREДля создания произвольного объедине-ния предусмотрены два способа:

применить синтаксис JOIN;применить синтаксис WHERE.

Стандарт ANSI SQL-92 предписывает при-менять синтаксис JOIN, но более старыестандарты SQL рекомендуют применятьсинтаксис WHERE. Отсюда следует, что обасинтаксиса законны для большинства ком-мерческих СУБД.Поскольку оба синтаксиса широко приме-няются на практике, приведем примеры,в которых можно использовать и JOIN,и WHERE. При этом запросы, применяющиесинтаксис JOIN, будут представлены в ос-новном тексте, а соответствующие им за-просы, применяющие синтаксис WHERE, –в советах.В данном разделе рассматриваются обасинтаксиса в приложении к объединени-ям двух таблиц. Отметим, что тот синтак-сис, который вы будете применять, строяреальные запросы, будет меняться в зави-симости от типа объединения, числа свя-зывающих столбцов, числа объединяе-мых таблиц и ограничений, налагаемыхна синтаксис конкретной СУБД.

Создание произвольного объединенияс использованием синтаксиса JOIN

Напечатайте:SELECT columns

FROM table1 join_type table2

ON join_conditions

[WHERE search_condition]

[GROUP BY grouping_conditions]

[HAVING search_condition]

[ORDER BY sort_columns];

Предполагается, что:

вместо columns вы подставите одно илинесколько выражений, разделенныхзапятыми, с именами столбцов или/исамих имен (заголовков) столбцов объ-единяемых таблиц table1 и table2;во избежание неоднозначной иденти-фикации столбцов вы уточните соот-ветствующими префиксами все ссыл-ки на те имена столбцов объединяе-мых таблиц, которые являются в нихобщими (см. раздел «Уточнение именстолбцов» в этой главе);вместо table1 и table2 вы подставитеимена объединяемых таблиц (можетеприсвоить этим именам псевдонимытак, как это описано в разделе «Созда-ние псевдонимов таблиц с помощьюпредложения AS»);вместо join_type вы подставите иденти-фикатор того типа объединения, кото-рый вам нужен:

– CROSS JOIN – для перекрестного объ-единения;

– NATURAL JOIN – для естественного объ-единения;

– INNER JOIN – для внутреннего объ-единения;

– LEFT OUTER JOIN – для левого внешне-го объединения;

– RIGHT OUTER JOIN – для правого внеш-него объединения;

– FULL OUTER JOIN – для полного внеш-него объединения;

вместо join_conditions вы подставитеодно или несколько условий объедине-ния, которые следует вычислять длякаждой пары объединяемых строк,и будете иметь в виду, что:– предложение ON не допускается для

перекрестных и естественных объ-единений;

Page 229: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

229

– любое условие объединения всегдапринимает следующую форму:

[table1.]column op [table2.]column,

где:[table1.]column – имя, возможноуточненное, связывающего столбцапервой таблицы;[table2.]column – имя, возможноуточненное, связывающего столбцавторой таблицы, соотносящегося состолбцом [table1.]column;op – оператор сравнения, которымчаще всего является оператор ра-венства (=), но может быть и любойоператор =, <>, <, <=, > или >=(подробнее об операторах сравне-ния см. в табл. 4.2);

вы можете комбинировать несколькоусловий объединения с помощью ло-гических операторов AND и OR (см. раз-дел «Комбинирование условий с по-мощью операторов AND, OR и NOT»в главе 4);предложения WHERE, ORDER BY описаныв главе 4, а предложения GROUP BY иHAVING – в главе 6.

Создание произвольного объединенияс использованием синтаксиса WHERE

Напечатайте:SELECT columns

FROM table1, table2

WHERE join_conditions

[GROUP BY grouping_conditions]

[HAVING search_condition]

[ORDER BY sort_columns];

Предполагается, что:

columns, table1, table2 означают то жесамое, что и в подразделе «Созданиепроизвольного объединения с исполь-зованием синтаксиса JOIN»;

join_conditions означает то же самое,что и в подразделе «Создание произ-вольного объединения с использова-нием синтаксиса JOIN», только теперьвместо op можно подставлять специаль-ный символ, означающий тип объеди-нения;

предложение WHERE может включатьеще и условия поиска (не имеющиепрямого отношения к объединениютаблиц), необходимые для фильтрациистрок (см. раздел «Фильтрация строкс помощью предложения WHERE» вглаве 4);

вы уже умеете строить предложенияORDER BY (см. главу 4), GROUP BY и HAVING(см. главу 6).

Далее рассмотрим листинги, в которыхпредставлены запросы, одинаково реша-ющие одну и ту же задачу, но с использо-ванием синтаксиса JOIN и WHERE соответ-ственно. Общий результат этих запросовпоказан на рис. 7.3.

На первый взгляд может показаться стран-ным, что предложение WHERE, по идее, яв-ляющееся инструментом фильтрования,применяется для того, чтобы указать усло-вия объединения. Однако ничего странногоздесь нет. Дело в том, что всякое условиеобъединения работает как фильтр. Когдавы объединяете две таблицы, СУБД неявнои незаметно для вас начинает с того, чтоспаривает каждую строку первой таблицыс каждой строкой второй таблицы, то естьстроит перекрестное объединение (см. сле-дующий раздел). И только после этогоСУБД фильтрует строки перекрестногообъединения условиями объединения (оче-видно, что на практике слишком дорогостроить огромное и бесполезное перекрест-ное объединение для каждого объедине-ния и что никакая СУБД так делать не бу-дет, для этого существуют оптимизаторы).

Создание объединений с помощью синтаксиса JOIN и WHERE

Page 230: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

230 Выбор данных из нескольких таблиц

Одно очевидное преимущество синтаксисаJOIN перед синтаксисом WHERE состоитв том, что первый явно объявляет тип объе-динения. Конечно, A LEFT OUTER JOIN Bгораздо понятнее, чем, скажем, A *= B.Однако в отношении самых ходовых объе-динений, то есть для простых внутреннихобъединений, синтаксис WHERE, наоборот,проще синтаксиса JOIN. В любом случаеоба синтаксиса одинаково широко распро-странены среди программистов. Поэтому,если вы хотите читать и понимать запросы,написанные не только вами, но и другимиспециалистами, придется выучить оба син-таксиса.

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

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

Связывающие столбцы могут иметь раз-ные имена.

Листинг 7.3a. Запрос, который применяетсинтаксис JOIN. Результат исполнения запросасм. на рис. 7.3

SELECT au_fname, au_lname, a.city

FROM authors a

INNER JOIN publishers p

ON a.city = p.city;

Листинг 7.3б. Тот же самый запрос,но применяющий синтаксис WHERE.Результат исполнения запроса см. на рис. 7.3

SELECT au_fname, au_lname, a.city

FROM authors a, publishers p

WHERE a.city = p.city;

au_fname au_lname city

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

Hallie Hull San Francisco

Klee Hull San Francisco

Christian Kells New York

Рис. 7.3. Результат исполнения листингов 7.3a и 7.3б

Page 231: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

231

Если вы применяете синтаксис WHERE сдвумя или большим количеством условийобъединения, то почти наверняка захотитеобъединить все эти условия логическимоператором AND. Здесь мы имеем в видуследующее: хотя объединять условия ло-гическим оператором OR вполне законно,смысл результирующего условия обычнотрудно понять. Поэтому применяйте луч-ше оператор AND. Подробнее об операто-рах AND и OR читайте в разделе «Комбини-рование условий с помощью операторовAND, OR и NOT» главы 4.

Большинство запросов, применяющих объ-единения, можно переписать с помощьюподзапросов (то есть запросов, вложен-ных в другие запросы). Но верно и то, чтобольшинство подзапросов можно перепи-сать как объединения. Подробнее о под-запросах читайте в главе 8.

СУБД Oracle 8i и более ранних версий неподдерживает синтаксис JOIN. Если у васименно такая СУБД, применяйте синтаксисWHERE. СУБД Oracle 9i поддерживает син-таксис JOIN.

Ваша СУБД может запретить объединенияпо связывающим столбцам определенныхтипов данных (почти наверняка под запретпопадут бинарные типы). Например, в сре-де Microsoft SQL Server под такой запретпопадают столбцы типов ntext, textи image, а в среде СУБД Oracle – столбцытипа BLOB. Чтобы полностью разобратьсяв этом вопросе, вам следует в документа-ции по своей СУБД организовать поиск поключу «joins» (объединения).

Последовательность исполнения запроса

Когда СУБД обрабатывает объедине-ния, она подчиняется определеннойпоследовательности шагов, котораяне только относится к обработке объ-единений, но и определяет алгоритмвыполнения всего запроса. Вот эта по-следовательность:1. Применить условия объединения,

заданные предложением JOIN.2. Применить условия объединения и

поиска, заданные предложениемWHERE.

3. Сгруппировать строки в соответ-ствии с предложением GROUP BY.

4. Применить к группам условия поис-ка, заданные предложением HAVING.

5. Отсортировать результат в соответ-ствии с предложением ORDER BY.

Связывающие столбцы могут относитьсяк разным типам данных. Но, если их типыне идентичны, они должны быть совмести-мыми или такими, чтобы ваша СУБД принеобходимости смогла конвертировать ихдруг в друга неявно. Если же эти типы дан-ных СУБД не может конвертировать неяв-но, вы сами должны конвертировать ихявно с использованием CAST() в текстезапроса, то есть в условиях объединения.Подробнее о явных и неявных преобразо-ваниях типов говорится в разделе «Преоб-разование типов данных с помощью функ-ции CAST()» главы 5.

Создание объединений с помощью синтаксиса JOIN и WHERE

Page 232: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

232 Выбор данных из нескольких таблиц

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

Стандартный SQL для синтаксиса JOIN допускает, чтобы вместо предложения ONприменялось предложение USING, но только в том случае, когда все связывающиестолбцы имеют попарно одинаковые имена (то есть на концах каждой связи долж-ны стоять столбцы с одинаковыми именами) и сравниваются строго на равенство.Тогда синтаксис JOIN будет таким:

FROM table1 joint_type table2

USING (columns)

Здесь под columns подразумевается одно или несколько разделенных запятымиимен столбцов, что означает одну или несколько пар одинаковых имен связываю-щих столбцов. Круглые скобки обязательны. Соответствующий запрос выполнитобъединение по равенству поименованной пары или поименованных пар столб-цов. Такой тип объединения принято называть объединением поименованных столб-цов. С предложением USING запрос, который представлен в листинге 7.3a, будет пере-писан следующим образом:

SELECT au_fname, au_lname, a.city

INNER JOIN publishers p

FROM authors a

USING (city);

Очевидно, что предложение USING работает так же, как естественное объединение,но если вы не хотите заносить в связывающие столбцы все столбцы с общимиименами, а хотите выбрать из всех столбцов с общими именами только некоторыеи сделать их связывающими, то можете воспользоваться предложением USING. Обра-тите внимание на то обстоятельство, что в нашем примере в качестве связывающеговыбран только столбец city, хотя он вовсе не единственный столбец с общим име-нем в двух таблицах, и естественное объединение включило бы автоматически в со-став связывающих помимо него еще и столбец state (см. раздел «Создание произ-вольного естественного объединения с использованием предложения NATURALJOIN» в этой главе).

Необходимо отметить, что предложение USING не создает в рамках SQL никакой до-полнительной функциональности. Абсолютно все, что можно сделать с помощьюпредложения USING, можно сделать с помощью предложения ON в рамках синтаксисаJOIN или с помощью предложения WHERE в рамках синтаксиса WHERE.

СУБД Microsoft Access и Microsoft SQL Server не поддерживают предложение USING.

СУБД Oracle 9i не допускает уточненных имен столбцов в списке SELECT для тех запросовс объединениями, в которых применяется предложение USING. Поэтому, чтобы реализо-вать в среде Oracle 9i только что приведенный пример с предложением USING, в предло-жении SELECT вам нужно поменять a.city на city.

Page 233: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

233

Создание произвольногоперекрестногообъединенияс использованиемпредложения CROSS JOINПеречислим основные характеристики пе-рекрестного объединения:

любое перекрестное объединение всегдавыдает все возможные комбинациистрок двух таблиц так, что каждая стро-ка первой таблицы объединяется с каж-дой строкой второй таблицы;никакое перекрестное объединение ни-когда не применяет никаких условийобъединения. Поэтому, если вы ис-пользуете синтаксис JOIN и хотите со-здать некое перекрестное объединение,просто опустите предложение ON, аесли вы применяете синтаксис WHEREи хотите создать некое перекрестноеобъединение, опустите все предложениеWHERE;перекрестные объединения редко при-меняют на практике, потому что их ре-зультаты трудно интерпретировать;перекрестные объединения могут вы-давать огромные результаты даже приобработке маленьких таблиц, простопотому, что, если первая таблица со-держит m строк, а вторая таблица со-держит n строк, перекрестное объеди-нение этих таблиц будет по определе-нию состоять из m×n строк;перекрестное объединение требуетслишком больших затрат на любой за-прос (имеются в виду вычислительныересурсы и время);перекрестное объединение еще назы-вают Декартовым, или прямым произ-ведением.

Создание произвольногоперекрестного объединения

Напечатайте:

SELECT columns

FROM table1

CROSS JOIN table2

Предполагается, что (см. листинг 7.4 ирис. 7.4):

вместо columns вы подставите одно илинесколько разделенных запятыми вы-ражений с именами столбцов или/исамих имен (заголовков) столбцов объ-единяемых таблиц table1 и table2;

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

вместо table1 и table2 вы подставитеимена объединяемых таблиц.

В рамках синтаксиса WHERE тот запрос, ко-торый представлен в листинге 7.4, будетвыглядеть так:

SELECT

au_id, pub_id,

a.state AS "au_state",

p.state AS "pub_state"

FROM authors a, publishers p;

Чтобы показать все столбцы обоих таблицв перекрестном объединении, используйтепредложение SELECT * (см. раздел «Выборстолбцов с помощью предложений SELECTи FROM» в главе 4). Например, следую-щие запросы выводят все столбцы таблицauthors и publishers:

для синтаксиса JOIN:

SELECT *

FROM authors

CROSS JOIN publishers;

Создание произвольного перекрестного объединения

Page 234: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

234 Выбор данных из нескольких таблиц

для синтаксиса WHERE:

SELECT *

FROM authors, publishers;

Чтобы показать все столбцы только однойкакой-либо таблицы в перекрестном объе-динении, используйте предложениеSELECT table.*. Например, следующиезапросы выводят все столбцы таблицыauthors и только один столбец pub_id изтаблицы publishers:

для синтаксиса JOIN:

SELECT authors.*, p.pub_id

FROM authors

CROSS JOIN publishers p;

для синтаксиса WHERE:

SELECT authors.*, p.pub_id

FROM authors, publishers p;

Чтобы найти прямое произведение N таб-лиц, напечатайте:

для синтаксиса JOIN:

SELECT columns

FROM table1

CROSS JOIN table2

CROSS JOIN tableN;

для синтаксиса JWHERE:SELECT columns

FROM table1, table2, …, tableN

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

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

SELECT

au_id,

pub_id,

a.state AS "au_state",

p.state AS "pub_state"

FROM authors a

CROSS JOIN publishers p;

au_id pub_id au_state pub_state

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

A01 P01 NY NY

A02 P01 CO NY

A03 P01 CA NY

A04 P01 CA NY

A05 P01 NY NY

A06 P01 CA NY

A07 P01 FL NY

A01 P02 NY CA

A02 P02 CO CA

A03 P02 CA CA

A04 P02 CA CA

A05 P02 NY CA

A06 P02 CA CA

A07 P02 FL CA

A01 P03 NY NULL

A02 P03 CO NULL

A03 P03 CA NULL

A04 P03 CA NULL

A05 P03 NY NULL

A06 P03 CA NULL

A07 P03 FL NULL

A01 P04 NY CA

A02 P04 CO CA

A03 P04 CA CA

A04 P04 CA CA

A05 P04 NY CA

A06 P04 CA CA

A07 P04 FL CA

Рис. 7.4. Результат исполнения запроса,представленного в листинге 7.4

Page 235: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

235

Хотя прямое произведение таблиц почтиникогда не является тем, чего вы добивае-тесь, ваша СУБД (теоретически) применяетего часто. Считается, что СУБД, приступаяк обработке любого объединения, сначаланеявно создает перекрестное объединениесоответствующих таблиц, а потом приме-няет предложение SELECT, чтобы удалитьиз прямого произведения столбцы, кото-рых нет в перечне этого предложения, иусловия объединения и поиска, чтобы уда-лить из прямого произведения лишниестроки.

Объединение t1 CROSS JOIN t2 эквива-лентно любому из следующих объедине-ний:

t1 INNER JOIN t2 ON 1 = 1;

t1 LEFT OUTER JOIN t2 ON 1 = 1;

t1 RIGHT OUTER JOIN t2 ON 1 = 1;

t1 FULL OUTER JOIN t2 ON 1 = 1.

Здесь t1 и t2 – таблицы, а 1 = 1 – любоеусловие, которое всегда выполняется.О внутренних и внешних объединенияхрассказывается в следующих разделах.

Одно практическое применение перекрес-тного объединения состоит в том, чтобыпроизводить наборы данных для тестиро-вания программного обеспечения. Предпо-ложим, что у вас есть некая функция от nаргументов и для каждого из них суще-ствует m эталонных контрольных значений.Так вот, вам понадобится таблица значе-ний n×m, каждый столбец которой будетпредставлять собой один контрольный на-бор всех аргументов вашей функции. Этутаблицу вы легко сможете получить, орга-низовав прямое произведение из n таблиц(по одной таблице на каждый аргумент),в каждой из которых есть только один стол-бец и лишь m строк (в каждой строке –одно контрольное значение для соответ-ствующего аргумента). Этот метод будетработать, даже если m меняется от аргу-мента к аргументу.

СУБД Microsoft Access поддерживает дляперекрестных объединений только син-таксис WHERE. Поэтому, чтобы запуститьв этой СУБД запрос, который представленв листинге 7.4, воспользуйтесь командойиз самого первого примечания настоящегораздела.

СУБД Oracle 8i и более ранних версий неподдерживает синтаксис JOIN. Поэтому,если у вас именно такая СУБД, применяйтесинтаксис WHERE. СУБД Oracle 9i и болеепоздних версий поддерживает синтаксисJOIN.

Создание произвольного перекрестного объединения

Page 236: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

236 Выбор данных из нескольких таблиц

Созданиепроизвольногоестественногообъединенияс использованиемпредложенияNATURAL JOINЛюбое естественное, или натуральное, объ-единение обладает следующими характе-ристиками:

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

работает только в том случае, если вовходных (то есть объединяемых) табли-цах найдется хотя бы одна пара столб-цов с одинаковыми именами, чтобы ихтипы данных были совместимы в ра-зумных пределах;выполняет объединения неявно. Вамне надо определять для естественногозапроса ни предложение ON, ни предло-жение USING;все, что может делать это объединение,выполняется явно или с помощьюпредложения ON в синтаксисе JOIN, илис помощью предложением WHERE в син-таксисе WHERE.

Создание произвольногоестественного объединения

Напечатайте:

SELECT columns

FROM table1

NATURAL JOIN table2

Предполагается, что:

вместо columns вы подставите одно илинесколько разделенных запятыми вы-ражений с именами столбцов или/исамих имен (заголовков) столбцов объ-единяемых таблиц table1 и table2;если этого потребует СУБД и для того,чтобы избежать неоднозначной иден-тификации столбцов, вы уточните со-ответствующими префиксами (то естьименами таблиц) имена столбцов объ-единяемых таблиц, которые являютсяв них общими (см. примечание DBMSв этом разделе);вместо table1 и table2 подставите име-на объединяемых таблиц.

Естественное объединение выполняетсятаким образом, что связующими объяв-ляются те столбцы, у которых имеетсяпара с таким же именем, но в другой таб-лице. В каждой паре объединяемых строкзначения этих пар столбцов сравнивают-ся на равенство и, если оно по всем парамстолбцов с одинаковыми именами дости-гается, строки объединяются.Предложение NATURAL JOIN создает есте-ственные внутренние объединения. О том,как создавать естественные внешние объ-единения, упоминается в одном из советовэтого раздела.Когда СУБД будет исполнять тот запрос,который представлен в листинге 7.5, она

Page 237: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

237

объединит строки таблицы publishers состроками таблицы titles, причем произ-вольная пара строк объединяется тогда итолько тогда, когда значения столбцовpublishers.pub_id и titles.pub_id, то естьединственной пары столбцов, имеющих вобоих таблицах одно и то же имя (не-уточненное; уточненные имена всегдаразличны!), на этой паре строк равны.Результат исполнения этого запроса по-казан на рис. 7.5.Листинг 7.6 представляет модифицирован-ный запрос из листинга 7.5. Суть этой мо-дификации состоит в том, что в запрос излистинга 7.5 мы добавили еще одно объ-единение, чтобы сопроводить упоминаниекаждой книги данными по сумме аванса,выплаченного за нее автору, и предложе-ние WHERE, которое разрешает выбирать толь-ко те книги, сумма аванса за которые мень-ше $20 000. Когда ваша СУБД будет ис-полнять этот модифицированный запрос,она сначала выполнит объединение таблицpublishers и titles, причем связывающимистолбцами здесь будут publishers.pub_idи titles.pub_id, и получит виртуальнуютаблицу, представленную на рис. 7.5. Пос-ле этого она объединит эту самую вир-туальную таблицу с таблицей royalties,причем связывающими столбцами будутtitles.title_id и royalties.title_id. ПотомСУБД удалит из результата все строки, в ко-торых сумма аванса составляет не меньше$20 000 (см. рис. 7.6).

Чтобы заменить любое естественное объ-единение каким-либо эквивалентным объ-единением, но в синтаксисе WHERE, следу-ет воспользоваться объединением по ра-венству с соответствующим предложениемWHERE, которое применяет оператор AND

Листинг 7.5. Распечатать перечень книг,учитываемых в нашей типовой базе данных,и издателей этих книг так, чтобы рядом сидентификатором книги были показаны в однойстроке идентификатор и наименование издателя,опубликовавшего эту книгу. Результат исполнениязапроса показан на рис. 7.5

SELECT

t.title_id,

t.pub_id,

p.pub_name

FROM publishers p

NATURAL JOIN titles t;

title_id pub_id pub_name

—----——— —----—— ————————-----------—

T01 P01 Abatis Publishers

T02 P03 Schadenfreude Press

T03 P02 Core Dump Books

T04 P04 Tenterhooks Press

T05 P04 Tenterhooks Press

T06 P01 Abatis Publishers

T07 P03 Schadenfreude Press

T08 P04 Tenterhooks Press

T09 P04 Tenterhooks Press

T10 P01 Abatis Publishers

T11 P04 Tenterhooks Press

T12 P01 Abatis Publishers

T13 P03 Schadenfreude Press

Рис. 7.5. Результат исполнения запроса,представленного в листинге 7.5

Создание произвольного естественного объединения

Page 238: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

238 Выбор данных из нескольких таблиц

для комбинации нескольких (по числу парстолбцов с одинаковыми именами) условийравенства значений связывающих столбцовв единое условие. Например, для запросовиз листингов 7.5 и 7.6 эквивалентные пред-ставления в синтаксисе WHERE таковы:

SELECT t.title_id, t.pub_id,

p.pub_name

FROM publishers p, titles t

WHERE p.pub_id = t.pub_id; – длязапроса, представленного в листин-ге 7.5;

SELECT t.title_id, t.pub_id,

p.pub_name, r.advance

FROM publishers p, titles t,

royalties r

WHERE p.pub_id = t.pub_id

AND t.title_id = r.title_id

AND r.advance < 20000; – длязапроса, представленного в листин-ге 7.6.

Чтобы заменить любое естественное объ-единение каким-либо эквивалентным внут-ренним или внешним объединением, нов синтаксисе JOIN, воспользуйтесь объе-динением по равенству с соответствую-щим предложением ON, которое применя-ет оператор AND для комбинации несколь-ких (по числу пар столбцов с одинаковы-ми именами) условий равенства значенийсвязывающих столбцов в единое условие.Например, для запросов из листингов 7.5и 7.6 эквивалентные представления в син-таксисе JOIN таковы:

SELECT t.title_id, t.pub_id,

p.pub_name

FROM publishers p

INNER JOIN titles t

ON p.pub_id = t.pub_id; – для за-проса, представленного в листинге 7.5;

Листинг 7.6. Перечислить идентификаторы длякниг, учитываемых в нашей типовой базе данных,авторы которых получили аванс меньше $20 000,в сочетании с идентификатором издателя,напечатавшего каждую книгу, именем этогоиздателя и суммой аванса. Результат исполнениязапроса представлен на рис. 7.6

SELECT

t.title_id,

t.pub_id,

p.pub_name,

r.advance

FROM publishers p

NATURAL JOIN titles t

NATURAL JOIN royalties r

WHERE r.advance < 20000;

title_id pub_id pub_name advance

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

T01 P01 Abatis Publishers 10000

T02 P03 Schadenfreude Press 1000

T03 P02 Core Dump Books 15000

T08 P04 Tenterhooks Press 0

T09 P04 Tenterhooks Press 0

Рис. 7.6. Результат исполнения листинга 7.6

Page 239: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

239

SELECT t.title_id, t.pub_id,

p.pub_name, r.advance

FROM publishers p

INNER JOIN titles t

ON p.pub_id = t.pub_id

INNER JOIN royalties r

ON t.title_id = r.title_id

WHERE r.advance < 20000; – длязапроса, представленного в листинге 7.6.

Вы можете заменить любое естественноеобъединение еще каким-нибудь эквивален-тным объединением в синтаксисе JOIN, ко-торое применяет предложение USING (см.врезку в разделе «Создание объединенийс помощью синтаксиса JOIN и WHERE»). Приэтом нужно понимать, что NATURAL JOIN –это сокращенная и упрощенная формапредложения USING, предложение NATURALJOIN тоже формирует список типа USING,но в него включаются только те столбцы,у которых есть пара во второй таблице (тоесть столбец с таким же именем). Напри-мер, запросы из листингов 7.5 и 7.6 с ис-пользованием предложения USING можнопереписать в следующем виде:

SELECT t.title_id, t.pub_id,

p.pub_name

FROM publishers p

INNER JOIN titles t

USING (pub_id); – для запроса,представленного в листинге 7.5;

SELECT t.title_id, t.pub_id,

p.pub_name, r.advance

FROM publishers p

INNER JOIN titles t

USING (pub_id)

INNER JOIN royalties r

USING (title_id)

WHERE r.advance < 20000; – для за- проса, представленного в листинге 7.6.

Имейте в виду, что синтаксис NATURALJOIN фактически создает некое внутрен-нее объединение, и именно поэтому пред-ложения NATURAL JOIN и NATURAL INNERJOIN полностью эквивалентны, а ключевоеслово INNER всегда опускают при постро-ении соответствующего объединения. Новы можете создавать не только естествен-ные внутренние, но и естественные вне-шние объединения с использованием сле-дующих предложений:

NATURAL LEFT [OUTER] JOIN;

NATURAL RIGHT [OUTER] JOIN;

NATURAL FULL [OUTER] JOIN.

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

Обратите внимание, что смысл естествен-ного объединения в реляционной модели(см. главу 2) и в стандарте SQL разный.Если в реляционной модели естественнымобъединением считается такое объедине-ние дочерней и материнской таблиц, когдаединственной парой связывающих столб-цов является внешний ключ дочерней таб-лицы и первичный ключ материнской таб-лицы, то в SQL под естественным объ-единением понимается такое объединениедвух произвольных таблиц, когда связыва-ющими столбцами являются все столбцыдвух таблиц, имеющие в двух таблицаходинаковые неуточненные имена. В ка-честве примера естественного объедине-ния, в состав связывающих столбцов кото-рого ни один ключ не входит, рассмотримзапрос из листинга 7.9. Отсюда следует:

Создание произвольного естественного объединения

Page 240: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

240 Выбор данных из нескольких таблиц

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

СУБД Microsoft Access и Microsoft SQLServer не поддерживают синтаксис NATU-RAL JOIN. Поэтому, чтобы запустить вэтих СУБД запросы, которые представле-ны в листингах 7.5 и 7.6, используйте син-таксис WHERE (см. самое первое примеча-ние этого раздела).

СУБД Oracle 8i и более ранних версий во-обще не поддерживает синтаксис JOIN.Поэтому вам придется полностью перейтина синтаксис WHERE.

СУБД Oracle 9i не разрешает применятьуточненные имена столбцов в предложени-ях SELECT, включающих естественныеобъединения. Поэтому, чтобы запустить вэтой СУБД запросы, представленные в ли-стингах 7.5 и 7.6, придется убрать все пре-фиксы-уточнители следующим образом:

SELECT title_id, pub_id, pub_name

FROM publishers

NATURAL JOIN titles; – для за-проса, представленного в листинге 7.5;

SELECT title_id, pub_id,

pub_name, advance

FROM publishers

NATURAL JOIN titles

NATURAL JOIN royalties

WHERE advance < 20000; – для за-проса, представленного в листинге 7.6.

СУБД Oracle 9i не разрешает применятьуточненные имена столбцов в предложени-ях SELECT тех запросов, где в объединени-ях применяется предложение USING. По-этому, чтобы запустить в среде Oracle 9iзапросы, которые мы привели как примерыиспользования предложения USING, заме-ните t.title_id на title_id и t.pub_idна pub_id и т.д.

В среде СУБД PostgreSQL применение пре-фиксов-уточнителей в ситуации неодноз-начного именования столбцов в естествен-ных объединениях допускается, но не яв-ляется обязательным.

Page 241: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

241

Созданиевнутреннего объединенияс помощью командыINNER JOINВнутреннее объединение отличается следу-ющими характеристиками:

использует оператор сравнения (=, <>, <,<=, > или >=), чтобы сопоставить стро-ки в двух таблицах на основании значе-ний в общих столбцах каждой таблицы.Например, вы можете считать все стро-ки, для которых идентификатор авто-ра (столбец au_id) в таблицах authorsи title_authors совпадает;считывает результат, который включа-ет только объединенные строки, соот-ветствующие условиям объединения;является самым распространенным ти-пом объединения.

Создание внутреннего объединенияВведите:SELECT columns

FROM table1

INNER JOIN table2

ON join_conditions

Здесь columns – это несколько выраженийили названий столбцов и table1 или table2,разделенных запятыми. table1

и table2 – это названия объединенных таб-лиц. Если таблицы имеют столбцыс одинаковыми названиями, обозначьтеих с помощью имен таблиц.join_conditions указывает условия объ-единения, которые должны быть рассчи-таны для каждой пары объединенныхстрок. Условие объединения приобретаетследующую форму:[table1.]column op [table2]column

op обычно имеет значение =, но может бытьлюбым оператором сравнения (=, <>, <, <=,> или >=; см. табл. 4.2). Вы можете комби-нировать несколько условий объединения

с помощью AND или OR (см. раздел «Комби-нирование условий с помощью операторовAND, OR и NOT» в главе 4).

Чтобы создать внутреннее объединениедля трех или более таблиц с помощью ко-манды JOIN, введите:SELECT columns FROM table1 INNER JOIN table2 ON join_condition1 INNER JOIN table3 ON join_condition2 …

Если вы желаете использовать синтаксисWHERE, введите:SELECT columns FROM table1, table2, … WHERE join_condition1 AND join_condition2 …

По умолчанию команда JOIN (без указа-ния CROSS, NATURAL, OUTER или любыхдругих) эквивалентна INNER JOIN.

Oracle 8i и более ранних версий не поддер-живает команду JOIN; вместо нее используй-те команду WHERE. Oracle 9i и более позднихверсий поддерживает команду JOIN.В Microsoft Access можно использоватьсинтаксис WHERE или JOIN, но, если вы бу-дете использовать JOIN для объединений,которые включают три таблицы или более,вам придется размещать объединения наразных уровнях с помощью следующейобщей структуры:SELECT columns FROM table1 INNER JOIN (table2 INNER JOIN (table3 INNER JOIN (table4 INNER JOIN …ON table3.column3 op table4.column4)ON table2.column2 op table3.column3)ON table1.column1 op table2.column2;

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

Создание внутреннего объединения с помощью команды INNER JOIN

Page 242: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

242 Выбор данных из нескольких таблиц

Листинг 7.7 объединяет две таблицы в столб-це au_id, чтобы отобразить список книг,которые написали все авторы (в том числев соавторстве). Информация о каждом авто-ре в столбце au_id соответствует строкамв таблице title_authors. Результат исполне-ния листинга показан на рис. 7.7. Обратитевнимание, что автор A07 (Paddy O’Furniture)не включен в результат, потому что он ненаписал ни одной книги и для него нет со-ответствий в строках title_authors.

При использовании синтаксиса WHERE ли-стинг 7.7 будет выглядеть следующим об-разом:

SELECT a.au_id, a.au_fname,

a.au_lname, ta.title_id

FROM authors a, title_authors ta

WHERE a.au_id = ta.au_id

ORDER BY a.au_id ASC,

ta.title_id = ASC;

Листинг 7.7. Отобразить список книг, которыенаписали все авторы (в том числе в соавторстве).Результат исполнения см. на рис. 7.7

SELECT

a.au_id,

a.au_fname,

a.au_lname,

ta.title_id

FROM authors a

INNER JOIN title_authors ta

ON a.au_id = ta.au_id

ORDER BY a.au_id ASC, ta.title_id ASC;

au_id au_fname au_lname title_id

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

A01 Sarah Buchman T01

A01 Sarah Buchman T02

A01 Sarah Buchman T13

A02 Wendy Heydemark T06

A02 Wendy Heydemark T07

A02 Wendy Heydemark T10

A02 Wendy Heydemark T12

A03 Hallie Hull T04

A03 Hallie Hull T11

A04 Klee Hull T04

A04 Klee Hull T05

A04 Klee Hull T07

A04 Klee Hull T11

A05 Christian Kells T03

A06 Kellsey T08

A06 Kellsey T09

A06 Kellsey T11

Рис. 7.7. Результат выполнения листинга 7.7

Page 243: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

243

Листинг 7.8 объединяет две таблицыв столбце pub_id, чтобы отобразить за-головки и информацию о каждой кни-ге, а также информацию о всех книгахдругих издательств. Обратите внимание,что объединение требуется только для то-го, чтобы считать название издательства(четвертый столбец в результате), все дру-гие столбцы есть в таблице titles. Ре-зультат исполнения листинга показан нарис. 7.8.

При использовании синтаксиса WHERE ли-стинг 7.8 будет выглядеть так:

SELECT t.title_id, t.title_name,

t.pub_id, p.pub_name

FROM titles t, publishers p

WHERE p.pub_id = t.pub_id

ORDER BY t.title_name ASC;

Листинг 7.8. Отобразить заголовки иинформацию о каждой книге, а такжеинформацию обо всех книгах других издательств.Результат исполнения см. на рис. 7.8

SELECT

t.title_id,

t.title_name,

t.pub_id,

p.pub_name

FROM titles t

INNER JOIN publishers p

ON p.pub_id = t.pub_id

ORDER BY t.title_name ASC;

title_id title_name pub_id pub_name

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

T01 1997! P01 Abatis Publishers

T02 200 Years of German Humor P03 Schadenfreude Press

T03 Ask Your System Administrator P02 Core Dump Books

T04 But I Did It Unconsciously P04 Tenterhooks Press

T05 Exchange of Platitudes P04 Tenterhooks Press

T06 How About Never? P01 Abatis Publishers

T07 I Blame My Mother P03 Schadenfreude Press

T08 Just Wait Until After School P04 Tenterhooks Press

T09 Kiss My Boo-Boo P04 Tenterhooks Press

T10 Not Without My Faberge Egg P01 Abatis Publishers

T11 Perhaps It's a Glandular Problem P04 Tenterhooks Press

T12 Spontaneouse, But Not Annoing P01 Abatis Publishers

T13 What Are The Civilian Applications? P03 Schadenfreude Press

Рис. 7.8. Результат выполнения листинга 7.8

Создание внутреннего объединения с помощью команды INNER JOIN

Page 244: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

244 Выбор данных из нескольких таблиц

Листинг 7.9 использует два объединения,чтобы отобразить список авторов, кото-рые живут во всех городах, где располо-жены издательства. Результат исполнениялистинга показан на рис. 7.9. Обратитевнимание, что этот запрос представляетсобой объединение столбцов city и stateв двух таблицах, причем эти столбцыимеют одинаковое название и не являют-ся ключами (см. раздел «Создание произ-вольного естественного объединения сиспользованием предложения NATURALJOIN» в этой главе). Приведем эквивалент-ный запрос:

SELECT a.au_id, a.au_fname, a.au_lname,

a.city, a.state

FROM authors a

NATURAL JOIN publishers p

ORDER BY a.au_id ASC;

При использовании синтаксиса WHERE ли-стинг 7.9 будет выглядеть следующим об-разом:

SELECT a.au_id, a.au_fname,

a.au_lname, a.city, a.state

FROM authors a, publishers p

WHERE a.city = p.city

AND a.state = p.state

ORDER BY a.au_id ASC;

Листинг 7.9. Отобразить список авторов, которыеживут во всех городах, где расположеныиздательства. Результат исполнения листинга см.на рис. 7.9

SELECT

a.au_id,

a.au_fname,

a.au_lname,

a.city,

a.state

FROM authors a

INNER JOIN publishers p

ON a.city = p.city

AND a.state = p.state

ORDER BY a.au_id ASC;

au_id au_fname au_lname city state

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

A03 Hallie Hull San Francisco CA

A04 Klee Hull San Francisco CA

A05 Christian Kells New York NY

Рис. 7.9. Результат выполнения листинга 7.9

Page 245: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

245

Листинг 7.10 комбинирует внутреннееобъединение с условиями WHERE, чтобы ото-бразить список книг, опубликованныхв штате Калифорния или вне крупных странСеверной Америки (см. раздел «Фильтрациястрок с помощью предложения WHERE»в главе 4). Результат исполнения листингапредставлен на рис. 7.10.

При использовании синтаксиса WHERE ли-стинг 7.10 будет выглядеть следующимобразом:

SELECT t.title_id, t.title_name,

p.state, p.country

FROM titles t, publishers p

WHERE t.pub_id = p.pub_id

AND (p.state = 'CA'

OR p.country NOT IN

('USA', 'Canada', 'Mexico'))

ORDER BY t.title_id ASC;

Листинг 7.10. Отобразить список книг,опубликованных в штате Калифорния или внекрупных стран Северной Америки. Результатисполнения см. на рис. 7.10

SELECT

t.title_id,

t.title_name,

p.state,

p.country

FROM titles t

INNER JOIN publishers p

ON t.pub_id = p.pub_id

WHERE p.state = 'CA'

OR p.country NOT IN

('USA', 'Canada', 'Mexico')

ORDER BY t.title_id ASC;

title_id title_name state country

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

T02 200 Years of German Humor NULL Germany

T03 Ask Your System Administrator CA USA

T04 But I Did It Unconsciously CA USA

T05 Exchange of Platitudes CA USA

T07 I Blame My Mother NULL Germany

T08 Just Wait Until After School CA USA

T09 Kiss My Boo-Boo CA USA

T11 Perhaps It's a Glandular Problem CA USA

T13 What Are The Civilian Applications? NULL Germany

Рис. 7.10. Результат выполнения листинга 7.10

Создание внутреннего объединения с помощью команды INNER JOIN

Page 246: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

246 Выбор данных из нескольких таблиц

Листинг 7.11 комбинирует внутреннееобъединение с функцией COUNT() и предло-жением GROUP BY, чтобы отобразить списоккниг, написанных всеми авторами (в томчисле в соавторстве). За информацией овозможностях и синтаксисе предложенияGROUP BY обращайтесь к главе 6. Результатисполнения листинга показан на рис. 7.11.Обратите внимание, что, как и на рис. 7.7,автор A07 (Paddy O’Furniture) не включен врезультат, поскольку он не написал ни од-ной книги, и для него нет записей в таблицесоответствий title_authors. В листинге 7.30приводится пример того, как отобразитьсписок авторов, которые не написали ниодной книги.

При использовании синтаксиса WHERE ли-стинг 7.11 будет выглядеть следующимобразом:

SELECT a.au_id,

COUNT (ta.title_id)

AS "Num books"

FROM authors a, title_authors ta

WHERE a.au_id = ta.au_id

GROUP BY a.au_id

ORDER BY a.au_id ASC;

Листинг 7.11. Отобразить список книг,написанных всеми авторами (в том числев соавторстве). Результат исполнениясм. на рис. 7.11

SELECT

a.au_id,

COUNT(ta.title_id) AS "Num books"

FROM authors a

INNER JOIN title_authors ta

ON a.au_id = ta.au_id

GROUP BY a.au_id

ORDER BY a.au_id ASC;

au_id Num books

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

A01 3

A02 4

A03 2

A04 4

A05 1

A06 3

Рис. 7.11. Результат выполнения листинга 7.11

Page 247: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

247

Листинг 7.12 использует условия WHERE,чтобы отобразить авансы, выплаченные закниги-биографии. Результат исполнениялистинга показан на рис. 7.12.

При использовании синтаксиса WHERE ли-стинг 7.12 будет выглядеть следующимобразом:

SELECT t.title_id, t.title_name,

r.advance

FROM royalties r, titles_t

WHERE r.title_id = t.title_id

AND t.type = 'biography'

AND r.advance IS NOT NULL

ORDER BY r.advance DESC;

Листинг 7.12. Отобразить авансы, выплаченные закниги-биографии. Результат исполнения листингасм. на рис. 7.12

SELECT

t.title_id,

t.title_name,

r.advance

FROM royalties r

INNER JOIN titles t

ON r.title_id = t.title_id

WHERE t.type = 'biography'

AND r.advance IS NOT NULL

ORDER BY r.advance DESC;

title_id title_name advance

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

T07 I Blame My Mother 1000000.00

T12 Spontaneouse, Not Annoying 50000.00

T06 How About Never? 20000.00

Рис. 7.12. Результат выполнения листинга 7.12

Создание внутреннего объединения с помощью команды INNER JOIN

Page 248: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

248 Выбор данных из нескольких таблиц

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

При использовании синтаксиса WHERE ли-стинг 7.13 будет выглядеть так:

SELECT t.type,

COUNT (r.advance)

AS "COUNT(r.advance)",

SUM(r.advance)

AS "SUM(r.advance)"

FROM royalties r, titles_t

WHERE r.title_id = t.title_id

AND r.advance IS NOT NULL

GROUP BY t.type

ORDER BY t.type DESC;

Листинг 7.13. Отобразить счет и аванс,выплаченный за каждый тип книг. Результатисполнения листинга см. на рис. 7.13

SELECT

t.type,

COUNT(r.advance)

AS "COUNT(r.advance)",

SUM(r.advance)

AS "SUM(r.advance)"

FROM royalties r

INNER JOIN titles t

ON r.title_id = t.title_id

WHERE r.advance IS NOT NULL

GROUP BY t.type

ORDER BY t.type ASC;

type COUNT(r.advance) SUM(r.advance)

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

biography 3 1070000.00

children 2 0.00

computer 1 15000.00

history 3 31000.00

psychology 3 220000.00

Рис. 7.13. Результат выполнения листинга 7.13

Page 249: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

249

Листинг 7.14. Отображение количествавыплаченных авансов и их общей суммы закаждый тип книг с разбивкой по издательствам.Результат исполнения листинга см. на рис. 7.14

SELECT

t.type,

t.pub_id,

COUNT(r.advance) AS

"COUNT(r.advance)",

SUM(r.advance) AS "SUM(r.advance)"

FROM royalties r

INNER JOIN titles t

ON r.title_id = t.title_id

WHERE r.advance IS NOT NULL

GROUP BY t.type, t.pub_id

ORDER BY t.type ASC, t.pub_id ASC;

type pub_id COUNT(r.advance) SUM(r.advance)

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

biography P01 2 70000.00

biography P03 1 1000000.00

children P04 2 0.00

computer P02 1 15000.00

history P01 1 10000.00

history P03 2 21000.00

psychology P04 3 220000.00

Рис. 7.14. Результат выполнения листинга 7.14

Листинг 7.14 является аналогом листин-га 7.13, только он использует дополни-тельный столбец, чтобы отобразить счети аванс, выплаченный за каждый тип книгиздательством. Результат исполнения ли-стинга показан на рис. 7.14.

При использовании синтаксиса WHERE ли-стинг 7.14 будет выглядеть так:

SELECT t.type, t.pub_id,

COUNT (r.advance)

AS "COUNT(r.advance)",

SUM(r.advance)

AS "SUM(r.advance)"

FROM royalties r, titles_t

WHERE r.title_id = t.title_id

AND r.advance IS NOT NULL

GROUP BY t.type, t.pub_id

ORDER BY t.type ASC, t.pub_id ASC;

Создание внутреннего объединения с помощью команды INNER JOIN

Page 250: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

250 Выбор данных из нескольких таблиц

Листинг 7.15 использует предложение HA-VING, чтобы отобразить список соавторовдля всех книг, у которых они есть. За ин-формацией о синтаксисе предложения HA-VING обратитесь к разделу «Фильтрациягрупп с помощью предложения HAVING»в главе 6. Результат исполнения листингапоказан на рис. 7.15.

При использовании синтаксиса WHERE ли-стинг 7.15 будет выглядеть следующимобразом:

SELECT ta.title_id,

COUNT (ta.au_id) AS "Num authors"

FROM authors a, title_authors ta

WHERE a.au_id = ta.au_id

GROUP BY ta.title_id

HAVING COUNT (ta.au_id) > 1

ORDER BY ta.title_id ASC;

Листинг 7.15. Отобразить список соавторов длявсех книг, у которых они есть. Результатисполнения листинга см. на рис. 7.15

SELECT

ta.title_id,

COUNT(ta.au_id) AS "Num authors"

FROM authors a

INNER JOIN title_authors ta

ON a.au_id = ta.au_id

GROUP BY ta.title_id

HAVING COUNT(ta.au_id) > 1

ORDER BY ta.title_id ASC;

title_id Num authors

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

T04 2

T07 2

T11 3

Рис. 7.15. Результат выполнения листинга 7.15

Page 251: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

251

Также вы можете объединять таблицы постолбцам, содержащим значения, которыене являются равными. Листинг 7.16 исполь-зует оператор «больше чем» (>), чтобынайти все книги, прибыль от которых (=цена × продажи) превышает сумму аванса,выплаченного автору, не меньше чемв 10 раз. Результат исполнения листингапоказан на рис. 7.16. Операторы <, <=, >и >= используются при объединении таб-лиц довольно часто, а оператор неравен-ства (<>) – редко. Как правило, объедине-ния по неравенству полезны только прииспользовании с самообъединениями (см.раздел «Создание самообъединения» далеев этой главе).

При использовании синтаксиса WHERE ли-стинг 7.16 будет выглядеть следующимобразом:

SELECT t.title_id, t.title_name,

r.advance,

t.price * t.sales AS "Revenue"

FROM titles t, royalties r

WHERE t.price * t.sales >

r.advance * 10

AND t.title_id = r.title_id

ORDER BY t.price * t.sales DESC;

Листинг 7.16. Отобразить все книги, прибыль откоторых (= цена × продажи) превышает суммуаванса, уплаченного автору, не меньше чемв 10 раз. Результат исполнения листингасм. на рис. 7.16

SELECT

t.title_id,

t.title_name,

r.advance,

t.price * t.sales AS "Revenue"

FROM titles t

INNER JOIN royalties r

ON t.price * t.sales > r.advance * 10

AND t.title_id = r.title_id

ORDER BY t.price * t.sales DESC;

title_id title_name advance Revenue

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

T07 I Blame My Mother 1000000.00 35929790.00

T05 Exchange of Platitudes 100000.00 1400008.00

T12 Spontaneouse, But Not Annoing 50000.00 1299012.99

T03 Ask Your System Administrator 15000.00 1025396.65

T13 What Are The Civilian Applications? 20000.00 313905.33

T06 How About Never? 20000.00 225834.00

T02 200 Years of German Humor 1000.00 190841.70

T09 Kiss My Boo-Boo .00 69750.00

T08 Just Wait Until After School .00 40950.00

Рис. 7.16. Результат выполнения листинга 7.16

Создание внутреннего объединения с помощью команды INNER JOIN

Page 252: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

252 Выбор данных из нескольких таблиц

Сложные запросы могут быть следствиемпростых вопросов. В листинге 7.17 нам не-обходимо объединить три таблицы, чтобыотобразить список авторов и названийкниг, которые написали все авторы (в томчисле в соавторстве). Результат исполне-ния листинга показан на рис. 7.17.

При использовании синтаксиса WHERE ли-стинг 7.17 будет выглядеть так:

SELECT a.au_fname, a.au_lname,t.title_name,

FROM authors a, title_authors ta,titles t

WHERE a.au_id = ta.au_id AND t.title_id = ta.title_id

ORDER BY a.au_lname ASC,a.au_fname ASC,

t.title_name ASC;

Листинг 7.17. Отобразить список авторови названий книг, которые написали все авторы(в том числе в соавторстве). Результатисполнения листинга см. на рис. 7.17

SELECT

a.au_fname,

a.au_lname,

t.title_name

FROM authors a

INNER JOIN title_authors ta

ON a.au_id = ta.au_id

INNER JOIN titles t

ON t.title_id = ta.title_id

ORDER BY a.au_lname ASC, a.au_fname

ASC,

t.title_name ASC;

Чтобы запустить листинг 7.17 в MicrosoftAccess, введите:

SELECT a.au_fname, a.au_lname,t.title_name

FROM titles AS t

INNER JOIN (authors AS aINNER JOIN title_authors AS ta

ON a.au_id = ta.au_id) ON t.title_id = ta.title_id

ORDER BY a.au_lname ASC,a.au_fname ASC,

t.title_name ASC;

au_fname au_lname title_name

———— ———— —————————————————

Sarah Buchman 1977!

Sarah Buchman 200 Years of German Humor

Sarah Buchman What Are The Civilian Applications?

Wendy Heydemark How About Never?

Wendy Heydemark I Blame My Mother

Wendy Heydemark Not Without My Faberge Egg

Wendy Heydemark Spontaneous, Not Annoying

Hallie Hull But I Did It Unconsciously

Hallie Hull Perhaps It's a Glandular Problem

Klee Hull But I Did It Unconsciously

Klee Hull Exchange of Platitudes

Klee Hull I Blame My Mother

Klee Hull Perhaps It's a Glandular Problem

Christian Kells Ask Your System Administrator

Kellsey Just Wait Until After School

Kellsey Kiss My Boo-Boo

Kellsey Perhaps It's a Glandular Problem

Рис. 7.17. Результат выполнения листинга 7.17

Page 253: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

253

Листинг 7.18 является усложненным вари-антом листинга 7.17. Он использует объе-динение четырех таблиц, чтобы отобра-зить названия издательств, а также именаавторов и названия книг. Результат испол-нения листинга показан на рис. 7.18.

Листинг 7.18. Отобразить имена авторов,названия всех книг, а также названия издательств.Результат исполнения листинга см. на рис. 7.18

SELECT

a.au_fname,

a.au_lname,

t.title_name,

p.pub_name

FROM authors a

INNER JOIN title_authors ta

ON a.au_id = ta.au_id

INNER JOIN titles t

ON t.title_id = ta.title_id

INNER JOIN publishers p

ON p.pub_id = t.pub_id

ORDER BY a.au_lname ASC, a.au_fname

ASC,

t.title_name ASC;

au_fname au_lname title_name pub_name

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

Sarah Buchman 1977! Abatis Publishers

Sarah Buchman 200 Years of German Humor Schadenfreude Press

Sarah Buchman What Are The Civilian Applications? Schadenfreude Press

Wendy Heydemark How About Never? Abatis Publishers

Wendy Heydemark I Blame My Mother Schadenfreude Press

Wendy Heydemark Not Without My Faberge Egg Abatis Publishers

Wendy Heydemark Spontaneous, Not Annoying Abatis Publishers

Hallie Hull But I Did It Unconsciously Tenterhooks Press

Hallie Hull Perhaps It's a Glandular Problem Tenterhooks Press

Klee Hull But I Did It Unconsciously Tenterhooks Press

Klee Hull Exchange of Platitudes Tenterhooks Press

Klee Hull I Blame My Mother Schadenfreude Press

Klee Hull Perhaps It's a Glandular Problem Tenterhooks Press

Christian Kells Ask Your System Administrator Core Dump Books

Kellsey Just Wait Until After School Tenterhooks Press

Kellsey Kiss My Boo-Boo Tenterhooks Press

Kellsey Perhaps It's a Glandular Problem Tenterhooks Press

Рис. 7.18. Результат выполнения листинга 7.18

Создание внутреннего объединения с помощью команды INNER JOIN

Page 254: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

254 Выбор данных из нескольких таблиц

При использовании синтаксиса WHERE ли-стинг 7.18 будет выглядеть так:

SELECT a.au_fname, a.au_lname,

t.title_name, p.pub_name

FROM authors a, title_authors ta,

titles t, publishers p

WHERE a.au_id = ta.au_id

AND t.title_id = ta.title_id

AND p.pub_id = t.pub_id

ORDER BY a.au_lname ASC,

a.au_fname ASC,

t.title_name ASC;

Чтобы запустить листинг 7.18 в MicrosoftAccess, введите:

SELECT a.au_fname, a.au_lname,

t.title_name, p.pub_name

FROM (publishers AS p

INNER JOIN titles AS t

ON p.pub_id = t.pub_id)

INNER JOIN (authors AS a)

INNER JOIN (authors AS ta

ON a.au_id = ta.au_id)

ON t.title_id = ta.title_id

ORDER BY a.au_lname ASC,

a.au_fname ASC,

t.title_name ASC;

Page 255: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

255

Листинг 7.19 рассчитывает гонорары за всекниги. Гонорар за книгу – это прибыль отее продажи (= цена × продажи), умножен-ная на процент гонорара (процент от при-были, выплачиваемый автору). В боль-шинстве случаев автор получает аванс какчасть гонорара. Чтобы выплатить остатокгонорара автору, издатель вычитает авансиз общей суммы гонорара. Если общаяприбыль больше нуля, издатель должензаплатить автору; если прибыль равнанулю или меньше нуля, автор не получаетгонорар, так как он не «отработал» дажесвой аванс. Результат исполнения листингапоказан на рис. 7.19. Общие гонорары поме-чены как "Total royalties", общие авансы –как "Total advances", а гонорары за вычетомавансов – как "Total due to authors".

Листинг 7.19. Рассчитать гонорары за все книги. Результат исполнения листинга см. на рис. 7.19

SELECT

SUM(t.sales * t.price * r.royalty_rate) AS "Total royalties",

SUM(r.advance) AS "Total advances",

SUM((t.sales * t.price * r.royalty_rate) - r.advance) AS "Total due to authors"

FROM titles t

INNER JOIN royalties r

ON r.title_id = t.title_id

WHERE t.sales IS NOT NULL;

Листинг 7.19 рассчитывает общие гонора-ры за все книги; в других примерах мыпокажем, как распределить гонорары поавторам, книгам, издательствам и другимгруппам.

При использовании синтаксиса WHERE лис-тинг 7.19 будет выглядеть так:

SELECT

SUM (t.sales * t.price *

→ r.royalty_rate)

AS "Total royalties",

SUM (r.advance)

AS "Total advances",

SUM (t.sales * t.price *

→ r.royalry_rate) – r.advance)

AS "Total due to authors"

FROM titles t, royalties r

WHERE r.title_id = t.title_id

AND t.sales IS NOT NULL;

Total royalties Total advances Total due to authors

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

4387219.55 1336000.00 3051219.55

Рис. 7.19. Результат выполнения листинга 7.19

Создание внутреннего объединения с помощью команды INNER JOIN

Page 256: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

256 Выбор данных из нескольких таблиц

В листинге 7.20 используется объединениетрех таблиц, чтобы рассчитать гонорар,полученный каждым автором за все кни-ги, которые он написал (в том числе в со-авторстве). Так как у книги может бытьнесколько авторов, при расчете гонорараучитывается процент гонорара за книгу,полученный каждым автором (а такжеаванс). Процент гонорара за книгу, полу-ченный автором, приводится в таблицеtitle_authors в столбце royalty_share.Если у книги один автор, процент royal-ty_share равен 1.0 (100%). Если у книги не-сколько авторов, значение royalty_shareдля каждого автора колеблется между 0и 1; все значения royalty_share для однойкниги в сумме должны равняться 1.0(100%). Результат исполнения листингапоказан на рис. 7.20. Сумма всех значенийпоследних трех столбцов результата соот-ветствует общему значению на рис. 7.19.

При использовании синтаксиса WHERE ли-стинг 7.20 будет выглядеть следующимобразом:

SELECT ta.au_id, t.title_id,

t.pub_id,

t.sales * t.price *

→ r.royalty_rate *

→ ta.royalty_share

AS "Royalty share",

r.advance * ta.royalty_share

AS "Advance share",

(t.sales * t.price *

→ r.royalty_rate *

→ ta.royalty_share) –

→ (r.advance *

→ ta.royalty_share)

AS "Due to author"

FROM title_authors ta,

titles t, royalties r

WHERE t.title_id = ta.title_id

AND r.title_id = t.title_id

AND t.sales IS NOT NULL

ORDER BY ta.au_id ASC,

t.title_id ASC;

Листинг 7.20. Рассчитать гонорар, полученный каждым автором за все книги, которые он написал(в том числе в соавторстве). Результат исполнения листинга см. на рис. 7.20

SELECT

ta.au_id,

t.title_id,

t.pub_id,

t.sales * t.price * r.royalty_rate * ta.royalty_share AS "Royalty share",

r.advance * ta.royalty_share AS "Advance share",

(t.sales * t.price * r.royalty_rate * ta.royalty_share) -

→ (r.advance * ta.royalty_share) AS "Due to author"

FROM title_authors ta

INNER JOIN titles t

ON t.title_id = ta.title_id

INNER JOIN royalties r

ON r.title_id = t.title_id

WHERE t.sales IS NOT NULL

ORDER BY ta.au_id ASC, t.title_id ASC;

Page 257: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

257

Чтобы запустить листинг 7.20 в MicrosoftAccess, введите:

SELECT ta.au_id, t.title_id,

t.pub_id,

t.sales * t.price *

→ r.royalty_rate *

→ ta.royalty_share

AS "Royalty share",

r.advance * ta.royalty_share

AS "Advance share",

(t.sales * t.price *

→ r.royalty_rate *

→ ta.royalty_share) –

→ (r.advance * ta.royalty_share) AS "Due to author"

FROM (titles AS t

INNER JOIN royalties AS r

ON t.title_id = r.title_id)

INNER JOIN title_authors AS ta

ON t.title_id = ta.title_id

WHERE t.sales IS NOT NULL

ORDER BY ta.au_id ASC,

t.title_id ASC;

au_id title_id pub_id Royalty share Advance share Due to author

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

A01 T01 P01 622.32 10000.00 -9377.68

A01 T02 P03 11450.50 1000.00 10450.50

A01 T13 P03 18834.32 20000.00 -1165.68

A02 T06 P01 18066.72 20000.00 -1933.28

A02 T07 P03 1976138.45 500000.00 1476138.45

A02 T12 P01 116911.17 50000.00 66911.17

A03 T04 P04 8106.38 12000.00 -3893.62

A03 T11 P04 15792.90 30000.00 -14207.10

A04 T04 P04 5404.26 8000.00 -2595.74

A04 T05 P04 126000.72 100000.00 26000.72

A04 T07 P03 1976138.45 500000.00 1476138.45

A04 T11 P04 15792.90 30000.00 -14207.10

A05 T03 P02 71777.77 15000.00 56777.77

A06 T08 P04 1638.00 .00 1638.00

A06 T09 P04 3487.50 .00 3487.50

A06 T11 P04 21057.20 40000.00 -18942.80

Рис. 7.20. Результат выполнения листинга 7.20

Создание внутреннего объединения с помощью команды INNER JOIN

Page 258: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

258 Выбор данных из нескольких таблиц

Листинг 7.21 является аналогом листинга7.20, только он добавляет еще таблицуauthors, чтобы распечатать имена авторов,а также предложение WHERE, чтобы извлечьстроки только с теми гонорарами, кото-рые больше нуля. Результат исполнениялистинга показан на рис. 7.21.

Листинг 7.21. Отобразить только те гонорары за книги, которые больше нуля. Результат исполнениялистинга см. на рис. 7.21

SELECT

a.au_id,

a.au_fname,

a.au_lname,

t.title_name,

(t.sales * t.price * r.royalty_rate * ta.royalty_share) -

→ (r.advance * ta.royalty_share) AS "Due to author"

FROM authors a

INNER JOIN title_authors ta

ON a.au_id = ta.au_id

INNER JOIN titles t

ON t.title_id = ta.title_id

INNER JOIN royalties r

ON r.title_id = t.title_id

WHERE t.sales IS NOT NULL

AND (t.sales * t.price * r.royalty_rate * ta.royalty_share) -

→ (r.advance * ta.royalty_share) > 0

ORDER BY a.au_id ASC, t.title_id ASC;

Page 259: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

259

При использовании синтаксиса WHERE ли-стинг 7.21 будет выглядеть так:

SELECT a.au_id, a.au_fname,

a.au_lname, t.title_name,

(t.sales * t.price *

→ r.royalty_rate *

→ ta.royalty_share) -

→ (r.advance * ta.royalty_share)

AS "Due to author"

FROM authors a, title_authors ta,

titles t, royalties r

WHERE a.au_id = ta.au_id

AND t.title_id = ta.title_id

AND r.title_id = t.title_id

AND t.sales IS NOT NULL

AND (t.sales * t.price *

→ r.royalty_rate *

→ ta.royalty_share) –

→ (r.advance *

→ ta.royalty_share) > 0

ORDER BY a.au_id ASC,

t.title_id ASC;

Чтобы запустить листинг 7.21 в MicrosoftAccess, введите:

SELECT a.au_id, a.au_fname,

a.au_lname, t.title_name,

→ (t.sales * t.price *

→ r.royalty_rate *

→ ta.royalty_share) -

→ (r.advance * ta.royalty_share)

AS "Due to author"

FROM (titles AS t

INNER JOIN royalties AS r

ON t.title_id = r.title_id)

INNER JOIN (authors AS a

INNER JOIN title_authors AS ta

ON a.au_id = ta.au_id)

ON t.title_id = ta.title_id

WHERE t.sales IS NOT NULL

AND (t.sales * t.price *

→ r.royalty_rate *

→ ta.royalty_share) –

→ (r.advance * ta.royalty_share)

→ > 0

ORDER BY a.au_id ASC,

t.title_id ASC;

au_id au_fname au_lname title_name Due to author

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

A01 Sarah Buchman 200YearsofGermanHumor 10450.50

A02 Wendy Heydemark IBlameMyMother 1476138.45

A02 Wendy Heydemark Spontaneouse,Notannoying 66911.17

A04 Klee Hull ExchangeofPlatitudes 26000.72

A04 Klee Hull IBlameMyMother 1476138.45

A05 Christian Kells AskYourSystemAdministrator 56777.77

A06 Kellsey JustWaitUntilAfterSchool 1638.00

A06 Kellsey KissMyBoo-Boo 3487.50

Рис. 7.21. Результат выполнения листинга 7.21

Создание внутреннего объединения с помощью команды INNER JOIN

Page 260: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

260 Выбор данных из нескольких таблиц

Листинг 7.22 использует предложение GRO-UP BY, чтобы рассчитать все гонорары, вы-плаченные каждым издателем. ФункцияCOUNT() определяет количество книг, закоторое каждое издательство выплачиваетгонорары. Обратите внимание, что здесьне нужно вводить процент от гонорара,полученный каждым автором, так как рас-четы по каждому автору не производятся.Результат исполнения листинга показан нарис. 7.22. Сумма всех значений последнихтрех столбцов результата соответствуетобщему значению на рис. 7.19.

При использовании синтаксиса WHERE ли-стинг 7.22 будет выглядеть так:

SELECT t.pub_id,

COUNT(t.sales) AS "Num books",

SUM(t.sales * t.price *

→ r.royalty_rate)

AS "Total royalties",

SUM(r.advance)

AS "Total advances",

SUM((t.sales * t.price *

→ r.royalty_rate) –

→ r.advance)

AS "Total due to authors"

FROM titles t, royalties r

WHERE r.title_id = t.title_id

AND t.sales IS NOT NULL

GROUP BY t.pub_id

ORDER BY t.pub_id ASC;

Листинг 7.22. Рассчитать гонорары, выплаченные каждым издателем. Результат исполнения листингасм. на рис. 7.22

SELECT

t.pub_id,

COUNT(t.sales) AS "Num books",

SUM(t.sales * t.price * r.royalty_rate) AS "Total royalties",

SUM(r.advance) AS "Total advances",

SUM((t.sales * t.price * r.royalty_rate) - r.advance) AS "Total due to authors"

FROM titles t

INNER JOIN royalties r

ON r.title_id = t.title_id

WHERE t.sales IS NOT NULL

GROUP BY t.pub_id

ORDER BY t.pub_id ASC;

pub_id Num books Total royalties Total advances Total due to authors

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

P01 3 135600.21 80000.00 55600.21

P02 1 71777.77 15000.00 56777.77

P03 3 3982561.72 1021000.00 2961561.72

P04 5 197279.85 220000 -22720.15

Рис. 7.22. Результат выполнения листинга 7.22

Page 261: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

261

Листинг 7.23 аналогичен листингу 7.22, норассчитывает все гонорары, полученныекаждым автором за все книги. Результатисполнения листинга представлен на рис.7.23. Сумма всех значений последних трехстолбцов результата соответствует обще-му значению на рис. 7.19.

Листинг 7.23. Рассчитать гонорары, полученные каждым автором за все книги. Результат исполнениялистинга см. на рис. 7.23

SELECT

ta.au_id,

COUNT(sales) AS "Num books",

SUM(t.sales * t.price * r.royalty_rate * ta.royalty_share) AS "Total royalties

share",

SUM(r.advance * ta.royalty_share) AS "Total advances share",

SUM((t.sales * t.price * r.royalty_rate * ta.royalty_share) -

→ (r.advance * ta.royalty_share)) AS "Total due to author"

FROM title_authors ta

INNER JOIN titles t

ON t.title_id = ta.title_id

INNER JOIN royalties r

ON r.title_id = t.title_id

WHERE t.sales IS NOT NULL

GROUP BY ta.au_id

ORDER BY ta.au_id ASC;

au_id Num books Total royalties share Total advances share Total due to authors

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

A01 3 30907.14 31000.00 -92.86

A02 3 2111116.34 570000.00 1541116.34

A03 2 23899.28 42000.00 -18100.72

A04 4 2123336.32 638000.00 1485336.32

A05 1 71777.77 15000.00 56777.77

A06 3 26182.70 40000.00 -13817.30

Рис. 7.23. Результат выполнения листинга 7.23

Создание внутреннего объединения с помощью команды INNER JOIN

Page 262: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

262 Выбор данных из нескольких таблиц

При использовании синтаксиса WHERE ли-стинг 7.23 будет выглядеть следующимобразом:

SELECT ta.au_id,

COUNT(sales) AS "Num books",

SUM(t.sales * t.price *

→ r.royalty_rate *

→ ta.royalty_share)

AS "Total royalties share",

SUM(r.advance *

→ ta.royalty_share)

AS "Total advances share",

SUM((t.sales * t.price *

→ r.royalty_rate *

→ ta.royalty_share) -

→ (r.advance *

→ ta.royalty_share))

AS "Total due to author"

FROM title_authors ta, titles t,

royalties r

WHERE t.title_id = ta.title_id

AND r.title_id = t.title_id

AND t.sales IS NOT NULL

GROUP BY ta.au_id

ORDER BY ta.au_id ASC;

Чтобы запустить листинг 7.23 в MicrosoftAccess, введите:

SELECT ta.au_id,

COUNT(sales) AS "Num books",

SUM(t.sales * t.price *

→ r.royalty_rate *

→ ta.royalty_share)

AS "Total royalties share",

SUM(r.advance *

→ ta.royalty_share)

AS "Total advances share",

SUM((t.sales * t.price *

→ r.royalty_rate *

→ ta.royalty_share) -

→ (r.advance *

→ ta.royalty_share))

AS "Total due to author"

FROM (title_authors AS ta

INNER JOIN titles AS t

ON t.title_id = ta.title_id)

INNER JOIN royalties AS r

ON r.title_id = t.title_id

WHERE t.sales IS NOT NULL

GROUP BY ta.au_id

ORDER BY ta.au_id ASC;

Page 263: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

263

Листинг 7.24 использует два сгруппирован-ных столбца, чтобы рассчитать гонорары,которые будут выплачены каждым изда-тельством, расположенным в США, каж-дому автору за все его книги. Предложе-ние HAVING отфильтровывает строки толь-ко с положительными значениями гоно-раров, а предложение WHERE – только теиздательства, которые находятся в США.Результат исполнения листинга продемон-стрирован на рис. 7.24.

Листинг 7.24. Рассчитать гонорары, которые будут выплачены каждым издательством, расположеннымв США, каждому автору за все его книги. Результат исполнения листинга см. на рис. 7.24

SELECT

t.pub_id,

ta.au_id,

COUNT(*) AS "Num books",

SUM(t.sales * t.price * r.royalty_rate * ta.royalty_share) AS "Total royalties share",

SUM(r.advance * ta.royalty_share) AS "Total advances share",

SUM((t.sales * t.price * r.royalty_rate * ta.royalty_share) -

→ (r.advance * ta.royalty_share)) AS "Total due to author"

FROM title_authors ta

INNER JOIN titles t

ON t.title_id = ta.title_id

INNER JOIN royalties r

ON r.title_id = t.title_id

INNER JOIN publishers p

ON p.pub_id = t.pub_id

WHERE t.sales IS NOT NULL

AND p.country IN ('USA')

GROUP BY t.pub_id, ta.au_id

HAVING SUM((t.sales * t.price * r.royalty_rate * ta.royalty_share) -

→ (r.advance * ta.royalty_share)) > 0

ORDER BY t.pub_id ASC, ta.au_id ASC;

pub_id au_id Num books Total royalties share Total advances share Total due to author

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

P01 A02 2 134977.89 70000.00 62977.89

P02 A05 1 71777.77 15000.00 56777.77

P04 A04 3 147197.87 138000.00 9197.87

Рис. 7.24. Результат выполнения листинга 7.24

Создание внутреннего объединения с помощью команды INNER JOIN

Page 264: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

264 Выбор данных из нескольких таблиц

При использовании синтаксиса WHERE ли-стинг 7.24 будет выглядеть так:

SELECT t.pub_id, ta.au_id,

COUNT(*) AS "Num books",

SUM(t.sales * t.price *

→ r.royalty_rate *

→ ta.royalty_share)

AS "Total royalties share",

SUM(r.advance *

→ ta.royalty_share)

AS "Total advances share",

SUM((t.sales * t.price *

→ r.royalty_rate *

→ ta.royalty_share) -

→ (r.advance *

→ ta.royalty_share))

AS "Total due to author"

FROM title_authors ta, titles t,

→ royalties r, publishers p

WHERE t.title_id = ta.title_id

AND r.title_id = t.title_id

AND p.pub_id = t.pub_id

AND t.sales IS NOT NULL

AND p.country IN ('USA')

GROUP BY t.pub_id, ta.au_id

HAVING SUM((t.sales * t.price *

→ r.royalty_rate *

→ ta.royalty_share) -

→ (r.advance * ta.royalty_share))→ > 0

ORDER BY t.pub_id ASC,

ta.au_id ASC;

Чтобы запустить листинг 7.24 в MicrosoftAccess, введите:

SELECT t.pub_id,

ta.au_id,

COUNT(*) AS "Num books",

SUM(t.sales * t.price *

→ r.royalty_rate *

→ ta.royalty_share)

AS "Total royalties share",

SUM(r.advance *

→ ta.royalty_share)

AS "Total advances share",

SUM((t.sales * t.price *

→ r.royalty_rate *

→ ta.royalty_share) -

→ (r.advance *

→ ta.royalty_share))

AS "Total due to author"

FROM ((publishers AS p

INNER JOIN titles AS t

ON p.pub_id = t.pub_id)

INNER JOIN royalties AS r

ON t.title_id =

r.title_id)

INNER JOIN title_authors AS ta

ON t.title_id = ta.title_id

WHERE t.sales IS NOT NULL

AND p.country IN ('USA')

GROUP BY t.pub_id, ta.au_id

HAVING SUM((t.sales * t.price *

→ r.royalty_rate *

→ ta.royalty_share) -

→ (r.advance * ta.royalty_share))

→ > 0

ORDER BY t.pub_id ASC,

ta.au_id ASC;

Page 265: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

265

Создание внешнихобъединений с помощьюкоманды OUTER JOINВ предыдущем разделе вы узнали, чтовнутренние объединения считывают стро-ки только в том случае, если хотя бы однастрока в двух таблицах соответствует ус-ловиям объединения. Внутреннее объеди-нение удаляет строки, которые не совпа-дают со строкой в другой таблице. Внеш-нее объединение считывает все строки хотябы из одной таблицы (если эти строки со-ответствуют любому условию поискаWHERE или HAVING).Внешние объединения полезны для отве-тов на вопросы, в которых есть пропу-щенные значения: например, авторы, ненаписавшие ни одной книги, или курсы,на которые не записался ни один студент.Кроме того, внешние объединения помо-гают создавать отчеты, в которых вы же-лаете отобразить все строки в одной таб-лице, а также совпадающие строки издругой таблицы. Например, чтобы ото-бразить всех авторов книг, копий кото-рых было продано больше, чем указано,либо чтобы отобразить заказы на все то-вары и включить те товары, которые ник-то не заказал.В отличие от других объединений, порядок,в котором вы задаете таблицы для внешне-го объединения, имеет значение. Поэтомурабочие таблицы внешнего объединенияназываются левой таблицей и правой таб-лицей. Внешние объединения бываюттрех видов.Левое внешнее объединение (LEFT OUTER JOIN).Результат исполнения левого внешнегообъединения включает все строки из левой

таблицы, заданные в пункте LEFT OUTERJOIN, а не только строки, которые совпада-ют для связанных столбцов. Если длястроки в левой таблице нет соответствия вправой таблице, то в результате строкабудет содержать NULL для всех столбцов всписке SELECT, которые были считаны изправой таблицы.Правое внешнее объединение (RIGHT OUTERJOIN) является прямой противоположнос-тью левому. Считываются все строки изправой таблицы. Если для строки из пра-вой таблицы нет соответствия в правойтаблице, то для левой таблицы считыва-ются NULL.Полное внешнее объединение (FULL OUTERJOIN) является комбинацией левого и пра-вого внешних объединений. Считываетвсе строки как из левой, так и из правойтаблиц. Если для строки нет соответ-ствия в другой таблице, столбцы в спис-ке SELECT другой таблицы будут содер-жать NULL. Если в таблицах есть соответ-ствие, то строка будет содержать значенияиз двух таблиц.Если левая таблица задается в левом внеш-нем объединении, считываются все стро-ки из нее, если в правом внешнем объе-динении задается правая таблица, соот-ветственно, считываются все строки изнее. Полное внешнее объединение считы-вает все строки из двух таблиц. В любомслучае строки, для которых нет соответ-ствий, заполняются значениями null. Та-ким образом, вы не сможете отличитьзначения null, которые изначально былив таблице, от тех, что добавлены приисполнении объединения. Помните, чтоусловие NULL = NULL является неизвестными не имеет совпадений (см. раздел «Значе-ние null» в главе 3).

Создание внешних объединений с помощью команды OUTER JOIN

Page 266: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

266 Выбор данных из нескольких таблиц

Создание левого внешнего объединения

Введите:

SELECT columns

FROM left_table LEFT [OUTER] JOIN right_table

ON join_conditions

Здесь columns – это несколько разделен-ных запятыми выражений или названийстолбцов из left_table или right_table.left_table и right_table – это названиясвязанных таблиц. Если в таблицах естьстолбцы с одинаковыми названиями, обо-значьте эти столбцы с помощью названийтаблиц.

join_conditions задает одно или несколь-ко условий объединения, которые должнырассчитываться для каждой пары связан-ных строк. Условие объединения имеетследующую форму:[left_table.] column op

→ [right_table.] column

op обычно принимает значение =, но мо-жет быть и любым оператором сравнения:=, <>, <, <=, > или >= (см. табл. 4.2). Выможете комбинировать условия с помо-щью AND или OR (см. раздел «Комбини-рование условий с помощью операторовAND, OR и NOT» в главе 4). Ключевоеслово OUTER является опциональным.

Создание правоговнешнего объединения

Введите:SELECT columns FROM left_table

RIGHT [OUTER] JOIN right_table ON join_conditions

columns, left_table, right_table и join_con-ditions имеют такое же значение, как вподразделе «Создание левого внешнегообъединения». Ключевое слово OUTER явля-ется опциональным.

Создание полноговнешнего объединения

Введите:

SELECT columns

FROM left_table

FULL [OUTER] JOIN right_table

ON join_conditions

columns, left_table, right_table и join_-conditions имеют такое же значение, какв подразделе «Создание левого внешнегообъединения». Ключевое слово OUTER явля-ется опциональным.

При работе с внешними объединениямивместо предложения WHERE следует исполь-зовать команду JOIN, поскольку она болееточная. SQL не имеет заданного стандартапредложения WHERE для функционированияс внешними объединениями, поэтому рабо-та с командой различается в разных СУБД.Система управления базами данных такжеможет запрещать использование внешнихобъединений, построенных с помощьюпредложения WHERE, которые не имеют со-ответствующих внешних объединенийJOIN. За дополнительной информациейобращайтесь к примечанию DBMS далее вэтом разделе.

Будьте внимательны при изменении поряд-ка ввода таблиц для внешнего объединения.В отличие от других объединений, внешниеобъединения не являются ассоциативны-ми. Это значит, что результат запроса, ис-пользующего внешнее объединение, зави-сит от порядка, в котором таблицы былисгруппированы и связаны. Два последую-щих внутренних объединения, включаю-щих три таблицы, эквивалентны (за исклю-чением порядка столбцов в результате):

SELECT * FROM table1

INNER JOIN table2

INNER JOIN table3

Page 267: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

267

и

SELECT * FROM table2

INNER JOIN table3

INNER JOIN table1

Но два следующих внешних объединения,включающих три таблицы, выдают разныерезультаты:

SELECT * FROM table1

LEFT OUTER JOIN table2

LEFT OUTER JOIN table3

и

SELECT * FROM table2

LEFT OUTER JOIN table3

LEFT OUTER JOIN table1

В стандарте SQL предусмотрено объеди-нение union, которое не сопоставляетстроки в двух таблицах, а генерирует пол-ное внешнее объединение и удаляет со-впадающие строки. Каждая строка в та-ком объединении дополняет столбцы од-ной связанной таблицы значениями nullдля столбцов в другой таблице. Результатвыполнения команды t1 UNION JOIN t2показан в табл. 7.3.

Мы не описываем команду UNION JOIN вспециальном разделе, поскольку ее прак-тическое применение ограничено и мно-гие СУБД ее не поддерживают. Вы може-те создать аналог объединения union с по-мощью полного внешнего объединения:

t1 UNION JOIN t2

которое является эквивалентомt1 FULL OUTER JOIN t2 ON 1 = 2

t1 и t2 – это таблицы, а 1 = 2 – условие,которое всегда ложно. Обратите внима-ние, что UNION JOIN отличается от UNION, ко-торое является командой слияния, а необъединением (см. раздел «Комбинирова-ние строк с помощью оператора UNION»далее в этой главе).

Таблица 7.3. Результат выполнения командыt1 UNION JOIN t2

Все строки t1 NULL

NULL Все строки t2

Создание внешних объединений с помощью команды OUTER JOIN

Page 268: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

268 Выбор данных из нескольких таблиц

Чтобы создать внешние объединения, Mic-rosoft SQL Server использует в предложе-нии WHERE оператор внешнего объедине-ния *. Чтобы создать левое или правоевнешнее объединение, добавьте оператор *слева или справа от оператора сравнения.При работе с внешними объединениямипредложение WHERE менее точно, чем OUTERJOIN, поэтому его использование можетпривести к возникновению некорректныхзапросов. Следующая версия SQL Serverможет не поддерживать операторы *=и =*, поэтому мы приведем всего несколь-ко примеров.

Oracle 8i и более ранних версий не поддер-живает предложение JOIN; вместо него выдолжны использовать WHERE. Oracle 9i иболее поздних версий поддерживает пред-ложение JOIN. Для создания внешнихобъединений Oracle использует операторвнешнего объединения (+) в предложенииWHERE. Добавьте оператор (+) после табли-цы, которую вам нужно расширить (запол-нить нулями). См. примеры в этом разделе.

Page 269: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

269

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

Листинг 7.25. Показать список городов, в которыхживут авторы и расположены издательства.Результат исполнения листинга см. на рис. 7.25

SELECT a.au_fname, a.au_lname, a.city

FROM authors a;

SELECT p.pub_name, p.city

FROM publishers p;

au_fname au_lname city

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

Sarah Buchman Bronx

Wendy Heydemark Boulder

Hallie Hull San Francisco

Klee Hull San Francisco

Christian Kells New York

Kellsey Palo Alto

Paddy O'Furniture Sarasota

pub_name city

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

Abatis Publishers New York

Core Dump Books San Francisco

Schadenfreude Press Hamburg

Tenterhooks Press Berkley

Рис. 7.25. Результат выполнения листинга 7.25

Создание внешних объединений с помощью команды OUTER JOIN

Page 270: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

270 Выбор данных из нескольких таблиц

Листинг 7.26 создает внутреннее объедине-ние для столбцов city в таблицах authorsи publishers. Результат исполнения пока-зан на рис. 7.26. Здесь отображен списоктолько тех авторов, которые живут в горо-дах, где расположены издательства. Выможете сравнить результат этого внутрен-него объединения с результатами внутрен-них объединений в трех последующихпримерах.

При использовании синтаксиса WHERE ли-стинг 7.26 будет выглядеть так:

SELECT a.au_fname, a.au_lname,

p.pub_name

FROM authors a, publishers p

WHERE a.city = p.city;

Листинг 7.26. Отобразить список авторов,которые живут в городах, где расположеныиздательства. Результат показан на рис. 7.26

SELECT a.au_fname, a.au_lname, p.pub_name

FROM authors a

INNER JOIN publishers p

ON a.city = p.city;

au_fname au_lname pub_name

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

Hallie Hull Core Dump Books

Klee Hull Core Dump Books

Christian Kells Abatis Publishers

Рис. 7.26. Результат выполнения листинга 7.26

Page 271: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

271

Листинг 7.27 использует левое внешнееобъединение, чтобы включить в результатвсех авторов, независимо от того, нахо-дятся ли издательства в городах их про-живания. Результат исполнения см. нарис. 7.27.

Чтобы запустить листинг 7.27 в MicrosoftSQL Server с использованием предложенияWHERE, введите:

SELECT a.au_fname, a.au_lname,

p.pub_name

FROM authors a, publishers p

WHERE a.city *= p.city

ORDER BY p.pub_name ASC,

a.au_lname ASC, a.au_fname ASC;

Чтобы запустить листинг 7.27 в Oracle 8i,введите:

SELECT a.au_fname, a.au_lname,

p.pub_name

FROM authors a, publishers p

WHERE a.city = p.city (+)

ORDER BY p.pub_name ASC,

a.au_lname ASC, a.au_fname ASC;

Листинг 7.27. Это левое внешнее объединениевключает в результат все строки, независимоот того, есть ли соответствие в столбце cityтаблицы publishers. Результат исполнениясм. на рис. 7.27

SELECT a.au_fname, a.au_lname, p.pub_name

FROM authors a

LEFT OUTER JOIN publishers p

ON a.city = p.city

ORDER BY p.pub_name ASC,

a.au_lname ASC, a.au_fname ASC;

au_fname au_lname pub_name

-------- ----------- -----------------Sarah Buchman NULL

Wendy Heydemark NULLKellsey NULL

Paddy O'Furniture NULLChristian Kells Abatis Publishers

Hollie Hull Core Dump BooksKlee Hull Core Dump Books

Рис. 7.27. Результат выполнения листинга 7.27.Обратите внимание, что для четырех авторовв списке нет соответствий, поэтому строкиc этими авторами в столбце pub_name содержатзначение null

Создание внешних объединений с помощью команды OUTER JOIN

Page 272: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

272 Выбор данных из нескольких таблиц

Листинг 7.28 использует правое внешнееобъединение, чтобы включить в результатвсе издательства, независимо от того, про-живают ли авторы в городах, где находятсяиздательства. Результат исполнения пред-ставлен на рис. 7.28.

Чтобы запустить листинг 7.28 в MicrosoftSQL Server с использованием синтаксисаWHERE, введите:

SELECT a.au_fname, a.au_lname,

p.pub_name

FROM authors a, publishers p

WHERE a.city =* p.city

ORDER BY p.pub_name ASC,

a.au_lname ASC, a.au_fname ASC;

Чтобы запустить листинг 7.28 в Oracle 8i,введите:

SELECT a.au_fname, a.au_lname,

p.pub_name

FROM authors a, publishers p

WHERE a.city (+) = p.city

ORDER BY p.pub_name ASC,

a.au_lname ASC, a.au_fname ASC;

Листинг 7.28. Это правое внешнее объединениевключает в результат все строки таблицыpublishers, независимо от того, есть лисоответствие в столбце city таблицы authors.Результат исполнения см. на рис. 7.28

SELECT a.au_fname, a.au_lname, p.pub_name

FROM authors a

RIGHT OUTER JOIN publishers p

ON a.city = p.city

ORDER BY p.pub_name ASC,

a.au_lname ASC, a.au_fname ASC;

au_fname au_lname pub_name

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

Christian Kells Abatis Publishers

Hollie Hull Core Dump Books

Klee Hull Core Dump Books

NULL NULL Schadenfreude Press

NULL NULL Tenterhooks Press

Рис. 7.28. Результат выполнения листинга 7.28.Обратите внимание, что для двух издательствв списке нет соответствий, поэтому в столбцахau_fname и au_lname напротив нихпредставлены нули

Page 273: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

273

Листинг 7.29 использует полное внешнееобъединение, чтобы включить в результатвсе издательства и всех авторов, независи-мо от того, совпадает ли город прожива-ния авторов с городом, где находится из-дательство. Результат исполнения листин-га показан на рис. 7.29.

Листинг 7.29. Это полное внешнее объединениевключает в результат все строки таблицpublishers и authors, независимо от того, естьли соответствия в столбцах city. Результатисполнения листинга см. на рис. 7.29

SELECT a.au_fname, a.au_lname, p.pub_name

FROM authors a

FULL OUTER JOIN publishers p

ON a.city = p.city

ORDER BY p.pub_name ASC,

a.au_lname ASC, a.au_fname ASC;

au_fname au_lname pub_name

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

Sarah Buchman NULL

Wendy Heydemark NULL

Kellsey NULL

Paddy O'Furniture NULL

Christian Kells Abatis Publishers

Hallie Hull Core Dump Books

Klee Hull Core Dump Books

NULL NULL Schadenfreude Press

NULL NULL Tenterhooks Press

Рис. 7.29. Результат выполнения листинга 7.29содержит девять строк: четыре строки с авторами,у которых нет соответствий в строках таблицыpublishers, три строки, в которых автори издательство находятся в одном городе,и две строки для издательств, у которых нетсоответствий в столбце city таблицы authors

Создание внешних объединений с помощью команды OUTER JOIN

Page 274: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

274 Выбор данных из нескольких таблиц

В Microsoft SQL Server для создания пол-ного внешнего объединения вы не можетепоместить оператор * с двух сторон отоператора сравнения. Вместо этого вы мо-жете совместить левое и правое внешниеобъединения (см. раздел «Комбинирова-ние строк с помощью оператора UNION»далее в этой главе). Чтобы запустить лис-тинг 7.29 в SQL Server с использованиемсинтаксиса WHERE, введите:

SELECT a.au_fname, a.au_lname,

p.pub_name

FROM authors a, publishers p

WHERE a.city *= p.city

UNION ALL

SELECT a.au_fname, a.au_lname,

p.pub_name

FROM authors a, publishers p

WHERE a.city =* p.city

AND a.city IS NULL;

В Oracle для создания полного внешнегообъединения вы не можете поместить опе-ратор (+) с двух сторон от оператора срав-нения. Вместо этого вы можете совместитьлевое и правое внешние объединения (см.раздел «Комбинирование строк c помощьюоператора UNION» далее в этой главе). Что-бы запустить листинг 7.29 в Oracle 8i, вве-дите:

SELECT a.au_fname, a.au_lname,

p.pub_name

FROM authors a, publishers p

WHERE a.city = p.city (+)

UNION ALL

SELECT a.au_fname, a.au_lname,

p.pub_name

FROM authors a, publishers p

WHERE a.city (+) = p.city

AND a.city IS NULL;

Microsoft Access и MySQL не поддержива-ют полные внешние объединения, но выможете совмещать левое и правое внеш-ние объединения (см. раздел «Комбини-рование строк с помощью оператора UNI-ON» далее в этой главе). В следующемпримере первая таблица команды UNIONпредставляет собой левое внешнее объ-единение, которое считывает все строкитаблицы authors и соответствующиестроки столбца city таблицыpublishers. Вторая таблица командыUNION представляет собой правое внеш-нее объединение, которое считываеттолько те строки таблицы publishers,для которых нет соответствий. Чтобы за-пустить листинг 7.29 в Access или MySQL,введите:

SELECT a.au_fname, a.au_lname,

p.pub_name

FROM authors a,

LEFT OUTER JOIN publishers p

ON a.city = p.city

UNION ALL

SELECT a.au_fname, a.au_lname,

p.pub_name

FROM authors a,

RIGHT OUTER JOIN publishers p

ON a.city = p.city

WHERE a.city IS NULL;

Page 275: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

275

Листинг 7.30 использует левое внешнееобъединение, чтобы отобразить количе-ство книг, написанных авторами (в томчисле в соавторстве). Результат исполне-ния представлен на рис. 7.30. Обратитевнимание, что, в отличие от листинга 7.11,автор A07 (Paddy O’Furniture) появится врезультате, хотя он не написал ни однойкниги.

Чтобы запустить листинг 7.30 в Oracle 8i,введите:

SELECT a.au_id,

COUNT(ta.title_id)

AS "Num books"

FROM authors a, title_authors ta

WHERE a.au_id = ta.au_id (+)

GROUP BY a.au_id

ORDER BY a.au_id ASC;

Листинг 7.30. Показать количество книг,написанных авторами (в том числе в соавторстве),включая тех, кто не написал ни одной книги.Результат исполнения см. на рис. 7.30

SELECT

a.au_id,

COUNT(ta.title_id) AS "Num books"

FROM authors a

LEFT OUTER JOIN title_authors ta

ON a.au_id = ta.au_id

GROUP BY a.au_id

ORDER BY a.au_id ASC;

au_id Num books

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

A01 3

A02 4

A03 2

A04 4

A05 1

A06 3

A07 0

Рис. 7.30. Результат выполнения листинга 7.30

Создание внешних объединений с помощью команды OUTER JOIN

Page 276: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

276 Выбор данных из нескольких таблиц

Листинг 7.31 использует условие WHERE,чтобы выполнить тестирование на нали-чие нулей и отобразить только тех авторов,которые не написали ни одной книги.Результат исполнения показан на рис. 7.31.

Чтобы запустить листинг 7.31 в Oracle 8i,введите:

SELECT a.au_id, a.au_fname,

a.au_lname

FROM authors a, title_authors ta

WHERE a.au_id = ta.au_id (+)

AND ta.au_id IS NULL;

Листинг 7.31. Отобразить только тех авторов,которые не написали ни одной книги(в том числе в соавторстве). Результатисполнения см. на рис. 7.31

SELECT a.au_id, a.au_fname, a.au_lname

FROM authors a

LEFT OUTER JOIN title_authors ta

ON a.au_id = ta.au_id

WHERE ta.au_id IS NULL;

au_id au_fname au_lname

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

A07 Paddy O'Furniture

Рис. 7.31. Результат выполнения листинга 7.31

Page 277: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

277

Листинг 7.32 комбинирует внутреннее объ-единение и левое внешнее объединение,чтобы отобразить авторов и их книги, тира-жи которых превысили 100 000 экземпляров.В этом примере мы сначала отфильтрова-ли результат команды INNER JOIN, а затемс помощью команды OUTER JOIN объедини-ли его с таблицей authors, чтобы отобра-зить все ее строки. Результат исполненияпоказан на рис. 7.32.

Листинг 7.32. Отобразить авторов и их книги,тиражи которых превысили 100 000 экземпляров.Результат исполнения см. на рис. 7.32

SELECT a.au_id, a.au_fname, a.au_lname,

tta.title_id, tta.title_name, tta.sales

FROM authors a

LEFT OUTER JOIN

(SELECT ta.au_id, t.title_id,

t.title_name, t.sales

FROM title_authors ta

INNER JOIN titles t

ON t.title_id = ta.title_id

WHERE sales > 100000) tta

ON a.au_id = tta.au_id

ORDER BY a.au_id ASC, tta.title_id ASC;

au_id au_fname au_lname title_id title_name sales

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

A01 Sarah Buchman NULL NULL NULL

A02 Wendy Heydemark T07 I Blame My Mother 1500200

A02 Wendy Heydemark T12 Spontaneous, Not Annoying 100001

A03 Hallie Hull NULL NULL NULL

A04 Klee Hull T05 Exchange of Platitudes 201440

A04 Klee Hull T07 I Blame My Mother 1500200

A05 Christian Kells NULL NULL NULL

A06 Kellsey NULL NULL NULL

A07 Paddy O'Furniture NULL NULL NULL

Рис. 7.32. Результат выполнения листинга 7.32

Создание внешних объединений с помощью команды OUTER JOIN

Page 278: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

278 Выбор данных из нескольких таблиц

Чтобы запустить листинг 7.32 в Oracle 8i,введите:

SELECT a.au_id, a.au_fname,a.au_lname,

tta.title_id, tta.title_name,tta.sales

FROM authors a,(SELECT ta.au_id, t.title_id,

t.title_name, t.salesFROM title_authors ta,

titles tWHERE t.title_id =

ta.title_idAND sales > 100000) tta

WHERE a.au_id = tta.au_id (+)ORDER BY a.au_id ASC,

tta.title_id ASC;

MySQL 4.0 и более ранних версий не под-держивает подзапросы (см. примечаниеDBMS в разделе «Принципы работы с под-запросами» в главе 8). Работая со сложны-ми запросами, вы можете создать времен-ную таблицу и поместить в нее результатвыполнения подзапроса (см. раздел «Со-здание временной таблицы с помощьюкоманды CREATE TEMPORARY TABLE» в гла-ве 10). Чтобы запустить листинг 7.32 в My-SQL, введите:

CREATE TEMPORARY TABLE tta

SELECT ta.au_id, t.title_id,t.title_name, t.sales

FROM title_authors taINNER JOIN titles t

ON t.title_id = ta.title_idWHERE sales > 100000;

SELECT a.au_id, a.au_fname,a.au_lname, tta.title_id,

tta.title_name, tta.salesFROM authors a

LEFT OUTER JOIN ttaON a.au_id = tta.au_id

ORDER BY a.au_id ASC,tta.title_id ASC;

DROP TABLE tta;

Page 279: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

279

СозданиесамообъединенияСамообъединение – это обычное объеди-нение SQL, которое объединяет таблицус самой собой и считывает строки из неепутем сравнения значений в несколькихстолбцах одной таблицы. Самообъедине-ния часто используются в таблицах с реф-лексивными связями (связи между первич-ными и вторичными ключами в столб-це или в нескольких столбцах таблицыи другими столбцами той же таблицы).За информацией о ключах обращайтеськ разделам «Первичные ключи» и «Внеш-ние ключи» в главе 2.

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

Поле emp_id – это первичный ключ, кото-рый идентифицирует каждого работника,а поле boss_id – начальника этого работни-ка. Каждый из начальников также являет-ся работником, поэтому следите за тем,чтобы данные каждого начальника, кото-рые вы добавляете в таблицу, совпадалис данными работника. Поле boss_id служитвнешним ключом для поля emp_id. Лис-тинг 7.33 использует эту рефлексивнуюсвязь, чтобы сравнить строки в таблице иотобразить имена начальников для всехсотрудников (вам не потребуется исполь-зовать объединение, если нужно извлечьтолько идентификаторы начальников). Ре-зультат исполнения листинга представленна рис. 7.33.

Таблица 7.4. Таблица employees

emp_id emp_name boss_id

E01 Lord Copper NULL

E02 Jocelyn Hitchcock E01

E03 Mr. Satler E01

E04 William Boot E03

E05 Mr. Corker E03

Листинг 7.33. Отобразить имена начальников длявсех сотрудников. Результат исполнения см. нарис. 7.33

SELECT

e1.emp_name AS "Employee name",

e2.emp_name AS "Boss name"

FROM employees e1

INNER JOIN employees e2

ON e1.boss_id = e2.emp_id;

Employee name Boss name

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

Jocelyn Hitchcock Lord Copper

Mr. Salter Lord Copper

William Boot Mr. Salter

Mr. Orker Mr. Salter

Рис. 7.33. Результат выполнения листинга 7.33

Создание самообъединения

Page 280: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

280 Выбор данных из нескольких таблиц

Эта же таблица (employees) появляетсядважды в листинге 7.33 с разными названи-ями (e1 и e2), которые используются, что-бы обозначить столбцы в условии объ-единения:e1. boss_id = e2.emp_id

Как для любого другого объединения, длясамообъединения необходимы две табли-цы, но вместо того чтобы добавлять вто-рую таблицу, вы добавляете копию тойже самой таблицы. Таким образом, выможете сравнить столбец в первой копиитаблицы и столбец во второй копии. Каки для всех таблиц, ваша СУБД комбиниру-ет и считывает те строки таблицы, ко-торые соответствуют условию объеди-нения. Вы не создаете копию таблицы,а объединяете ее с самой собой. Но вам бу-дет легче понять результат, если вы пред-ставите, что имеете дело с двумя копиямиодной таблицы.

Порядок создания самообъединения

Введите:SELECT columns

FROM table [AS] alias1

INNER JOIN table [AS] alias2

ON join_conditions

columns – это список разделенных запяты-ми выражений или названий столбцов изtable; alias1 и alias2 – различные названия,

которые будут использоваться для table вjoin_conditions (см. раздел «Созданиепсевдонимов таблиц с помощью предло-жения AS» ранее в этой главе).join_conditions задает одно или несколь-ко условий объединения, которые долж-ны рассчитываться для каждой пары свя-занных строк. Условие объединения име-ет следующий вид:alias1.column op alias2.column

op может быть любым оператором сравне-ния: =, <>, <, <=, > или >= (см. табл. 4.2 вглаве 4). Вы можете комбинировать не-сколько условий объединения с помощьюAND или OR (см. раздел «Комбинированиеусловий с помощью операторов AND, ORи NOT» в главе 4).

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

Oracle 8i и более ранних версий не под-держивает синтаксис JOIN; советуем ис-пользовать синтаксис WHERE. Oracle 9i иболее поздних версий поддерживает син-таксис JOIN.

Page 281: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

281

Листинг 7.34 использует условие поискаWHERE и самообъединение столбца state,чтобы найти всех авторов, которые живутв одном штате с автором A04 (Klee Hull).Результат исполнения показан на рис. 7.34.

При использовании синтаксиса WHERE ли-стинг 7.34 будет эквивалентен следующе-му листингу:

SELECT a1.au_id, a1.au_fname,

a1.au_lname, a1.state

FROM authors a1, authors a2

WHERE a1.state = a2.state

AND a2.au_id = 'A04';

Самообъединения можно преобразоватьв подзапросы (см. главу 8). При использо-вании подзапроса листинг 7.34 будет экви-валентен следующему листингу:

SELECT au_id, au_fname,

au_lname, state

FROM authors

WHERE state IN

(SELECT state

FROM authors

WHERE au_id = 'A04');

Листинг 7.34. Отобразить всех авторов, которыеживут в одном штате с автором A04 (Klee Hull).Результат исполнения см. на рис. 7.34

SELECT a1.au_id, a1.au_fname,

a1.au_lname, a1.state

FROM authors a1

INNER JOIN authors a2

ON a1.state = a2.state

WHERE a2.au_id = 'A04';

au_id au_fname au_lname state

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

A03 Hallie Hull CA

A04 Klee Hull CA

A06 Kellsey CA

Рис. 7.34. Результат выполнения листинга 7.34

Создание самообъединения

Page 282: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

282 Выбор данных из нескольких таблиц

Листинг 7.35 отображает список биогра-фий, которые продаются лучше. Обрати-те внимание, что условие поиска WHEREдолжно быть type = 'biography' как длятаблицы t1, так и для таблицы t2, по-скольку условие объединения рассматри-вает столбец type как два разных столбца.Результат исполнения показан на рис. 7.35.

При использовании синтаксиса WHERE ли-стинг 7.35 будет эквивалентен следующе-му листингу:

SELECT t1.title_id, t1.sales,

t2.title_id AS "Better seller",

t2.sales AS "Higher sales"

FROM titles t1, titles t2

WHERE t1.sales < t2.sales

AND t1.type = 'biography'

AND t2.type = 'biography'

ORDER BY t1.title_id ASC,

t2.sales ASC;

title_id sales Better seller Higher sales

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

T06 11320 T12 100001

T06 11320 T07 1500200

T12 100001 T07 1500200

Рис. 7.35. Результат выполнения листинга 7.35

Листинг 7.35. Для каждой биографии отобразитьзаголовки и данные о продажах другихбиографий, которые пользуютcя большимспросом. Результат исполнения см. на рис. 7.35

SELECT t1.title_id, t1.sales,

t2.title_id AS "Better seller",

t2.sales AS "Higher sales"

FROM titles t1

INNER JOIN titles t2

ON t1.sales < t2.sales

WHERE t1.type = 'biography'

AND t2.type = 'biography'

ORDER BY t1.title_id ASC, t2.sales ASC;

`

Page 283: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

283

Листинг 7.36 представляет собой самообъ-единение, которое находит все пары авто-ров из штата Нью-Йорк. Результат испол-нения представлен на рис. 7.36.

При использовании синтаксиса WHERE ли-стинг 7.36 будет эквивалентен следующе-му листингу:

SELECT

a1.au_fname, a1.au_lname,

a2.au_fname, a2.au_lname

FROM authors a1, authors a2

WHERE a1.state = a2.state

AND a1.state = 'NY'

ORDER BY a1.au_id ASC,

a2.au_id ASC;

Листинг 7.36. Отобразить все пары авторовиз штата Нью-Йорк. Результат исполнениясм. на рис. 7.36

SELECT

a1.au_fname, a1.au_lname,

a2.au_fname, a2.au_lname

FROM authors a1

INNER JOIN authors a2

ON a1.state = a2.state

WHERE a1.state = 'NY'

ORDER BY a1.au_id ASC, a2.au_id ASC;

au_fname au_lname au_fname au_lname

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

Sarah Buchman Sarah Buchman

Sarah Buchman Christian Kells

Christian Kells Sarah Buchman

Christian Kells Christian Kells

Рис. 7.36. Результат выполнения листинга 7.36

Создание самообъединения

Page 284: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

284 Выбор данных из нескольких таблиц

Первая и четвертая строки на рис. 7.36 –это повторы, так как они показывают, чтоСара Бухман (Sarah Buchman) живет в томже штате, что и Сара Бухман, и аналогич-ную информацию о Кристиане Келлсе(Christian Kells). Чтобы удалить эти стро-ки, мы добавили еще одно условие объе-динения, считывающее только те строки,в которых два автора различаются (см.листинг 7.37 и рис. 7.37).

При использовании синтаксиса WHERE ли-стинг 7.37 будет эквивалентен следующе-му листингу:

SELECT

a1.au_fname, a1.au_lname,

a2.au_fname, a2.au_lname

FROM authors a1, authors a2

WHERE a1.state = a2.state

AND a1.au_id <> a2.au_id

AND a1.state = 'NY'

ORDER BY a1.au_id ASC,

a2.au_id ASC;

Листинг 7.37. Отобразить пары разных авторов изштата Нью-Йорк. Результат исполнениясм. на рис. 7.37

SELECT

a1.au_fname, a1.au_lname,

a2.au_fname, a2.au_lname

FROM authors a1

INNER JOIN authors a2

ON a1.state = a2.state

AND a1.au_id <> a2.au_id

WHERE a1.state = 'NY'

ORDER BY a1.au_id ASC, a2.au_id ASC;

au_fname au_lname au_fname au_lname

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

Sarah Buchman Christian Kells

Christian Kells Sarah Buchman

Рис. 7.37. Результат выполнения листинга 7.37

Page 285: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

285

Но даже результат на рис. 7.37 не являет-ся оптимальным, так как две строки по-вторяют друг друга. Первая строка сооб-щает, что Сара Бухман живет в том жештате, что и Кристиан Келлс, а вторая –дублирует эту информацию. Чтобы избе-жать повторений, в условии второго объ-единения заменим оператор сравнения «не-равно» на «меньше чем» (см. листинг 7.38и рис. 7.38).

При использовании синтаксиса WHERE ли-стинг 7.38 будет эквивалентен следующе-му листингу:

SELECT

a1.au_fname, a1.au_lname,

a2.au_fname, a2.au_lname

FROM authors a1, authors a2

WHERE a1.state = a2.state

AND a1.au_id < a2.au_id

AND a1.state = 'NY'

ORDER BY a1.au_id ASC,

a2.au_id ASC;

Листинг 7.38. Отобразить пары разных авторов изштата Нью-Йорк без повторов. Результатисполнения см. на рис. 7.38

SELECT

a1.au_fname, a1.au_lname,

a2.au_fname, a2.au_lname

FROM authors a1

INNER JOIN authors a2

ON a1.state = a2.state

AND a1.au_id < a2.au_id

WHERE a1.state = 'NY'

ORDER BY a1.au_id ASC, a2.au_id ASC;

Создание самообъединения

au_fname au_lname au_fname au_lname

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

Sarah Buchman Christian Kells

Рис. 7.38. Результат выполнения листинга 7.38

Page 286: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

286 Выбор данных из нескольких таблиц

Комбинирование строкс помощью оператораUNIONВ последующих трех разделах мы рас-скажем об операторах UNION, INTERSECTи EXCEPT, предназначенных для преобразо-вания результатов двух команд SELECTв единый результат. Вы можете смеши-вать эти операторы так, чтобы комбини-ровать несколько таблиц, а не только две.Оператор UNION широко используется; двадругих оператора встречаются реже.Оператор UNION комбинирует результатыдвух запросов в один результат, которыйобъединяет строки, считанные двумя за-просами (это действие отличается от ком-бинирования столбцов из двух таблиц).Выражение UNION удаляет из результатаповторяющиеся строки; выражение UNIONALL сохраняет повторы. Операторы UNIONпросты, но имеют ряд ограничений:

списки столбцов команды SELECT в двухзапросах должны включать одинаковоечисло столбцов (названий столбцов,арифметических выражений, функций ит.д.);соответствующие столбцы в двух за-просах должны быть заданы в одина-ковом порядке;если имена соответствующих столбцовсовпадают, то их название будет ис-пользовано в результате. Если назва-ния соответствующих столбцов разли-чаются, то СУБД самостоятельно опре-делит имя столбца в результате. Боль-шинство СУБД заимствуют названия

столбцов для результата из первогоотдельного запроса в команде UNION.Если вы желаете переименовать стол-бец в результате, используйте предло-жение AS в первом запросе (см. раздел«Создание псевдонимов столбцов с по-мощью предложения AS» в главе 4);

предложение ORDER BY может исполь-зоваться только в последнем запросекоманды UNION. Эта сортировка при-меняется к конечному результату пос-ле комбинирования. Так как названиястолбцов в итоге различаются для раз-ных СУБД, то для указания порядкасортировки проще всего использоватьотносительные положения столбцов(см. раздел «Сортировка строк с по-мощью предложения ORDER BY» в гла-ве 4);

можно задавать предложения GROUP BY иHAVING только в отдельных запросах; ихнельзя использовать для изменения ко-нечного результата.

Комбинирование строк

Введите:select_statement1

UNION [ALL]

select statement2;

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

Page 287: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

287

Листинг 7.39 отображает список штатов,в которых живут авторы и находятся из-дательства. По умолчанию UNION удаляетиз результата повторяющиеся строки. Ре-зультат исполнения показан на рис. 7.39.

Листинг 7.39. Отобразить список штатов,в которых живут авторы и находятся издательства.Результат исполнения см. на рис. 7.39

SELECT state FROM authors

UNION

SELECT state FROM publishers;

state

------

NULL

CA

CO

FL

NY

Рис. 7.39. Результат выполнения листинга 7.39

Комбинирование строк с помощью оператора UNION

Page 288: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

288 Выбор данных из нескольких таблиц

Листинг 7.40 аналогичен листингу 7.39, од-нако использует ключевое слово ALL. По-этому в результат включены все строки,в том числе и повторы (см. рис. 7.40).

Листинг 7.40. Отобразить список штатов,в которых живут авторы и находятся издательства(в том числе повторы). Результат исполнениясм. на рис. 7.40

SELECT state FROM authors

UNION ALL

SELECT state FROM publishers;

state

-----

NY

CO

CA

CA

NY

CA

FL

NY

CA

NULL

CA

Рис. 7.40. Результат выполнения листинга 7.40

Page 289: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

289

Листинг 7.41 отображает список всех авто-ров и издательств. Предложение AS в пер-вом запросе задает названия для столбцовв результате. Для сортировки результатапредложение ORDER BY использует относи-тельное положение столбца вместо егоназвания. Результат исполнения показан нарис. 7.41.

Листинг 7.41. Отобразить список всех авторови издательств. Результат исполнениясм. на рис. 7.41

SELECT au_fname || ' ' || au_lname AS

"Name"

FROM authors

UNION

SELECT pub_name

FROM publishers

ORDER BY 1 ASC;

Name

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

Kellsey

Abatis Publishers

Christian Kells

Core Dump Books

Hallie Hull

Klee Hull

Paddy O'Furniture

Sarah Buchman

Schadenfreude Press

Tenterhooks Press

Wendy Heydemark

Рис. 7.41. Результат выполнения листинга 7.41

Комбинирование строк с помощью оператора UNION

Page 290: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

290 Выбор данных из нескольких таблиц

Листинг 7.42 расширяет листинг 7.41 и за-дает дополнительный столбец Type, с по-мощью которого определяется, из какойтаблицы была считана каждая строка.Условие WHERE считывает только авторови издательства из штата Нью-Йорк. Ре-зультат исполнения показан на рис. 7.42.

Листинг 7.42. Отобразить список всех авторов ииздательств из штата Нью-Йорк, отсортировав ихпо типу и названию. Результат исполнения см. нарис. 7.42

SELECT

'author' AS "Type",

au_fname || ' ' || au_lname AS "Name",

state

FROM authors

WHERE state = 'NY'

UNION

SELECT

'publisher',

pub_name,

state

FROM publishers

WHERE state = 'NY'

ORDER BY 1 ASC, 2 ASC;

Type Name state

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

author Christian Kells NY

author Sarah Buchman NY

publisher Abatis Publishers NY

Рис. 7.42. Результат выполнения листинга 7.42

Page 291: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

291

Листинг 7.43 добавляет третий запрос клистингу 7.42, чтобы считать также назва-ния книг, опубликованных в штате Нью-Йорк. Результат исполнения показан нарис. 7.43.

Листинг 7.43. Отобразить список всех авторови издательств из штата Нью-Йорк, а такженазваний книг, опубликованных в этом штате,отсортировав их по типу и названию. Результатисполнения см. на рис. 7.43

SELECT

'author' AS "Type",

au_fname || ' ' || au_lname AS "Name"

FROM authors

WHERE state = 'NY'

UNION

SELECT

'publisher',

pub_name

FROM publishers

WHERE state = 'NY'

UNION

SELECT

'title',

title_name

FROM titles t

INNER JOIN publishers p

ON t.pub_id = p.pub_id

WHERE p.state = 'NY'

ORDER BY 1 ASC, 2 ASC;

Комбинирование строк с помощью оператора UNION

Type Name

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

author Christian Kells

author Sarah Buchman

publisher Abatis Publishers

title 1977!

title How About Never?

title Not Without My Faberge Egg

title Spontaneous, Not Annoying

Рис. 7.43. Результат выполнения листинга 7.43

Page 292: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

292 Выбор данных из нескольких таблиц

Листинг 7.44 аналогичен листингу 7.43,только отображает количество авторов,издательств и книг в штате Нью-Йорк. Ре-зультат исполнения листинга представленна рис. 7.44.

Листинг 7.44. Отобразить количество авторов,издательств из штата Нью-Йорк и книг,опубликованных в этом штате, отсортировав ихпо типу. Результат исполнения см. на рис. 7.44

SELECT

'author' AS "Type",

COUNT(au_id) AS "Count"

FROM authors

WHERE state = 'NY'

UNION

SELECT

'publisher',

COUNT(pub_id)

FROM publishers

WHERE state = 'NY'

UNION

SELECT

'title',

COUNT(title_id)

FROM titles t

INNER JOIN publishers p

ON t.pub_id = p.pub_id

WHERE p.state = 'NY'

ORDER BY 1 ASC;

Type Count

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

author 2

publisher 1

title 4

Рис. 7.44. Результат выполнения листинга 7.44

Page 293: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

293

В листинге 7.45 мы повторяем листинг 5.30.Но вместо условия CASE для измененияцен на книги используем несколько за-просов, результат выполнения которыхобъединен с помощью команды UNION(см. рис. 7.45).

Листинг 7.45. Повысить цены на книги по историина 10%, а на книги по психологии – на 20%,не меняя цены на другие книги. Результатисполнения листинга показан на рис. 7.45

SELECT title_id, type, price,

price * 1.10 AS "New price"

FROM titles

WHERE type = 'history'

UNION

SELECT title_id, type, price, price * 1.20

FROM titles

WHERE type = 'psychology'

UNION

SELECT title_id, type, price, price

FROM titles

WHERE type NOT IN ('psychology','history')

ORDER BY type ASC, title_id ASC;

Комбинирование строк с помощью оператора UNION

title_id type price New price

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

T06 biography 19.95 19.95

T07 biography 23.95 23.95

T10 biography NULL NULL

T12 biography 12.99 12.99

T08 children 10.00 10.00

T09 children 13.95 13.95

T03 computer 39.95 39.95

T01 history 21.99 24.19

T02 history 19.95 21.95

T13 history 29.99 32.99

T04 psychology 12.99 15.59

T05 psychology 6.95 8.34

T11 psychology 7.99 9.59

Рис. 7.45. Результат выполнения листинга 7.45

Page 294: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

294 Выбор данных из нескольких таблиц

UNION является коммутативной командой:A UNION B – это то же самое, что B UNION A.

В SQL операция INTERSECT имеет болеевысокий приоритет по сравнению с UNIONи EXCEPT, но приоритеты для вашей СУБДмогут быть другими. Чтобы задать порядокрасчета в запросах со смешанными опера-торами, пользуйтесь круглыми скобками(см. раздел «Определение последователь-ности вычисления» в главе 5).

Для объединения в одной команде UNIONи UNION ALL пользуйтесь круглыми скоб-ками, чтобы задать порядок расчетов. Да-вайте рассмотрим два примера:

SELECT * FROM table1

UNION ALL

(SELECT * FROM table2

UNION

SELECT * FROM table3);

и

(SELECT * FROM table1

UNION ALL

SELECT * FROM table2)

UNION

SELECT * FROM table3;

Первая команда удаляет повторы в объеди-нении table2 и table3, но не удаляет ихв результате и table1. Вторая командасохраняет повторы в объединении table1и table2, но удаляет их в последующемобъединении с table3, поэтому ALL невлияет на конечный результат команды.

Результат исполнения команды UNION мо-жет быть отсортирован, даже если вы незадали предложение ORDER BY, так какваша СУБД выполняет внутреннюю сорти-ровку, чтобы идентифицировать и удалитьповторяющиеся строки (результаты ко-манды UNION ALL не подвергаются внут-ренней сортировке).

В Microsoft Access и Microsoft SQL Serverиспользуйте символ «+», чтобы связатьстроки (см. раздел «Объединение строкс помощью оператора ||» в главе 5). Что-бы запустить листинги 7.41–7.43 в Accessили SQL Server, измените выражения свя-зи следующим образом:

au_fname + ' ' + au_lname

В MySQL пользуйтесь командой CONCAT(), чтобы связать строки (см. раздел«Объединение строк с помощью операто-ра ||» в главе 5). Чтобы запустить листин-ги 7.41–7.43 в MySQL, измените выраже-ния связи следующим образом:

CONCAT(au_fname, ' ', au_lname)

В PostgreSQL преобразуйте числа с пла-вающей запятой в листинге 7.45 в типDECIMAL (см. раздел «Преобразование ти-пов данных с помощью функции CAST()»в главе 5). Чтобы запустить листинг 7.45в PostgreSQL, замените расчеты новых ценна следующие:

price * CAST((1.10) AS DECIMAL)

price * CAST((1.10) AS DECIMAL)

Page 295: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

295

Поиск общих строкс помощью командыINTERSECTКоманда INTERSECT преобразует результатыдвух запросов в один результат, которыйвключает все общие строки в результатахвыполнения двух запросов. Пересеченияимеют такие же ограничения, как и объ-единения union (см. раздел «Комбинирова-ние строк с помощью оператора UNION»ранее в этой главе).

Поиск общих строк

Введите:select_statement1

INTERSECT

select_statement2;

select_statement1 и select_statement2 – этокоманды SELECT. Вам необходимо задатьколичество и порядок столбцов для двухкоманд, а тип данных в соответствующихстолбцах должен быть совместимым. По-вторяющиеся строки будут удалены из ре-зультата.Листинг 7.46 использует команду INTER-SECT, чтобы отобразить список городов,в которых живут авторы и находятся из-дательства. Результат исполнения показанна рис. 7.46.

INTERSECT является коммутативной ко-мандой: A INTERSECT B – это то же самое,что B INTERSECT A.

В SQL у INTERSECT более высокий приори-тет по сравнению с UNION и EXCEPT, ноприоритеты в вашей СУБД могут бытьдругими. Чтобы задать порядок расчетав запросах со смешанными операторами,пользуйтесь круглыми скобками (см. раз-дел «Определение последовательности вы-числения» в главе 5).

Листинг 7.46. Отобразить список городов,в которых живут авторы и находятсяиздательства. Результат исполнениясм. на рис. 7.46

SELECT city

FROM authors

INTERSECT

SELECT city

FROM publishers;

city

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

New York

San Francisco

Рис. 7.46. Результат выполнения листинга 7.46

Поиск общих строк с помощью команды INTERSECT

Page 296: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

296 Выбор данных из нескольких таблиц

Очень полезно воспринимать UNION каклогический вариант OR, а INTERSECT – каклогический вариант AND (см. раздел «Ком-бинирование и отмена условий с помощьюоператоров AND, OR и NOT» в главе 4). На-пример, если вы желаете узнать, какие то-вары поставляются продавцом A, а какие –продавцом B, введите:

SELECT product_id

FROM vendor_a_product_list

UNION

SELECT product_id

FROM vendor_b_product_list;

Если вы желаете узнать, какие товары по-ставляются продавцами A и B, введите:

SELECT product_id

FROM vendor_a_product_list

INTERSECT

SELECT product_id

FROM vendor_b_product_list;

Если ваша СУБД не поддерживает командуINTERSECT, можно эмулировать ее с помо-щью INNER JOIN или подзапроса с исполь-зованием предложения EXISTS (см. главу 8).Каждая из последующих команд эквивален-тна листингу 7.46 (внутреннее объединение):

SELECT DISTINCT authors.city

FROM authors

INNER JOIN publishers

ON authors.city +

publishers.city;

или (с использованием предложенияEXISTS):

SELECT DISTINCT city

FROM authors

WHERE EXISTS

(SELECT *

FROM publishers

WHERE authors.city =

publishers.city);

Microsoft Access, Microsoft SQL Server и MySQL не

поддерживают команду INTERSECT. Чтобызапустить листинг 7.46 в Access, SQL Serverили MySQL, используйте эквивалентныйзапрос INNER JOIN, о котором мы расска-зали в предыдущем примере.

Page 297: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

297

Поиск разных строкс помощью командыEXCEPTКоманда EXCEPT (по-другому ее называютотличием) преобразует результаты двух за-просов в один результат, который включа-ет только строки первого запроса. Раз-ница между INTERSECT и EXCEPT состоитв том, что A INTERSECT B включает строкииз таблицы A, которые повторяются в таб-лице B, а A EXCEPT B включает строки изтаблицы A, которые не повторяются в таб-лице B. Отличия имеют такие же ограниче-ния, как и объединения union (см. раздел«Комбинирование строк с помощью опе-ратора UNION» ранее в этой главе).

Поиск разных строкВведите:select_statement1

EXCEPT

select_statement2;

Здесь select_statement1 и select_state-ment2 – это предложения SELECT. Вам необ-ходимо задать количество и порядок стол-бцов для двух команд, а тип данных в со-ответствующих столбцах должен быть со-вместимым. Повторяющиеся строки будутудалены из результата.Листинг 7.47 использует команду EXCEPT,чтобы отобразить список городов, в кото-рых живут авторы, но нет издательств. Ре-зультат исполнения показан на рис. 7.47.

В отличие от команд UNION и INTERSECT,команда EXCEPT не является коммутатив-ной: A EXCEPT B – это не то же самое, чтоB EXCEPT A.

Листинг 7.47. Отобразить список городов,в которых живут авторы, но нет издательств.Результат исполнения см. на рис. 7.47

SELECT city

FROM authors

EXCEPT

SELECT city

FROM publishers;

city

---------

Boulder

Bronx

Palo Alto

Sarasota

Рис. 7.47. Результат выполнения листинга 7.47

Поиск разных строк с помощью команды EXCEPT

Page 298: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

298 Выбор данных из нескольких таблиц

В SQL у INTERSECT более высокий при-оритет по сравнению с UNION и EXCEPT, ноприоритеты именно в вашей СУБД могутбыть другими. Чтобы задать порядок рас-чета в запросах со смешанными операто-рами, пользуйтесь круглыми скобками (см.раздел «Определение последовательностивычисления» в главе 5).

Если ваша СУБД не поддерживает EXCEPT,можете продублировать ее с помощьювнешнего запроса, запроса NOT EXISTSили NOT IN (см. главу 8). Каждая из после-дующих команд эквивалентна листингу7.47 (внешнее объединение):

SELECT DISTINCT a.city

FROM authors a

LEFT OUTER JOIN publishers p

ON a.city = p.city

WHERE p.city IS NULL;

или (запрос NOT EXISTS)

SELECT DISTINCT city

FROM authors

WHERE NOT EXISTS

(SELECT *

FROM publishers

WHERE authors.city =

publishers.city);

или (запрос NOT IN)

SELECT DISTINCT city

FROM authors

WHERE city NOT IN

(SELECT city

FROM publishers);

СУБД Microsoft Access, Microsoft SQL Serverи MySQL не поддерживают команду EXCEPT.Чтобы запустить листинг 7.47 в Access,SQL Server или MySQL, используйте экви-валентный запрос OUTER JOIN, о котороммы рассказали в предыдущем запросе.

В Oracle оператор EXCEPT называется MINUS.Чтобы запустить листинг 7.47 в Oracle,введите:

SELECT city FROM authors

MINUS

SELECT city FROM publishers;

Page 299: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

88888Подзапросы

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

предложении SELECT, FROM, WHERE илиHAVING другой команды SELECT;другом подзапросе;команде INSERT, UPDATE или DELETE.

Допускается использовать подзапрос в лю-бом доступном месте в SQL-команде. Одна-ко ваша СУБД может запрещать распола-гать подзапросы в ряде мест. В этой главемы расскажем о подзапросах, которые раз-мещаются в команде SELECT или других зап-росах. В главе 9 будет рассказано о подзап-росах, которые используются в командахINSERT, UPDATE и DELETE.

Page 300: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

300 Подзапросы

Принципы работыс подзапросамиВ этом разделе мы рассмотрим ряд терми-нов и приведем пример команды SELECT,которая включает простой подзапрос.В следующих разделах будет рассказаноо разных типах запросов, а также ихструктуре и семантике.Предположим, что вы желаете привестисписок издателей биографий. Первый спо-соб сделать это состоит в том, чтобы напи-сать два запроса: один будет считывать ин-формацию обо всех издателях биографий(см. листинг 8.1 и рис. 8.1), а другой – при-водить список результатов первого запро-са (см. листинг 8.2 и рис. 8.2).Более удобный способ – использование ко-манды INNER JOIN (см. листинг 8.3 и рис. 8.3),о котором рассказывается в разделе «Созда-ние внутреннего объединения с помощьюкоманды INNER JOIN» в главе 7.Также вы можете использовать подзапрос(см. листинг 8.4 и рис. 8.4), отмеченныйполужирным шрифтом. Подзапрос ещеназывается внутренним запросом, а ко-манда, которая включает такой запрос, –внешним запросом. Другими словами, вло-женный запрос является внутренним длявнешнего запроса. Помните, что подзап-рос может находиться в другом подзапро-се; поэтому понятия «внешний» и «внут-ренний» являются относительными длякоманд с несколькими запросами.В разделе «Простые и сложные запросы»далее в этой главе мы опишем, как СУБД

Листинг 8.1. Отобразить список издателейбиографий. Результаты показаны на рис. 8.1

SELECT pub_id

FROM titles

WHERE type = 'biography';

Листинг 8.2. Этот запрос использует результатлистинга 8.1, чтобы отобразить названия всехиздателей биографий. Результаты см. на рис. 8.2

SELECT pub_name

FROM publishers

WHERE pub_id IN ('P01', 'P03');

pub_id

------

P01

P03

P01

P01

Рис. 8.1. Результат исполнения листинга 8.1.Чтобы привести список только один раз, можетедобавить к пункту SELECT команду DISTINCT(см. раздел «Удаление повторяющихся строкс помощью ключевого слова DISTINCT» в главе 4)

pub_name

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

Abatis Publishers

Schadenfreude Press

Рис. 8.2. Результат выполнения листинга 8.2

Page 301: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

301

выполняет запросы. Пока вам достаточнознать, что в листинге 8.4 СУБД сначалаобрабатывает внутренний запрос (поме-чен жирным шрифтом), затем использу-ет его результат для запуска другого за-проса и получает конечный результат.Ключевое слово IN вводит подзапросы длясписка и работает так же, как слово IN вразделе «Фильтрация с помощью операто-ра IN» в главе 4. Обратите внимание, чтовнутренний запрос в листинге 8.4 такой же,как в листинге 8.1, а внешний запрос – та-кой же, как в листинге 8.2

Вы можете заметить, что термин «подзап-рос» используется для обозначения целойкоманды SQL, которая включает один илинесколько запросов. Чтобы избежать оши-бок, в данной книге подобная терминоло-гия не применяется.

MySQL 4.0 и более ранних версий не поддер-живает подзапросы (хотя MySQL 4.1 должнаих поддерживать). Примеры запросов, опи-санные в данной главе, не будут работатьв MySQL 4.0 и более ранних версий. Вы мо-жете решить эту проблему тремя способа-ми: заменить запрос на объединение (см.раздел «Подзапросы и объединения» далеев этой главе), создать временную таблицудля результатов запроса (см. раздел «Созда-ние временной таблицы с помощью коман-ды CREATE TEMPORARY TABLE» в главе 10и примеры временных таблиц в примеча-нии DBMS к разделу «Создание внешнихобъединений с помощью команды OUTERJOIN» в главе 7, листинг 7.32) либо со-здать имитацию запроса с помощью языкахоста, например Perl, PHP или Java (в этойкниге языки программирования не рассмат-риваются).

Листинг 8.3. С помощью команды INNER JOIN выможете отобразить всех издателей биографий.Результат см. на рис. 8.3

SELECT DISTINCT pub_name

FROM publishers p

INNER JOIN titles t

ON p.pub_id = t.pub_id

WHERE t.type = 'biography';

pub_name

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

Abatis Publishers

Schadenfreude Press

Рис. 8.3. Результат исполнения листинга 8.3

Листинг 8.4. С помощью подзапроса вы можетеотобразить названия издателей биографий.Результат см. на рис. 8.4

SELECT pub_name

FROM publishers

WHERE pub_id IN

(SELECT pub_id

FROM titles

WHERE type = 'biography');

Рис. 8.4. Результат исполнения листинга 8.4

pub_name

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

Abatis Publishers

Schadenfreude Press

Принципы работы с подзапросами

Page 302: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

302 Подзапросы

Структура подзапросовСтруктура подзапросов такая же, как струк-тура обычной команды SELECT (см. главы 4–7). Отличия заключаются в следующем:

вы можете поместить подзапрос в пред-ложение SELECT, FROM, WHERE, HAVING илив другой запрос;подзапрос всегда должен заключаться вкруглые скобки;не заканчивайте подзапрос точкой сзапятой (вам следует завершить точ-кой с запятой команду, которая вклю-чает подзапрос);не помещайте в подзапрос предложе-ние ORDER BY (подзапрос выдает мгно-венный результат, который вы не види-те; сортировка запроса бессмысленна);подзапрос включает одну команду SELECT(вы не можете использовать в качествеподзапроса несколько команд SELECT);подзапрос может использовать столб-ц ыв таблицах, которые приводятся в пред-ложении FROM самого подзапроса илидругого подзапроса;если таблица появляется во внутреннем,а не во внешнем запросе, вы не сможетевключить столбцы этой таблицы в ко-нечный результат (то есть в предложе-ние SELECT внешнего запроса);в зависимости от ситуации подзапросможет использоваться для считыванияограниченного количества строк илистолбцов. В соответствии со стандар-том SQL определяет запрос по числустрок и столбцов, которые он считыва-ет (см. табл. 8.1). Во всех случаях подза-прос может также считывать пустуютаблицу.

Таблица 8.1. Сравнение результатов запросов

Запрос низшего уровня Строки Столбцы

Скалярный запрос 1 1

Запрос строки 1 ≥1

Запрос столбца ≥1 ≥1

Чаще всего вы будете работать с подза-просами в предложении WHERE, которые бу-дут принимать одну из следующих форм:

WHERE test_expr op (subquery);

WHERE test_expr [NOT] IN (subquery);

WHERE test_expr op ALL (subquery);

WHERE test_expr op ANY (subquery);

WHERE [NOT] EXISTS (subquery).

test_expr является буквенным значением,названием столбца, выражением или за-просом; op – это оператор сравнения (=,<>, <, <=, > или >=); subquery – простойили сложный запрос. Далее в этой главе мырассмотрим все формы. Вы также можетеих использовать в предложении HAVING.

SQL не задает максимальное количествоподуровней для запросов, поэтому вашаСУБД присвоит свое ограничение. Как пра-вило, это встроенное ограничение оченьвелико. Например, Microsoft SQL Server до-пускает 32 подуровня.

Page 303: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

303

Подзапросы и объединенияВ разделе «Принципы работы с подзапро-сами» ранее в этой главе (листинги 8.3и 8.4) были рассмотрены два эквивалент-ных запроса: один использует объедине-ние, а другой – подзапрос. Многие запро-сы могут быть преобразованы в объедине-ния. Запрос – это лишь способ сгруппиро-вать одну таблицу с другой, не создаваяпри этом объединения.

Поскольку создавать и исполнять подза-просы иногда затруднительно, вы можетепредпочесть им объединения. Однако не-которые задачи вы сумеете решить толь-ко с помощью подзапросов. Как правило,нет никакой разницы между командой,которая использует подзапрос, и ее экви-валентом, который использует объедине-ние (но бывают и исключения – см. раз-дел «Сравнение эквивалентных запросов»далее в этой главе).

На представленные ниже запросы приве-дены эквивалентные команды, которыеиспользуют подзапросы и объединения, –запрос с использованием в условии опе-ратора IN:

SELECT *

FROM table1

WHERE id IN

(SELECT id FROM table2);

и внутреннее объединение:

SELECT table1.*

FROM table1

INNER JOIN table2

ONtable1.id = table2.id;

В качестве примера см. листинги 8.5a и 8.5б,а также рис. 8.5.

Листинг 8.5a. Эта команда использует подзапрос,чтобы отобразить список всех авторов, которыеживут в городе, где находится издатель. Результатсм. на рис. 8.5

SELECT au_id, city

FROM authors

WHERE city IN

(SELECT city FROM publishers);

au_id city

—---— --------——————

A03 San Francisco

A04 San Francisco

A05 New York

Рис. 8.5. Результат выполнения листингов 8.5a и 8.5б

Листинг 8.5б. Эта команда эквивалентна листингу8.5a, но вместо подзапроса используетвнутреннее объединение. Результат см. на рис. 8.5

SELECT a.au_id, a.city

FROM authors a

INNER JOIN publishers p

ON a.city = p.city;

Подзапросы и объединения

Page 304: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

304 Подзапросы

Следующие три команды тоже эквивален-тны (подзапрос NOT IN):

SELECT *FROM table1

WHERE id NOT IN

(SELECT id FROM table2);

и (подзапрос NOT EXISTS):SELECT table1.*

FROM table1

WHERE NOT EXISTS

(SELECT id FROM table2);

и (внешнее объединение)SELECT table1.*

FROM table1

LEFT OUTER JOIN table2

ON table1.id = table2.id

WHERE table2.id IS NULL;

В качестве примера см. листинги 8.6a, 8.6би 8.6в, а также рис. 8.6. Запросы IN и EXISTSмы рассмотрим далее в этой главе.

Листинг 8.6a. Эта команда использует подзапросc оператором NOT IN, чтобы отобразить списоквсех авторов, которые не участвовали в написаниикниг. Результат см. на рис. 8.6

SELECT au_id, au_fname, au_lname

FROM authors

WHERE au_id NOT IN

(SELECT au_id FROM title_authors);

Листинг 8.6б. Эта команда эквивалентна листингу8.6a, но вместо оператора IN используетоператор EXISTS. Результат см. на рис. 8.6

SELECT au_id, au_fname, au_lname

FROM authors a

WHERE NOT EXISTS

(SELECT *

FROM title_authors ta

WHERE a.au_id = ta.au_id);

Листинг 8.6в. Эта команда эквивалентна листингам8.6a и 8.6б, но вместо подзапроса используетсявнешнее объединение. Результат см. на рис. 8.6

SELECT a.au_id, a.au_fname, a.au_lname

FROM authors a

LEFT OUTER JOIN title_authors ta

ON a.au_id = ta.au_id

WHERE ta.au_id IS NULL;

au_id au_fname au_lname

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

A07 Paddy O'Furniture

Рис. 8.6. Результат выполнения листингов 8.6a,8.5б и 8.6в

Page 305: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

305

В качестве подзапроса вы также можетеиспользовать самообъединение (см. лис-тинги 8.7a, 8.7б и рис. 8.7). За дополни-тельной информацией о самообъединени-ях обращайтесь к разделу «Создание само-объединения» в главе 7.

Можно в любой момент преобразоватьвнутреннее объединение в подзапрос, ноне наоборот. Это происходит потому, чтовнутренние объединения коммутативны;вы можете объединять таблицы A и Bв любом порядке и получать один и тот жерезультат. Подзапросы не имеют подоб-ного свойства (вы можете в любой мо-мент преобразовать внешнее объединениев запрос, хотя внешние объединения не-коммутативны).Листинг 8.7б. Эта команда эквивалентна листингу

8.7a, но вместо подзапроса используетвнутреннее объединение. Результат см. на рис. 8.7

SELECT a1.au_id, a1.au_fname,

a1.au_lname, a1.state

FROM authors a1

INNER JOIN authors a2

ON a1.state = a2.state

WHERE a2.au_id = 'A04';

Листинг 8.7a. Эта команда использует подзапрос,чтобы отобразить список всех авторов, которыеживут с автором A04 (Klee Hull) в одном штате.Результат см. на рис. 8.7

SELECT au_id, au_fname, au_lname, state

FROM authors

WHERE state IN

(SELECT state

FROM authors

WHERE au_id = 'A04');

au_id au_fname au_lname state

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

A03 Hallie Hull CA

A04 Klee Hull CA

A06 Kellsey CA

Рис. 8.7. Результат выполнения листингов 8.7a и 8.7б

Подзапросы и объединения

Page 306: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

306 Подзапросы

Подзапросы очень ценны в том случае,если вам необходимо сравнить среднеезначение с другими значениями (см. лис-тинг 8.8 и рис. 8.8). Если вы не пользуе-тесь подзапросом, вам придется использо-вать две команды SELECT, чтобы отобра-зить все книги, которые имеют самую вы-сокую цену: один запрос, чтобы найтисамую высокую цену, и один запрос, чтобыотобразить все книги, которые продаютсяпо этой цене. За дополнительной инфор-мацией об агрегатных функциях обращай-тесь к главе 6.

Если в результат вы включаете столбцы изнескольких таблиц, следует использоватьобъединения. Листинг 8.5б использует объ-единение, чтобы считать список авторов,которые живут в городе, где находитсяиздательство. Чтобы включить в результатинформацию об издателе, к списку столб-цов команды SELECT мы добавили столбецpub_id (см. листинг 8.9 и рис. 8.9).

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

SELECT a.au_id, a.city, p.pub_id

FROM authors a

WHERE a.city IN

(SELECT p.city FROM publishers p); —неправильно.

MySQL 4.0 и более ранних версий не под-держивает подзапросы. За дополнитель-ной информацией обращайтесь к приме-чанию DBMS в разделе «Принципы рабо-ты с подзапросами» ранее в этой главе.

Листинг 8.8. Эта команда отобразит список всехкниг, цена на которые соответствует самойвысокой стоимости. Результат показан на рис. 8.8

SELECT title_id, price

FROM titles

WHERE price =

(SELECT MAX(price)

FROM titles);

title_id price

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

T03 39.95

Рис. 8.8. Результат выполнения листинга 8.8

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

SELECT a.au_id, a.city, p.pub_id

FROM authors a

INNER JOIN publishers p

ON a.city = p.city;

au_id city pub_id

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

A03 San Francisco P02

A04 San Francisco P02

A05 New York P01

Рис. 8.9. Результат выполнения листинга 8.9

Page 307: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

307

Простые и сложныезапросыМожно использовать два типа запросов –простые и сложные.Простой подзапрос – это запрос, которыйможет рассматриваться независимо от еговнешнего запроса и обрабатывается толь-ко один раз для всей команды. Все приме-ры запросов, которые приводились ранеев этой главе, представляют собой простыезапросы (за исключением листинга 8.6б).Сложный подзапрос не может рассматри-ваться отдельно от внешнего запроса; онпредставляет собой внутренний запрос,который зависит от данных внешнегозапроса. Сложный запрос используетсяв том случае, если команде необходимообработать таблицу во внутреннем запро-се для каждой строки внешнего запроса.Сложные запросы имеют более сложнуюструктуру и другой порядок исполнения,чем простые запросы. Вы можете исполь-зовать сложные запросы для решениязадач, которые нельзя решить с помо-щью простых запросов или объединений.В этом разделе мы приведем пример про-стого и сложного запросов, а затем рас-скажем, как СУБД их исполняет. Последу-ющие разделы данной главы содержатпримеры запросов разного типа.

Простые запросы

Чтобы рассчитать простой запрос, СУБДзапускает внутренний запрос один раз,затем помещает его результат во внешнийзапрос. Простой запрос выполняется до инезависимо от внешнего запроса.Давайте вспомним листинг 8.5a, которыймы рассматривали ранее в этой главе. Ли-стинг 8.10 (идентичен листингу 8.5a) ис-пользует простой запрос, чтобы отобра-зить список всех авторов, которые живут

Простые и сложные запросы

Page 308: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

308 Подзапросы

в том городе, где расположено издатель-ство. Результат исполнения листинга пока-зан на рис. 8.10.СУБД обрабатывает запрос в два этапа ввиде двух разных команд SELECT:

1. Внутренний запрос (простой запрос)считывает список всех городов, в кото-рых находятся издательства (см. лис-тинг 8.11 и рис. 8.11).

2. СУБД передает значения, полученныевнутренним запросом при исполнениипункта 1, во внешний запрос, которыйнаходит информацию об авторах в со-ответствии с городами, где расположе-ны издательства (см. листинг 8.12 ирис. 8.12).

au_id city

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

A03 San Francisco

A04 San Francisco

A05 New York

Рис. 8.10. Результат выполнения листинга 8.9

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

SELECT au_id, city

FROM authors

WHERE city IN

(SELECT city

FROM publishers);

Листинг 8.11. Эта команда отобразит список всехгородов, в которых находятся издательства.Результат показан на рис. 8.11

SELECT city

FROM publishers;

city

——————

New York

San Francisco

Hamburg

Berkley

Рис. 8.11. Результат выполнения листинга 8.11

Page 309: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

309

Сложные запросы

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

отличается от простого порядком ис-полнения, а также количеством испол-нений;

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

исполняется несколько раз – один раздля каждой строки, которая была вы-брана с помощью внешнего запроса;

всегда обращается к таблице, указан-ной в предложении FROM внешнего за-проса;

использует названия столбцов, чтобыссылаться на значения, указанные вовнешнем запросе. При работе со слож-ными запросами такие столбцы назы-ваются корреляционными переменны-ми. За дополнительной информациейпо названиям столбцов и таблиц обра-щайтесь к разделам «Уточнение именстолбцов» и «Создание псевдонимовтаблиц с помощью предложения AS»в главе 7;

базовая структура запроса, который со-держит сложный подзапрос, такова:

SELECT outer columns

FROM outer_table

WHERE outer_column_value IN

(SELECT inner_column

FROM inner_table

WHERE inner_column = outer_column)

Простые и сложные запросы

au_id city

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

A03 San Francisco

A04 San Francisco

A05 New York

Листинг 8.12. Эта команда отобразит список всехавторов, которые живут в одном из городов,считанных с помощью листинга 8.11. Результатсм. на рис. 8.12

SELECT au_id, city

FROM authors

WHERE city IN

('New York', 'San Francisco',

'Hamburg', 'Berkley');

Рис. 8.12. Результат выполнения листинга 8.12

Page 310: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

310 Подзапросы

Исполнение всегда начинается с внешнегозапроса, который выбирает каждую от-дельную строку в outer_table. Для каждойвыбранной строки СУБД исполняет слож-ный внутренний запрос (выделен полу-жирным шрифтом) один раз и помечаетте строки в inner_table, которые соответ-ствуют условию WHERE для значе-ния outer_column_value. СУБД протестиру-ет условие WHERE для отмеченных строкв inner_table и отобразит строки, которыесоответствуют условию. Этот процесс бу-дет продолжаться до тех пор, пока всестроки, выбранные с помощью внешнегозапроса, не будут обработаны.Листинг 8.13 использует сложный запросдля того, чтобы отобразить список книг,уровень продаж которых выше среднегоуровня продаж книг соответствующеготипа (результат исполнения листинга см.на рис. 8.13). candidate (после пунктаtitles во внешнем запросе) и average (пос-ле пункта titles во внутреннем запросе)представляют собой названия таблицы дляtitles. При этом информация может обра-батываться таким образом, как будто онасчитывается из двух разных таблиц (см.раздел «Создание самообъединения» вглаве 7).В листинге 8.13 подзапрос не может рас-сматриваться отдельно от внешнего за-проса. Для запроса необходимо значениепеременной candidate.type. Это значениеявляется корреляционной переменной,которая изменяется, в то время как СУБДпроверяет различные строки в таблицеcandidate. Столбец average.type долженсоответствовать candidate.type во внеш-нем запросе. Средний уровень продажкниг этого типа рассчитывается в подза-просе с использованием типов всех книгиз таблицы внешнего запроса (candidate).Подзапрос рассчитывает средний уровеньпродаж книг этого типа, затем сравнивает

Листинг 8.13. Эта команда отобразит список всехкниг, продажи которых равны или превышаютсредний уровень продаж книг аналогичного типа.Корреляционная переменная candidate.typeопределяет внутреннее условие, которому должнысоответствовать строки внутренней таблицыaverage. Внешнее условие WHERE (sales >=)определяет конечное условие, которому должнысоответствовать строки внутренней таблицыaverage. Результат показан на рис. 8.13

SELECT

candidate.title_id,

candidate.type,

candidate.sales

FROM titles candidate

WHERE sales >=

(SELECT AVG(sales)

FROM titles average

WHERE average.type = candidate.type);

title_id type sales

———----— ——-----——- —--——-

T02 history 9566

T03 computer 25667

T05 psychology 201440

T07 biography 1500200

T09 children 5000

T13 history 10467

Рис. 8.13. Результат выполнения листинга 8.13

Page 311: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

311

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

1. Тип книги в первой строке candidate ис-пользуется в подзапросе для расчетасреднего уровня продаж. Рассмотримстроку для книги T01, типом которой яв-ляется история. При этом значениев столбце type в первой строке таблицыcandidate будет history. Запрос низше-го уровня примет вид:SELECT AVG(sales)

FROM titles average

WHERE average.type = 'history';

При исполнении этого запроса вы по-лучите значение 6,866 – средний уро-вень продаж книг по истории. Вовнешнем запросе уровень продаж кни-ги T01 (566) сравнивается со среднимуровнем продаж книг по истории. Про-дажи книги T01 ниже среднего уровня,поэтому в результате книга T01 показа-на не будет.

2. Далее рассчитывается строка книги T01в таблице candidate.T01 – тоже книга по истории, поэтомузапрос низшего уровня такой же, как впункте 1:SELECT AVG(sales)

FROM titles average

WHERE average.type = 'history';

При исполнении этого запроса вы по-лучите значение 6,866 – средний уро-вень продаж книг по истории. Во внеш-нем запросе уровень продаж книги T02(9,566) сравнивается со средним уров-нем продаж книг по истории. Продажикниги T02 выше среднего уровня, поэто-му в результате книга T02 будет пока-зана.

3. Далее рассчитывается строка книги T03в таблице candidate.T03 – книга по компьютерам, поэтомуподзапрос примет вид:SELECT AVG(sales)

FROM titles average

WHERE average.type = 'computer';

При исполнении этого запроса вы по-лучите значение 25,667 – средний уро-вень продаж книг по компьютерам.Продажи книги T03 (25,667) равны сред-нему уровню (это единственная книга покомпьютерам), поэтому в результатекнига T03 будет показана.

4. СУБД будет повторять расчет до техпор, пока не проверит все строки в таб-лице candidate.

Если вы можете получить одинаковый ре-зультат с использованием как простого, таки сложного запросов, мы рекомендуемпользоваться простым, так как он обраба-тывается быстрее. Листинги 8.14a и 8.14bсодержат два эквивалентных запроса, кото-рые отображают список всех авторов, полу-чающих 100% (1.0) гонорара за книгу. Ли-стинг 8.14a (использует простой запрос)более эффективен, чем листинг 8.14b, ко-торый использует сложный запрос. В про-стом запросе СУБД считывает внутреннюютаблицу title_authors один раз. В слож-ном запросе СУБД должна считатьtitle_authors пять раз – по одному разудля каждой строки во внешней таблицеauthors. Результат исполнения показан нарис. 8.14. Почему мы говорим, что коман-да, использующая сложный запрос, будет,вероятно, работать медленнее, чем эквива-лентная команда, использующая простойзапрос? Ведь очевидно, что сложный запросвыполняет более трудную работу. Причина втом, что ваша СУБД может распознать слож-ный запрос и преобразовать его в эквивален-тный простой запрос перед тем, как испол-нить команду. За дополнительной информа-цией обращайтесь к разделу «Сравнение эк-вивалентных запросов» далее в этой главе.

Простые и сложные запросы

Page 312: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

312 Подзапросы

MySQL 4.0 и более ранних версий не под-держивает подзапросы. За дополнительнойинформацией обращайтесь к примечаниюDBMS в разделе «Принципы работы с под-запросами» ранее в этой главе. В Postgre-SQL вам следует конвертировать числас плавающей запятой в листингах 8.14aи 8.14б в тип DECIMAL (см. раздел «Преоб-разование типов данных с помощью функ-ции CAST()» в главе 5). Чтобы запуститьлистинги 8.14a и 8.14б в PostgreSQL, заме-ните числа с плавающей запятой в каждомлистинге на CAST (1.0 AS DECIMAL).

Листинг 8.14a. Эта команда использует простойзапрос, чтобы отобразить список всех авторов,которые получают 100% (1.0) гонорара за книгу.Результат см. на рис. 8.14

SELECT au_id, au_fname, au_lname

FROM authors

WHERE au_id IN

(SELECT au_id

FROM title_authors

WHERE royalty_share = 1.0);

Листинг 8.14б. Эта команда эквивалентналистингу 8.14a, но вместо простого запросаиспользует сложный. Он, вероятно, будетисполнен медленнее, чем листинг 8.14a.Результат показан на рис. 8.14

SELECT au_id, au_fname, au_lname

FROM authors

WHERE 1.0 IN

(SELECT royalty_share

FROM title_authors

WHERE title_authors.au_id =

authors.au_id);

au_id au_fname au_lname

——--- —-----———- ————------

A01 Sarah Buchman

A02 Wendy Heydemark

A04 Klee Hull

A05 Christian Kells

A06 Kellsey

Рис. 8.14. Результат выполнения листингов 8.14aи 8.14б

Page 313: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

313

Определение названийстолбцов в подзапросахВспомните, что в разделе «Уточнение именстолбцов» главы 7 вы научились присваи-вать название столбцу в точном соответ-ствии с названием таблицы. Это позволяетбезошибочно определять саму таблицу.В командах, которые содержат подзапросы,названия столбцов определяются в соответ-ствии с тем, какая таблица указана в пред-ложении FROM на том же уровне.Например, в листинге 8.15a названия столб-цов указываются точно, что означает:

столбец pub_id в предложении WHEREвнешнего запроса определяется в соот-ветствии с таблицей publishers в пред-ложении FROM внешнего запроса;столбец pub_id в предложении SELECTвнешнего запроса определяется в соот-ветствии с таблицей titles в предложе-нии FROM внешнего запроса.

Листинг 8.15б представляет собой листинг8.15a, в котором столбцы указаны полнос-тью. Результат исполнения листинга см. нарис. 8.15.

Вы не допустите ошибки, если точно обо-значите название таблицы.

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

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

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

Листинг 8.15a. Таблицы publishers и titlesсодержат столбец pub_id, но в этом запросе вамне нужно точно его указывать, так как SQLопределяет названия таблиц. Результат показанна рис. 8.15

SELECT pub_name

FROM publishers

WHERE pub_id IN

(SELECT pub_id

FROM titles

WHERE type = 'biography');

Листинг 8.15б. Этот запрос эквивалентен листингу8.15a, только название столбца pub_id указаноточно. Результат представлен на рис. 8.15

SELECT pub_name

FROM publishers

WHERE publishers.pub_id IN

(SELECT titles.pub_id

FROM titles

WHERE type = 'biography');

pub_name

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

Abatis Publishers

Schadenfreude Press

Рис. 8.15. Результат выполнения листингов 8.15aи 8.15б

MySQL 4.0 и более ранних версий не под-держивает подзапросы. За дополнитель-ной информацией обращайтесь к разделу«Принципы работы с подзапросами» ра-нее в этой главе.

Page 314: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

314 Подзапросы

Значения nullв подзапросахБудьте внимательны относительно значе-ний null; их присутствие существенноусложняет запросы. Если вы не удалитеэти значения вовремя, можете получитьнеправильный результат.Запрос может включать сравнение с NULL(обратитесь к разделу «Значение null» вглаве 3). Значения null не равны междусобой, и вы не можете сказать, соответ-ствует NULL другому значению или нет.Давайте рассмотрим пример, которыйвключает условие NOT IN (см. раздел «Про-верка на вхождение во множество с помо-щью оператора IN» далее в этой главе).Представьте две таблицы, каждая из кото-рых состоит из одного столбца.Первая таблица называется table1:col

---

1

2

Вторая таблица называется table2:col

---

1

2

3

Листинг 8.16. Показать все значения в table2,которых нет в table1. Результат представленна рис. 8.16

SELECT col

FROM table2

WHERE col NOT IN

(SELECT col

FROM table1);

col

---

col

---

3

Рис. 8.16a. Результат выполнения листинга 8.16при условии, что table1 не содержит NULL

Рис. 8.16б. Результат выполнения листинга 8.16при условии, что table1 содержит NULL

Page 315: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

315

Если запустить листинг 8.16, чтобы ото-бразить все значения из table2, которыхнет в table1, получится результат, пока-занный на рис. 8.16a. Давайте добавим вtable1 значение null:col

----

1

2

NULL

При повторном запуске листинга 8.16 мыполучим результат, отображенный нарис. 8.16б (пустую таблицу), что логичес-ки правильно, но не соответствует ожида-емому результату. Почему таблица пуста?Ответ требует некоторых расчетов.Мы можем переместить оператор NOT внезапроса, не изменяя при этом листинг 8.16:SELECT col

FROM table2

WHERE NOT col IN

(SELECT col FROM table2);

Оператор IN определяет, соответствует лизначение в table2 любому значению вtable1. Можно переписать запрос в болеекомпактном виде:SELECT col

FROM table2

WHERE NOT ((col = 1)

OR (col = 2)

OR (col = NULL));

Если использовать законы Моргана (см.табл. 4.6 в главе 4), запрос примет следую-щий вид:SELECT col

FROM table2

WHERE (col <>1)

AND (col <> 2)

AND (col <> NULL);

Последняя строка, col <> NULL, всегда неиз-вестна. Обратитесь к таблице истинностиAND (табл. 4.3 в главе 4), и вы увидите, чтовсе условие поиска для предложения WHEREстановится неизвестным, а оно всегда от-вергает неизвестное условие.

Чтобы исправить листинг 8.16 и избежатьдобавления NULL в table1, дополните за-прос условием IS NOT NULL (см. раздел«Проверка на значение null с помощью опе-ратора IS NULL» в главе 4):

SELECT col

FROM table2

WHERE col NOT IN

(SELECT col

FROM table 1

WHERE col IS NOT NULL);

MySQL 4.0 и более ранних версий не под-держивает подзапросы. За дополнительнойинформацией обращайтесь к примечаниюDBMS в разделе «Принципы работы с под-запросами» ранее в этой главе.

Значения null в подзапросах

Page 316: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

316 Подзапросы

Использованиеподзапросов в качествевыражений в спискезаголовков столбцовИз глав 4, 5 и 6 вы узнали, что предло-жения команды SELECT могут быть отдель-ными буквами, названиями столбцов иливыражениями. SQL также позволяет встав-лять в список предложения SELECT подза-просы.

Запрос, который используется в качествестолбца, должен быть скалярным. Вспом-ните, что скалярный запрос считывает од-но значение, то есть результат расчета од-ной строки или столбца (см. табл. 8.1).В большинстве случаев вам придется ис-пользовать функцию или условие запретав предложении WHERE запроса, чтобы гаран-тировать выборку только одной строки.

Структура списка предложений командыSELECT такая же, как структура всех другихсписков. Исключение состоит в том, что выможете указать в качестве имени столбцазапрос, помещенный в скобки. В листинге8.17 показаны два простых запроса в видезаголовков столбцов. Эти запросы исполь-зуются для того, чтобы отобразить каждуюбиографию, ее цену, среднюю цену всехкниг (не только биографий) и разницув цене между биографией и средней ценойвсех книг. Функция AVG() гарантирует, чтокаждый запрос считает только одно значе-ние. Результат исполнения листинга пред-ставлен на рис. 8.17. Помните, что функцияAVG() игнорирует нули при расчете средне-го значения (см. раздел «Порядок расчетасреднего значения с помощью функцииAVG()» в главе 6).

Листинг 8.17. Отобразить каждую биографию, еецену, среднюю цену всех книг и разницу в ценемежду биографией и средней ценой всех книг.Результат показан на рис. 8.17

SELECT title_id,

price,

(SELECT AVG(price) FROM titles)

AS "AVG(price)",

price - (SELECT AVG(price) FROM titles)

AS "Difference"

FROM titles

WHERE type='biography';

title_id price AVG(price) Difference

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

T06 19.95 18.3875 1.5625

T07 23.95 18.3875 5.5625

T10 NULL 18.3875 NULL

T12 12.99 18.3875 -5.3975

Рис. 8.17. Результат выполнения листинга 8.17

Page 317: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

317

Листинг 8.18 использует сложные запросы,чтобы отобразить список авторов всех книгв одной строке в виде списка. Результат ис-полнения листинга представлен на рис. 8.18.Обратите внимание, что в каждом предло-жении WHERE SQL точно определяет title_idпри помощи заголовка таблицы ta, ко-торый задан в предложении FROM запроса.См. раздел «Определение названий столбцовв подзапросах» ранее в этой главе (если выжелаете научиться работать с данным запро-сом более эффективно, см. советы и приме-чания в этом разделе).В листинге 8.19 мы повторяем листинг 7.30из главы 7. Однако в этом примере, чтобыотобразить список книг, которые написалкаждый автор (один или в соавторстве),вместо внешнего объединения мы исполь-зуем сложный запрос. Результат исполне-ния листинга показан на рис. 8.19.Листинг 8.20 использует сложный запрос,чтобы отобразить список всех авторови даты публикации последних книг. Что-бы сделать точную ссылку на таблицу, не-обходимо в каждом запросе, содержащемобъединение, определить название каж-дого столбца. Результат исполнения лис-тинга показан на рис. 8.20.Листинг 8.21 использует сложный запрос,чтобы рассчитать промежуточную суммудля продаж всех книг. Промежуточнаясумма представляет собой простой расчет:мы желаем рассчитать для каждой книгисумму продаж всех книг, которые следуютперед ней в алфавитном порядке. В дан-ном случае мы рассматриваем те книги,заголовки которых в title_id расположе-ны перед заголовком текущей книги. Об-ратите внимание, что заголовки таблициспользуются для обозначения одной итой же таблицы в двух контекстах. Запрос

Листинг 8.18. Отобразить список всех авторов всехкниг в одной строке. Результат см. на рис. 8.18

SELECT title_id,

(SELECT au_id

FROM title_authors ta

WHERE au_order = 1

AND title_id = t.title_id)

AS "Author 1",

(SELECT au_id

FROM title_authors ta

WHERE au_order = 2

AND title_id = t.title_id)

AS "Author 2",

(SELECT au_id

FROM title_authors ta

WHERE au_order = 3

AND title_id = t.title_id)

AS "Author 3"

FROM titles t;

title_id Author 1 Author 2 Author 3

———----— ——----—— —-----——— ——----——

T01 A01 NULL NULL

T02 A01 NULL NULL

T03 A05 NULL NULL

T04 A03 A04 NULL

T05 A04 NULL NULL

T06 A02 ULL NULL

T07 A02 A04 NULL

T08 A06 NULL NULL

T09 A06 NULL NULL

T10 A02 NULL NULL

T11 A06 A03 A04

T12 A02 NULL NULL

T13 A01 NULL NULL

Рис. 8.18. Результат выполнения листинга 8.18

Подзапросы как выражения в списке заголовков столбцов

Page 318: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

318 Подзапросы

считывает сумму продаж всех книг, кото-рые находятся перед текущей книгой в ал-фавитном порядке. Порядок определяетсястрокой t1.title_id. Результат исполне-ния листинга показан на рис. 8.21.

Вы также можете использовать запрос впредложении FROM. В советах к разделу «Ис-ключение повторных значений с помощьюпредложения DISTINCT» в главе 6 мы при-меняли запрос FROM, чтобы повторить от-дельную функцию. Листинг 8.22 используетэтот запрос для расчета наибольшего коли-чества книг, написанных любым автором(в том числе в соавторстве). Результат ис-полнения листинга показан на рис. 8.22. Об-ратите внимание на то, что внешний запросиспользует заголовок столбца (ta) и ярлыкстолбца (count_titles), чтобы создатьссылку на результат внутреннего запроса.

Вы также можете использовать запрос ввиде выражения в форме столбца в коман-дах UPDATE, INSERT и DELETE (см. главу 9),но не в предложении ORDER BY.

Чтобы более эффективно работать с лис-тингом 8.18, используйте выражения CASEвместо сложных запросов (см. раздел «Вы-числение условных значений с помощьювыражения CASE» в главе 5):

SELECT title_id,

MIN (CASE au_order WHEN 1

THEN au_id

END)

AS "Author 1",

MIN (CASE au_order WHEN 2

THEN au_id

END)

AS "Author 2",

MIN (CASE au_order WHEN 3

THEN au_id

END)

AS "Author 3"

FROM title_authors

GROUP BY title_id

ORDER BY title_id ASC;

Листинг 8.19. Отобразить список книг, которыенаписал каждый автор (один или в соавторстве),включая авторов, не написавших ничего.Результат см. на рис. 8.19

SELECT au_id,

(SELECT COUNT(*)

FROM title_authors ta

WHERE ta.au_id = a.au_id)

AS "Num books"

FROM authors a

ORDER BY au_id ASC;

au_id Num books

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

A01 3

A02 4

A03 2

A04 4

A05 1

A06 3

A07 0

Рис. 8.19. Результат выполнения листинга 8.19

MySQL 4.0 и более ранних версий не под-держивает подзапросы. За дополнительнойинформацией обращайтесь к примечаниюDBMS в разделе «Принципы работы с под-запросами» ранее в этой главе.

В Microsoft Access вам следует увеличитьточность расчета средней цены в листин-ге 8.17. Для этих целей используйте функ-цию конвертации – CDbl() (см. примечаниеDBMS в разделе «Преобразование типовданных с помощью функции CAST()» в гла-ве 5). Чтобы запустить листинг 8.17 вAccess, замените оба параметра AVG(price)на CDbl(AVG(price)).

Page 319: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

319

Листинг 8.20. Отобразить список всех авторови даты публикации последних книг. Результатсм. на рис. 8.19

SELECT au_id,

(SELECT MAX(pubdate)

FROM titles t

INNER JOIN title_authors ta

ON ta.title_id = t.title_id

WHERE ta.au_id = a.au_id)

AS "Latest pub date"

FROM authors a;

au_id Latest pub date

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

A01 2000-08-01

A02 2000-08-31

A03 2000-11-30

A04 2001-01-01

A05 2000-09-01

A06 2002-05-31

A07 NULL

Рис. 8.20. Результат выполнения листинга 8.19

Листинг 8.21. Рассчитать промежуточную суммупродаж всех книг. Результат см. на рис. 8.21

SELECT t1.title_id, t1.sales,

(SELECT SUM(t2.sales)

FROM titles t2

WHERE t2.title_id < t1.title_id)

AS "Running total"

FROM titles t1;

Листинг 8.22. Рассчитать наибольшее количествокниг, написанных любым автором (в том числев соавторстве). Результат представлен на рис. 8.22

SELECT MAX(ta.count_titles)

→ AS "Max titles"

FROM (SELECT COUNT(*) AS "count_titles"

FROM title_authors

GROUP BY au_id) ta;

title_id sales Running total

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

T01 566 NULL

T02 9566 566

T03 25667 10132

T04 13001 35799

T06 201440 48800

T06 11320 250240

T07 1500200 261560

T08 4095 1761760

T09 5000 1765855

T10 NULL 1770855

T11 94123 1770855

T12 100001 1864978

T13 10467 1964979

Рис. 8.21. Результат выполнения листинга 8.21

Max titles

-----------

4

Рис. 8.22. Результат выполнения листинга 8.22

Подзапросы как выражения в списке заголовков столбцов

Page 320: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

320 Подзапросы

Сравнение значений,возвращаемыхподзапросом,с использованиемоператоров сравненияВы можете использовать запрос в каче-стве фильтра в предложении WHERE илиHAVING внешнего запроса с помощью одно-го из операторов сравнения (=, <>, <, <=,> или >=). Перечислим важные параметрыпри сравнении запроса:

операторы сравнения работают так же,как в других выражениях (см. табл. 4.2в главе 4);запрос может быть простым или слож-ным (см. раздел «Простые и сложныезапросы» ранее в этой главе);список столбцов команды SELECT за-проса может включать только одновыражение или название столбца;сравниваемые значения должны отно-ситься к одному типу данных или бытьконвертируемыми (см. раздел «Преоб-разование типов данных с помощьюфункции CAST()» в главе 5);сравнения могут быть зависимымии независимыми, что обусловлено кон-кретной СУБД (см. примечание DBMSв разделе «Фильтрация строк с помо-щью предложения WHERE» главы 4);запрос должен возвращать одно значе-ние (результат одной строки или столб-ца). Запрос, который возвращает не-сколько значений, приводит к ошибке;

если результат запроса содержит стро-ки со значением null, сравнение дастневерный результат.

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

при использовании агрегатной функ-ции на несгруппированной таблицевсегда получается одно значение (см.главу 6);

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

Сравнение значений запроса

В предложении WHERE команды SELECT напе-чатайте WHERE test_expr op (subquery).

test_expr – это буквенное значение, назва-ние столбца, выражение или запрос, кото-рый считывает одно значение; op – опера-тор сравнения (=, <>, <, <=, > или >=);subquery – скалярный запрос, который воз-вращает один столбец и ни одной илиодну строку.

Если значение, считанное с помощью запро-са, соответствует сравнению с test_expr, ре-зультат сравнения определяется как истин-ный. Результат определяется как ложный,если значение запроса не соответствуетсравнению. Результат сравнения не опре-делен, если подзапрос не вернул ни однойстроки или вернул NULL.При использовании предложения HAVINGсинтаксис имеет такую же структуру:HAVING test_expr op (subquery)

Листинг 8.23 проверяет результат просто-го запроса на соответствие списку всех ав-торов, которые живут в штате, где находит-ся издательство Tenterhooks Press. Такое

Page 321: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

321

название имеет только одно издательство,поэтому предложение WHERE подзапроса га-рантирует, что он вернет одно значение.Результат исполнения листинга показан нарис. 8.23.Листинг 8.24 аналогичен листингу 8.23, от-личается лишь названием издательства.Не существует издательства XXX, поэтомуподзапрос возвращает пустую таблицу.Сравнение выполняется с NULL, вот поче-му окончательный результат пустой (см.рис. 8.24).Листинг 8.25 отображает список всех книг,продажи которых выше среднего уровня.Запросы, работающие с операторами срав-нения, часто используют функции, что-бы считать лишь одно значение. Резуль-тат исполнения листинга представлен нарис. 8.25.

Листинг 8.23. Отобразить список всех авторов,которые живут в штате, где находится издательствоTenterhooks. Результат см. на рис. 8.23

SELECT au_id, au_fname, au_lname, state

FROM authors

WHERE state =

(SELECT state

FROM publishers

WHERE pub_name = 'Tenterhooks

Press');

au_id au_fname au_lname state

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

A03 Hallie Hull CA

A04 Klee Hull CA

A06 Kellsey CA

Рис. 8.23. Результат выполнения листинга 8.23

au_id au_fname au_lname state

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

Рис. 8.24. Результат выполнения листинга 8.24

Листинг 8.25. Отобразить список всех книг,продажи которых выше среднего уровня.Результат см. на рис. 8.25

SELECT title_id, sales

FROM titles

WHERE sales >

(SELECT AVG(sales)

FROM titles);

title_id sales

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

T05 201440

T07 1500200

Рис. 8.25. Результат выполнения листинга 8.25

Листинг 8.24. Отобразить список всех авторов,которые живут в штате, где находитсяиздательство XXX. Результат показан на рис. 8.24

SELECT au_id, au_fname, au_lname, state

FROM authors

WHERE state =

(SELECT state

FROM publishers

WHERE pub_name = 'XXX');

Сравнение значений, возвращаемых подзапросом

Page 322: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

322 Подзапросы

Листинг 8.26. Отобразить с помощьюобъединения и запроса список авторов всех книг,продажи которых выше среднего уровня.Результат показан на рис. 8.26

SELECT ta.au_id, ta.title_id

FROM titles t

INNER JOIN title_authors ta

ON ta.title_id = t.title_id

WHERE sales >

(SELECT AVG(sales)

FROM titles)

ORDER BY ta.au_id ASC, ta.title_id ASC;

Чтобы отобразить список авторов всехкниг, продажи которых выше среднегоуровня, мы добавили к листингу 8.25внутреннее объединение (см. листинг 8.26и рис. 8.26).

Вспомните (см. начало этой главы), что выможете использовать подзапрос практи-чески в любом месте выражения, поэтомуWHERE (subquery) op (subquery)

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

Левый подзапрос должен считать однозначение. Листинг 8.27 эквивалентен лис-тингу 8.26, но мы убрали внутреннее объ-единение и заменили его сложным за-просом слева от оператора сравнения. Ре-зультат исполнения листинга показан нарис. 8.27.

Вы можете включить предложение GROUP BYили HAVING в запрос, если знаете, что в ре-зультате будет считано только одно значе-ние. Листинг 8.28 показывает список книг,цены на которые выше, чем на самую до-рогую биографию. Результат исполнениялистинга представлен на рис. 8.28.

Листинг 8.29 использует запрос в предло-жении HAVING, чтобы отобразить списокиздательств, средние продажи которыхвыше общих средних продаж. Запроссчитывает одно значение (среднее длявсех продаж). Результат исполнения лис-тинга показан на рис. 8.29.

Листинг 8.30 использует сложный запрос,чтобы отобразить список авторов, гоно-рар которых меньше, чем самый высо-кий гонорар любого соавтора книги. Внеш-ний запрос выделяет строки title_authors(то есть ta1) одну за другой. Подзапросрассчитает самый высокий гонорар длякаждой книги на основании выделеннойобласти внешнего запроса. Для каждого

au_id title_id

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

A02 T07

A04 T05

A04 T07

Рис. 8.26. Результат выполнения листинга 8.26

Листинг 8.27. Отобразить с помощью двухзапросов список авторов всех книг, продажикоторых выше среднего уровня. Результатсм. на рис. 8.27

SELECT au_id, title_id

FROM title_authors ta

WHERE

(SELECT AVG(sales)

FROM titles t

WHERE ta.title_id = t.title_id)

>

(SELECT AVG(sales)

FROM titles)

ORDER BY au_id ASC, title_id ASC;

au_id title_id

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

A02 T07

A04 T05

A04 T07

Рис. 8.27. Результат выполнения листинга 8.27

Page 323: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

323

возможного значения ta1 СУБД выпол-няет подзапрос и помещает строку в ре-зультат при условии, что гонорар автораменьше, чем заданный максимум. Ре-зультат исполнения листинга показан нарис. 8.30.

Листинг 8.31 использует сложный под-запрос, чтобы сымитировать предложе-ние GROUP BY и отобразить список всехкниг, цена на которые выше, чем средняяцена на книги данного типа. Для каждоговозможного значения t1 СУБД выполня-ет подзапрос и включает строку в резуль-тат при условии, что цена в этой строкебольше, чем заданный средний уровень.Нет необходимости точно группировать

Листинг 8.28. Отобразить список книг,цены на которые выше, чем на самую дорогуюбиографию. Результат см. на рис. 8.28

SELECT title_id, price

FROM titles

WHERE price >

(SELECT MAX(price)

FROM titles

GROUP BY type

HAVING type = 'biography');

title_id price

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

T03 39.95

T13 29.99

Рис. 8.28. Результат выполнения листинга 8.28

Листинг 8.29. Отобразить список издательств,средние продажи которых выше общих среднихпродаж. Результат см. на рис. 8.29

ELECT pub_id, AVG(sales) AS "AVG(sales)"

FROM titles

GROUP BY pub_id

HAVING AVG(sales) >

(SELECT AVG(sales)

FROM titles);

pub_id AVG(sales)

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

P03 506744.33

Рис. 8.29. Результат выполнения листинга 8.29

Листинг 8.30. Отобразить список авторов, гонораркоторых меньше, чем самый высокий гонорарлюбого соавтора книги. Результат см. на рис. 8.30

SELECT ta1.au_id, ta1.title_id,

ta1.royalty_share

FROM title_authors ta1

WHERE ta1.royalty_share <

(SELECT MAX(ta2.royalty_share)

FROM title_authors ta2

WHERE ta1.title_id = ta2.title_id);

au_id title_id royalty_share

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

A04 T04 0.40

A03 T11 0.30

A04 T11 0.30

Рис. 8.30. Результат выполнения листинга 8.30

Сравнение значений, возвращаемых подзапросом

Page 324: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

324 Подзапросы

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

Листинг 8.32 использует ту же структуру,что и листинг 8.31, чтобы отобразить спи-сок всех книг, продажи которых ниже, чемпродажи бестселлера данного типа. Ре-зультат исполнения листинга представленна рис. 8.32.

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

SELECT type, title_id, priceFROM titles t1

WHERE price >

(SELECT AVG(t2.price) FROM titles t2

WHERE t1.type = t2.type)ORDER BY type ASC, title_id ASC;

type title_id price

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

biography T06 19.95

biography T07 23.95

children T09 13.95

history T13 29.99

psychology T04 12.99

Рис. 8.31. Результат выполнения листинга 8.31

Листинг 8.32. Отобразить список всех книг,продажи которых ниже, чем продажи бестселлераданного типа. Результат см. на рис. 8.32

SELECT type, title_id, sales

FROM titles t1

WHERE sales <

(SELECT MAX(sales)

FROM titles t2

WHERE t1.type = t2.type

AND sales IS NOT NULL)

ORDER BY type ASC, title_id ASC;

type title_id sales

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

biography T06 11320

biography T12 100001

children T08 4095

history T01 566

history T02 9566

psychology T04 13001

psychology T11 94123

Рис. 8.32. Результат выполнения листинга 8.32

Если запрос считывает несколько строк,вы можете использовать ключевые словаALL и ANY, чтобы изменить оператор срав-нения, либо ввести запрос с помощью опе-ратора IN.

MySQL 4.0 и более ранних версий не под-держивает подзапросы. За дополнитель-ной информацией обращайтесь к приме-чанию DBMS в разделе «Принципы рабо-ты с подзапросами» ранее в этой главе.

Page 325: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

325

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

IN работает со значениями в результатезапроса так же, как со списком значе-ний (см. раздел «Фильтрация с помо-щью оператора IN» в главе 4);

запрос может быть простым или слож-ным (см. раздел «Простые и сложныезапросы» ранее в этой главе);

список столбцов команды SELECT за-проса может включать только одно вы-ражение или название столбца;

сравниваемые значения должны отно-ситься к одному типу данных или бытьконвертируемыми (см. раздел «Преоб-разование типов данных с помощьюфункции CAST()» в главе 5);

сравнения могут быть зависимыми и не-зависимыми, смотря какую СУБД выиспользуете (см. примечание DBMS вразделе «Фильтрация строк с помощьюпредложения WHERE» в главе 4);

запрос должен считать одно значение(результат одной строки или столбца).Запрос, который считывает несколькозначений, приводит к ошибке;

вы не можете использовать операторNOT IN, чтобы проверить на отсутствиезначения в списке. Если вы зададите NOTIN, СУБД будет выполнять действие,указанное в команде SQL, при условии,что в результате запроса нет соответ-ствующего значения.

Проверка на вхождение значенияво множество

В предложении WHERE команды SELECTнапечатайте WHERE test_expr [NOT] IN (sub-query).

test_expr – это буквенное значение, назва-ние столбца, выражение или запрос, кото-рый возвращает значение; subquery – за-прос, который считывает один столбеци любое количество строк.

Если значение test_expr равно любомузначению, полученному с помощью за-проса, условие IN задается как истинное.Условие IN задается как ложное, если резуль-тат запроса пустой, ни одна строкав результате запроса не соответствует test_-expr или если все значения в результате за-проса равны NULL. Чтобы инвертировать ре-зультат условия, укажите NOT.

Предложение HAVING имеет такую же струк-туру: HAVING test_expr [NOT] (subquery).Листинг 8.33 отображает список всех изда-тельств, которые опубликовали биогра-фии. СУБД выполняет эту команду в дваэтапа. Сначала внутренний запрос считы-вает информацию обо всех издательствах,которые опубликовали биографии (P01и P03). Затем СУБД помещает эти зна-чения во внешний запрос, который най-дет имена в соответствии с информациейв таблице publishers. Результат исполне-ния листинга показан на рис. 8.33.

Проверка на вхождение во множество с помощью оператора IN

Page 326: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

326 Подзапросы

Листинг 8.33. Отобразить список всехиздательств, которые опубликовали биографии.Результат показан на рис. 8.33

SELECT pub_name

FROM publishers

WHERE pub_id IN

(SELECT pub_id

FROM titles

WHERE type = 'biography');

pub_name

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

Abatis Publishers

Schadenfreude Press

Рис. 8.33. Результат выполнения листинга 8.33

Листинг 8.34. Отобразить список всехиздательств, которые не опубликовалибиографии. Результат см. на рис. 8.34

SELECT pub_name

FROM publishers

WHERE pub_id NOT IN

(SELECT pub_id

FROM titles

WHERE type = 'biography');

Продемонстрируем версию листинга 8.33с использованием объединения:SELECT DISTINCT pub_name

FROM publishers p

INNER JOIN titles t

ON p.pub_id = t.pub_id

AND type = 'biography';

Листинг 8.34 эквивалентен листингу 8.33,только он использует NOT IN, чтобы ото-бразить список всех издательств, которыене публиковали биографии. Результатисполнения листинга см. на рис. 8.34. Ко-манда, представленная в этом листинге, неможет быть конвертирована в объедине-ние. Аналогичное объединение имеет дру-гое значение – отображает список всех из-дательств, которые опубликовали какую-либо книгу, но не биографию.

pub_name

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

Core Dump Books

Tenterhooks Press

Рис. 8.34. Результат выполнения листинга 8.34

Page 327: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

327

Листинг 8.35. Отобразить список всех авторов,которые не написали ни одной книги. Результатпоказан на рис. 8.35

SELECT au_id, au_fname, au_lname

FROM authors

WHERE au_id NOT IN

(SELECT au_id

FROM title_authors);

au_id au_fname au_lname

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

A07 Paddy O'Furniture

Рис. 8.35. Результат выполнения листинга 8.35

Листинг 8.36. Отобразить список всех авторов,которые опубликовали книги в издательстве P03.Результат см. на рис. 8.36

SELECT DISTINCT a.au_id, au_fname, au_lname

F ROM title_authors ta

INNER JOIN authors a

ON ta.au_id = a.au_id

WHERE title_id IN

(SELECT title_id

FROM titles

WHERE pub_id = 'P03');

au_id au_fname au_lname

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

A01 Sarah Buchman

A02 Wendy Heydemark

A04 Klee Hull

Рис. 8.36. Результат выполнения листинга 8.36

Листинг 8.35 эквивалентен листингу 7.31в главе 7, только он использует запросвместо внешнего объединения, чтобыотобразить список всех авторов, которыене написали ни одной книги (в том числев соавторстве). Результат исполнения лис-тинга см. на рис. 8.35.

Листинг 8.36 отображает всех авторов, кото-рые опубликовали книги в издательстве P03(Schadenfreude Press). Объединение с табли-цей authors необходимо, чтобы включитьимена авторов (не только информациюо них) в результат. Результат исполнениялистинга представлен на рис. 8.36.

Запрос может включать один или несколь-ко подзапросов. В листинге 8.37 приведенсписок авторов, которые участвовалив написании хотя бы одной биографии.Самый глубокий запрос считывает заго-ловки T06, T07, T10 и T12. СУБД обрабаты-вает запрос на более высоком уровне с по-мощью данных об этих книгах и выдаетинформацию об авторах. Наконец, запроссамого высокого уровня использует ин-формацию об авторах, чтобы найти ихимена. Результат исполнения листингапредставлен на рис. 8.37.

Проверка на вхождение во множество с помощью оператора IN

Page 328: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

328 Подзапросы

Если вы размещаете запрос на большомколичестве уровней, это усложняет вы-полнение команды. Чаще всего прощепреобразовать запрос в соединение. При-ведем вариант листинга 8.37 с использова-нием соединения:SELECT DISTINCT a.au_id, au_fname, au_lname

FROM authors a

INNER JOIN title_authors ta

ON a.au_id = ta.au_id

INNER JOIN titles t

ON t.title_id = ta.title_id

WHERE type = 'biography';

Листинг 8.38 отображает список соавто-ров (au_order > 1), которые живут в Кали-форнии и получают менее 50% гонорараза книгу. Результат исполнения листингапредставлен на рис. 8.38. Покажем вари-ант листинга 8.38 с использованием объе-динения:

SELECT DISTINCT a.au_id, au_fname, au_lname

FROM authors a

INNER JOIN title_authors ta

ON a.au_id = ta.au_id

WHERE state = 'CA'

AND royalty_share < 0.5

AND au_order > 1;

Листинг 8.37. Отобразить список всех авторов,которые участвовали в написании хотя бы однойбиографии. Результат см. на рис. 8.37

SELECT au_id, au_fname, au_lname

FROM authors

WHERE au_id IN

(SELECT au_id

FROM title_authors

WHERE title_id IN

(SELECT title_id

FROM titles

WHERE type = 'biography'));

au_id au_fname au_lname

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

A02 Wendy Heydemark

A04 Klee Hull

Рис. 8.37. Результат выполнения листинга 8.37

Листинг 8.38. Отобразить список соавторов,которые живут в Калифорнии и получают менее50% гонорара за книгу. Результат см. на рис. 8.38

SELECT au_id, au_fname, au_lnameFROM authors

WHERE state = 'CA'AND au_id IN

(SELECT au_id FROM title_authors

WHERE royalty_share < 0.5AND au_order > 1);

au_id au_fname au_lname

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

A03 Hallie Hull

A04 Klee Hull

Рис. 8.38. Результат выполнения листинга 8.38

Page 329: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

329

Листинг 8.39 отображает список соавто-ров книги. Чтобы определить, являетсяли конкретное лицо соавтором книги,проверяется его гонорар за книгу. Еслигонорар меньше 100% (1.0), то это соав-тор, в противном случае – у автора нетсоавторов. Результат исполнения листин-га см. на рис. 8.39.

Листинг 8.40 использует сложный запрос,чтобы отобразить список авторов, у кото-рых нет соавторов (то есть тех авторов,которые получили за книгу 100% (1.0) го-норара). Результат исполнения листингасм. на рис. 8.40. СУБД считает каждуюстроку в таблице внешнего запроса authorsкак вариант для включения в результат.Когда СУБД проверяет первую строкув authors, она задает корреляционную пе-ременную a.au_id как равную A01 (SarahBuchmann) и подставляет это значение вовнутренний запрос:

SELECT royalty_share

FROM title_authors ta

WHERE ta.au_id = 'A01';

Внутренний запрос выдает 1.0, поэтомувнешний запрос принимает следующийвид:

SELECT a.au_id, au_fname, au_lname

FROM authors a

WHERE 1.0 IN (1.0)

Условие WHERE истинно, поэтому автор A01включается в результат. СУБД повторяетэту процедуру для всех авторов (см. раз-дел «Простые и сложные запросы» ранеев этой главе).

Листинг 8.39. Отобразить список соавторов книги.Результат показан на рис. 8.39

SELECT au_id, au_fname, au_lname

FROM authors a

WHERE au_id IN

(SELECT au_id

FROM title_authors

WHERE royalty_share < 1.0);

au_id au_fname au_lname

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

A02 Wandy Heydemark

A03 Hallie Hull

A04 Klee Hull

A06 Kellsey

Рис. 8.39. Результат выполнения листинга 8.39

Листинг 8.40. Отобразить список авторов,у которых нет соавторов. Результат см. на рис. 8.40

SELECT a.au_id, au_fname, au_lname

FROM authors a

WHERE 1.0 IN

(SELECT royalty_share

FROM title_authors ta

WHERE ta.au_id = a.au_id);

au_id au_fname au_lname

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

A01 Sarah Buchman

A02 Wendy Heydemark

A04 Klee Hull

A05 Christian Kells

A06 Kellsey

Рис. 8.40. Результат выполнения листинга 8.40

Проверка на вхождение во множество с помощью оператора IN

Page 330: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

330 Подзапросы

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

Можно переписать листинг 8.41 в видеобъединения или пересечения. Приведемвариант листинга 8.41 с использованиемобъединения:SELECT DISTINCT a.au_id, au_fname, au_lname

FROM authors a

INNER JOIN title_authors ta1

ON a.au_id = ta1.au_id

INNER JOIN title_authors ta2

ON a.au_id = ta2.au_id

WHERE ta1.royalty_share < 1.0

AND ta2.royalty_share = 1.0;

Продемонстрируем вариант листинга 8.41с использованием пересечения (см. раздел«Поиск общих строк с помощью командыINTERSECT» в главе 7):SELECT DISTINCT a.au_id, au_fname, au_lname

FROM authors a

INNER JOIN title_authors ta

ON a.au_id = ta.au_id

WHERE ta.royalty_share < 1.0

INTERSECT

SELECT DISTINCT a.au_id, au_fname, au_lname

FROM authors a

INNER JOIN title_authors ta

ON a.au_id = ta.au_id

WHERE ta.royalty_share = 1.0;

Листинг 8.41. Отобразить список авторов,которые написали книги как в соавторстве,так и самостоятельно. Результат см. на рис. 8.41

SELECT DISTINCT a.au_id, au_fname,

au_lname

FROM authors a

INNER JOIN title_authors ta

ON a.au_id = ta.au_id

WHERE ta.royalty_share < 1.0

AND a.au_id IN

(SELECT a.au_id

FROM authors a

INNER JOIN title_authors ta

ON a.au_id = ta.au_id

AND ta.royalty_share = 1.0);

au_id au_fname au_lname

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

A02 Wendy Heydemark

A04 Klee Hull

A06 Kellsey

Рис. 8.41. Результат выполнения листинга 8.41

Page 331: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

331

Листинг 8.42. Отобразить типы книг,которые были опубликованы несколькимииздательствами. Результат см. на рис. 8.42

SELECT DISTINCT t1.type

FROM titles t1

WHERE t1.type IN

(SELECT t2.type

FROM titles t2

WHERE t1.pub_id <> t2.pub_id);

type

---------

biography

history

Рис. 8.42. Результат выполнения листинга 8.42

Листинг 8.42 использует сложный запрос,чтобы отобразить типы книг, которыебыли опубликованы несколькими изда-тельствами. Результат исполнения листин-га показан на рис. 8.42. Приведем вариантлистинга 8.42 с использованием самостоя-тельного объединения:SELECT DISTINCT t1.type

FROM titles t1

INNER JOIN titles t2ON t1.type = t2.type

AND t1.pub_id <> t2.pub_id;

Оператор IN эквивалентен ключевому сло-ву ANY (см. раздел «Сравнение некоторыхзначений запроса с помощью ключевогослова ANY» далее в этой главе).

Оператор NOT IN эквивалентен выражению<> ALL (не <> ANY) (см. раздел «Сравне-ние всех значений запроса с помощьюключевого слова ALL» далее в этой главе).

Проверка на вхождение во множество с помощью оператора IN

Page 332: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

332 Подзапросы

MySQL 4.0 и более ранних версий не под-держивает подзапросы. За дополнительнойинформацией обращайтесь к примечаниюDBMS в разделе «Принципы работы с под-запросами» ранее в этой главе.

Чтобы запустить листинг 8.41 в MicrosoftAccess, напечатайте:

SELECT DISTINCT a.au_id, au_fname,

au_lname

FROM (authors AS a

INNER JOIN title_authors AS ta1

ON a.au_id = ta1.au_id)

INNER JOIN title_authors AS ta2

ON a.au_id = ta2.au_id

WHERE ta1.royalty_share < 1.0

AND ta2.royalty_share = 1.0;

В PostgreSQL вам следует конвертироватьчисла с плавающей запятой в листингах8.38–8.41 в DECIMAL (см. раздел «Преоб-разование типов данных с помощью функ-ции CAST()» в главе 5). Чтобы запуститьлистинги 8.38–8.41 в PostgreSQL, изменитечисла с плавающей запятой следующимобразом (листинг 8.38):

CAST (0.5 AS DECIMAL)

(листинг 8.39):CAST (1.0 AS DECIMAL)

(листинг 8.40):CAST (1.0 AS DECIMAL)

(листинг 8.41):

CAST (1.0 AS DECIMAL) (в двух местах)Некоторые СУБД позволяют одновременнотестировать несколько значений с исполь-зованием следующей структуры:SELECT columns

FROM table1

WHERE (col1, col2, …, colN) IN

(SELECT colA, colB, …, colN

FROM table2);

Выражение слева от IN – это список столб-цов в таблице table1. Запрос считывает токоличество столбцов, которое есть в спис-ке. СУБД сравнивает значения в соответству-ющих столбцах. Например, следующийзапрос работает в Oracle и PostgreSQL:

SELECT au_id, city, state

FROM authors

WHERE (city, state) IN

(SELECT city, state

FROM publishers);

В результате выполнения запроса появля-ется список авторов, которые живут в го-роде и штате, где находится определенноеиздательство:

au_id city state

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

A03 San Francisco CA

A04 San Francisco CA

A05 New York NY

Page 333: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

333

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

ALL изменяет оператор сравнения в тестезапроса и сопровождает его оператора-ми =, <>, <, <=, > или >= (см. раздел«Сравнение значений, возвращаемыхподзапросом, с использованием опера-торов сравнения» ранее в этой главе);

комбинация оператора сравнения иключевого слова ALL сообщает СУБД,как применить тест сравнения ко всемзначениям, которые считал запрос. На-пример, < ALL обозначает, что сравне-ние должно быть меньше, чем любоезначение в результате запроса, > ALL –сравнение должно быть больше, чемлюбое значение в результате запроса;

если ALL используется с операторами <,<=, > или >=, сравнение эквивалентнорасчету максимального или минималь-ного значения в результате запроса.< ALL говорит о том, что значение мень-ше, чем любое другое значение в запро-се, то есть меньше минимального значе-ния; > ALL – больше максимального зна-чения. В табл. 8.2 перечислены эквива-лентные выражения ALL и функциидля работы со столбцами. В листин-ге 8.45 мы покажем, как заменить запрос> ALL с помощью MAX ();

сравнение = ALL возможно, но исполь-зуется нечасто. = ALL всегда будет лож-ным условием, только если все значе-ния, считанные запросом, не идентич-ны (и не равны значению при тести-ровании);

запрос может быть простым или слож-ным (см. раздел «Простые и сложныезапросы» ранее в этой главе);

список столбцов команды SELECT за-проса может включать только одно вы-ражение или название столбца;

сравниваемые значения должны отно-ситься к одному типу данных или бытьконвертируемыми (см. раздел «Преоб-разование типов данных с помощьюфункции CAST()» в главе 5);

сравнения могут быть зависимымии независимыми, это обусловлено кон-кретной СУБД (см. примечание DBMSв разделе «Фильтрация строк с помо-щью предложения WHERE» главы 4);

запрос должен считать одно значение(результат одной строки или столбца).Запрос, который считывает несколькозначений, приводит к ошибке;

если результат запроса не считываетстроки, условие ALL становится истин-ным.

Таблица 8.2. Выражения, эквивалентные ALL

Выражение ALL Функция столбца

< ALL (subquery) < MIN (subquery values)

> ALL (subquery) > MAX (subquery values)

Сравнение всех значений запроса с помощью ключевого слова ALL

Page 334: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

334 Подзапросы

Сравнение всех значений запроса

В предложении WHERE команды SELECT напе-чатайте:

WHERE test_expr op ALL (subquery)

test_expr – это буквенное значение, на-звание столбца, выражение или запрос,который считывает одно значение; op –оператор сравнения (=, <>, <, <=, > или>=); subquery – скалярный запрос, кото-рый считывает один столбец.

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

Предложение HAVING имеет такую же струк-туру: HAVING test_expr op ALL (subquery).

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

Вы можете использовать NOT IN, чтобы по-вторить листинг 8.43:

SELECT au_id, au_lname, au_fname, city

FROM authors

WHERE city NOT IN

(SELECT city FROM publishers);

Листинг 8.43. Отобразить список авторов,которые живут в городе, где нет ни одногоиздательства. Результат показан на рис. 8.43

SELECT au_id, au_lname, au_fname, city

FROM authors

WHERE city <> ALL

(SELECT city

FROM publishers);

Рис. 8.43. Результат выполнения листинга 8.43

au_id au_lname au_fname city

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

A01 Buchman Sarah Bronx

A02 Heydemark Wendy Boulder

A06 Kellsey Palo Alto

A07 O'Furniture Paddy Sarasota

Page 335: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

335

Листинг 8.44 отображает список всех книг(не биографий), цена которых меньше,чем цена биографий. Внутренний запроснаходит все цены на биографии. Внешнийзапрос находит самую низкую цену всписке и определяет, дешевле ли другиекниги. Результат исполнения листинга см.на рис. 8.44. Условие price IS NOT NULL необ-ходимо, так как цена на биографию T10равна нулю. Без этого условия весь запросбудет выдавать NULL, поскольку невозмож-но определить, что цена меньше значенияnull (см. раздел «Значение null» в главе 3).

Листинг 8.45 отображает список книг, ко-торые продаются лучше, чем книги, напи-санные автором A06 (в том числе в соав-торстве). Внутренний запрос используетсоединение, чтобы найти информациюо продажах всех книг, которые написал ав-тор A06. Внешний запрос обрабатывает по-казатели самых высоких продаж в спискеи определяет разницу в продажах. Резуль-тат исполнения листинга см. на рис. 8.45.Условие IS NOT NULL необходимо на слу-чай, если цены книг автора A06 будут рав-ны NULL. Мы можем повторить листинг 8.45с помощью предложений GROUP BY, HAVINGи MAX () (вместо ALL):

SELECT title_id

FROM titles

GROUP BY title_id

HAVING MAX (sales) >

(SELECT MAX (sales)

FROM title_authors ta

INNER JOIN titles t

ON t.title_id = ta.title_id

WHERE ta.au_id = 'A06');

Листинг 8.44. Отобразить список книг(не биографий), цена которых меньше, чем ценабиографий. Результат показан на рис. 8.44

SELECT title_id, title_name

FROM titlesWHERE type <> 'biography'

AND price < ALL(SELECT price

FROM titlesWHERE type = 'biography'

AND price IS NOT NULL);

Листинг 8.45. Отобразить список книг, которыепродаются лучше, чем книги, написанные авторомA06 (в том числе в соавторстве). Результатсм. на рис. 8.45

SELECT title_id, title_nameFROM titles

WHERE sales > ALL(SELECT sales

FROM title_authors ta INNER JOIN titles t

ON t.title_id = ta.title_id WHERE ta.au_id = 'A06'

AND sales IS NOT NULL);

title_id title_name

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

T05 Echange of Platitudes

T08 Just Wait Until After School

T11 Perhaps It's a Gladular Problem

Рис. 8.44. Результат выполнения листинга 8.44

title_id title_name

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

T05 Exchange of Platitudes

T07 I Blame My Mother

T12 Spontaneous, Not Annoying

Рис. 8.45. Результат выполнения листинга 8.45

Сравнение всех значений запроса с помощью ключевого слова ALL

Page 336: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

336 Подзапросы

Листинг 8.46 использует сложный запросв пункте HAVING внешнего запроса, чтобыотобразить типы книг, уровень продаж ко-торых более чем в два раза превышаетсредние показатели продажи книг этоготипа. Внутренний запрос рассчитываетсяодин раз для каждой группы, указанной вовнешнем запросе (для каждого типа кни-ги). Результат исполнения листинга см. нарис. 8.46.

Ключевое слово ALL является эквивален-том оператора NOT IN (см. раздел «Про-верка на вхождение во множество с помо-щью оператора IN» ранее в этой главе).

MySQL 4.0 и более ранних версий не под-держивает подзапросы. За дополнитель-ной информацией обращайтесь к приме-чанию DBMS в разделе «Принципы рабо-ты с подзапросами» ранее в этой главе.

В PostgreSQL следует конвертировать чис-ла с плавающей запятой в листинге 8.46в тип DECIMAL (см. раздел «Преобразованиетипов данных с помощью функции CAST()»в главе 5). Чтобы запустить листинг 8.46в PostgreSQL, измените числа с плавающейзапятой следующим образом:

CAST(2.0 AS DECIMAL)

type

---------

biography

Рис. 8.46. Результат выполнения листинга 8.46

Листинг 8.46. Отобразить типы книг, уровеньпродаж которых более чем в два раза вышесреднего уровня продаж книг этого типа.Результат показан на рис. 8.46

SELECT t1.type

FROM titles t1

GROUP BY t1.type

HAVING MAX(t1.sales) >= ALL

(SELECT 2.0 * AVG(t2.sales)

FROM titles t2

WHERE t1.type = t2.type);

Page 337: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

337

Таблица 8.3. Выражения, эквивалентные ANY

Выражение ANY Функция столбца

< ANY (subquery) < MIN (subquery values)

> ANY (subquery) > MAX (subquery values)

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

ANY изменяет оператор сравнения в те-сте запроса и сопровождает его опера-торами =, <>, <, <=, > или >= (см. раз-дел «Сравнение значений, возвращае-мых подзапросом, с использованиемоператоров сравнения» ранее в этойглаве);комбинация оператора сравнения иANY сообщает СУБД, как применитьтест сравнения ко всем значениям, ко-торые считал запрос. Например, < ANYозначает, что сравнение должно бытьменьше, чем хотя бы одно значениев результате запроса; > ANY означает,что сравнение должно быть больше,чем хотя бы одно значение в результа-те запроса;если ANY используется с операторами <,<=, > или >=, сравнение будет эквива-лентно расчету максимального или ми-нимального значения в результате за-проса. < ANY свидетельствует о том, чтозначение меньше, чем хотя бы одно зна-чение в запросе, то есть меньше мини-мального значения; > ANY – больше мак-симального значения. В табл. 8.3 пере-числены эквивалентные выражения ANYи функции для работы со столбцами.В листинге 8.49 мы покажем, как заме-нить запрос > ANY с помощью MIN();

Сравнение некоторых значений запроса с помощью ключевого слова ANY

Page 338: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

338 Подзапросы

сравнение = ANY эквивалентно IN (см.раздел «Проверка на вхождение во мно-жество с помощью оператора IN» ранеев этой главе);

запрос может быть простым или слож-ным (см. раздел «Простые и сложныезапросы» ранее в этой главе);

список команды SELECT запроса можетвключать только одно выражение илиназвание столбца;

сравниваемые значения должны отно-ситься к одному типу данных или бытьконвертируемыми (см. раздел «Преоб-разование типов данных с помощьюфункции CAST()» в главе 5);

сравнения могут быть зависимымии независимыми, что обусловлено кон-кретной СУБД (см. примечание DBMSв разделе «Фильтрация строк с помо-щью предложения WHERE» главы 4);

запрос должен считать одно значение(результат одной строки или столбца).Запрос, который считывает несколькозначений, приводит к ошибке;

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

Сравнение некоторых значений запроса

В предложении WHERE команды SELECT напе-чатайте WHERE test_expr op ANY (subquery).

Здесь test_expr – буквенное значение, на-звание столбца, выражение или запрос,который считывает одно значение; op –оператор сравнения (=, <>, <, <=, > или>=); subquery – скалярный запрос, кото-рый считывает один столбец.Если хотя бы одно значение, считанноес помощью запроса, соответствует усло-

вию ANY, то это условие задается как ис-тинное. Условие ANY задается как ложное,если ни одно значение запроса не соответ-ствует условию или если запрос пуст (со-держит строки с нулями) либо содержиттолько нули.

Предложение HAVING имеет такую жеструктуру:

HAVING test_expr op ANY (subquery)

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

Вы можете использовать IN, чтобы повто-рить листинг 8.47:

SELECT au_id, au_lname, au_fname, city

FROM authors

WHERE city IN

(SELECT city FROM publishers);

Листинг 8.48 отображает список всех книг(не биографий), цена которых меньше,чем цена хотя бы одной биографии. Внут-ренний запрос находит все цены на био-графии. Внешний запрос находит самуювысокую цену в списке и определяет, де-шевле ли все биографии. Результат ис-полнения листинга показан на рис. 8.48.В отличие от сравнения с ANY в листин-ге 8.44, условие price IS NOT NULL здесь ненужно, даже несмотря на то, что цена набиографию T10 равна NULL. СУБД не выяс-няет, истинны ли все сравнения по цене,а уточняет, истинно ли хотя бы одно срав-нение, поэтому сравнение с NULL игнориру-ется.

Page 339: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

339

Листинг 8.47. Отобразить список всех авторов,которые живут в городе, где расположеноиздательство. Результат см. на рис. 8.47

SELECT au_id, au_lname, au_fname, city

FROM authors

WHERE city = ANY

(SELECT city

FROM publishers);

Листинг 8.48. Отобразить список всех книг(не биографий), цена которых меньше, чем ценахотя бы одной биографии. Результат показан нарис. 8.48

SELECT title_id, title_name

FROM titles

WHERE type <> 'biography'

AND price < ANY

(SELECT price

FROM titles

WHERE type = 'biography');

au_id au_lname au_fname city

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

A03 Hull Hallie San Francisco

A04 Hull Klee San Francisco

A05 Kells Christian New York

Рис. 8.47. Результат выполнения листинга 8.47

title_id title_name

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

T01 1977!

T02 200 Years of German Humor

T04 But I Did It Unconsciously

T05 Exchange of Platitudes

T08 Just Wait Until After School

T09 Kiss My Boo-Boo

T11 Perhaps It's a Glandular Problem

Рис. 8.48. Результат выполнения листинга 8.48

Листинг 8.49 отображает список всех книг,которые продаются лучше, чем хотя быодна книга, написанная автором A06 (в томчисле в соавторстве). Внутренний запросиспользует соединение, чтобы найти ин-формацию о продажах всех книг, которыенаписал автор A06. Внешний запрос изу-чает показатели самых низких продажв списке и определяет разницу в продажах.Результат исполнения листинга представ-лен на рис. 8.49. В отличие от примерас ALL в листинге 8.45, условие IS NOT NULL

Листинг 8.49. Отобразить список всех книг,которые продаются лучше, чем хотя бы однакнига, написанная автором A06 (в том числев соавторстве). Результат см. на рис. 8.49

SELECT title_id, title_name

FROM titles

WHERE sales > ANY

(SELECT sales

FROM title_authors ta

INNER JOIN titles t

ON t.title_id = ta.title_id

WHERE ta.au_id = 'A06');

title_id title_name

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

T02 200 Years of German Humor

T03 Ask Your System Administrator

T04 But I Did It Unconsciously

T05 Exchange of Platitudes

T06 How About Never?

T07 I Blame My Mother

T09 Kiss My Boo-Boo

T11 Perhaps It's a Glandular Problem

T12 Spontaneouse, Not Annoying

T13 What Are You Civilian Applications?

Рис. 8.49. Результат выполнения листинга 8.49

Сравнение некоторых значений запроса с помощью ключевого слова ANY

Page 340: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

340 Подзапросы

здесь не нужно. Мы можем повторить ли-стинг 8.49 с помощью предложений GROUPBY, HAVING и MIN() (вместо ANY):SELECT title_id

FROM titles

GROUP BY title_id

HAVING MIN (sales) >

(SELECT MIN (sales)

FROM title_authors ta

INNER JOIN titles t

ON t.title_id = ta.title_id

WHERE ta.au_id = 'A06');

Ключевое слово ANY эквивалентно IN, од-нако <> ANY не является эквивалентом NOTIN. Если запрос считал значения x, y, z, товыражение:

test_expr <> ANY (subquery)

является эквивалентомtest_expr <> x OR

test_expr <> y OR

test_expr <> z

Но выражение

test_expr NOT IN (subquery)

является эквивалентомtest_expr <> x AND

test_expr <> y AND

test_expr <> z

(NOT IN эквивалентно <> ALL).

В SQL слова ANY и SOME являются синони-мами. Во многих СУБД вы можете исполь-зовать ключевое слово SOME вместо ANY.

MySQL 4.0 и более ранних версий не под-держивает подзапросы. За дополнитель-ной информацией обращайтесь к приме-чанию DBMS в разделе «Принципы рабо-ты с подзапросами» ранее в этой главе.

Page 341: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

341

Проверка наличия выборкис помощью оператораEXISTSВ этой главе мы использовали операторыIN, ALL и ANY, чтобы сравнить значения по-лей в условии фильтрации со значениями,полученными с помощью подзапроса.Операторы EXISTS и NOT EXISTS не срав-нивают значения, а проверяют, вернулли подзапрос хотя бы одну строку илинет. Перечислим основные параметры та-кой проверки:

тест на наличие строк в результате вы-полнения подзапроса не сравниваетзначения, поэтому не сопровождаетсяоператором сравнения;запрос может быть простым или слож-ным, но обычно он сложный (см. раз-дел «Простые и сложные запросы» ра-нее в этой главе);

запрос может считать любое количе-ство столбцов и строк;

предложение SELECT в подзапросе зада-ется как SELECT * для считывания всехстолбцов. Нет необходимости указы-вать отдельные названия столбцов, таккак EXISTS просто проверяет наличиестрок, которые соответствуют условиюзапроса; сами значения в строках нерассматриваются;

все запросы с IN, ALL и ANY могут сопро-вождаться EXISTS или NOT EXISTS. В неко-торых примерах далее мы приведемэквивалентные выражения;если запрос считывает хотя бы однустроку, тест на EXISTS становится истин-ным, а тест на NOT EXISTS – ложным;

если запрос не считывает строки, тестна NOT EXISTS становится истинным,а тест на EXISTS – ложным;

строка запроса, содержащая во всехполях NULL, все равно считается стро-кой (тест на EXISTS становится истин-ным, а тест на NOT EXISTS – ложным);

поскольку тест EXISTS не выполняетсравнений, он не имеет проблем со зна-чениями null, как тесты, использую-щие операторы сравнения (IN, ALL илиANY.

Выполнение теста на наличие выборки

В предложении WHERE команды SELECT напе-чатайте WHERE [NOT] EXISTS (subquery).

Здесь subquery – это запрос, который считы-вает любое количество столбцов и строк.

Если запрос считывает хотя бы однустроку, условие EXISTS задается как истин-ное. Если запрос считывает пустые стро-ки, условие EXISTS задается как ложное.Чтобы изменить результат тестирования,укажите NOT.

Предложение HAVING имеет такую жеструктуру:

HAVING [NOT] EXISTS (subquery)

Листинг 8.50 отображает список всех изда-тельств, которые опубликовали биогра-фии. Этот запрос поочередно рассмат-ривает информацию о каждом издатель-стве и определяет, не выполняется лидля него тест на наличие результата под-запроса. Первое издательство – P01 (AbatisPublishers). СУБД определяет, существуютли в таблице titles строки, в которых

Проверка наличия выборки с помощью оператора EXISTS

Page 342: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

342 Подзапросы

Листинг 8.50. Отобразить список всехиздательств, которые опубликовали биографии.Результат показан на рис. 8.50

SELECT pub_name

FROM publishers p

WHERE EXISTS

(SELECT *

FROM titles t

WHERE t.pub_id = p.pub_id

AND type = 'biography');

pub_name

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

Abatis Publishers

Achadenfreude Press

Рис. 8.50. Результат выполнения листинга 8.50

Листинг 8.51. Отобразить список авторов, которыене написали ни одной книги (в том числев соавторстве). Результат представлен на рис. 8.51

SELECT au_id, au_fname, au_lname

FROM authors a

WHERE NOT EXISTS

(SELECT *

FROM title_authors ta

WHERE ta.au_id = a.au_id);

au_id au_fname au_lname

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

A07 Paddy O'Furniture

Рис. 8.51. Результат выполнения листинга 8.51

pub_id – это P01, а type – biography. Если та-кая строка есть, то Abatis Publishers включа-ется в конечный результат. СУБД повторяетэту процедуру для всех издательств. Резуль-тат исполнения листинга см. на рис. 8.50.Если мы желаем отобразить информациюоб издательствах, которые не публикова-ли биографии, следует заменить EXISTS наNOT EXISTS. См. листинг 8.33 ранее в этойглаве (эквивалентный запрос, который ис-пользует IN).

Листинг 8.51 отображает список авторов,которые не написали ни одной книги (в томчисле в соавторстве). Результат исполнениялистинга см. на рис. 8.51. См. листинг 8.35ранее в этой главе (эквивалентный запрос,который использует NOT IN).

Листинг 8.52 отображает список авторов,которые живут в городе, где расположе-но издательство. Результат исполнениялистинга представлен на рис. 8.52. См. лис-тинг 8.47 ранее в этой главе (эквивалент-ный запрос, который использует + ANY).

Вспомните из раздела «Поиск общих строкс помощью команды INTERSECT» в главе 7о том, что вы можете использовать коман-ду INTERSECT, чтобы считать общие строкидля двух таблиц. Также для этого вы мо-жете использовать оператор EXISTS. Лис-тинг 8.53 содержит список городов, в кото-рых живут авторы и расположены изда-тельства. Результат исполнения листингапоказан на рис. 8.53. См. листинг 7.46 в гла-ве 7 (эквивалентный запрос, который ис-пользует команду INTERSECT).

Page 343: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

343

Листинг 8.52. Отобразить список авторов,которые живут в городе, где расположеноиздательство. Результат см. на рис. 8.52

SELECT au_id, au_lname, au_fname, city

FROM authors a

WHERE EXISTS

(SELECT *

FROM publishers p

WHERE p.city = a.city);

au_id au_lname au_fname city

——--- ———----— ——------—— ———-------———

A03 Hull Hallie San Francisco

A04 Hull Klee San Francisco

A05 Kells Christian New York

Рис. 8.52. Результат выполнения листинга 8.52

Листинг 8.53. Отобразить список городов,в которых живут авторы и расположеныиздательства. Результат представлен на рис. 8.53

SELECT DISTINCT city

FROM authors a

WHERE EXISTS

(SELECT *

FROM publishers p

WHERE p.city = a.city);

city

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

New York

San Francisco

Рис. 8.53. Результат выполнения листинга 8.53

Вы также можете повторить этот запросс помощью внутреннего объединения:

SELECT DISTINCT a.city

FROM authors a

INNER JOIN publishers p

ON a.city = p.city;

Вспомните из раздела «Поиск разных строкс помощью команды EXCEPT» в главе 7о том, что вы можете использовать коман-ду EXCEPT, чтобы считать строки из однойтаблицы, которых нет в другой таблице.Также для этого вы можете использоватьоператор NOT EXISTS. Листинг 8.54 отобра-жает список городов, в которых живут ав-торы, но нет издательств. Результат испол-нения листинга см. на рис. 8.54. См. лис-тинг 7.47 в главе 7 (эквивалентный запрос,который использует EXCEPT).

Листинг 8.54. Отобразить список городов,в которых живут авторы, но нет издательств.Результат см. на рис. 8.54

SELECT DISTINCT city

FROM authors aWHERE NOT EXISTS

(SELECT * FROM publishers p

WHERE p.city = a.city);

Рис. 8.54. Результат выполнения листинга 8.54

city

---------Boulder

BronxPalo Alto

Sarasota

Проверка наличия выборки с помощью оператора EXISTS

Page 344: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

344 Подзапросы

Листинг 8.56. Отобразить список авторов,которые написали (в том числе в соавторстве) каккниги для детей, так и книги по психологии.Результат см. на рис. 8.56

SELECT au_id, au_fname, au_lname

FROM authors a

WHERE EXISTS

(SELECT *

FROM title_authors ta

INNER JOIN titles t

ON t.title_id = ta.title_id

WHERE ta.au_id = a.au_id

AND t.type = 'children')

AND EXISTS

(SELECT *

FROM title_authors ta

INNER JOIN titles t

ON t.title_id = ta.title_id

WHERE ta.au_id = a.au_id

AND t.type = 'psychology');

au_id au_fname au_lname

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

A06 Kellsey

Рис. 8.56. Результат выполнения листинга 8.56

Вы также можете повторить этот запросс помощью оператора NOT IN:SELECT DISTINCT city

FROM authors

WHERE city NOT IN

(SELECT city

FROM publishers);

Или с помощью внешнего объединения:SELECT DISTINCT a.city

FROM authors a

LEFT OUTER JOIN publishers p

ON a.city = p.city

WHERE p.city IS NULL;

Листинг 8.55 отображает список авто-ров, которые написали (в том числе в со-авторстве) три или более книги. Резуль-тат исполнения листинга представлен нарис. 8.55.

Листинг 8.56 содержит два условия на на-личие выборки, чтобы отобразить спи-сок авторов, которые написали (в томчисле в соавторстве) не только книги длядетей, но и книги по психологии. Резуль-тат исполнения листинга представлен нарис. 8.56.

Листинг 8.57 выполняет тест на уникаль-ность, чтобы определить, есть ли повторыв столбце au_id таблицы authors. Еслив столбце есть повторяющиеся строки, за-прос отобразит Yes; в противном случае ре-зультат будет пустым, то есть ни одна стро-ка возвращена не будет. Результат исполне-ния листинга показан на рис. 8.57. au_idявляется ключевым столбцом в таблицеauthors, поэтому не содержит повторов.

Листинг 8.58 выполняет аналогичный тестдля таблицы title_authors, которая дей-ствительно содержит повторяющиеся зна-чения au_id. Результат исполнения листин-га показан на рис. 8.58. Вы можете добавитьк предложению GROUP BY сгруппированныестолбцы, чтобы определить, повторяютсяли они.

Листинг 8.55. Отобразить список авторов,которые написали (в том числе в соавторстве) триили более книги. Результат см. на рис. 8.55

SELECT au_id, au_fname, au_lname

FROM authors a

WHERE EXISTS

(SELECT *

FROM title_authors ta

WHERE ta.au_id = a.au_id

HAVING COUNT(*) >= 3);

au_id au_fname au_lname

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

A01 Sarah Buchman

A02 Wendy Heydemark

A04 Klee Hull

A06 Kellsey

Рис. 8.55. Результат выполнения листинга 8.55

Page 345: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

345

Вы также можете использовать функциюCOUNT(*), чтобы определить, считыва-ет ли запрос хотя бы одну строку. CO-UNT(*), как правило, не так эффектив-на, как EXISTS. СУБД перестает обрабаты-вать запрос EXISTS тогда, когда опреде-лит, что он считывает хотя бы одну стро-ку. В то же время COUNT(*) заставляетСУБД обработать весь запрос. Он экви-валентен листингу 8.52, но работает мед-леннее:

SELECT au_id, au_lname, au_fname, city

FROM authors a

WHERE

(SELECT COUNT(*)

FROM publishers p

WHERE p.city = a.city) > 0;

Хотя SELECT * является самой распрост-раненной формой предложения SELECTв запросе EXISTS, вы можете использоватьзапрос SELECT column или SELECT con-stant_value, чтобы ускорить выполнениезапроса (это следует делать в том случае,если СУБД не может самостоятельно опре-делить, что ей не нужно создавать всютаблицу для запроса EXISTS). За допол-нительной информацией обращайтесь кразделу «Сравнение эквивалентных запро-сов» далее в этой главе.

Хотя мы используем SELECT COUNT(*) в не-которых специальных запросах (см. приме-чание DBMS к данному разделу), выдолжны быть внимательными при исполь-зовании функции в предложении SELECTзапроса. Например, тест на наличие (см.листинг 8.59) будет истинным, так какCOUNT(*) всегда будет считывать стро-ку (в данном случае с нулем). Результатлистинга (см. рис. 8.59) является ошибоч-ным, так как не существует издательствас индексом XXX.

Листинг 8.57. Проверить, есть ли повторяющиесязначения в столбце au_id таблицы authors.Результат см. на рис. 8.57

SELECT DISTINCT 'Yes' AS "Duplicates?"

WHERE EXISTS

(SELECT *

FROM authors

GROUP BY au_id

HAVING COUNT(*) > 1);

Duplicates?

-----------

Рис. 8.57. Результат выполнения листинга 8.57

Листинг 8.58. Проверить, есть ли повторяющиесязначения в столбце au_id таблицы title_authors.Результат см. на рис. 8.58

SELECT DISTINCT 'Yes' AS "Duplicates?"

WHERE EXISTS

(SELECT *

FROM title_authors

GROUP BY au_id

HAVING COUNT(*) > 1);

Duplicates?

-----------

Yes

Рис. 8.58. Результат выполнения листинга 8.58

Проверка наличия выборки с помощью оператора EXISTS

Page 346: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

346 Подзапросы

Листинг 8.59. Вы должны быть внимательнымипри использовании функций в предложенииSELECT запроса. Результат см. на рис. 8.59

SELECT pub_id

FROM publishers

WHERE EXISTS

(SELECT COUNT(*)

FROM titles

WHERE pub_id = 'XXX');

pub_id

------

P01

P02

P03

P04

Рис. 8.59. Результат выполнения листинга 8.59

MySQL 4.0 и более ранних версий не под-держивает подзапросы. За дополнительнойинформацией обращайтесь к примечаниюDBMS в разделе «Принципы работы с под-запросами» ранее в этой главе.

Чтобы запустить листинги 8.57 и 8.58в Oracle, добавьте предложение FROM DUALк внешнему запросу (см. примечание DBMSк разделу «Создание производных столб-цов» в главе 5).

Чтобы запустить листинги 8.55, 8.57 и8.58 в Microsoft Access, напечатайте (лис-тинг 8.55):

SELECT au_id, au_fname, au_lname

FROM authors a

WHERE EXISTS

(SELECT COUNT(*)

FROM title_authors ta

WHERE ta.au_id = a.au_id

HAVING COUNT(*) >= 3);

(листинг 8.57):

SELECT DISTINCT 'YES' AS

"Duplicates?"

FROM authors

WHERE EXISTS

(SELECT COUNT(*)

FROM authors

GROUP BY au_id

HAVING COUNT(*) >= 1);

(листинг 8.58):

SELECT DISTINCT 'YES' AS

"Duplicates?"

FROM title_authors

WHERE EXISTS

(SELECT COUNT(*)

FROM title_authors

GROUP BY au_id

HAVING COUNT(*) > 1);

Чтобы запустить листинги 8.55, 8.57 и 8.58в PostgreSQL, напечатайте (листинг 8.55):

SELECT au_id, au_fname, au_lname

FROM authors a

WHERE EXISTS

(SELECT COUNT(*)

FROM title_authors ta

WHERE ta.au_id = a.au_id

HAVING COUNT(*) >= 3);

(листинг 8.57):

SELECT 'YES' AS "Duplicates?"

WHERE EXISTS

(SELECT COUNT(*)

FROM authors

GROUP BY au_id

HAVING COUNT(*) > 1);

(листинг 8.58):

SELECT 'YES' AS "Duplicates?"

WHERE EXISTS

(SELECT COUNT(*)

FROM title_authors

GROUP BY au_id

HAVING COUNT(*) > 1);

Page 347: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

347

Сравнениеэквивалентных запросовКак вы уже знаете, один и тот же запросможно отобразить разными способами(разная структура, одинаковая семанти-ка). Чтобы разъяснить эту особенность,мы написали один и тот же запрос шес-тью семантически одинаковыми способа-ми. Каждая из команд в листинге 8.60 ото-бражает список авторов, которые написа-ли (в том числе в соавторстве) хотя быодну книгу. Результат исполнения лис-тинга показан на рис. 8.60.Первые два запроса (внутреннее объедине-ние) будут работать с одинаковой скорос-тью. Третий запрос (использует подзапро-сы), пожалуй, самый медленный из всех.СУБД может (и, вероятно, так и сделает)остановить обработку других запросов,если найдет одно соответствующее значе-ние. Но запрос в последней команде дол-жен пересчитать все подходящие строки дотого, как определить соответствие истинно-му или ложному условию. Внутренние объ-единения должны работать примерно с тойже скоростью, что и самая быстрая коман-да, которая использует запросы.Вам может понравиться эта гибкость принаписании программы, но ее не любятспециалисты, которые создают программыдля оптимизации работы СУБД. Причинаэтого в том, что им приходится рассчиты-вать все возможные способы для выраже-ния запроса, определять, какой из них рабо-тает быстрее, а также переформулироватьваш запрос оптимальным образом (а на этоуходит очень много времени). Если вашаСУБД располагает хорошей программойоптимизации, она будет обрабатывать всешесть запросов листинга 8.60 с одинаковойскоростью. Но на это не стоит рассчиты-вать, поэтому вам придется поэксперимен-тировать с СУБД, чтобы увидеть, какаяверсия функционирует быстрее всех.

Листинг 8.60. Запросы отображают списокавторов, которые написали (в том числев соавторстве) хотя бы одну книгу. Результатпоказан на рис. 8.60

SELECT DISTINCT a.au_id

FROM authors a

INNER JOIN title_authors ta

ON a.au_id = ta.au_id;

SELECT DISTINCT a.au_id

FROM authors a, title_authors ta

WHERE a.au_id = ta.au_id;

SELECT au_id

FROM authors a

WHERE au_id IN

(SELECT au_id

FROM title_authors);

SELECT au_id

FROM authors a

WHERE au_id = ANY

(SELECT au_id

FROM title_authors);

SELECT au_id

FROM authors a

WHERE EXISTS

(SELECT *

FROM title_authors ta

WHERE a.au_id = ta.au_id);

SELECT au_id

FROM authors a

WHERE 0 <

(SELECT COUNT(*)

FROM title_authors ta

WHERE a.au_id = ta.au_id);

au_id

-----

A01

A02

A03

A04

A05

A06

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

Сравнение эквивалентных запросов

Page 348: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

Добавление,обновлениеи удаление строк 99999

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

команда INSERT добавляет в таблицуновые строки;команда UPDATE изменяет значения в су-ществующих строках таблицы;команда DELETE удаляет строки из таб-лицы.

Перечисленные команды не считываютрезультат, но ваша СУБД выдаст сообще-ние о том, была ли выполнена командаили нет (и, соответственно, была ли изме-нена таблица или нет). Чтобы увидеть эф-фект, который команда произвела на таб-лицу, введите SELECT, например SELECT *FROM table.В отличие от SELECT, которая только полу-чает доступ к данным, указанные коман-ды изменяют данные, поэтому админист-ратор базы данных может не разрешитьвам их использовать.

Page 349: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

349

Отображение названийстолбцов в таблицеЧтобы использовать команды INSERT, UP-DATE или DELETE, необходимо знать назва-ния столбцов той таблицы, данные в кото-рой вы изменяете, а именно:

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

В табл. 2.2–2.6 главы 2 мы привели обозна-чения столбцов для таблиц в базе данных.Вы можете получить эту информациюс помощью инструментов СУБД, которыеработают с объектами баз данных. В этомразделе мы расскажем, как использоватьтакие инструменты, чтобы отобразить на-звания столбцов.

Порядок отображения названийстолбцов таблицы в Microsoft Access

1. Нажмите клавишу F11, чтобы открытьокно Database (База данных).

2. Щелкните по кнопке Tables (Таблицы),которая находится под кнопкой Objects(Объекты).

3. Щелкните по таблице в списке.

4. Нажмите на кнопку Design (Конструк-тор) на панели инструментов, чтобыоткрыть таблицу в режиме редактиро-вания – Design View (см. рис. 9.1).

Рис. 9.1. Отображение названий столбцовв Microsoft Access

Отображение названий столбцов в таблице

Page 350: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

350 Добавление, обновление и удаление строк

Порядок отображения названийстолбцов таблицы в Microsoft SQLServer

1. Запустите SQL Query Analyzer или кон-сольный интерпретатор osql (см. раздел«Microsoft SQL Server» в главе 1).Команда выдает очень много информа-ции, поэтому мы рекомендуем выбратьпункты меню Query ⇒ Results in Grid(Запрос ⇒ Результат в виде таблицы) вSQL Query Analyzer.

2. Введите команду sp_help table; (table –это название таблицы).

3. В SQL Query Analyzer выберите пунктыменю Query ⇒ Execute (Запрос ⇒ Вы-полнить) либо нажмите клавишу F5(см. рис. 9.2) или в строке osql нажмитеклавишу Enter, введите go и нажмите наEnter.

Порядок отображения названийстолбцов таблицы в Oracle

1. Запустите программу SQL*Plus или ути-литу командной строки sqlplus (см. раз-дел «Oracle» в главе 1).

2. Введите describe table;, нажмите клави-шу Enter (см. рис. 9.3). table – это назва-ние таблицы.

Порядок отображения названийстолбцов таблицы в MySQL

1. Запустите монитор mysql (см. раздел«MySQL» в главе 1).

2. Введите describe table;, нажмите клави-шу Enter (см. рис. 9.4). table – это назва-ние таблицы.

Рис. 9.2. Отображение названий столбцовтаблицы в Microsoft SQL Server

Рис. 9.3. Отображение названий столбцовтаблицы в Oracle

Рис. 9.4. Отображение названий столбцовтаблицы в MySQL

Page 351: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

351

Порядок отображения названийстолбцов таблицы в PostgreSQL

1. Запустите монитор psql (см. раздел«PostgreSQL» в главе 1).

2. Введите \d table, нажмите клавишуEnter (см. рис. 9.5). table – это названиетаблицы. Обратите внимание, что пос-ле команды вы не ставите точку с запя-той.

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

SELECT * FROM table WHERE 1 = 2;

table – это название таблицы, а 1 = 2 ото-бражает какое-либо условие, которое всег-да будет ложным.

За общей информацией о столбцах обра-щайтесь к разделу «Таблицы, столбцы истроки» в главе 2. За дополнительной ин-формацией о ключах обращайтесь к раз-делам «Первичные ключи» и «Внешниеключи» в главе 2. За информацией о ти-пах данных обращайтесь к разделу «Типыданных» в главе 3.

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

Рис. 9.5. Отображение названий столбцовтаблицы в PostgreSQL

Отображение названий столбцов в таблице

Page 352: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

352 Добавление, обновление и удаление строк

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

добавить строку с помощью положениястолбцов в таблице (INSERT VALUES);

добавить строку с помощью названийстолбцов (INSERT VALUES);

добавить строки из одной таблицыв другую (INSERT SELECT).

Перечислим важные параметры командыINSERT:

при добавлении строк с помощью по-ложения в таблице вы добавляете зна-чения в новую строку в той же по-следовательности, в которой они по-являются в таблице (см. раздел «До-бавление строки с помощью положениястолбца» далее в этом разделе). Привставке строки в столбец вы задаете на-звание столбца, в который добавляетезначения для новой строки (см. подраз-дел «Добавление строки с помощью на-званий столбцов» далее в этом разделе).

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

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

INSERT VALUES добавляет в таблицу однустроку, а INSERT SELECT – любое количе-ство строк;каждое добавленное значение должнобыть того же типа (или иметь возмож-ность для конвертации), что и другиеданные в столбце (см. раздел «Преобра-зование типов данных с помощью фун-кции CAST()» в главе 5);чтобы сохранить систему ссылок, встав-ленный внешний ключ должен содер-жать либо NULL, либо значение существу-ющего ключа из первичной или уни-кальной ссылки вторичного ключа (см.разделы «Первичные ключи» и «Вне-шние ключи» в главе 2);добавленное значение не может отме-нить ограничения. См. раздел «Проверказначений столбца с помощью огра-ничения CHECK» в главе 10;ни одно выражение не должно приводитьк арифметической ошибке (например,переполнению или делению на нуль);вспомните из раздела «Таблицы, столбцыи строки» в главе 2, что порядок строкв таблице не имеет значения и что вы неможете управлять расположением строк,поэтому новые строки могут появиться влюбом месте таблицы.

Добавление строкис помощью положения столбца

Введите:INSERT INTO table

VALUES(value1, value2, …, valueN);

table – это название таблицы, в которуювы добавляете строку, value1, value2, …,valueN – это список буквенных обозначе-ний или выражений, который задает значе-ния для всех столбцов в новой строке.

Page 353: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

353

Количество значений должно соответство-вать количеству столбцов в table, а значе-ния должны быть указаны в той же после-довательности, что и столбцы. СУБДвставляет каждое значение в столбец,который совпадает с положением значе-ния в table, value1 добавляется в первыйстолбец новой строки, value2 – во второйстолбец и т.д.Команда-пример добавит в таблицу однустроку (см. листинг 9.1).

Добавление строкис помощью названий столбцов

Введите:INSERT INTO table

(column1, column2, …, columnN)

VALUES(value1, value2, …, valueN);

table – это название таблицы, в которуювы добавляете строку; column1, column2, …,columnN – список названий столбцов вtable; value1, value2, …, valueN – список бук-венных обозначений или выражений, кото-рые задают значения для указанных стол-бцов в новой строке.Количество значений должно соответство-вать количеству столбцов в списке, а зна-чения должны быть указаны в той же по-следовательности, что и названия столбцов.СУБД вставляет каждое значение в стол-бец, используя соответствующие значенияв списке. Значение value1 добавляетсяв столбец column1 новой строки, значениеvalue2 – в столбец column2 и т.д. Пропущен-ному столбцу присваивается значение поумолчанию или NULL.Команда-пример добавит в таблицу однустроку. Проще всего указывать названиястолбцов в том же порядке, в котором ониприведены в таблице (см. листинг 9.2), но выможете перечислить их в произвольном

Листинг 9.1. Команда INSERT добавит в таблицуauthors новую строку, вставив значения в томпорядке, в котором идут столбцы в списке.Результат исполнения листинга показан на рис. 9.6

INSERT INTO authors

VALUES(

'A08',

'Michael',

'Polk',

'512-953-1231',

'4028 Guadalupe St',

'Austin',

'TX',

'78701');

Листинг 9.2. Команда INSERT добавит в таблицуauthors новую строку, вставив значения в томпорядке, в котором идут столбцы в списке.Результат исполнения листинга показан на рис. 9.6

INSERT INTO authors(

au_id,

au_fname,

au_lname,

phone,

address,

city,

state,

zip)

VALUES(

'A09',

'Irene',

'Bell',

'415-225-4689',

'810 Throckmorton Ave',

'Mill Valley',

'CA',

'94941');

Вставка строк с помощью команды INSERT

Page 354: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

354 Добавление, обновление и удаление строк

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

Если вы желаете указать значения толькодля определенных столбцов, можете про-пустить некоторые столбцы (см. лис-тинг 9.4). Если вы пропустите столбец, СУБДдолжна сама указать для него значение наосновании названия столбца. СУБД добавитзначение по умолчанию (если оно было за-дано) или NULL (если возможно). Если вы про-пустите столбец, который не имеет значе-ния по умолчанию и в который нельзя доба-вить NULL, СУБД выдаст сообщение обошибке и не добавит новую строку. За ин-формацией о том, как задать значение поумолчанию и разрешить значение null длястолбца, обращайтесь к разделам «Присво-ение значения по умолчанию с помощьюограничения DEFAULT» и «Запрет значенияnull с помощью ограничения NOT NULL» вглаве 10. На рис. 9.6 показаны новые стро-ки в таблице authors после исполнения ли-стингов 9.1–9.4.

Добавление строкиз одной таблицы в другую

Введите:

INSERT INTO table

[(column1, column2, …, columnN)]

select_statement;

table – это название таблицы, в которуювы добавляете строку; column1, column2, …,columnN – список названий столбцов в ней;select_statement – любая команда SELECT,считывающая строки данных, которыедолжны быть добавлены в таблицу.

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

INSERT INTO authors(

zip,

phone,

address,

au_lname,

au_fname,

state,

au_id,

city)

VALUES(

'60614',

'312-998-0020',

'1937 N. Clark St',

'Weston',

'Dianne',

'IL',

'A10',

'Chicago');

Листинг 9.4. Мы добавили строку для новогоавтора, но пропустили столбцы и значения дляадреса автора. СУБД автоматически добавилав пропущенные столбцы NULL. Результатисполнения листинга показан на рис. 9.6

INSERT INTO authors(

au_id,

au_fname,

au_lname,

phone)

VALUES(

'A11',

'Max',

'Allard',

'212-502-0955');

Page 355: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

355

Количество столбцов в результате исполне-ния команды select_statement должно соот-ветствовать количеству столбцов в table илисписке столбцов. СУБД игнорирует назва-ния столбцов при выполнении командыselect_statement и вместо них используетположение в столбце. Для первого столбцав table или column1 используется первыйстолбец select_statement в процессе выпол-нения и т.д. Пропущенному столбцу задает-ся значение по умолчанию или NULL.

au_id au_fname au_lname phone address city state zip

----- ---------- ------------ ------------ --------------------- —---------———— ——--- —----—

A01 Sarah Buchman 718-496-7223 75 West 205 St Bronx NY 10468

A02 Wendy Heydemark 303-986-7020 2922 Baseline Rd Boulder CO 80303

A03 Hallie Hull 415-549-4278 3800 Waldo Ave, #14F San Francisco CA 94123

A04 Klee Hull 415-549-4278 3800 Waldo Ave, #14F San Francisco CA 94123

A05 Christian Kells 212-771-4680 114 Horatio St New York NY 10014

A06 Kellsey 650-836-7128 390 Serra Mall Palo Alto CA 94305

A07 Paddy O’Furniture 941-925-0752 1442 Main St Sarasota FL 34236

A08 Michael Polk 512-953-1231 4028 Guadalupe St Austin TX 78701

A09 Irene Bell 415-225-4689 810 Throckmorton Ave Mill Valley CA 94941

A10 Dianne Weston 312-998-0020 1937 N. Clark St Chicago IL 60614

A11 Max Allard 212-502-0955 NULL NULL NULL NULL

Рис. 9.6. После исполнения листингов 9.1–9.4 в таблицу authors были добавлены четыре новые строки

Команда-пример добавит в table несколь-ко строк.В остальных примерах этого раздела ис-пользуется таблица new_publishers (см.рис. 9.7), которую мы создали, чтобы пока-зать, как работает команда INSERT SELECT.Таблица new_publishers имеет ту же струк-туру, что и publishers, и используется толь-ко в качестве источника новых строк, но неизменяется в результате исполнения коман-ды INSERT.

pub_id pub_name city state country

—---—— ——————————----------- ——-----———- ——---- —-------—————-

P05 This is Pizza? Press New York NY USA

P06 This is Beer? Press Toronto ON Canada

P07 This is Irany? Press London NULL United Kindom

P08 This is Fame? Press Los Angeles CA USA

Рис. 9.7. Таблица new_publishers используется в листингах 9.5–9.7. Она имеет такую же структуру, кактаблица publishers

Вставка строк с помощью команды INSERT

Page 356: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

356 Добавление, обновление и удаление строк

Листинг 9.5 добавляет строки с информа-цией об издательствах Лос-Анджелеса изтаблицы new_publishers в publishers. Мыпропустили список столбцов, поэтомуСУБД будет использовать положениястолбцов в таблице publishers для добав-ления значений. Команда листинга 9.5 до-бавит в publishers одну строку; результатисполнения листинга показан на рис. 9.8.

Листинг 9.6 добавляет строки с информа-цией о неамериканских издательствах изтаблицы new_publishers в publishers. На-звания столбцов в пунктах INSERT и SELECTздесь одинаковые, однако это необязатель-но, так как СУБД игнорирует названиястолбцов, считанные командой SELECT, ииспользует их положения. Эта команда до-бавит в publishers две строки; результатисполнения листинга показан на рис. 9.8.

Предложение SELECT может считать пустойрезультат (нуль строк). Листинг 9.7 до-бавляет строки с информацией об изда-тельствах XXX из таблицы new_publishersв publishers. Мы можем использоватьSELECT* вместо списка названий столбцов,так как new_publishers и publishers имеютодинаковую структуру. Команда из лис-тинга 9.7 не добавит в publishers ни однойстроки, поскольку издательства с названи-ем XXX в таблице new_publishers нет.

Листинг 9.5. Добавить строки с информациейоб издательствах Лос-Анджелеса из таблицыnew_publishers в publishers. Результатисполнения листинга см. на рис. 9.8

INSERT INTO publishers

SELECT

pub_id,

pub_name,

city,

state,

country

FROM new_publishers

WHERE city = 'Los Angeles';

Листинг 9.6. Добавить строки с информацией онеамериканских издательствах из таблицыnew_publishers в publishers. Результатисполнения листинга см. на рис. 9.8

INSERT INTO publishers(

pub_id,

pub_name,

city,

state,

country)

SELECT

pub_id,

pub_name,

city,

state,

country

FROM new_publishers

WHERE country <> 'USA';

pub_id pub_name city state country

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

P01 Abatis Publishers New York NY USA

P02 Core Dump Books San Francisco CA USA

P03 Schadenfreude Press Hamburg NULL Germany

P04 Tenterhooks Press Berkeley CA USA

P06 This is Beer? Press Toronto ON Canada

P07 This is Irany? Press London NULL United Kindom

P08 This is Fame? Press Los Angeles CA USA

Рис. 9.8. После исполнения листингов 9.5–9.7 в таблицу publishers были добавлены три новые строки

Page 357: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

357

Листинг 9.7. Добавить строки с информациейоб издательствах XXX из таблицыnew_publishers в publishers. Результатисполнения листинга см. на рис. 9.8

INSERT INTO publishers(

pub_id,

pub_name,

city,

state,

country)

SELECT *

FROM new_publishers

WHERE pub_name = 'XXX';

На рис. 9.8 показана таблица publishersпосле исполнения листингов 9.5–9.7.

Процесс добавления строк в таблицу в пер-вый раз называется заполнением таблицы.

Вы можете использовать команду SELECTINTO, чтобы создать новую таблицу и за-полнить ее строками, которые вернет ко-манда SELECT (см. раздел «Создание новойтаблицы на основе существующей с помо-щью команды SELECT INTO» в главе 10).

Если вы желаете быть особенно вниматель-ными при добавлении строк, можете прове-рить команду INSERT с помощью временнойтаблицы (см. разделы «Создание временнойтаблицы с помощью команды CREATETEMPORARY TABLE» и «Создание новойтаблицы на основе существующей с помо-щью команды SELECT INTO» в главе 10).

Вы также можете добавлять строки с помо-щью представлений (см. раздел «Изменениеданных через представления» в главе 12).

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

Если table1 и table2 имеют одинаковуюструктуру, можете вставить все строки изtable2 в table1. Для этого введите:

INSERT INTO table1

SELECT * FROM table2;

В SQL ключевое слово INTO является опциейдля команды INSERT. Microsoft Access,Oracle и PostgreSQL обязательно требуютввода INTO, поэтому мы никогда его непропускаем. Посмотрите в документации,как ваша СУБД обрабатывает значения привставке в столбцы, данные в которых ав-томатически создают новый первичныйключ для строки (см. примечание DBMSв разделе «Первичные ключи» главы 2).СУБД может не разрешить добавлениезначений в эти столбцы.

Вставка строк с помощью команды INSERT

Page 358: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

358 Добавление, обновление и удаление строк

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

все строки в таблице;отдельные строки в таблице.

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

Перечислим важные параметры командыUPDATE:

использует предложение WHERE, в кото-ром указывается, какие строки нужноизменить. Без предложения WHERE ко-манда UPDATE изменит все строки в таб-лице;может быть небезопасна, потому чтовы можете случайно пропустить пред-ложение WHERE (и изменить все строки)или неправильно указать условие по-иска для WHERE (и изменить не те стро-ки). Перед запуском команды UPDATEмы рекомендуем запустить командуSELECT с тем же предложением WHERE,что и для обновления строк. КомандаSELECT отобразит все строки, которыебудут изменены СУБД при запуске ко-манды UPDATE. Чтобы отобразить толь-ко количество этих строк, пользуйтеськомандой SELECT COUNT (*);каждое измененное значение должнобыть того же типа (или иметь возмож-ность для конвертации), что и другиеданные в столбце (см. раздел «Преоб-разование типов данных с помощьюфункции CAST()» в главе 5);

чтобы сохранить ссылочную целост-ность, СУБД позволяет указать действие,которое будет выполняться автомати-чески с помощью команды UPDATE приизменении значения, на которое указы-вает вторичный ключ (см. советы к раз-делу «Задание внешнего ключа с помо-щью ограничения FOREIGN KEY» в гла-ве 10);измененное значение не может отменитьограничение, наложенное на столбец(см. раздел «Проверка значений столб-ца с помощью ограничения CHECK»в главе 10);ни одно выражение не должно приво-дить к арифметической ошибке (на-пример, переполнению или делению нануль);вспомните из раздела «Таблицы, столб-цы и строки» в главе 2, что порядокстрок в таблице не имеет значения и чтовы не можете управлять расположениемстрок, поэтому новые строки могут по-явиться в любом месте таблицы.

Изменение строкВведите:UPDATE table

SET column = expr

[WHERE search_condition];

table – это название таблицы, которую выбудете обновлять; column – название столб-ца (с данными для изменения) в table;expr – буквенное обозначение, выражениеили запрос, который считывает одно зна-чение. Значение, считанное expr, заменитсуществующее значение в column. Чтобыизменить значения в нескольких столб-цах, введите в пункте SET список выраже-ний (column = expr), разделенных запяты-ми. Вы можете задавать список полей дляобновления в любом порядке. Условиеsearch_condition задает условия, которыедолжны соблюдаться для изменяемых

Page 359: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

359

строк. Этими условиями могут быть усло-вия WHERE (операторы сравнения, LIKE,BETWEEN, IN и IS NULL, см. главу 4) или усло-вия запроса (операторы сравнения, IN, ALL,ANY и EXISTS, см. главу 8) в комбинациис AND, OR и NOT. Если вы опустите предложе-ние WHERE, будут изменены все строки втаблице.Листинг 9.8 заменяет значение contract ну-лем. Отсутствие предложения WHERE сооб-щает СУБД, что следует изменить значенияв столбце contract во всех строках. Коман-да в листинге 9.8 изменяет 13 строк. Ре-зультат исполнения см. на рис. 9.9.Листинг 9.9 использует арифметическоевыражение и условие WHERE, чтобы удво-ить цену на книги по истории. Команда,представленная в этом листинге, изменяеттри строки. Результат исполнения см.на рис. 9.9.Листинг 9.10 изменяет столбцы type иpages для книг по психологии. Вы исполь-зуете только одно предложение SET, что-бы изменить несколько столбцов, разде-лив выражения column = expr запятыми(не следует ставить запятую в конце по-следнего выражения). Команда из листин-га 9.10 изменяет три строки. Результат ис-полнения показан на рис. 9.9.Листинг 9.11 использует подзапрос ифункцию, чтобы уменьшить в два разапродажи книг, которые продаются лучше,чем все книги в целом. Команда, пред-ставленная в этом листинге, изменяет двестроки. Результат исполнения показан нарис. 9.9.Вы можете изменить значения в таблицена основании значений из другой табли-цы. Листинг 9.12 использует подзапросы,чтобы изменить дату публикации для всехкниг, которые написала Сара Бухманн(Sarah Buchmann). Представленная в этомлистинге команда изменяет три строки.Результат исполнения показан на рис. 9.9.

Листинг 9.8. Заменить значение contract нулемво всех строках titles. Результат исполнениялистинга показан на рис. 9.9

UPDATE titles

SET contract = 0;

Листинг 9.9. Удвоить цену на книги по истории.Результат исполнения листинга показан на рис. 9.9

UPDATE titles

SET price = price * 2.0

WHERE type = 'history';

Листинг 9.10. Изменить столбцы type и pagesдля книг по психологии. Результат исполнениялистинга см. на рис. 9.9

UPDATE titles

SET type = 'self help',

pages = NULL

WHERE type = 'psychology';

Листинг 9.11. Уменьшить в два раза продажикниг, которые находятся на среднем уровне.Результат исполнения листинга см. на рис. 9.9

UPDATE titles

SET sales = sales * 0.5

WHERE sales >

(SELECT AVG(sales)

FROM titles);

Изменение строк с помощью команды UPDATE

Page 360: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

360 Добавление, обновление и удаление строк

Предположим, что издательство AbatisPublishers (P01) купило издательство Ten-terhooks Press (P04). Теперь все книги пос-леднего выпускаются в Abatis Publishers.Листинг 9.13 меняет информацию об изда-тельстве в titles с P04 на P01. Запрос впредложении WHERE считывает pub_id для из-дательства Tenterhooks Press. СУБД исполь-зует pub_id, чтобы считать из таблицыtitles все книги, которые выпускаются из-дательством Tenterhooks Press. Затем СУБДс помощью значения, считанного запросомв предложении SET, изменяет соответству-ющие строки в таблице titles. Посколькузапросы используются с неизменным опе-ратором сравнения, они должны быть ска-лярными запросами и считывать един-ственные значения (то есть результат однойстроки или столбца). См. раздел «Сравне-ние значений, возвращаемых подзапросом,с использованием операторов сравнения» вглаве 8. Команда из листинга 9.13 изменяетпять строк.На рис. 9.9 показана таблица titles послеисполнения листингов 9.8–9.13. Каждыйлистинг изменяет данные в отдельномстолбце (столбцах). Измененные значе-ния выделены полужирным шрифтом.

СУБД будет рассчитывать выражения впредложении SET или WHERE с использова-нием значений, которые находились в столб-цах до начала изменений. Рассмотрим ко-манду UPDATE:

UPDATE mytable

SET col1 = col1 * 2,

col2 = col1 * 4,

col3 = col2 * 8,

WHERE col1 = 1

AND col2 = 2;

СУБД задает col1 равным 2, col2 – рав-ным 4 (1×4, а не 2×4), а col3 – равным16 (2×8, а не 4×8).

Листинг 9.12. Заменить дату публикации для всехкниг, которые написала Сара Бухманн, на 1 января2003 года. Результат исполнения листингапоказан на рис. 9.9

UPDATE titles

SET pubdate = DATE '2003-01-01'

WHERE title_id IN

(SELECT title_id

FROM title_authors

WHERE au_id IN

(SELECT au_id

FROM authors

WHERE au_fname = 'Sarah'

AND au_lname = 'Buchman'));

Листинг 9.13. «Приписать» все книги,выпущенные издательством Tenterhooks Press,издательству Abatis Publishers. Результатисполнения листинга показан на рис. 9.9

UPDATE titles

SET pub_id =

(SELECT pub_id

FROM publishers

WHERE pub_name = 'Abatis Publishers')

WHERE pub_id =

(SELECT pub_id

FROM publishers

WHERE pub_name = 'Tenterhooks Press');

Page 361: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

361

Рис. 9.9. Таблица titles после исполнения листингов 9.8–9.13. Измененные значения отмеченыполужирным шрифтом

title_id title_name type pub_id pages price sales pubdate contract

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

T01 1977! history P01 107 43.98 566 2003-01-01 0

T02 200 Years of German Humor history P03 14 39.90 9566 2003-01-01 0

T03 Ask Your System Administrator computer P02 1226 39.95 25667 2000-09-01 0

T04 But I Did It Unconsciously self help P01 NULL 12.99 13001 1999-05-31 0

T05 Exchange of Platitudes self help P01 NULL 6.95 100720 2001-01-01 0

T06 How About Never? biography P01 473 19.95 11320 2000-07-31 0

T07 I Blame My Mother biography P03 333 23.95 750100 1999-10-01 0

T08 Just Wait Until After School children P01 86 10.00 4095 2001-06-01 0

T09 Kiss My Boo-Boo children P01 22 13.95 5000 2002-05-31 0

T10 Not Without My Faberge Egg biography P01 NULL NULL NULL NULL 0

T11 Perhaps It’s a Glandular Problem self help P01 NULL 7.99 94123 2000-11-30 0

T12 Spontaneous, Not Annoying biography P01 507 12.99 100001 2000-08-31 0

T13 What Are The Civilian Applications? history P03 802 59.98 10467 2003-01-01 0

Если вы желаете быть особенно внима-тельными при изменении строк, можетепроверить команду UPDATE с помощьювременной копии таблицы (см. разделы«Создание временной таблицы с помощьюкоманды CREATE TEMPORARY TABLE» и«Создание новой таблицы на основе суще-ствующей с помощью команды SELECTINTO» в главе 10).

Вы также можете изменять строки с по-мощью представлений (см. раздел «Из-менение данных через представления» вглаве 12).

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

Изменение строк с помощью команды UPDATE

Page 362: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

362 Добавление, обновление и удаление строк

В Microsoft Access при работе с датами ненужно вводить ключевое слово DATE. Вы-делять буквенное значение следует с помо-щью символов #, а не кавычек. Чтобы за-пустить листинг 9.12 в Access, в качествезначения даты используйте #2003-01-01#.

Microsoft Access не поддерживает скаляр-ные запросы в предложени SET. Чтобызапустить листинг 9.13 в Access, раздели-те команду UPDATE на две команды: снача-ла с помощью команды SELECT выберитеpub_id для издательства Abatis Publishersиз таблицы publishers, затем с помощьюpub_id измените значения для всех книгиздательства Tenterhooks Press в таблицеtitles.

В Microsoft SQL Server при работе с датамине следует вводить ключевое слово DATE.Чтобы запустить листинг 9.12 в SQL Server,используйте значение даты '2003-01-01'.

MySQL 4.0 и более ранних версий не под-держивает подзапросы и не сможет вы-полнить листинги 9.11, 9.12 и 9.13. Задополнительной информацией обращай-тесь к примечанию DBMS в разделе «Ос-новные принципы работы с подзапроса-ми» главы 8.

В PostrgeSQL вам следует заменить числас плавающей запятой в листингах 9.9и 9.11 на DECIMAL (см. раздел «Преобра-зование типов данных с помощью функ-ции CAST()» в главе 5). Чтобы запуститьлистинги 9.9 и 9.11 в PostgreSQL, исполь-зуйте значения с плавающей запятой CAST(2.0 AS DECIMAL) (листинг 9.9) и CAST(0.5 AS DECIMAL) (листинг 9.11).

Посмотрите в документации, как вашаСУБД обрабатывает значения при вставке встолбцы, данные в которых автоматичес-ки создают новое значение для первично-го ключа (см. примечание DBMS в разделе«Первичные ключи» главы 2). СУБД можетне разрешить изменять значения в этихстолбцах.

Page 363: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

363

Удаление строкс помощью команды DELETEКоманда DELETE удаляет строки из табли-цы. Вы можете использовать эту команду,чтобы удалять:

все строки в таблице;отдельные строки в таблице.

Чтобы удалить строки, нужно указать:

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

Перечислим важные параметры командыDELETE:

в отличие от команд INSERT и UPDATE, этакоманда не требует ввода названийстолбцов, так как удаляет строки цели-ком;удаляет строки из таблицы, но не удаля-ет саму таблицу. Даже если вы уберетеиз таблицы все строки, сама таблицаостанется. Если вы желаете удалить таб-лицу (вместе со всеми данными, индек-сами и т.д.), обратитесь к разделу «Уда-ление таблицы с помощью командыDROP TABLE» в главе 10;использует (опционально) предложе-ние WHERE, чтобы определить, какиеименно строки следует удалить. Есливы не укажете условие поиска, командаDELETE удалит все строки в таблице;может быть небезопасна, потому чтовы можете случайно пропустить пред-ложение WHERE (и удалить все строки)или неправильно указать условие поис-ка для WHERE (и удалить не те строки).Перед запуском команды DELETE реко-мендуем запустить команду SELECTс та-ким же предложением WHERE. Команда

SELECT отобразит все строки, которыебудут удалены СУБД при запуске ко-манды DELETE. Чтобы отобразить толь-ко количество таких строк, пользуйтеськомандой SELECT COUNT (*);чтобы сохранить ссылочную целост-ность, СУБД позволяет указать дейст-вие, которое будет выполняться автома-тически с помощью команды DELETEпри удалении строк, на которые ука-зывает вторичный ключ (см. советык разделу «Задание внешнего ключа спомощью ограничения FOREIGN KEY» вглаве 10);ни одно выражение не должно приво-дить к арифметической ошибке (на-пример, переполнению или делению нануль);вспомните из раздела «Таблицы, столб-цы и строки» в главе 2, что порядокстрок в таблице не имеет значенияи что вы не можете управлять располо-жением строк, поэтому их удалениеможет произвольным образом изме-нить расположение других строк в таб-лице.

Удаление строк

Введите:DELETE FROM table

[WHERE search_condition];

table – это название таблицы, из которойвы будете удалять строки. search_conditionзадает условия, которые должны соблю-даться для удаляемых строк. Этими усло-виями могут быть условия WHERE (операто-ры сравнения, LIKE, BETWEEN, IN и IS NULL, см.главу 4) или условия запроса (операто-ры сравнения, IN, ALL, ANY и EXISTS, см. гла-ву 8) в комбинации с AND, OR и NOT. Если выопустите предложение WHERE, будут уда-лены все строки в таблице.

Удаление строк с помощью команды DELETE

Page 364: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

364 Добавление, обновление и удаление строк

В следующих примерах мы будем игнори-ровать ссылки. Разумеется, не следует этоделать при работе с настоящей базой дан-ных.Листинг 9.14 удаляет все строки в royalties.Отсутствие предложения WHERE сообщаетСУБД, что следует удалить все строки. Ко-манда, представленная в этом листинге,удаляет 13 строк. Результат исполнения по-казан на рис. 9.10.В листинге 9.15 предложение WHERE сообща-ет СУБД, что из таблицы authors следуетудалить всех авторов с фамилией Халл(Hull). Команда этого листинга удаляетдве строки. Результат исполнения см. нарис. 9.11.

Листинг 9.14. Удалить все строки в royalties.Результат исполнения листинга см. на рис. 9.10

DELETE FROM royalties;

title_id advance royalty_rate

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

Рис. 9.10. Результат выполнения листинга 9.14

Рис. 9.11. Результат выполнения листинга 9.15

au_id au_fname au_lname phone address city state zip

——--- —————---- —————------- ——————------- ————————--------- ————------ ——---- ——----

A01 Sarah Buchman 718-496-7223 75 West 205 St Bronx NY 10468

A02 Wendy Heydemark 303-986-7020 2922 Baseline Rd Boulder CO 80303

A05 Christian Kells 212-771-4680 114 Horatio St New York NY 10014

A06 Kellsey 650-836-7128 390 Serra Mall Palo Alto CA 94305

A07 Paddy O’Furniture 941-925-0752 1442 Main St Sarasota FL 34236

Листинг 9.16. Удалить из title_authors всекниги, выпущенные издательствами P01 и P04.Результат исполнения листинга см. на рис. 9.12

DELETE FROM title_authors

WHERE title_id IN

(SELECT title_id

FROM titles

WHERE pub_id IN ('P01', 'P04'));

Листинг 9.15. Удалить из authors всех авторовс фамилией Халл. Результат исполнения листингасм. на рис. 9.11

DELETE FROM authors

WHERE au_lname = 'Hull';Вы можете использовать данные из однойтаблицы, чтобы удалить данные в другойтаблице. Листинг 9.16 использует подза-прос, чтобы удалить из title_authors всекниги, выпущенные издательствами P01и P04. Эта команда удаляет 12 строк. Ре-зультат исполнения показан на рис. 9.12.

Page 365: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

365

Если вы желаете быть особенно внима-тельными при удалении строк, можетепроверить команду DELETE с помощьювременной копии таблицы (см. разделы«Создание временной таблицы с помощьюкоманды CREATE TEMPORARY TABLE» и«Создание новой таблицы на основе суще-ствующей с помощью команды SELECTINTO» в главе 10).

Вы также можете удалять строки с по-мощью представлений (см. раздел «Из-менение данных через представления»в главе 12).

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

Если вы желаете удалить все строки в таб-лице, лучше использовать командуTRUNCATE вместо DELETE. Команда TRUNCATEне является стандартной командой SQL, ноподдерживается Microsoft SQL Server,Oracle, MySQL и PostgreSQL. Эта командафункционально идентична DELETE без пред-ложения WHERE и тоже удаляет все строкив таблице. Но TRUNCATE работает быстрееи потребляет меньше системных ресурсов,чем DELETE, потому что не сканирует всютаблицу и не записывает результаты в жур-нал (см. главу 13). Есть еще одно отличие:при работе с TRUNCATE вы сможете отме-нить изменения, если допустите ошибку.Приведем структуру команды:

TRUNCATE TABLE table;

table – это таблица, которую вы желаетеизменить. За дополнительной информаци-ей о команде TRUNCATE обращайтесь к ру-ководству по вашей СУБД.

title_id au_id au_order royalty_share

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

T02 A01 1 1.00

T03 A05 1 1.00

T07 A02 1 0.50

T07 A04 2 0.50

T13 A01 1 1.00

Рис. 9.12. Результат выполнения листинга 9.16

Удаление строк с помощью команды DELETE

Page 366: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

366 Добавление, обновление и удаление строк

В SQL ключевое слово FROM является опци-ей для команды DELETE. Microsoft Access,MySQL и PostgreSQL требуют обязательно-го ввода этого слова, поэтому мы нигдеего не опускаем.

MySQL 4.0 и более ранних версий не под-держивает подзапросы и не сможет вы-полнить листинг 9.16. За дополнительнойинформацией обращайтесь к примечаниюDBMS в разделе «Основные принципы ра-боты с подзапросами» главы 8.

Page 367: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

Многие СУБД располагают интерактивны-ми графическими инструментами, с помо-щью которых вы можете создавать и изме-нять таблицы и их свойства, например на-звания столбцов и ограничения. В текущейглаве мы объясним, как использовать инст-рументы SQL, чтобы работать с таблицами:

команда CREATE TABLE создает новуютаблицу;команда ALTER TABLE изменяет структу-ру существующей таблицы;команда DROP TABLE удаляет таблицу ивсе ее данные;команда CREATE TEMPORARY TABLE создаетвременную таблицу, которая затем бу-дет удалена;команда SELECT INTO создает новую таб-лицу на основе уже существующей.

Перечисленные команды не считываютрезультат, но ваша СУБД выдаст сообще-ние о том, была ли выполнена командаили нет. Чтобы увидеть эффект, которыйкоманда произвела на таблицу, изучитеструктуру таблиц с помощью одной изкоманд, описанных в разделе «Отображе-ние названий столбцов в таблице» главы 9.Эти команды изменяют объекты базы дан-ных (а в некоторых случаях и сами дан-ные), вот почему администратор базы дан-ных может не разрешить их использовать.

1010101010Создание, изменениеи удаление таблиц

Page 368: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

368 Создание, изменение и удаление таблиц

Порядок создания таблицДизайнеры баз данных тратят много вре-мени на упорядочивание таблиц и органи-зацию ограничений и ссылок, чтобы затемнаписать одну строку SQL-кода. Если выбудете создавать таблицы для рабочих базданных, мы рекомендуем изучить принци-пы проектирования баз данных, которыеописываются в главе 2.Вспомните из раздела «Таблицы, столбцыи строки» в главе 2, что базы данных стро-ятся на основе таблиц. Для обычногопользователя и для SQL-программистабаза данных представляет собой группутаблиц (и более ничего). Чтобы создатьтаблицу, следует указать:

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

Названия таблицы и столбцов должны со-ответствовать требованиям SQL. См. раз-дел «Синтаксис SQL» в главе 3 (каждаяСУБД имеет собственные требования).Типом данных для каждого столбца могутбыть символы, числа, информация о датеи времени или данные другого типа (см.раздел «Типы данных» в главе 3). Огра-ничения позволяют задавать свойства, на-пример вводить значения null, значения по

умолчанию, ключи и допустимые значе-ния.

Вы создаете новую таблицу с помощьюкоманды CREATE TABLE, которая имеет сле-дующую структуру:

CREATE TABLE table

(

column1 data_type1 [col_constraints1],

column2 data_type2 [col_constraints2],

columnN data_typeN [col_constraintsN]

[, table_constraint1]

[, table_constraint2]

[, table_constraintN]

);

Каждое обозначение столбца включаетего название, тип данных, а также списокограничений для столбца (опционально).Не следует разделять ограничения дляразных столбцов с помощью запятых.Список ограничений таблицы вводитсяпосле описания последнего столбца. Заописанием каждого столбца (кроме по-следнего) и ограничений следует запятая.Чтобы получить дополнительную ин-формацию об ограничениях, обратитеськ разделу «Основные принципы работыс ограничениями».

Ввод описания каждого столбца таблицыначинается с новой строки1.

1 Это не является требованием стандарта, одна-ко повышает читабельность команд. – Прим.науч. ред.

Page 369: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

369

Основные принципыработы с ограничениямиОграничения позволяют указать требованияк значениям в столбцах (см. табл. 10.1).Ваша СУБД будет использовать эти требо-вания, чтобы автоматически редактироватьтаблицу. Ограничения бывают двух типов:

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

Вы можете указать несколько ограниче-ний для столбца или таблицы. Использо-вание ограничений будет зависеть от кон-текста. Например, если первичный ключсодержит один столбец, вы можете задатьего в качестве ограничения столбца илитаблицы. Если первичный ключ включа-ет два столбца или более, следует исполь-зовать ограничение таблицы.Если вы укажете названия ограничений,это упростит работу с ними. Например,вы легко сможете изменить или удалитьтакое ограничение с помощью командыALTER TABLE. Названия ограничений вво-дить необязательно, но многие SQL-про-граммисты и дизайнеры баз данных при-сваивают названия всем ограничениям.Вы можете не давать названий ограниче-ниям NOT NULL и DEFAULT, но мы рекомен-дуем указать названия всех других огра-ничений (несмотря на то, что в примерахданной книги мы этого не сделали).Если вы не присвоили названия ограниче-нию, СУБД сделает это за вас. Подобныеназвания включают много символов, и их

Таблица 10.1. Ограничения

Ограничения Описание

NOT NULL Не разрешает присваиватьстолбцу значение null

DEFAULT Задает для столбца значениепо умолчанию

PRIMARY KEY Задает столбец (столбцы) пер-вичного ключа для таблицы

FOREIGN KEY Задает столбец (столбцы) вто-ричного ключа для таблицы

UNIQUE Не разрешает добавлять в стол-бец повторяющиеся значения

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

Основные принципы работы с ограничениями

Page 370: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

370 Создание, изменение и удаление таблиц

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

Присвоение названия ограничению

Перед тем как вводить описание ограниче-ния, напечатайте:CONSTRAINT constraint_name

constraint_name – это название ограниче-ния, которое будет использоваться SQLдля его идентификации. Такие названиядля одной таблицы должны быть уникаль-ными.Ограничения с названиями будут показа-ны далее в примерах этой главы.

MySQL не поддерживает предложение CON-STRAINT для ограничений столбцов (но выможете использовать его для ограниченийтаблиц).

Page 371: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

371

Листинг 10.1. Создать таблицу titles

CREATE TABLE titles

(

title_id CHAR(3) ,

title_name VARCHAR(40) ,

type VARCHAR(10) ,

pub_id CHAR(3) ,

pages INTEGER ,

price DECIMAL(5,2) ,

sales INTEGER ,

pubdate DATE ,

contract SMALLINT

);

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

Создание новой таблицы

Введите:CREATE TABLE table

(

column1 data_type1,

column2 data_type2,

column data_typeN

);

table – это название новой таблицы, кото-рую вы создаете; column1, column2, …,columnN – названия столбцов в table. Выдолжны создать хотя бы один столбец.data_type1, data_type2, …, data_typeN задаюттип данных SQL для соответствующихстолбцов. Тип данных может включатьдлину, масштаб или точную специфика-цию (см. главу 3).Названия таблицы в базе данных и каждо-го столбца в таблице должны быть уни-кальными.Листинг 10.1 создает таблицу titles, лис-тинг 10.2 – таблицу title_authors.

Листинг 10.2. Создать таблицу title_authors

CREATE TABLE title_authors

(

title_id CHAR(3) ,

au_id CHAR(3) ,

au_order SMALLINT ,

royalty_share DECIMAL(5,2)

);

Создание новой таблицы с помощью команды CREATE TABLE

Page 372: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

372 Создание, изменение и удаление таблиц

Чтобы увидеть результат исполнения ко-манды CREATE TABLE, изучите структурутаблицы с помощью одной из команд,описанных в разделе «Отображение низ-ваний столбцов в таблице» главы 9.

Если вы попытаетесь создать таблицу сназванием, которое уже существует в базеданных, СУБД выдаст ошибку. Во избежа-ние сохранения одной таблицы вместо дру-гой SQL требует, чтобы до создания таб-лицы вы удалили таблицу с тем же назва-нием с помощью команды DROP TABLE (см.раздел «Удаление таблицы с помощью ко-манды DROP TABLE» далее в этой главе).

Сразу после создания таблица пуста (неимеет строк). Чтобы заполнить таблицуданными, пользуйтесь командой INSERT(см. раздел «Вставка строк с помощью ко-манды INSERT» в главе 9).

По умолчанию значения null в столбцахразрешены. Если вы желаете их запретить,обратитесь к разделу «Запрет значенияnull с помощью ограничения NOT NULL»далее в этой главе.

Чтобы изменить структуру существующейтаблицы, обратитесь к разделу «Изменениетаблицы с помощью команды ALTERTABLE» далее в этой главе.

Чтобы создать таблицу на основе структу-ры и данных существующей таблицы, об-ратитесь к разделу «Создание новой табли-цы на основе существующей с помощьюкоманды SELECT INTO» далее в этой главе.

Microsoft SQL Server не поддерживает типданных DATE. Чтобы запустить листинг 10.1в SQL Server, в столбце pubdate восполь-зуйтесь типом данных DATETIME.

MySQL без предупреждения заменит столб-цы VARCHAR, которые состоят менее чем изчетырех символов, на CHAR.

Page 373: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

373

Запрет значения nullс помощью ограниченияNOT NULLОт способности столбца принимать значе-ния null зависит, могут ли строки содер-жать NULL, то есть обязательно ли вводитьзначения в этих строках или нет. Про зна-чение null и его свойства мы уже расска-зывали в разделе «Значение null» главы 3.Вкратце повторим:

значение null является маркером, кото-рый сообщает, что значение не быловведено;значение null отображает значение, ко-торого нет, которое неизвестно или ко-торое неприменимо. Значение null встолбце price свидетельствует о том,что цена неизвестна или не была указа-на, а не о том, что товар не имеет ценыили что цена равна нулю;значение null – не то же самое, что (0),пустое поле или пробел (' ');значение null не относится ни к одномутипу данных и может быть вставленов любой столбец, который это допус-кает;в командах SQL слово NULL указываетна значение null.

При установке ограничения на значениеnull следует учитывать следующие фак-торы:

ограничение на значение null всегда яв-ляется ограничением столбца, а не таб-лицы (см. раздел «Основные принципыработы с ограничениями» ранее в этойглаве);вы задаете ограничения на значение nullс помощью ключевых слов NULL или NOT

NULL в обозначении столбца в командеCREATE TABLE;

желательно избегать разрешения зна-чения null, так как это усложняет за-просы;

запрет значения null в столбце можетпомочь сохранить целостность данных,так как при их вводе обязательно ука-зывать значения. СУБД не будет встав-лять или изменять строку, если столбец(для которого запрещено значение null)содержит NULL;

некоторые ограничения (например,PRIMARY KEY) не могут использоватьсяв столбцах, для которых разрешенозначение null;

значение null влияет на ограничениявторичных ключей (см. раздел «Заданиевторичного ключа с помощью огра-ничения FOREIGN KEY» далее в этойглаве);

если вы добавили строку с помощьюкоманды INSERT, но не указали значе-ние столбца, который допускает значе-ние null, ваша СУБД вставит NULL (приусловии, что нет ограничения DEFAULT).См. разделы «Вставка строк с помо-щью команды INSERT» в главе 9 и «При-своение значения по умолчанию с по-мощью ограничения DEFAULT» далеев этой главе;

вы можете ввести NULL непосредственнов столбце, для которого разрешено зна-чение null, причем независимо от того,какой тип данных или значение по умол-чанию указаны для этого столбца;

если вы не укажете NULL или NOT NULL, поумолчанию значение null будет разре-шено.

Запрет значения null с помощью ограничения NOT NULL

Page 374: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

374 Создание, изменение и удаление таблиц

Установка ограничения NOT NULLдля столбца

Добавьте к описанию столбца в коман-де CREATE TABLE следующее ограничениестолбца:[CONSTRAINT constraint_name]

[NOT] NULL

Укажите ограничение NULL, чтобы разре-шить значение null для столбца, или NOTNULL, чтобы запретить. Если вы не указалиничего, по умолчанию задается ограниче-ние NULL. Дополнительную информацию обобщей структуре команды CREATE TABLE см.в разделе «Порядок создания таблиц» ранеев этой главе. Ключевое слово CONSTRAINTявляется опциональным, а constraint_name –это название ограничения (см. раздел «Ос-новные принципы работы с ограничения-ми» ранее в этой главе).Листинг 10.3 создает таблицу authors и зада-ет для каждого столбца ограничение на зна-чение null. Адреса и телефонные номера ча-сто пропускаются, поэтому для этих столб-цов мы разрешили значение null. Обратитевнимание, что в столбцах с именами и фа-милиями мы их запретили. Если приводитсятолько фамилия автора и отсутствует егоимя (например, автор A06, Келси (Kellsey)), встолбец au_lname мы добавим фамилию, а встолбец au_fname – пустую строку (''). Либомы можем разрешить значение null в полеau_fname и вставлять его в столбец au_fnameдля авторов, имена которых не указаны.Либо мы можем разрешить значение null встолбцах au_fname и au_lname и добавить ог-раничение, которое требует ввода данныххотя бы в одном столбце строки. Дизайнербазы данных принимает подобное решениееще до создания таблицы.

Листинг 10.3. Создать таблицу authors и задатьдля каждого столбца ограничение на значение null

CREATE TABLE authors

(

au_id CHAR(3) NOT NULL ,

au_fname VARCHAR(15) NOT NULL ,

au_lname VARCHAR(15) NOT NULL ,

phone VARCHAR(12) NULL ,

address VARCHAR(20) NULL ,

city VARCHAR(15) NULL ,

state CHAR(2) NULL ,

zip CHAR(5) NULL

);

Листинг 10.4. Создать базу данных titles.Если в описании столбца ограничение опущено,то для него СУБД разрешает значение null

CREATE TABLE titles

(

title_id CHAR(3) NOT NULL ,

title_name VARCHAR(40) NOT NULL ,

type VARCHAR(10) ,

pub_id CHAR(3) NOT NULL ,

pages INTEGER ,

price DECIMAL(5,2) ,

sales INTEGER ,

pubdate DATE ,

contract SMALLINT NOT NULL

);

Page 375: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

375

Листинг 10.4 создает базу данных titles.Если в описании столбца ограничение опу-щено, то для него СУБД разрешает значе-ние null

Чтобы увидеть результат исполнения ко-манды CREATE TABLE, изучите структурутаблицы с помощью одной из команд,описанных в разделе «Отображение назва-ний столбцов в таблице» главы 9.

При добавлении строки в таблицу нужноуказать точные значения для столбцов, вкоторых запрещено значение null (и не за-даны значения по умолчанию). Например,команда INSERT для таблицы authors, со-зданной листингом 10.3, выглядит так:

INSERT INTO authors (

au_id,

au_fname,

au_lname)

VALUES (

'A08',

'Michael',

'Polk');

СУБД автоматически помещает NULL встолбцы таблицы authors, которые небыли указаны в списке столбцов командыINSERT (phone, address и т.д.); см. раздел«Вставка строк с помощью командыINSERT» в главе 9.

При добавлении данных для строковогополя не помещайте ключевое слово NULLв кавычки. Если вы это сделаете, СУБД ин-терпретирует это как ввод слова NULL, а некак значение null.

Вы можете найти значение null с помощьюкоманды IS NULL (см. раздел «Проверкана значение null с помощью оператора ISNULL» в главе 4).

Чтобы отобразить значение вместо NULL,можно использовать функцию COALESCE()(см. раздел «Проверка на значения nullс использованием функции COALESCE()»в главе 5).

Можно использовать функцию NULLIF(),чтобы конвертировать значения в расчетах(см. раздел «Сравнение выражений с по-мощью функции NULLIF()» в главе 5).

Все столбцы, для которых было заданоограничение PRIMARY KEY, должны бытьобозначены как NOT NULL. Если вы не за-дадите этот параметр, СУБД автоматичес-ки укажет для всех столбцов PRIMARY KEYпараметр NOT NULL (см. раздел «Заданиепервичного ключа с помощью ограниченияPRIMARY KEY» далее в этой главе).

Microsoft SQL Server не поддерживает типданных DATE. Чтобы запустить листинг10.4 в SQL Server, замените тип данных встолбце pubdate на DATETIME.

MySQL не поддерживает ключевое словоCONSTRAINT для ограничений столбцов (новы можете использовать его для ограни-чений таблиц).

Oracle распознает пустую строку ('') какNULL (см. примечание DBMS в разделе«Значение null» главы 3). Для СУБД, кото-рые мы рассматриваем в данной главе, ог-раничение на значение null является опци-ей, но другие СУБД могут потребовать,чтобы вы задали для каждого столбца па-раметр NULL или NOT NULL.

Чтобы узнать, как СУБД работает с ограни-чением на значение null для столбцов, дан-ные в которых автоматически создают уни-кальные значения для строк, обратитесь кдокументации. См. примечание DBMS в раз-деле «Первичные ключи» главы 2.

Запрет значения null с помощью ограничения NOT NULL

Page 376: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

376 Создание, изменение и удаление таблиц

Присвоение значенияпо умолчанию с помощьюограничения DEFAULTЗначение по умолчанию присваивает значе-ние, которое СУБД задает для столбца,если вы пропустите значение при добавле-нии строки. См. раздел «Вставка строкс помощью команды INSERT» в главе 9.При работе со значениями по умолчаниювам следует помнить, что:

ограничение для значения по умолча-нию всегда является ограничениемстолбца, а не таблицы (см. раздел «Ос-новные принципы работы с ограниче-ниями» ранее в этой главе);вы задаете ограничение для значения поумолчанию с помощью ключевого сло-ва DEFAULT в описании столбца в коман-де CREATE TABLE;значение по умолчанию может бытьлюбым выражением, результатом ис-полнения которого является константа;значение по умолчанию должно отно-ситься к тому же типу данных (илибыть конвертируемым), что и соответ-ствующий столбец (см. раздел «Преоб-разование типов данных с помощьюфункции CAST()» в главе 5);столбец должен быть такой длины,чтобы вместить значение по умолча-нию;если вы добавили строку с помощьюINSERT, но не указали значение для столб-ца, ваша СУБД будет использовать зна-чение по умолчанию. Если для столбцане задано значение по умолчанию, будетиспользоваться значение null;если для столбца установлено ограниче-ние NOT NULL, но не задано значение поумолчанию, при добавлении строки с

помощью INSERT необходимо указатьзначения. В противном случае СУБДотобразит сообщение об ошибке и невставит строку (см. раздел «Вставкастрок с помощью команды INSERT» вглаве 9).

Задание столбцу значенияпо умолчанию

Добавьте к описанию столбца в коман-де CREATE TABLE следующее ограничениестолбца:[CONSTRAINT constraint_name]

DEFAULT expr

expr – это выражение, которое являетсяконстантой, например буквой, встроеннойфункцией, математическим выражениемили NULL. Если не было задано ограничениезначению по умолчанию, указывается огра-ничение NULL. Информацию об общейструктуре команды CREATE TABLE см. в раз-деле «Порядок создания таблиц» ранее вэтой главе. Ключевое слово CONSTRAINT яв-ляется опциональным, а constraint_name –это название ограничения (см. раздел «Ос-новные принципы работы с ограничения-ми» ранее в этой главе).Листинг 10.5 задает значения по умолча-нию для столбцов в таблице titles. Длястолбцов title_id и pub_id не присвоенызначения по умолчанию и установлено ог-раничение NOT NULL, поэтому значения дляних вы должны указать в команде INSERT.Пометка DEFAULT NULL для столбца pages эк-вивалентна пропуску ограничения DEFAULT.Значения по умолчанию pubdate и contractпоказывают, что они могут быть болеесложными выражениями, чем обычныебуквенные.Листинг 10.6 содержит команду INSERT,которой вы можете пользоваться, чтобыдобавить строку в таблицу titles (создан-ную листингом 10.5).

Page 377: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

377

Чтобы увидеть результат исполнения ко-манды CREATE TABLE, изучите структурутаблицы с помощью одной из команд,описанных в разделе «Отображение назва-ний столбцов в таблице» главы 9.

Microsoft Access не разрешает применятьарифметические выражения в ограниченииDEFAULT; пользуйтесь цифрами. Чтобыотобразить системную дату, пользуйтесьфункцией DATE(), а не CURRENT_DATE (см.примечание DBMS в разделе «Извлечениезначений текущих даты и времени» главы5). Чтобы запустить листинг 10.5 в Access,

Листинг 10.5. Задать значения по умолчанию для столбцов в таблице titles

CREATE TABLE titles

(

title_id CHAR(3) NOT NULL ,

title_name VARCHAR(40) NOT NULL DEFAULT '' ,

type VARCHAR(10) NULL DEFAULT 'undefined' ,

pub_id CHAR(3) NOT NULL ,

pages INTEGER DEFAULT NULL ,

price DECIMAL(5,2) NOT NULL DEFAULT 0.00 ,

sales INTEGER NULL ,

pubdate DATE NULL DEFAULT CURRENT_DATE ,

contract SMALLINT NOT NULL DEFAULT (3 * 7) - 21

);

title_id title_name type pub_id pages price sales pubdate contract

———-----— —-----———— ——-------- ---——— —---- ——--- ---—— —-----—---— —----———

T14 undefined P01 NULL 0.00 NULL 2002-05-06 0

Рис. 10.1. Листинг 10.6 добавит в таблицу titles новую строку

замените ограничение значения по умолча-нию для столбца pubdate на DEFAULT

DATE(), а ограничение значения по умолча-нию для столбца contract – на DEFAULT 0.

Microsoft SQL Server не поддерживает типданных DATE. Вместо него используйтеDATETIME. Чтобы отобразить дату систе-мы, пользуйтесь функцией GETDATE(), ане CURRENT_DATE (см. примечание DBMS вразделе «Извлечение значений текущихдаты и времени» главы 5). Чтобы запус-тить листинг 10.5 в SQL Server, заменитетип данных для столбца pubdate наDATETIME, а ограничение значения поумолчанию – на DEFAULT GETDATE 0.

Листинг 10.6. СУБД добавляет значения по умолчанию для столбцов, пропущенных в команде INSERT.Если не было задано значение по умолчанию, СУБД добавит значение null. Результат исполненияпоказан на рис. 10.1

INSERT INTOtitles(title_id, pub_id) VALUES('T14','P01');

Присвоение значения по умолчанию с помощью ограничения DEFAULT

Page 378: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

378 Создание, изменение и удаление таблиц

В Oracle ограничения для значений по умол-чанию следуют за типом данных до всех дру-гих ограничений столбцов, включая ограниче-ние на значение null. Oracle 9i и более позд-них версий поддерживает CURRENT_DATE.В Oracle 8i и более ранних версиях вместоCURRENT_DATE следует использоватьSYSDATE (см. примечание DBMS в разделе«Извлечение значений текущих даты и вре-мени» главы 5). Oracle воспринимает пустуюстроку ('') как значение null, поэтому мызаменили значение по умолчаниюtitle_name символом пробела (см. при-мечание DBMS в разделе «Значение null»главы 3). Версия листинга 10.5 для Oracleпредставлена в листинге 10.7.Oracle не поддерживает ключевое словоCONSTRAINT для ограничения DEFAULT (новы можете использовать его для других ог-раничений столбцов и всех ограниченийтаблиц).

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

Листинг 10.7. В Oracle ограничение для значения по умолчанию должно следовать перед другимиограничениями столбца

CREATE TABLE titles

(

title_id CHAR(3) NOT NULL,

title_name VARCHAR(40) DEFAULT ' ' NOT NULL,

type VARCHAR(10) DEFAULT 'undefined' NULL ,

pub_id CHAR(3) NOT NULL,

pages INTEGER DEFAULT NULL ,

price DECIMAL(5,2) DEFAULT 0.00 NOT NULL,

sales INTEGER NULL ,

pubdate DATE DEFAULT SYSDATE NULL ,

contract SMALLINT DEFAULT (3 * 7) - 21 NOT NULL

);

значения по умолчанию для столбца с да-той CURRENT_DATE. Чтобы запустить лис-тинг 10.5 в MySQL, удалите ограничениезначения по умолчанию столбца pubdate(или замените значение по умолчанию набуквенное), а также замените ограничениезначения по умолчанию для столбцаcontract на DEFAULT 0.

MySQL не поддерживает ключевое словоCONSTRAINT для ограничений столбца (новы можете использовать его для ограни-чений таблицы).

Если для столбца не было задано ограни-чение DEFAULT, но задано ограничениеNOT NULL, некоторые СУБД будут устанав-ливать значение по умолчанию в соответ-ствии с типом данных столбца. Например,MySQL будет присваивать значение поумолчанию для числовых столбцов, в ко-торых запрещены значения null.

Чтобы узнать, как СУБД работает с ограни-чениями значения по умолчанию для столб-цов, данные в которых автоматически созда-ют уникальные значения для строк, обрати-тесь к документации. См. примечание DBMSк разделу «Первичные ключи» в главе 2.

Page 379: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

379

Задание первичного ключас помощью ограниченияPRIMARY KEYПро первичные ключи мы уже рассказыва-ли в одноименном разделе главы 2. Вкрат-це повторим:

первичный ключ идентифицируеткаждую строку в таблице как уникаль-ную;

две строки не могут иметь одинаковыйпервичный ключ;

первичные ключи не могут содержатьNULL;

в каждой таблице может быть толькоодин первичный ключ;

первичный ключ – это столбец или не-сколько столбцов. Простой первичныйключ состоит из одного столбца; слож-ный – из нескольких;

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

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

При установке ограничения первичногоключа следует принять в расчет следую-щую информацию:

простой ключ всегда может быть какограничением столбца, так и ограни-чением таблицы; сложный ключ мо-жет быть только ограничением табли-цы (см. раздел «Основные принципыработы с ограничениями» ранее в этойглаве);

вы задаете ограничение первичного клю-ча с помощью ключевых слов PRIMARYKEY в обозначении столбца в командеCREATE TABLE;если PRIMARY KEY является ограничениемтаблицы, вам необходимо задать назва-ния столбца (столбцов). Если PRIMARYKEY является ограничением столбца,оно относится к соответствующемустолбцу;SQL позволяет создать таблицу без пер-вичного ключа (нарушая требования кмодели таблицы). На практике вам все-гда следует задавать первичный ключдля любой таблицы;для одной таблицы разрешено задаватьне более одного первичного ключа;ограничения первичных ключей почтивсегда имеют уникальные названия.Чтобы задать название первичногоключа, пользуйтесь предложением CON-STRAINT (см. раздел «Основные прин-ципы работы с ограничениями» ранеев этой главе);ограничением на значение null длястолбцов первичных ключей должнобыть NOT NULL. Если вы не укажете этоограничение, СУБД автоматически за-даст его для всех столбцов первичныхключей (см. раздел «Запрет значения nullс помощью ограничения NOT NULL» ра-нее в этой главе);при добавлении строк с помощью ко-манды INSERT следует указать значениядля первичного ключа, в противномслучае они будут сгенерированы авто-матически в соответствии с типом дан-ных столбца (см. примечание DBMSк разделу «Первичные ключи» в главе 2).За информацией о добавлении строк об-ращайтесь к разделу «Вставка строк спомощью команды INSERT» в главе 9;

Задание первичного ключа с помощью ограничения PRIMARY KEY

Page 380: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

380 Создание, изменение и удаление таблиц

значения первичного ключа редко из-меняются. Мы не рекомендуем вам из-менять значения первичных ключей спомощью команды UPDATE (см. раздел«Изменение строк с помощью командыUPDATE» в главе 9);не используйте для других строк значе-ние первичного ключа удаленной с по-мощью команды DELETE строки (см.раздел «Удаление строк с помощью ко-манды DELETE» в главе 9);по всем вопросам, связанным с добав-лением, изменением и удалением пер-вичных ключей, связанных с внешнимиключами, обращайтесь к разделу «За-дание внешнего ключа с помощью ог-раничения FOREIGN KEY» далее в этойглаве.

Задание простого первичного ключа

Чтобы задать простой первичный ключ вкачестве ограничения столбца, добавьте кописанию столбца в команде CREATE TABLEследующее ограничение столбца:[CONSTRAINT constraint_name]

PRIMARY KEY

ИлиЧтобы задать простой первичный ключ вкачестве ограничения таблицы, добавьтепосле описания столбцов в команде CREATETABLE следующее ограничение таблицы:[CONSTRAINT constraint_name]

PRIMARY KEY (key_column)

key_column – это название столбца пер-вичного ключа. Для одной таблицы мож-но ввести только один первичный ключ.Информацию об общей структуре коман-ды CREATE TABLE см. в разделе «Порядоксоздания таблиц» ранее в этой главе.Предложение CONSTRAINT является оп-циональным, а constraint_name – это назва-ние ограничения (см. раздел «Основные

принципы работы с ограничениями» ранеев этой главе).Листинги 10.8a, 10.8б и 10.8в отражаюттри эквивалентных способа задать про-стой первичный ключ для таблицы pub-lishers.Листинг 10.8a использует ограничениестолбца, чтобы задать первичный ключ.Здесь показан самый доступный способсоздания простого первичного ключа.Листинг 10.8б использует неименованноеограничение таблицы, чтобы задать пер-вичный ключ. Мы добавили к столбцуpub_id ограничение NOT NULL, хотя это не-обязательно, так как СУБД самостоятель-но задает его (но не в MySQL; см. приме-чание DBMS далее в этой главе).

Листинг 10.8б. Задать простой первичный ключбез названия для таблицы publishersс помощью ограничения таблицы

CREATE TABLE publishers

(

pub_id CHAR(3) NOT NULL,

pub_name VARCHAR(20) NOT NULL,

city VARCHAR(15) NOT NULL,

state CHAR(2) NULL ,

country VARCHAR(15) NOT NULL,

PRIMARY KEY (pub_id)

);

Листинг 10.8a. Задать простой первичный ключдля таблицы publishers с помощьюограничения столбца

CREATE TABLE publishers

(

pub_id CHAR(3) PRIMARY KEY,

pub_name VARCHAR(20) NOT NULL ,

city VARCHAR(15) NOT NULL ,

state CHAR(2) NULL ,

country VARCHAR(15) NOT NULL

);

Page 381: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

381

Листинг 10.8в. Задать именованный простойпервичный ключ для таблицы publishersс помощью ограничения таблицы

CREATE TABLE publishers

(

pub_id CHAR(3) NOT NULL ,

pub_name VARCHAR(20) NOT NULL ,

city VARCHAR(15) NOT NULL ,

state CHAR(2) NULL ,

country VARCHAR(15) NOT NULL ,

CONSTRAINT publishers_pk

PRIMARY KEY (pub_id)

);

Листинг 10.8в использует именованное ог-раничение таблицы, чтобы задать первич-ный ключ. Здесь показан предпочтитель-ный способ добавления первичного ключа;если вы решите впоследствии удалить илиизменить ключ, можете использовать на-звание publishers_pk (см. раздел «Измене-ние таблицы с помощью команды ALTERTABLE» далее в этой главе).

Задание сложного первичного ключа

Добавьте к описанию столбца в командеCREATE TABLE следующее ограничение таб-лицы:[CONSTRAINT constraint_name]

PRIMARY KEY (key_columns)

key_column – это список столбцов первич-ного ключа, разделенных запятыми. Дляодной таблицы можно ввести только пер-вичный ключ. Информацию об общейструктуре команды CREATE TABLE см. в раз-деле «Порядок создания таблиц» ранее вэтой главе. Предложение CONSTRAINT явля-ется опциональным, а constraint_name –это название ограничения (см. раздел «Ос-новные принципы работы с ограничения-ми» ранее в этой главе).

Листинг 10.9 задает сложный первичныйключ для таблицы title_authors. В неговойдут столбцы title_id и au_id, а ключбудет называться title_authors_pk.

Чтобы увидеть результат исполнения ко-манды CREATE TABLE, изучите структурутаблицы с помощью одной из команд,описанных в разделе «Отображение назва-ний столбцов в таблице» главы 9.

Чтобы задать вторичный ключ, обратитеськ разделу «Задание внешнего ключа с по-мощью ограничения FOREIGN KEY» далеев этой главе.

Листинг 10.9. Задать сложный первичный ключдля таблицы title_authors с помощьюименованного ограничения таблицы

CREATE TABLE title_authors

(

title_id CHAR(3) NOT NULL,

au_id CHAR(3) NOT NULL,

au_order SMALLINT NOT NULL,

royalty_share DECIMAL(5,2) NOT NULL,

CONSTRAINT title_authors_pk

PRIMARY KEY (title_id, au_id)

);

Задание первичного ключа с помощью ограничения PRIMARY KEY

Page 382: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

382 Создание, изменение и удаление таблиц

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

Чтобы изменить или удалить существу-ющее ограничение, обратитесь к разделу«Изменение таблицы с помощью командыALTER TABLE» далее в этой главе.

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

CREATE TABLE title_authors(

title_id CHAR(3) PRIMARY KEY,

au_id CHAR(3) PRIMARY KEY,

au_order SMALLINT NOT NULL,

...

);

СУБД MySQL требует, чтобы вы задалиограничение NOT NULL для столбцов пер-вичного ключа, если он создается как ог-раничение таблицы, а не как ограничениестолбцов (см. раздел «Запрет значенияnull с помощью ограничения NOT NULL»ранее в этой главе).

MySQL не поддерживает предложение CON-STRAINT для ограничений столбцов (но выможете использовать его для ограниченийтаблиц).

Oracle воспринимает пустую строку ('') какNULL (см. примечание DBMS в разделе «Зна-чение null» главы 3).

Page 383: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

383

Задание внешнего ключас помощью ограниченияFOREIGN KEYПро внешние ключи мы уже рассказывали водноименном разделе главы 2. Вкратце по-вторим:

внешний ключ используется для связимежду двумя таблицами;

внешний ключ – это столбец (или не-сколько столбцов) в таблице, значения вкоторой связаны со значениями в другойтаблице или ссылаются на них;

внешний ключ гарантирует, что строкив одной таблице имеют соответствую-щие строки в другой таблице, котораяназывается родительской;

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

в отличие от первичного ключа, столб-цы внешнего ключа могут содержатьзначение null;

в каждой таблице может быть несколь-ко внешних ключей (или не быть вооб-ще);

как правило, значения внешнего клю-ча не являются уникальными в таб-лице;

столбцы внешнего ключа в разных стол-бцах могут ссылаться на один столбецв родительской таблице;

простой внешний ключ включает одинстолбец; сложный – несколько.

При установке ограничения внешнего клю-ча вам следует учитывать следующие мо-менты:

простой ключ может быть как ограни-чением столбца, так и ограничениемтаблицы; сложный ключ может бытьтолько ограничением таблицы (см. раз-дел «Основные принципы работыс ограничениями» ранее в этой главе);

вы задаете внешний ключ с помощьюключевых слов FOREIGN KEY или REFE-RENCES в обозначении столбца в коман-де CREATE TABLE. Ключевые слова FOREIGNKEY и REFERENCES являются вариантамиодного и того же ограничения; мы, какправило, будем использовать FOREIGNKEY;

внешний ключ может иметь названиестолбца, отличное от родительского;

тип данных внешнего ключа долженбыть того же типа (или конвертируе-мым), что и родительский ключ (см.раздел «Преобразование типов данныхс помощью функции CAST()» в главе 5);

столбец внешнего ключа необязатель-но должен ссылаться только на столбецпервичного ключа родительской таб-лицы; он может ссылаться на уникаль-ный столбец в ней (см. раздел «При-своение уникальных значений с по-мощью ограничения UNIQUE» далеев этой главе);

таблица может включать сколько угод-но внешних ключей (и даже ни одного);

ограничения внешних ключей почтивсегда имеют точные названия. Чтобызадать название ограничению, поль-зуйтесь предложением CONSTRAINT (см.раздел «Основные принципы работыс ограничениями» ранее в этой главе);

Задание внешнего ключа с помощью ограничения FOREIGN KEY

Page 384: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

384 Создание, изменение и удаление таблиц

ограничение на значение null для столб-цов внешних ключей может быть какNULL, так и NOT NULL. Если вы не укажетеэто ограничение, СУБД автоматическизадаст его как NULL для всех столбцоввторичных ключей (см. раздел «Запретзначения null с помощью ограниченияNOT NULL» ранее в этой главе);

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

Чтобы сохранить ссылочную целостность,СУБД будет предотвращать создание не-правильных строк (строк в подчиненнойтаблице, которые не имеют соответству-ющих строк в родительской таблице).Если вы будете изменять столбец внеш-него ключа (который имеет ссылку настолбец PRIMARY KEY в родительской таб-лице) с помощью команд INSERT, UPDATEили DELETE, СУБД выполнит следующиепроверки:

при добавлении строки в подчиненнуютаблицу – соответствует ли новое зна-чение внешнего ключа значению пер-вичного ключа в родительской табли-це. Если такого соответствия нет, СУБДне добавит строку;при изменении строки в подчиненнойтаблице – соответствует ли измененноезначение внешнего ключа значениюпервичного ключа в родительской таб-лице. Если такого соответствия нет,СУБД не изменит строку;

при удалении строки из подчиненнойтаблицы проверка ссылочной целост-ности не нужна;при добавлении строки в родительскуютаблицу проверка ссылочной целост-ности не нужна;при изменении строки в родительскойтаблице – не соответствует ли какое-либо значение внешнего ключа значе-нию первичного ключа в родительскойтаблице, которое будет изменено. Еслитакое соответствие существует, СУБДне изменит строку;при удалении строки из родительскойтаблицы – не соответствует ли какое-либо значение внешнего ключа значе-нию первичного ключа в родительскойтаблице, которое будет удалено. Еслитакое соответствие существует, СУБДне удалит строку.

СУБД не выполняет проверку ссылочнойцелостности для строк внешнего ключа,которые имеют значение NULL.

Задание простого внешнего ключа

Чтобы задать простой внешний ключ в ка-честве ограничения столбца, добавьте кописанию столбца в команде CREATE TABLEследующее ограничение столбца:[CONSTRAINT constraint_name]

REFERENCES ref_table (ref_column)

Или

Чтобы задать простой внешний ключ в ка-честве ограничения таблицы, добавьте пос-ле описания столбцов в команде CREATETABLE следующее ограничение таблицы:[CONSTRAINT constraint_name]

FOREIGN KEY (key_column)

REFERENCES ref_table (ref_column)

key_column – это название столбца внеш-него ключа; ref_table – название роди-тельской таблицы, ссылку на которую

Page 385: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

385

создает ограничение внешнего ключа; ref_-column – название столбца в ref_table, кото-рый является ключом для ссылки. В табли-це можно ввести несколько внешних клю-чей (или ни одного). Информация об общейструктуре команды CREATE TABLE представле-на в разделе «Порядок создания таблиц»ранее в этой главе. Предложение CONSTRAINTявляется опциональным, а constraint_name –это имя ограничения (см. раздел «Основныепринципы работы с ограничениями» ранеев этой главе).Листинг 10.10 использует ограничениестолбца, чтобы создать внешний ключв таблице titles. Здесь показан самый до-ступный способ создания простого внеш-него ключа. После запуска этой командыСУБД проверит, существуют ли в столб-це pub_id в publishers те значения, кото-рые вы вводите в одноименный столбецв titles. Обратите внимание, что в столб-це внешнего ключа значения null запреще-ны, поэтому необходимо ввести издатель-ство для каждой книги.Таблица royalties имеет связь с таблицейtitles, поэтому листинг 10.11 задает стол-бец title_id как первичный и внешнийключи для связи title_id и titles. За ин-формацией о связях обращайтесь к разде-лу «Связи» в главе 2.Листинг 10.12 использует именованноеограничение таблицы, чтобы задать двавнешних ключа. Здесь показан предпоч-тительный способ добавления внешнихключей; если вы решите впоследствииудалить или изменить ключи, можете ис-пользовать их названия (см. раздел «Из-менение таблицы с помощью командыALTER TABLE» далее в этой главе). Каж-дый столбец внешнего ключа являетсясамостоятельным ключом, а не частьюсложного ключа. Обратите внимание, чтовместе внешние ключи составляют слож-ный первичный ключ таблицы.

Листинг 10.11. Задать простой вторичный ключдля таблицы royalties с помощьюименованного ограничения таблицы

CREATE TABLE royalties

(

title_id CHAR(3) NOT NULL,

advance DECIMAL(9,2) NULL ,

royalty_rate DECIMAL(5,2) NULL ,

CONSTRAINT royalties_pk

PRIMARY KEY (title_id),

CONSTRAINT royalties_title_id_fk

FOREIGN KEY (title_id)

REFERENCES titles(title_id)

);

Листинг 10.10. Создать столбец вторичного ключав таблице titles с помощью ограничениястолбца

CREATE TABLE titles

(

title_id CHAR(3) NOT NULL

PRIMARY KEY,

title_name VARCHAR(40) NOT NULL,

type VARCHAR(10) NULL,

pub_id CHAR(3) NOT NULL

REFERENCES publishers(pub_id),

pages INTEGER NULL,

price DECIMAL(5,2) NULL,

sales INTEGER NULL,

pubdate DATE NULL,

contract SMALLINT NOT NULL

);

Задание внешнего ключа с помощью ограничения FOREIGN KEY

Page 386: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

386 Создание, изменение и удаление таблиц

Задание сложного внешнего ключа

Добавьте после описания столбцов в ко-манде CREATE TABLE следующее ограниче-ние таблицы:[CONSTRAINT constraint_name]

FOREIGN KEY (key_columns)

REFERENCES ref_table (ref_columns)

key_columns – это список столбцов внешне-го ключа, разделенных запятыми; ref_table – название родительской таблицы,ссылку на которую создает ограничениевнешнего ключа; ref_columns – названиястолбцов в ref_table, которые являютсяключами для ссылок. Списки key_columnsи ref_columns должны включать одинако-вое число столбцов в соответствующемпорядке. В таблице можно ввести несколь-ко внешних ключей (или ни одного). Ин-формацию об общей структуре командыCREATE TABLE см. в разделе «Порядок созда-ния таблиц» ранее в этой главе. Предложе-ние CONSTRAINT является опциональным,а constraint_name – это имя ограничения(см. раздел «Основные принципы работыс ограничениями» ранее в этой главе). Ин-формация об общей структуре командыCREATE TABLE приведена в разделе «Порядоксоздания таблиц» ранее в этой главе.

Наша база данных не содержит внеш-них ключей. Предположим, что мы созда-ли таблицу out_of_print, чтобы хранитьинформацию о книгах каждого автора,которые вышли из печати. Таблица ti-tle_authors имеет сложный первичныйключ. Следующее ограничение показыва-ет, как связать его с таблицей out_of_print:

CONSTRAINT out_of_print_fk

FOREIGN KEY

(title_id, au_id)

REFERNCES

title_authors (title_id, au_id)

Листинг 10.12. Задать простые вторичные ключидля таблицы title_authors с помощьюименованных ограничений таблицы

CREATE TABLE title_authors

(

title_id CHAR(3) NOT NULL,

au_id CHAR(3) NOT NULL,

au_order SMALLINT NOT NULL,

royalty_share DECIMAL(5,2) NOT NULL,

CONSTRAINT title_authors_pk

PRIMARY KEY (title_id, au_id),

CONSTRAINT title_authors_title_id_fk

FOREIGN KEY (title_id)

REFERENCES titles(title_id),

CONSTRAINT title_authors_au_id_fk

FOREIGN KEY (au_id)

REFERENCES authors(au_id)

);

Page 387: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

387

Чтобы увидеть результат исполнения ко-манды CREATE TABLE, изучите структурутаблицы с помощью одной из команд,описанных в разделе «Отображение назва-ний столбцов в таблице» главы 9.

Чтобы задать первичный ключ, обратитеськ разделу «Задание первичного ключа с по-мощью ограничения PRIMARY KEY» ранеев этой главе.

Чтобы изменить или удалить существую-щее ограничение, обратитесь к разделу«Изменение таблицы с помощью командыALTER TABLE» далее в этой главе.

Выражение (ref_column) или (ref_columns)в предложении REFERENCES можно опустить,если столбец (или столбцы) являетсяпервичным ключом для ref_table.

SQL позволяет указать действие, выполня-емое СУБД, если вы попытаетесь изменитьили удалить значение ключа (в родительс-кой таблице), на которое указывает значе-ние внешнего ключа. Чтобы указать дей-ствие, задайте предложение ON UPDATEили ON DELETE в ограничении FOREIGNKEY. Разные СУБД по-разному выполняютэто действие; обратитесь к документациипо вашей СУБД. Стандартное исполнениеСУБД этих пунктов мы опишем в двух сле-дующих примечаниях.

Ограничение FOREIGN KEY может обра-щаться к другому столбцу в той же табли-це. Вспомните из раздела «Создание само-объединения» в главе 7, что таблицаemployees имеет ссылку на себя (эту таб-лицу мы создали только для иллюстрации;она не является частью базы данных).Таблица employees имеет три столбца:emp_id, emp_name и boss_id. Столбецemp_id – это первичный ключ, которыйидентифицирует сотрудника, а столбец

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

CREATE TABLE employees

(

emp_id CHAR(3) NOT NULL,

emp_name CHAR(3) NOT NULL,

boss_id CHAR(3) NULL,

CONSTRAINT employees_pk

PRIMARY KEY (emp_id),

CONSTRAINT employees_fk

FOREIGN KEY (boss_id)

REFERENCES employees (emp_id)

);

Предложение ON UPDATE action задаетдействие, выполняемое СУБД, если вы из-мените значение ключа в строке (родитель-ской таблицы), которое связано ссылкамисо вторичными ключами в других табли-цах. action может иметь одно из четырехзначений:

CASCADE заменит значения внешних клю-чей в соответствии с новым значениемпервичного ключа;

SET NULL заменит значения внешнихключей на NULL;

SET DEFAULT заменит значения внешнихключей значениями по умолчанию (см.раздел «Присвоение значения по умолча-нию с помощью ограничения DEFAULT»ранее в этой главе);

NO ACTION выдаст ошибку для внешне-го ключа. Эта установка задается поумолчанию.

Предложение ON DELETE action задаетдействие, которое выполнит СУБД, есливы удалите строку родительской таблицы,связанной ссылками с внешними ключамив других таблицах. action может иметьодно из четырех значений:

Задание внешнего ключа с помощью ограничения FOREIGN KEY

Page 388: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

388 Создание, изменение и удаление таблиц

CASCADE удалит строки, которые со-держат значения внешних ключей, со-ответствующие удаленному первично-му ключу;

SET NULL заменит значения внешнихключей на NULL;

SET DEFAULT заменит значения внеш-них ключей значениями по умолча-нию (см. раздел «Присвоение значенияпо умолчанию с помощью ограниченияDEFAULT» ранее в этой главе);

NO ACTION выдаст ошибку для внешне-го ключа. Эта установка задается поумолчанию.

Microsoft SQL Server не поддерживает типданных DATE. Чтобы запустить листинг 10.10в SQL Server, замените тип данных в столб-це pubdate на DATETIME.

MySQL 4.0 и более ранних версий не под-держивает внешние ключи. Эти версииразрешают указывать FOREIGN KEY иREFERENCES в команде CREATE TABLE длясовместимости со стандартом SQL, ноMySQL не может поддерживать ссылочнуюцелостность. Чтобы работать с внешнимиключами, пользуйтесь типом таблиц InnoDB(это коммерческое дополнение к MySQL).Обратитесь к документации по MySQL илизайдите на Web-сайт www.innodb.com.

MySQL не поддерживает предложение CON-STRAINT для ограничений столбцов (но выможете использовать его для ограниченийтаблиц).

Oracle воспринимает пустую строку ('') какNULL (см. примечание DBMS в разделе«Значение null» главы 3).

Page 389: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

389

Присвоение уникальныхзначений с помощьюограничения UNIQUEОграничение уникальности удостоверяет,что столбец (или несколько столбцов) несодержит повторяющихся значений. Огра-ничение уникальности похоже на первич-ный ключ, только уникальный столбец мо-жет содержать значения null и таких столб-цов в таблице может быть несколько (заинформацией об ограничении первичныхключей обращайтесь к разделу «Заданиепервичного ключа с помощью ограниченияPRIMARY KEY» ранее в этой главе).Предположим, что мы добавили столбецisbn (с ISBN книги) в таблицу titles.ISBN – это уникальный стандартизиро-ванный номер, который служит для точ-ной идентификации книги. titles ужеимеет первичный ключ (title_id), поэто-му для уникальности каждого номера мыдобавили к столбцу isbn ограничениеуникальности.При установке ограничения уникальностиследует принять в расчет следующее:

простое ограничение уникальностивключает один столбец; сложное – не-сколько;значения сложного ограничения могутповторяться в одном столбце, но каж-дая комбинация значений должна бытьуникальной;простое ограничение уникальности мо-жет быть ограничением столбца илитаблицы; сложное ограничение всегдаявляется ограничением таблицы (см.раздел «Основные принципы работыс ограничениями» ранее в этой главе);вы задаете ограничение уникальностис помощью ключевого слова UNIQUEв описании столбца в команде CREATETABLE;

ограничение таблицы UNIQUE требуетввода названия столбца (или столбцов).Ограничение столбца UNIQUE применяет-ся к столбцу, для которого оно былозадано;таблица может включать сколько угод-но ограничений уникальности (в томчисле ни одного);ограничение уникальности почти все-гда имеет точное название. Чтобы за-дать название, пользуйтесь предложе-нием CONSTRAINT (см. раздел «Основныепринципы работы с ограничениями»ранее в этой главе);ограничение на значение null для столб-цов уникальности может быть как NULL,так и NOT NULL. Если вы не укажете огра-ничение, СУБД автоматически задастего как NULL для всех столбцов (см. раз-дел «Запрет значения null с помощьюограничения NOT NULL» ранее в этойглаве).

Задание простого ограниченияуникальности

Чтобы задать простое ограничение уни-кальности в качестве ограничения столб-ца, добавьте к описанию столбца в коман-де CREATE TABLE следующее ограничениестолбца:[CONSTRAINT constraint_name] UNIQUE

Или

Чтобы задать простое ограничение уни-кальности в качестве ограничения табли-цы, добавьте после описания столбцов вкоманде CREATE TABLE следующее ограниче-ние таблицы:[CONSTRAINT constraint_name]

UNIQUE (unique_column)

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

Присвоение уникальных значений с помощью ограничения UNIQUE

Page 390: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

390 Создание, изменение и удаление таблиц

несколько ограничений уникальности (втом числе ни одного). Информацию об об-щей структуре команды CREATE TABLE см. вразделе «Порядок создания таблиц» ранеев этой главе. Предложение CONSTRAINT явля-ется опциональным, а constraint_name – этоназвание ограничения (см. раздел «Основ-ные принципы работы с ограничениями»ранее в этой главе).

Листинги 10.13a и 10.13б демонстрируютдва эквивалентных способа указать про-стое ограничение уникальности для табли-цы titles.

Листинг 10.13a использует ограничениестолбца, чтобы задать уникальный стол-бец. Здесь показан самый доступный спо-соб создания простого ограничения уни-кальности.

Листинг 10.13б использует именованноеограничение таблицы, чтобы задать уни-кальный столбец. Здесь показан предпоч-тительный способ добавления ограниченияуникальности; если впоследствии вы ре-шите изменить или удалить его, можнобудет использовать заданное имя (см. раз-дел «Изменение таблицы с помощью ко-манды ALTER TABLE» далее в этой главе).

Задание сложного ограниченияуникальности

Добавьте после описания столбцов в ко-манде CREATE TABLE следующее ограниче-ние таблицы:[CONSTRAINT constraint_name]

UNIQUE (unique_columns)

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

Листинг 10.13a. Создать простое ограничениеуникальности в столбце title_name таблицыtitles с помощью ограничения столбца

CREATE TABLE titles

(

title_id CHAR(3)PRIMARY KEY ,

title_name VARCHAR(40) NOT NULL UNIQUE ,

type VARCHAR(10) NULL ,

pub_id CHAR(3)NOT NULL ,

pages INTEGER NULL ,

price DECIMAL(5,2) NULL ,

sales INTEGER NULL ,

pubdate DATE NULL ,

contract SMALLINT NOT NULL

);

Листинг 10.13б. Создать простое ограничениеуникальности в столбце title_name таблицыtitles с помощью именованного ограничениятаблицы

CREATE TABLE titles

(

title_id CHAR(3) NOT NULL ,

title_name VARCHAR(40) NOT NULL ,

type VARCHAR(10) NULL ,

pub_id CHAR(3) NOT NULL ,

pages INTEGER NULL ,

price DECIMAL(5,2) NULL ,

sales INTEGER NULL ,

pubdate DATE NULL ,

contract SMALLINT NOT NULL ,

CONSTRAINT titles_pk

PRIMARY KEY (title_id),

CONSTRAINT titles_title_name_unique

UNIQUE (title_name)

);

Page 391: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

391

уникальности (или ни одного). Информа-цию об общей структуре команды CREATETABLE см. в разделе «Порядок создания таб-лиц» ранее в этой главе. Предложение CON-STRAINT является опциональным, а con-straint_name – это название ограничения(см. раздел «Основные принципы работыс ограничениями» ранее в этой главе).Листинг 10.14 задает сложное ограничениеуникальности для таблицы authors. Этоограничение обеспечивает уникальностьимени и фамилии каждого автора.

Чтобы увидеть результат исполнения ко-манды CREATE TABLE, изучите структурутаблицы с помощью одной из команд,описанных в разделе «Отображение назва-ний столбцов в таблице» главы 9.

Столбец внешнего ключа может указыватьна уникальный столбец (см. раздел «Зада-ние внешнего ключа с помощью ограниче-ния FOREIGN KEY» ранее в этой главе).

Чтобы изменить или удалить существующееограничение, обратитесь к разделу «Измене-ние таблицы с помощью команды ALTERTABLE» далее в этой главе.

Вы можете создать уникальный индексвместо ограничения уникальности (см. раз-дел «Создание индекса с помощью ко-манды CREATE INDEX» в главе 11). Чтобыузнать, работает ли ваша СУБД с индекса-ми или ограничениями, обратитесь к до-кументации.

Листинг 10.14. Задать сложное ограничениеуникальности для столбцов au_fname и au_lnameтаблицы authors с помощью именованногоограничения таблицы

CREA чTE

TABLE authors(

au_id CHAR(3) NOT NULL ,au_fname VARCHAR(15) NOT NULL ,

au_lname VARCHAR(15) NOT NULL ,phone VARCHAR(12) NULL ,

address VARCHAR(20) NULL ,city VARCHAR(15) NULL ,

state CHAR(2) NULL ,zip CHAR(5) NULL ,

CONSTRAINT authors_pk PRIMARY KEY (au_id),

CONSTRAINT authors_au_name_unique

UNIQUE (au_fname, au_lname)

);

Microsoft SQL Server не поддерживаеттип данных DATE. Чтобы запустить листин-ги 10.13a и 10.13б в SQL Server, заменитетип данных в столбце pubdate на DATETIME.

MySQL не поддерживает предложениеCONSTRAINT для ограничений столбцов (новы можете использовать его для ограни-чений таблиц).

Oracle воспринимает пустую строку ('') какNULL (см. примечание DBMS в разделе«Значение null» в главе 3).

SQL допускает не более одного значения nullв уникальном столбце, для которого разре-шены эти значения. Microsoft SQL Server сле-дует стандарту, но Microsoft Access, Oracle,MySQL и PostgreSQL допускают много зна-чений null в уникальных столбцах, для кото-рых не задано ограничение NOT NULL.

Присвоение уникальных значений с помощью ограничения UNIQUE

Page 392: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

392 Создание, изменение и удаление таблиц

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

проверки минимального или макси-мального значения. Например, не даютуровню продаж принимать значениениже нуля;

проверки специальных значений. На-пример, не дают задать для столбцаscience никакие значения, кроме bio-logy, chemistry и physics;

проверки диапазона значений. Напри-мер, проверяют, находится ли значениеавторского гонорара между 2 и 20 про-центами.

Ограничение check напоминает внешнийключ, так как все значения, нужные дляограничения, располагаются в столбце(см. раздел «Задание внешнего ключас помощью ограничения FOREIGN KEY»ранее в этой главе). Разница заключаетсяв том, как эти ограничения определяют до-пустимые значения. Ограничение внешнегоключа считывает список допустимых зна-чений из другой таблицы, в то время какограничение check определяет эти значе-ния с помощью логического выражения.Например, это ограничение не допускает,чтобы зарплата какого-либо сотрудникабыла более $50 000:

CHECK (salary <= 50000)

При установке ограничения check вам нуж-но учитывать следующие моменты:

ограничение check, которое относитсяк одному столбцу, может быть какограничением столбца, так и ограни-чением таблицы; ограничение check, ко-торое относится к нескольким столб-цам, всегда является ограничением таб-лицы (см. раздел «Основные принципыработы с ограничениями» ранее в этойглаве);вы задаете ограничение check с помо-щью ключевого слова CHECK в обозначе-нии столбца в команде CREATE TABLE;столбец может включать сколько угод-но ограничений check (или ни одного);если вы создаете несколько ограниче-ний check для столбца, выполняйте этоосторожно, чтобы они не конфликтова-ли друг с другом. Не рассчитывайте нато, что СУБД самостоятельно располо-жит ограничения в нужном порядке илиисправит все конфликты;ограничения check почти всегда имеютточные названия. Чтобы задать назва-ние ограничению, пользуйтесь предло-жением CONSTRAINT (см. раздел «Основ-ные принципы работы с ограничения-ми» ранее в этой главе);условием для ограничения check можетбыть любое условие WHERE, напримерсравнение (=, <>, <=, >, >=), LIKE,BETWEEN, IN или IS NULL. Вы можете со-единять разные условия с помощьюоператоров AND, OR и NOT. За информациейоб условиях обращайтесь к разделу«Фильтрация строк с помощью предло-жения WHERE» в главе 4;условие ограничения check может отно-ситься к любому столбцу в таблице, ноне может относиться к столбцамв других таблицах;

Page 393: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

393

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

Задание ограничения check

Чтобы задать ограничение check в качествеограничения столбца или таблицы, до-бавьте к описанию столбца в командеCREATE TABLE следующее ограничение таб-лицы:[CONSTRAINT constraint_name]

CHECK (condition)

condition – это логическое условие, кото-рое проверяется СУБД, если вы изменяетесодержимое таблицы с помощью командINSERT, UPDATE или DELETE. Если в процессеизменения это условие становится истин-ным или неизвестным (значение равноNULL), изменение разрешается, если лож-ным – отменяется и выдается сообщениеоб ошибке. Информацию об общей струк-туре команды CREATE TABLE см. в разделе«Порядок создания таблиц» ранее в этойглаве. Предложение CONSTRAINT являетсяопциональным, а constraint_name – это на-звание ограничения (см. раздел «Основныепринципы работы с ограничениями» ранеев этой главе).Листинг 10.15 показывает различные огра-ничения check столбца и таблицы для таб-лицы titles. Ограничение title_id_chk про-веряет, что каждое значение первичногоключа принимает вид Tnn, где nn – этоформат значений от 00 до 99 включитель-но.

Чтобы увидеть результат исполнения ко-манды CREATE TABLE, изучите структурутаблицы с помощью одной из команд,описанных в разделе «Отображение назва-ний столбцов в таблице» главы 9.

Листинг 10.15. Задать различные ограниченияcheck столбца и таблицы для таблицы titles

CREATE TABLE titles

(

title_id CHAR(3) NOT NULL,

title_name VARCHAR(40) NOT NULL,

type VARCHAR(10) NULL

CONSTRAINT type_chk

CHECK (type IN ('biography',

'children','computer',

'history','psychology')) ,

pub_id CHAR(3) NOT NULL,

pages INTEGER NULL

→ CHECK (pages > 0) ,

price DECIMAL(5,2) NULL ,

sales INTEGER NULL ,

pubdate DATE NULL ,

contract SMALLINT NOT NULL,

CONSTRAINT titles_pk

PRIMARY KEY (title_id),

CONSTRAINT titles_pub_id_fk

FOREIGN KEY (pub_id)

REFERENCES publishers(pub_id),

CONSTRAINT title_id_chk

CHECK (

(SUBSTRING(title_id FROM 1 FOR 1) = 'T')

AND

(CAST(SUBSTRING(title_id FROM 2 FOR 2)

AS INTEGER) BETWEEN 0 AND 99)),

CONSTRAINT price_chk

CHECK (price >= 0.00

AND price < 100.00),

CONSTRAINT sales_chk

CHECK (sales >= 0),

CONSTRAINT pubdate_chk

CHECK (pubdate >= DATE '1950-01-01'),

CONSTRAINT title_name_contract_chk

CHECK (title_name <> ''

AND contract >= 0),

CONSTRAINT revenue_chk

CHECK (price * sales >= 0.00)

);

Проверка значений столбца с помощью ограничения CHECK

Page 394: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

394 Создание, изменение и удаление таблиц

Чтобы изменить или удалить существую-щее ограничение, обратитесь к разделу«Изменение таблицы с помощью коман-ды ALTER TABLE» далее в этой главе.

В соответствии со стандартными требовани-ями SQL условие не может включать функ-цию получения времени (CURRENT_DATE,CURRENT_TIME или CURRENT_TIMESTAMP),а также функцию получения имени пользо-вателя (CURRENT_USER, SESSION_USER илиSYSTEM_USER), но некоторые СУБД их разре-шают. Microsoft Access, Microsoft SQL Serverи PostgreSQL (но не Oracle) разрешают, на-пример, следующее ограничение check:

CHECK (ship_time >= CURRENT_TIMESTAMP)

В Access вместо CURRENT_TIMESTAMP сле-дует использовать Now() (см. разделы «Из-влечение значений текущих даты и време-ни» и «Отображение информации опользователе» в главе 5).

Microsoft SQL Server и Oracle позволяют со-здавать пользовательские типы данных, ко-торые представляют собой стандартныетипы данных (CHARACTER, INTEGER и т.д.)с различными ограничениями. Например, выможете создать тип данныхmarital_status, состоящий из одногосимвола CHARACTER, который допускаеттолько значения S, M, W, D или NULL (дляхолостых, женатых, вдовцов, разведенныхили неизвестного типа). Преимущество ис-пользования такого типа данных заключа-ется в том, что вы можете создать его одинраз и затем использовать для разных таб-лиц, а не повторять описание ограничений.Обратитесь к документации по вашей СУБД.

Чтобы запустить листинг 10.15 в MicrosoftAccess, замените два ограничения столбца(для столбцов type и pages) ограничени-ями таблицы, поместив их после описаниястолбцов, первое выражение полученияподстроки – Mid(title_id, 1, 1); выра-жение CAST – CInt (Mid(title_id, 2, 2));уберите из команды даты слово DATE идополните ее символами # вместо кавычек(#1950-01-01#).

Чтобы запустить листинг 10.15 в Oracle,вместо двух выражений получения под-строки используйте SUBSTR(title_id, 1, 1)и SUBSTR(title_id, 2, 2).

MySQL 4.0 и более ранних версий не под-держивает ограничение check. Эти версиидопускают наличие ключевого слова checkв командах CREATE TABLE для совместимо-сти со стандартом SQL, но MySQL не вы-полняет необходимых действий для дан-ных ограничений. Если вы зададите огра-ничение check, это должно быть ограни-чение таблицы, но не столбца.

Чтобы запустить листинг 10.15 в Postgre-SQL, вместо значений с плавающей точкой0.00 и 100.00 воспользуйтесь CAST(0.00AS DECIMAL) и CAST(0.00 AS DECIMAL) (см.раздел «Преобразование типов данных спомощью функции CAST()» в главе 5).

В Microsoft SQL Sever вы можете за-дать ограничение title_id_chk как CHECK(title_id LIKE ‘[T] [0-9] [0-9]’); об-ращайтесь к SQL Help.

Oracle воспринимает пустую строку (' ') какNULL (см. примечание DBMS в разделе «Зна-чение null» главы 3).

Page 395: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

395

Создание временнойтаблицы с помощьюкоманды CREATETEMPORARY TABLEВсе таблицы, которые мы создавали рань-ше, были постоянными (их еще называютбазовыми). Эти таблицы всегда хранят дан-ные, если вы не удалите их с помощьюкоманды DROP. SQL также позволяет вамсоздавать временные таблицы для недолго-го хранения данных или промежуточныхрезультатов. Обычно временные таблицыиспользуются для:

хранения результатов сложного запро-са. Это позволяет впоследствии исполь-зовать их для других запросов и значи-тельно ускорять вычисления;

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

хранения результата подзапроса. См.примечание DBMS в разделе «Созданиевнешних объединений с помощью ко-манды OUTER JOIN» главы 7 (напри-мер, листинг 7.32).

СУБД автоматически удаляет временнуютаблицу после завершения сессии илитранзакции (данные в таблице удаляютсятоже). Сессия – это время вашего подклю-чения к СУБД (между входом и выходом),в течение которого СУБД принимает и ис-полняет команды. Транзакция – это наборкоманд SQL, которые выполняются в каче-стве единой группы (см. главу 13).

При создании временной таблицы следу-ет принять в расчет следующие моменты:

два типа временной таблицы (глобаль-ная и локальная) различаются по до-ступу. Глобальная временная таблицадоступна для всех активных сессий; ло-кальная временная таблица – только длясессии, которая ее создала;временные таблицы отвечают тем жетребованиям в отношении названийтаблиц, столбцов, типов данных и т.д.,что и базовые таблицы;вы задаете временную таблицу с по-мощью стандартной команды CREATETABLE, добавив перед ключевым словомTABLE параметр GLOBAL TEMPORARY илиLOCAL TEMPORARY;изначально во временной таблице нетстрок. Для изменения таблицы вы мо-жете использовать команды INSERT,UPDATE и DELETE так же, как для базовойтаблицы (см. главу 9);если вы создадите большую временнуютаблицу, можете самостоятельно уда-лить ее и освободить таким образом па-мять, а не ждать, пока это сделаетСУБД (см. раздел «Удаление таблицы спомощью команды DROP TABLE» да-лее в этой главе);если вы создадите временную таблицус таким же названием, что и базовая, тоона будет скрывать базовую до тех пор,пока вы ее не удалите;команда CREATE TEMPORARY TABLE позволя-ет администраторам базы данных пре-доставлять пользователям рабочее про-странство и при этом не разрешать имработать с потенциально опасными ко-мандами CREATE TABLE, ALTER TABLE и DROPTABLE.

Создание временной таблицы с помощью команды CREATE TEMPORARY TABLE

Page 396: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

396 Создание, изменение и удаление таблиц

Создание временной таблицы

Введите:CREATE {LOCAL | GLOBAL} TEMPORARY

→ TABLE table

(

column1 data_type1 [constraint1],

column2 data_type2 [constraint2],

columnN data_typeN [constraintN],

[, table_constraints]

);

table – это название временной таблицы,которую вы создаете. Ключевое слово LOCALпоказывает, что таблица будет локальной.GLOBAL означает, что таблица будет глобаль-ной (см. листинги 10.16 и 10.17).column1, column2, …, columnN – это названиястолбцов в таблице table; data_type1,data_type2, …, data_typeN задают тип дан-ных для соответствующих столбцов.Ограничения столбцов и таблиц для вре-менных таблиц отличаются в зависимостиот СУБД. Обратитесь к документации повашей СУБД. Общая информация об огра-ничениях представлена в разделе «Основ-ные принципы работы с ограничениями»ранее в этой главе.

Чтобы увидеть результат исполнения ко-манды CREATE TABLE, изучите структурутаблицы с помощью одной из команд,описанных в разделе «Отображение назва-ний столбцов в таблице» главы 9.

Чтобы изменить временную таблицу, об-ратитесь к разделу «Изменение таблицы спомощью команды ALTER TABLE» далее вэтой главе.

Чтобы создать временную копию сущест-вующей таблицы, обратитесь к разделу «Со-здание новой таблицы на основе сущест-вующей с помощью команды SELECT INTO»далее в этой главе.

Листинг 10.16. Локальная временная таблицадоступна только для вас. Она удаляется, когда вызавершите сессию СУБД

CREATE LOCAL TEMPORARY TABLE editors

(

ed_id CHAR(3) ,

ed_fname VARCHAR(15) ,

ed_lname VARCHAR(15) ,

phone VARCHAR(12) ,

pub_id CHAR(3)

);

Листинг 10.17. Глобальная временная таблицадоступна для вас и других пользователей. Онаудаляется, когда вы завершите сессию СУБД, и вседругие задания перестают обращаться к ней

CREATE GLOBAL TEMPORARY TABLE editors

(

ed_id CHAR(3) ,

ed_fname VARCHAR(15) ,

ed_lname VARCHAR(15) ,

phone VARCHAR(12) ,

pub_id CHAR(3)

);

Page 397: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

397

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

Microsoft Access не поддерживает вре-менные таблицы.В Microsoft SQL Server уберите строку{LOCAL | GLOBAL} TEMPORARY и укажи-те имя временной таблицы, добавив пе-ред ее названием один или два символа#. Если вы обращаетесь к названию вре-менной таблицы, необходимо добавлятьодин или два символа #. Чтобы создатьлокальную временную таблицу в SQLServer, введите:CREATE TABLE #table (…);

Чтобы создать глобальную временнуютаблицу в SQL Server, введите:CREATE TABLE ##table (…);

В Oracle обозначение временной табли-цы «видно» для всех сессий, но ее дан-ные доступны только для сессии, из ко-торой они добавляются. Чтобы создатьвременную таблицу в Oracle, введите:CREATE GLOBAL TEMPORARY

→ TABLE table (…);

MySQL поддерживает только локальныевременные таблицы; пропустите ключе-вое слово LOCAL. Чтобы создать времен-ную таблицу в MySQL, введите:CREATE TEMPORARY TABLE table (…);

PosgreSQL поддерживает только локаль-ные временные таблицы; ключевое сло-во LOCAL является опциональным. ВашаСУБД может поддерживать опциональноепредложение ON COMMIT, которое задает-ся стандартом SQL. Команда ON COMMITPRESERVE ROWS сохраняет все измененияво временной таблице при исполненииCOMMIT, а команда ON COMMIT DELETEROWS удаляет таблицу после исполненияCOMMIT. За информацией о команде COM-MIT обращайтесь к главе 13.СУБД по-разному работает с временнымитаблицами (это касается доступа, ограни-чений, внешних ключей (ссылочной цело-стности), индексов и представлений). Об-ратитесь к документации по вашей СУБД.

Создание временной таблицы с помощью команды CREATE TEMPORARY TABLE

Page 398: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

398 Создание, изменение и удаление таблиц

Создание новой таблицына основе существующейс помощью командыSELECT INTOКоманда SELECT INTO создает новую табли-цу и заполняет ее результатами исполнениякоманды SELECT. Эта команда аналогичнасозданию пустой таблицы с помощьюCREATE TABLE и заполнению ее с помощьюINSERT SELECT (см. раздел «Вставка строк спомощью команды INSERT» в главе 9). Об-ратите внимание, что команда SELECT INTOэкспортирует строки из существующейтаблицы, в то время как команда INSERTSELECT импортирует строки в существую-щую таблицу. Обычно команда SELECT INTOиспользуется для:

архивирования отдельных строк;

создания резервных копий данных;

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

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

создания данных для тестирования;

копирования таблицы для безопаснойпроверки команд INSERT, UPDATE и DELETE.

При использовании команды SELECT INTOнужно учитывать следующие моменты:

вы можете выбирать строки для новойтаблицы с помощью стандартных пред-ложений WHERE, JOIN, GROUP BY и HAVINGкоманды SELECT либо с помощью любойопции SELECT, описанной в главах 4–8;

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

свойства столбцов и выражений всписке предложений команды SELECTопределяют структуру новой таблицы;

если вы включите в список пунктовкоманды SELECT столбец с производ-ными (рассчитанными) данными, тозначения в столбцах новой таблицыбудут соответствовать значениям, ко-торые были рассчитаны на моментвыполнения команды SELECT INTO (см.раздел «Создание производных столб-цов» в главе 5);

название новой таблицы должно отли-чаться от названия существующей;

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

Создание новой таблицына основе существующей

Введите:SELECT columns

INTO new_table

FROM existing table

[WHERE search_condition];

new_table – это название новой таблицы,которую вы создаете; existing_table – на-звание таблицы-источника; columns – спи-сок разделенных запятыми выражений илиназваний столбцов из existing_table.

СУБД рассчитывает выражения в columns,чтобы определить структуру new_table.Столбцы в new_table создаются в порядке,заданном columns. Каждый столбец в new_-table имеет такое же название, тип данныхи значение, что и соответствующий стол-бец в columns. Предложение WHERE опреде-ляет, какие строки из existing_table будутдобавлены в new_table. Вы также можете

Page 399: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

399

указать предложения GROUP BY, HAVING иJOIN. search_condition – это любое условиепоиска WHERE (см. раздел «Фильтрациястрок с помощью предложения WHERE»в главе 4).Листинг 10.18 копирует структуру и дан-ные существующей таблицы authorsв новую таблицу authors2.Листинг 10.19 использует условие WHERE(всегда являющееся ложным), чтобы ско-пировать только структуру (но не данные)существующей таблицы publishers в но-вую таблицу с названием publishers2.Листинг 10.20 создает глобальную времен-ную таблицу titles2, которая содержит за-головки и информацию о продажах книг,выпущенных издательством P01 (см. раз-дел «Создание временной таблицы с по-мощью команды CREATE TEMPORARYTABLE» ранее в этой главе).Листинг 10.21 использует объединения,чтобы создать новую таблицу author_-title_names, которая содержит имена авто-ров, не живущих в штатах Нью-Йорки Калифорния, а также названия их книг.

Чтобы увидеть результат исполнения ко-манды SELECT INTO, введите командуSELECT, например SELECT * FROM

new_table. Чтобы изучить структуру таб-лицы, воспользуйтесь одной из команд,описанных в разделе «Отображение назва-ний столбцов в таблице» главы 9.

Чтобы изменить таблицу, обратитесь к раз-делу «Изменение таблицы с помощью ко-манды ALTER TABLE» далее в этой главе.

Чтобы добавить строки в существующуютаблицу, используйте команду INSERT(см. раздел «Вставка строк с помощью ко-манды INSERT» в главе 9).

Листинг 10.18. Копировать структуру и данныесуществующей таблицы authors в новую таблицуauthors2

SELECT *

INTO authors2

FROM authors;

Листинг 10.19. Скопировать только структуру(но не данные) существующей таблицыpublishers в новую таблицу с названиемpublishers2

SELECT *

INTO publishers2

FROM publishers

WHERE 1 = 2;

Листинг 10.20. Создать глобальную временнуютаблицу titles2, которая содержит заголовкии информацию о продажах книг, выпущенныхиздательством P01

SELECT title_name, sales

INTO GLOBAL TEMPORARY TABLE titles2

FROM titles

WHERE pub_id = 'P01';

Листинг 10.21. Создать новую таблицуauthor_title_names, которая содержит именаавторов, не живущих в штатах Нью-Йорки Калифорния, а также названия их книг

SELECT a.au_fname, a.au_lname, t.title_name

INTO author_title_names

FROM authors a

INNER JOIN title_authors ta

ON a.au_id = ta.au_id

INNER JOIN titles t

ON ta.title_id = t.title_id

WHERE a.state NOT IN ('CA', 'NY');

Создание новой таблицы на основе существующей

Page 400: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

400 Создание, изменение и удаление таблиц

Еще один распространенный способ ис-пользования команды SELECT INTO заклю-чается в том, чтобы создать временнуютаблицу, содержащую данные на текущийдень, например:

SELECT *

INTO GLOBAL TEMPORARY TABLE todays_sales

FROM ordersWHERE order_date = CURRENT DATE;

Стандарт SQL по-другому определяет ко-манду SELECT INTO. Эта команда выбира-ет значение в списке переменных в про-грамме, а не создает новую таблицу. Вер-сия команды SELECT INTO в ORACLE ра-ботает аналогичным образом.

Microsoft Access не поддерживает времен-ные таблицы. Чтобы запустить листинг10.20 в Access, удалите ключевые словаGLOBAL TEMPORARY TABLE. Чтобы запуститьлистинг 10.21 в Access, напечатайте:

SELECT a.au_fname, a.au_lname,

t.title_name INTO author_title_names

FROM titles t INNER JOIN (authors a

INNER JOIN title_authors ta ON a.au_id = ta.au_id)

ON t.title_id = ta.title.id WHERE a.state NOT IN (‘NY’, ‘CA’);

Microsoft SQL Server использует символ ##,чтобы создать глобальную временнуютаблицу. Чтобы запустить листинг 10.20в Microsoft SQL Server, замените INTOGLOBAL TEMPORARY TABLE titles2 на INTO##titles2.

В Oracle команда SELECT INTO использу-ется только для того, чтобы задать пере-менные в PL/SQL. Команда CREATE TABLEAS SELECT в Oracle (еще ее называют CTAS)семантически эквивалентна SELECT INTO.Приведем структуру команды:

CREATE TABLE new_table AS SELECT columns

FROM existing_table

[WHERE search_condition];

Чтобы запустить листинги 10.18–10.21в Oracle, введите (листинг 10.18):

CREATE TABLE authors2 AS

SELECT *

FROM authors;

(листинг 10.19):

CREATE TABLE publishers2 AS

SELECT *

FROM publishers

WHERE 1 = 2;

(листинг 10.20):

CREATE GLOBAL TEMPORARY TABLE

titles2 AS

SELECT title_name, sales

FROM titles

WHERE 1 = pub_id = ‘P01’;

(листинг 10.21):

CREATE TABLE author_title_names AS

SELECT a.au_fname, a.au_lname,

t_title_name

FROM authors a

INNER JOIN title_authors ta

ON a.au_id = ta.au_id

INNER JOIN titles t

ON ta.title.id = t.title_id

WHERE a.state NOT IN (‘CA’, ‘NY’);

В Oracle 8i используйте в листинге 10.21предложение WHERE вместо JOIN:

CREATE TABLE author_title_names AS

SELECT a.au_fname, a.au_lname,

t.title_name

FROM authors a, title_authors ta,

titles t

WHERE a.au_id = ta.au_id

AND ta.title_id = t.title_id

AND a.state NOT IN ('CA', 'NY')

MySQL не поддерживает команду SELECTINTO, в листингах 10.18–10.21 используй-те команды CREATE TABLE и INSERT SELECT.В PostgreSQL удалите из листинга 10.20ключевое слово GLOBAL. PostgreSQL такжеподдерживает команду CREATE TABLE AS,которая является семантическим эквива-лентом SELECT INTO.

Page 401: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

401

Изменение таблицыс помощью командыALTER TABLEИспользуйте команду ALTER TABLE, чтобы из-менить таблицу путем добавления, измене-ния или удаления столбцов и пометок.

Использование команды ALTER TABLE су-щественно различается для разных СУБД.Чтобы определить, что именно вы можетеизменять, и условия, при которых измене-ния будут разрешены, обратитесь к доку-ментации по вашей СУБД. В зависимостиот функций СУБД вы можете использоватькоманду ALTER TABLE, чтобы:

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

Порядок изменения таблицы

Введите:ALTER TABLE table

alter_table_action;

table – это название таблицы, которуювы изменяете; alter_table_action – описа-ние действия для выполнения, начинаетсяс ключевого слова ADD, ALTER или DROP.Например, эти команды можно использо-вать, чтобы изменить или удалить столб-цы либо ограничения:ADD [COLUMN] column_data_typeDROP COLUMN column

ADD constraint_definitionDROP CONSTRAINT constraint_name

Изменение таблицы с помощью команды ALTER TABLE

Page 402: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

402 Создание, изменение и удаление таблиц

Листинги 10.22 и 10.23, соответственно, до-бавляют и удаляют из таблицы authorsстолбец email_address.Если команда ALTER TABLE в СУБД не под-держивает нужное вам действие (напри-мер, не может удалить или переименоватьстолбец или ограничение), вы можетевручную создать и заполнить таблицу.

Создание и заполнение таблицы

1. Используйте команду CREATE TABLE, что-бы создать новую таблицу с новымиописаниями столбцов, ограничениямистолбцов и таблиц (обратитесь к разде-лу «Создание новой таблицы с помо-щью команды CREATE TABLE» ранее вэтой главе).

2. С помощью команды INSERT SELECT ско-пируйте строки (из соответствующихстолбцов) из старой таблицы в новую(см. раздел «Вставка строк с помощьюкоманды INSERT» в главе 9).

3. С помощью SELECT * FROM new_tableпроверьте, что в новую таблицу добав-лены нужные строки (см. раздел «Выборстолбцов с помощью предложений SE-LECT и FROM» в главе 4).

4. Чтобы удалить старую таблицу, исполь-зуйте команду DROP TABLE (см. раздел«Удаление таблицы с помощью командыDROP TABLE» далее в этой главе).

5. Замените новое название таблицы ста-рым (см. примечание DBMS в этом раз-деле).

6. При необходимости повторно создайтеиндексы (см. раздел «Создание индекса спомощью команды CREATE INDEX» вглаве 11). Также необходимо восстано-вить все другие свойства, которые былиудалены вместе со старой таблицей, на-пример права доступа и триггеры.

Листинг 10.22. Добавить в таблицу authorsстолбец email_address

ALTER TABLE authors

ADD email_address CHAR(25);

Листинг 10.23. Удалить из таблицы authorsстолбец email_address

ALTER TABLE authors

DROP COLUMN email_address;

Page 403: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

403

Чтобы увидеть результат исполнения ко-манды ALTER TABLE, изучите структуру спомощью одной из команд, описанных вразделе «Отображение названий столбцовв таблице» главы 9.

Чтобы изменить или удалить ограничение,используйте название, которое вы ввели впредложение CONSTRAINT при его созда-нии (см. раздел «Основные принципы ра-боты с ограничениями» ранее в этой гла-ве). Если вы не задали названия для огра-ничения, используйте название, котороебыло автоматически сгенерировано СУБД.

СУБД обычно накладывает на пустые таб-лицы меньше ограничений на модифика-цию, чем на заполненные. Например, есливы добавляете новый столбец в таблицу,в которой уже есть одна или несколькострок, он не может иметь ограничение NOTNULL (а новый столбец в пустой таблицеможет иметь любое ограничение).

В PostgreSQL команда ALTER TABLE позво-ляет вам добавить или переименоватьстолбец, но не удалить его, поэтому лис-тинг 10.23 не будет работать в PostgreSQL.Чтобы удалить столбец, обратитесь к под-разделу «Создание и заполнение табли-цы» в этом разделе.

Каждая СУБД по-разному переимено-вывает таблицу. В Microsoft Access вамследует выделить имя таблицы в окнеDatabase (База данных) и вызвать длянее контекстное меню, а затем выбратьпункт Rename (Переименовать). В Micro-soft SQL Server выполните команду EXECsp_rename ‘old_name’, ‘new_name’; вOracle – команду RENAME old_name TOnew_name;, в MySQL – команду RENAMETABLE old_name TO new_name;, вPostgreSQL – команду ALTER TABLE

old_name RENAME TO new_name;.

Изменение таблицы с помощью команды ALTER TABLE

Page 404: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

404 Создание, изменение и удаление таблиц

Удаление таблицыс помощью командыDROP TABLEИспользуйте команду DROP TABLE, чтобыудалить таблицу из базы данных. При этомвам следует помнить, что:

вы можете удалить базовую или вре-менную таблицу;удаленная таблица не подлежит восста-новлению. Вы не сможете отменить ко-манду DROP TABLE;будет удалена структура таблицы, дан-ные, индексы, ограничения, права дос-тупа и т.д.;удаление таблицы – не то же самое,что удаление всех ее строк. Вы можетеудалить из таблицы все строки с помо-щью команды DELETE FROM table, но несаму таблицу (см. раздел «Удалениестрок с помощью команды DELETE»в главе 9);не будут удалены представления, кото-рые ссылаются на таблицу (см. главу 12);у вас появятся сложности с внешнимиключами или представлениями, кото-рые ссылаются на удаленную таблицу,если вы не измените или не удалите их.

Удаление таблицы

Введите:DROP TABLE table

table – это название таблицы, которуювы желаете удалить (см. листинг 10.24).

Единственный способ восстановить уда-ленную таблицу – заново создать ее и вос-становить данные, воспользовавшись по-следней резервной копией.

Некоторые СУБД требуют, чтобы до уда-ления таблицы вы удалили или изменилиотдельные свойства. Например, в MicrosoftSQL Server с помощью команды DROP TABLEнельзя удалить таблицу, на которую ссы-лается другая таблица посредством внеш-него ключа, до тех пор, пока не будет уда-лен сам ключ или таблица. Стандарт SQLпозволяет задать опцию удаления какRESTRICT или CASCADE. RESTRICT (безо-пасное удаление) не дает удалить таблицу,на которую ссылаются представления илидругие ограничения. CASCADE (опасное уда-ление) удаляет все связанные объекты вме-сте с таблицей. Чтобы определить, под-держивает ли ваша СУБД эту функцию, об-ратитесь к документации.

Листинг 10.24. Удалить таблицу royalties

DROP TABLE royalties;

Page 405: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

Индексы 1111111111Вспомните из раздела «Таблицы, столбцыи строки» в главе 2, что строки сохраняют-ся в таблице без соблюдения порядка. Этопозволяет СУБД быстро выполнять такиекоманды, как INSERT, UPDATE и DELETE, но от-рицательный эффект заключается в том,что поиск и сортировка данных затрудня-ются. Предположим, что вы запустили навыполнение следующий запрос:SELECT *

FROM authors

WHERE au_lname = 'Hull';

Чтобы выполнить этот запрос, СУБД долж-на осуществить поиск по всей таблицеauthors, сравнивая значения в столбцеau_lname каждой строки со строкой Hull.Выполнить поиск в таблице в небольшойбазе данных просто, но таблицы в круп-ных базах данных могут включать милли-оны строк.В СУБД есть функция под названием ин-декс (Index), которая предназначена длятого же, что и ее эквивалент в книге илибиблиотеке, – для быстрого поиска дан-ных. Проще говоря, индекс представляетсобой список, где каждое значение в индек-сированном столбце (или нескольких столб-цах) хранится вместе с адресом (физичес-ким расположением) строк, содержащих

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

Создание индексас помощью командыCREATE INDEXИндексы достаточно сложны; их эффек-тивность и воздействие на производи-тельность системы зависят от того, какработает программа оптимизации СУБД.В этом разделе мы расскажем об общихпринципах работы с индексами, но реко-мендуем вам внимательно изучить доку-ментацию по вашей СУБД. Обычно ин-дексы следует использовать для столбцов,которые:

часто используются для поиска;часто используются для сортировки;регулярно используются для объеди-нений.

Page 406: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

406 Индексы

Индексы не следует использовать длястолбцов, которые:

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

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

команды SQL для работы с индексамиизменяют объекты базы данных, поэто-му для работы с ними вам необходимополучить разрешение администраторабазы данных;индекс не изменяет данные, а лишь уп-рощает и ускоряет доступ к ним;в таблице может быть сколько угодноиндексов (в том числе ни одного);в идеале вам следует создавать индексыдля таблицы одновременно с самойтаблицей. На практике процесс созда-ния индекса требует времени. Толькосамые важные индексы создаются одно-временно с таблицей. Другие индексыдобавляются по мере того, как в нихвозникает необходимость. Большинст-во СУБД располагает инструментамидля проверки эффективности индексов;не создавайте больше индексов, чемнужно. После того как вы добавляете,изменяете или удаляете строки в табли-це (см. главу 9), СУБД должна обно-вить (а может, и реорганизовать) ин-декс. С учетом увеличения количестваиндексов возрастает время, котороеСУБД тратит на обновление индексовпри изменении таблицы;ваша СУБД будет автоматически под-держивать и использовать индексыпосле их создания. Ни пользователям,ни программистам не понадобится

выполнять какие-либо действия, чтобыотразить изменения во всех индексах;использование индексов одинаково какдля пользователя, так и для программи-ста. Отсутствие или присутствие ин-декса не требует внесения измененийв структуру какой-либо команды SQL;индекс может ссылаться на один илинесколько столбцов в таблице. Индекс,который ссылается на один столбец,является простым; индекс, который ссы-лается на несколько столбцов, – слож-ным. Столбцы в сложном индексе нео-бязательно должны соседствовать в таб-лице;порядок следования столбцов в слож-ном индексе имеет значение. Сложныйиндекс относится только к группестолбцов, для которой он задан, но не ккаждому столбцу по отдельности или ктем же столбцам в другом порядке;вы можете создать несколько сложныхиндексов, которые используют одина-ковые столбцы, если укажете разныекомбинации столбцов. Например, дваследующих выражения задают разныекомбинации для одной таблицы:CREATE INDEX au_name_idx1

ON authors (au_fname, au_lname);

CREATE INDEX au_name_idx2

ON authors (au_fname, au_lname);

кроме ускорения поиска индекс можетслужить средством обеспечения уни-кальности значения. Уникальный индекстребует уникальности значения столб-ца (или комбинации значений для не-скольких столбцов), на который онссылается;если вы попробуете создать уникаль-ный индекс для столбцов, в которыхуже существуют повторяющиеся значе-ния, ваша СУБД выдаст сообщение обошибке и не создаст индекс;

Page 407: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

407

СУБД автоматически создаст уникаль-ный индекс, если вы зададите первич-ный ключ или ограничение уникально-сти;

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

все СУБД работают с индексами, хотяиндексы не входят в основную модель(но и не нарушают ни один из ее прин-ципов);

так как индексы не описаны в стан-дарте SQL-92, команды SQL для рабо-ты с индексами различаются в разныхСУБД. Стандартная команда CREATEINDEX работает одинаково во многихСУБД.

Порядок создания индекса

Введите:

CREATE [UNIQUE] INDEX index

ON table (index_columns);

index – это название индекса, который высоздаете. Названия индексов в таблицедолжны быть уникальными. В Oracleи PostgreSQL названия индексов должныбыть уникальными в базе данных.

table – это название таблицы, для которойбудет создан индекс, а index_columns – этосписок названий столбцов для индекса,разделенных запятыми.

Чтобы создать уникальный индекс, укажи-те опцию UNIQUE. Эта опция заставит СУБДвыполнить поиск повторяющихся значе-ний в index_columns. Если table содержитстроки с повторами в index_columns, СУБДне создаст индекс. Если вы попробуете

вставить в index_columns повторяющиесязначения или заменить существующие зна-чения повторяющимися, СУБД выдастошибку и отменит действие.

Листинг 11.1 создает простой индекс с на-званием pub_id_idx по столбцу pub_id таб-лицы titles. pub_id является вторичнымключом и может использоваться для ин-декса, так как:

изменения в ограничениях первичногоключа проверяются с помощью ограни-чений вторичного ключа в соответству-ющих таблицах;

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

Листинг 11.2 создает простой уникальныйиндекс с названием title_name_idx постолбцу title_name таблицы titles.СУБД создаст этот индекс только приусловии, что в столбце title_name нет по-вторяющихся значений. Этот индекс так-же запрещает добавление повторяющих-ся значений с помощью команд INSERTи UPDATE.

Листинг 11.3 создает сложный индекс сназванием state_city_idx по столбцамtitle и city таблицы authors. СУБД будетиспользовать этот индекс, если вы начнетесортировать строки по столбцам state,city. Этот индекс бесполезен для сорти-ровки и поиска только по столбцу state,только по столбцу city или по столбцамcity, state; для этих целей вам понадо-бятся отдельные индексы.

Создание индекса с помощью команды CREATE INDEX

Page 408: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

408 Индексы

Не употребляйте термины «индекс» и«ключ» как эквивалентные (хотя в литера-туре вы увидите, что их так используют).Индекс представляет собой физическиймеханизм, который используется СУБД дляулучшения производительности системы.Ключ является логическим (основанным наданных) принципом, который применяет-ся СУБД для сохранения ссылочной целос-тности и обновления данных в разных ре-жимах.

Вместо уникального индекса вы можете со-здать ограничение уникальности; см. раздел«Присвоение уникальных значений с помо-щью ограничения UNIQUE» в главе 10.

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

Последовательный поиск по таблице (в ко-торой отсутствуют индексы) называетсясканированием таблицы.

Для программистов: большинство индек-сов построены по принципу B-дерева. От-дельные СУБД позволяют указывать струк-туру данных и алгоритм, который будетиспользоваться при создании индекса.

Если задана опция UNIQUE, Microsoft SQLServer будет рассматривать значения nullкак повторяющиеся и не допустит болееодного такого значения в столбцах с уни-кальным индексом. СУБД Microsoft Access,Oracle, MySQL и PostgreSQL допускаютв таких столбцах производное количествозначений null.

Листинг 11.1. Создать простой индекс в столбцеpub_id таблицы titles

CREATE INDEX pub_id_idx

ON titles (pub_id);

Листинг 11.2. Создать простой уникальныйиндекс в столбце title_name таблицы titles

CREATE UNIQUE INDEX title_name_idx

ON titles (title_name);

Листинг 11.3. Создать сложный индекс в столбцахtitle и city таблицы authors

CREATE INDEX state_city_idx

ON authors (state, city);

Page 409: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

409

Удаление индексас помощью командыDROP INDEXЧтобы удалить индекс, пользуйтесь ко-мандой DROP INDEX. Так как индекс логичес-ки и физически независим от данных в со-ответствующей таблице, вы можете уда-лить его в любое время, и это не повлияетна таблицу (или на другие индексы). Есливы удалите индекс, все SQL-запросы идругие приложения продолжат работу, нодоступ к данным, для которых вы задава-ли индекс, будет более медленным.Основные причины для удаления индекса:

он больше не нужен;замедление работы СУБД при испол-нении команд INSERT, UPDATE и DELETEпревышает то время, которое вы выиг-рываете благодаря использованию ин-декса.

В стандарте SQL-92 не предусмотрены ин-дексы, поэтому команды для работы сними различаются для разных СУБД. Мырасскажем, как удалить индексы для каж-дой СУБД, описанной в этой книге. Есливы работаете с СУБД, не рассматриваемойздесь, обратитесь к документации, чтобыудалить индекс.В Oracle и PostgreSQL названия индексовв базе данных должны быть уникаль-ными, поэтому при удалении индекса вамне нужно указывать название таблицы.В СУБД Microsoft Access, Microsoft SQLServer и MySQL названия индексов в табли-це должны быть уникальными, но могутповторяться в других таблицах, поэтомупри удалении индекса вы должны указатьи таблицу. В примерах этого разделаудаляется индекс, который был созданв листинге 11.1.

Удаление индекса с помощью команды DROP INDEX

Page 410: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

410 Индексы

Удаление индекса в СУБДMicrosoft Access или MySQL

Введите:DROP INDEX index

ON table;

index – это название индекса, который выжелаете удалить; table – название табли-цы, к которой относится данный индекс(см. листинг 11.4a).

Удаление индекса в СУБДMicrosoft SQL Server

Введите:DROP INDEX table.index;

index – это название индекса, который выжелаете удалить; table – название табли-цы, к которой относится данный индекс(см. листинг 11.4б).

Удаление индекса в СУБД Oracleили PostgreSQL

Введите:DROP INDEX index;

index – это название индекса, который выжелаете удалить (см. листинг 11.4в).

Листинг 11.4a. Удалить индекс pub_id_idx(Microsoft Access или MySQL)

DROP INDEX pub_id_idx

ON titles;

Листинг 11.4б. Удалить индекс pub_id_idx(Microsoft SQL Server)

DROP INDEX titles.pub_id_idx;

Листинг 11.4в. Удалить индекс pub_id_idx(Oracle или PostgreSQL)

DROP INDEX pub_id_idx;

Page 411: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

Представление (view) – это команда SELECT,считывающая таблицу, данные в которойзаимствованы из других таблиц (такиетаблицы называются таблицами низшегоуровня).

При работе с представлениями вам следу-ет помнить, что:

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

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

1212121212Представления

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

независимо от того, на какое число таб-лиц низшего уровня ссылается пред-ставление или как эти таблицы комби-нируются, представление всегда являет-ся одной таблицей (см. примечанияи советы в разделе «Таблицы, столбцыи строки» главы 2);

Создание представленияс помощью командыCREATE VIEWВы можете оформить представление ввиде презентации, которая состоит из од-ного окна для показа нескольких базовыхтаблиц. Окно может отображать всю ба-зовую таблицу, часть ее или комбинациюиз нескольких таблиц (или их частей).Представление также может отображатьданные в этих базовых таблицах посред-ством других представлений («окна в ок-нах»). SQL-программисты используют

Page 412: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

412 Представления

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

упрощенный доступ к данным. Пред-ставления скрывают сложные данные иупрощают команды, поэтому поль-зователям легче выполнять действияс представлениями, чем с базовымитаблицами. Если вы создадите сложноепредставление (например, включаю-щее несколько базовых таблиц, объе-динений и подзапросов), пользователисмогут обращаться к нему и при этомне тратить время на то, чтобы разоб-раться в сложных связях между табли-цами (и даже не знать, что эти таблицынаходятся в работе);автоматическое обновление. При об-новлении базовой таблицы все пред-ставления, которые ссылаются на нее,автоматически отражают это измене-ние. Например, если вы добавите в таб-лицу authors строку с информациейо новом авторе, все представления, свя-занные с authors, автоматически изме-нятся. Это позволяет сэкономить дис-ковое пространство и избежать дубли-рования данных, так как без представ-лений СУБД пришлось бы хранить всезаимствованные данные, чтобы сохра-нить их целостность;повышенную безопасность. Один из са-мых распространенных способов ис-пользования представлений – это сокры-тие данных от пользователей путемфильтрации таблиц низшего уровня.Предположим, что таблица employeesвключает столбцы salary и commission.Если вы создадите представление, кото-рое пропускает эти два столбца, адми-нистратор базы данных может датьпользователям разрешение только на

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

Виртуальное представление делает про-грамму независимой от логических измене-ний в структуре базы данных. Предполо-жим, что многие программы работают стаблицей titles. Книги время от времениотправляются в печать, поэтому дизайнербазы данных принял решение уменьшитьнагрузку на систему путем выделениякниг, выходящих из печати, в отдельнуютаблицу. Он разделил таблицу titles надве: in_print_titles и out_of_print_titles.При этом все программы перестают пра-вильно работать, так как они обращаютсяк таблице titles, теперь не существую-щей. Но если эти программы будут обра-щаться к представлению titles, а не кнастоящей таблице, вы сможете изменитьего таким образом, чтобы оно выгляделов виде группы таблиц in_print_titlesи out_of_print_titles (см. раздел «Комби-нирование строк с помощью оператораUNION» в главе 7). Программы воспримут

Page 413: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

413

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

команды SQL для работы с представле-ниями изменяют объекты и данныебазы данных, поэтому для их использо-вания следует получить разрешение ад-министратора вашей базы данных;названия представлениям даются потем же правилам, что и названия табли-цам;названия представлений в базе данныхдолжны быть уникальными (они не мо-гут иметь такое же название, как другаятаблица или представление);столбцы в представлениях получаютназвания столбцов по умолчанию, за-данные для таблиц низшего уровня.С помощью предложения AS вы можетеуказать для столбцов представлениядругие названия; см. раздел «Созданиепсевдонимов столбцов с помощьюпредложения AS» в главе 4;если столбец в представлении будетиметь такое же название, как другойстолбец в нем, вам придется присвоитьему другое название (так как описаниепредставления включает объединение,а столбцы из нескольких разных таб-лиц низшего уровня имеют одинаковоеназвание);столбец в представлении может бытьпростой ссылкой на столбец, бук-венным обозначением или выражени-ем, которое включает выражения илифункции;

если в некоторых СУБД столбец заим-ствуется из арифметического выраже-ния, встроенной функции или буквен-ного значения, вам придется точно ука-зать название столбца в представлении;столбец в представлении получает тотже тип данных, что и столбец или вы-ражение, из которого он заимствуется;не существует ограничения по количе-ству представлений, которые вы може-те создать. Как правило, вы будете со-здавать представления для групп дан-ных, с которыми работает большоечисло пользователей;некоторые СУБД не разрешают созда-вать представления для временных таб-лиц;представления можно задать с помо-щью практически любой команды SE-LECT, хотя использование предложенияORDER BY обычно запрещено;можно располагать представления наразных уровнях, то есть команда SE-LECT, описывающая представление, мо-жет считывать данные из другого пред-ставления. Все представления на раз-ных уровнях должны обращаться к ба-зовым таблицам (иначе вы ничего неувидите). Максимальное количествоуровней для представлений различает-ся для разных СУБД;можно использовать представления дляупрощения сложных запросов. Сохра-нив запрос, который выполняет слож-ные расчеты, в виде представления, высможете повторять расчеты при оче-редном обращении к нему;представление может содержать запрос,который нельзя выразить другим спосо-бом. Например, вы можете создать пред-ставление, которое объединяет сгруппи-рованное представление с базовой таб-лицей или соединяет представление,сформированное с помощью командыUNION, с базовой таблицей;

Создание представления с помощью команды CREATE VIEW

Page 414: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

414 Представления

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

представления могут отображать дан-ные в другом формате, чем в таблицахнизшего уровня;

в отличие от базовой таблицы, пред-ставление не разрешает использованиеиндексов и ограничений;

когда вы создаете представление с по-мощью команды SELECT *, SQL конвер-тирует знак «*» в список всех столбцов.Это преобразование выполняется толь-ко один раз в процессе создания пред-ставления (а не при исполнении коман-ды), поэтому при добавлении столбцав таблицу низшего уровня (с помощьюкоманды ALTER TABLE) описание пред-ставления не изменится и его надо бу-дет обновить вручную;

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

Создание представления

Введите:CREATE VIEW view [(view_columns)]

AS select_statement;

view – это название представления, кото-рое вы желаете создать. Это названиев базе данных должно быть уникальным.view_columns – это опциональный списокразделенных запятыми названий для столб-цов в представлении. Количество столб-цов в view_columns должно соответство-вать количеству столбцов в предложенииSELECT команды select_statement (если вы

укажете название хотя бы одного столбца,вам придется перечислить и остальныестолбцы). Следует указывать view_columns,если столбец в select_statement заимству-ется из арифметического выражения, фун-кции или буквенного обозначения; если не-сколько столбцов в представлении получа-ют одинаковое название (обычно из-за ис-пользования объединений); либо для того,чтобы присвоить столбцу название, отлич-ное от названия столбца, из которого онзаимствуется. Если вы пропускаете спи-сок view_columns, представление заимству-ет названия столбцов из команды se-lect_statement. Вы также можете задаватьназвания столбцов в select_statement с по-мощью предложений AS. Название каждогостолбца в представлении должно быть уни-кальным.

select_statement – это команда SELECT,идентифицирующая столбцы и строкив таблице или таблицах, на которых бази-руется представление. select_statementможет быть достаточно сложной коман-дой и включать несколько таблиц илидругих представлений. Обычно использо-вание предложения ORDER BY запрещено. Задополнительной информацией о командеSELECT обращайтесь к главам 4–8. За инфор-мацией о запретах разных СУБД касатель-но использования команды SELECT для со-здания представлений обращайтесь к до-кументации по вашей СУБД (см. листинги12.1–12.5).

Чтобы выбирать строки и столбцы при за-просе по представлению, обратитесь кразделу «Считывание данных из представ-ления» далее в этой главе.

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

Page 415: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

415

Листинг 12.2. Создать представление, котороеотображает список всех авторов, проживающихв городах, где расположены издательства.Обратите внимание, что мы используемв представлении названия столбцов au_cityи pub_city. Мы переименовали эти столбцы,чтобы избежать затруднений, которые возниклибы, если бы оба столбца заимствовали названиеcity из таблиц низшего уровня

CREATE VIEW cities

(au_id, au_city, pub_id, pub_city)

AS

SELECT a.au_id, a.city, p.pub_id, p.city

FROM authors a

INNER JOIN publishers p

ON a.city = p.city;

Чтобы удалить представление, обратитеськ разделу «Удаление представления с по-мощью команды DROP VIEW» далее вэтой главе.

Нельзя использовать команду SELECT INTOс представлением; см. раздел «Создание но-вой таблицы на основе существующей с по-мощью команды SELECT INTO» в главе 10.

Вы не можете создавать временные пред-ставления. Представления и временныетаблицы отличаются по длительности су-ществования. Представление существует,пока выполняется команда SQL, обращаю-щаяся к нему; временная таблица суще-ствует на протяжении сессии. См. раздел«Создание временной таблицы с помощьюкоманды CREATE TEMPORARY TABLE» вглаве 10.

SQL не имеет команды ALTER VIEW. Еслитаблицы низшего уровня или представле-ния были изменены после создания, уда-лите их и создайте заново (Microsoft SQLServer и Oracle поддерживают командуALTER VIEW).

Если вы запустите команду CREATE VIEWв Microsoft Access, представление отобра-зится в виде объекта запроса в окне Da-tabase (База данных). Чтобы запуститьлистинг 12.4 в Access, замените все сим-волы «||» знаком «+» (см. примечаниеDBMS в разделе «Объединение строк с по-мощью оператора ||» главы 5). Чтобы за-пустить листинг 12.5 в Access, введите:

CREATE VIEW au_titles

(LastName, Title) AS

SELECT an.au_lname, t.title_name FROM au_names an

INNER JOIN (titles t INNER JOIN title_authors ta

ON t.title_id = ta.title_id) ON an.au_id = ta.au_id

WHERE an.au_id IN ('A02', 'A05');

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

CREATE VIEW au_names

AS

SELECT au_id, au_fname, au_lname

FROM authors;

Листинг 12.3. Создать представление, котороеотображает список прибыли (= цена × продажи),сгруппированный по типу книг для издательства.Последующее использование запросов к этомупредставлению будет простым, так как мы задалиназвание для результата арифметическоговыражения, а не позволили СУБД присвоитьназвание по умолчанию

CREATE VIEW revenues

(Publisher, BookType, Revenue)

AS

SELECT pub_id, type, SUM(price * sales)

FROM titles

GROUP BY pub_id, type;

Создание представления с помощью команды CREATE VIEW

Page 416: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

416 Представления

Microsoft SQL Server не поддерживает точкус запятой после команды CREATE VIEW (чтостранно). Чтобы запустить листинги 12.1–12.5 в SQL Server, уберите точку с запятойиз каждой команды. Кроме того, чтобызапустить листинг 12.4 в SQL Server, заме-ните все символы «||» знаком «+», а всефункции TRIM(x) – выражением LTRIM(RTRIM(x)) (см. примечания DBMS в раз-делах «Объединение строк с помощью опе-ратора ||» и «Удаление пробелов с помо-щью функции TRIM()» главы 5).

Oracle 9i запустит листинги 12.2 и 12.5 безизменений. Чтобы запустить листинги12.2 и 12.5 в Oracle 8i, используйте син-таксис WHERE вместо JOIN.

Введите (листинг 12.2):

CREATE VIEW cities

(au_id, au_city, pub_id, pub_city) AS

SELECT a.au_id, a.city, p.pub_id, p.city

FROM authors a, publishers p WHERE a.city = p.city;

и (листинг 12.5)

CREATE VIEW au_titles (LastName, Title)

AS SELECT a.au_lname, t.title_name,

FROM title_authors ta, au_names an, titles t

WHERE ta.au_id = an.au_id AND t.title_id = ta.title_id

AND an.au_id in ('A02', 'A05');

СУБД MySQL 4.0 и более ранних версий неподдерживает представления и не сможетзапустить листинги 12.1–12.5. Чтобы скрытьданные, с помощью системы привилегийMySQL запретите доступ к столбцам.

SQL позволяет задать опциональное пред-ложение WITH [CASCADED | LOCAL] CHECKOPTION при создании представления. Этопредложение применяется только к изме-няемым представлениям и позволяет до-бавлять, заменять или удалять только теданные, которые считываются в представ-лении. См. раздел «Изменение данных че-рез представление» далее в этой главе.

Листинг 12.4. Создать представление, котороеупрощает распечатку почтовых адресов авторов.Обратите внимание, что мы задали названиястолбцов в предложении SELECT,а не в предложении CREATE VIEW

CREATE VIEW mailing_labels

AS

SELECT

TRIM(au_fname || ' ' || au_lname)

AS "address1",

TRIM(address)

AS "address2",

TRIM(city) || ', ' || TRIM(state) ||

→ ' ' || TRIM(zip)

AS "address3"

FROM authors;

Листинг 12.5. Создать представление, котороеотображает список фамилий авторов A02 и A05,а также книг, написанных ими (в том числе всоавторстве). Обратите внимание, что эта командаиспользует несколько уровней представлений –ссылается на представление au_names, созданноев листинге 12.1

CREATE VIEW au_titles (LastName, Title)

AS

SELECT an.au_lname, t.title_name

FROM title_authors ta

INNER JOIN au_names an

ON ta.au_id = an.au_id

INNER JOIN titles t

ON t.title_id = ta.title_id

WHERE an.au_id in ('A02','A05');

Page 417: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

417

Например, если в представлении показанытолько авторы из штата Нью-Йорк, вы несможете в этом режиме добавить, изменитьили удалить информацию о тех авторах, ко-торые не живут в штате Нью-Йорк. ОпцииCASCADED и LOCAL применяются только кмногоуровневым представлениям. CASCADEDпроверяет текущее представление и всепредставления, на которые оно ссылается.LOCAL проверяет только текущее представ-ление, даже если оно ссылается на другие.Microsoft SQL Server и Oracle поддерживаютCHECK OPTION (обратитесь к документации).

Считывание данныхиз представленияПри создании представления на экране неотображается ничего. Команда CREATE VIEWвсего лишь дает указание СУБД сохранитьпредставление в виде команды SELECT. Что-бы увидеть данные в представлении, надовыполнить команду SELECT точно так же,как вы запрашиваете данные из обычнойтаблицы. Вы можете:

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

с помощью операторов и функцийвыполнять расчеты;

изменять заголовки столбцов с помо-щью AS;

фильтровать строки с помощью WHERE;

группировать строки с помощью GROUPBY;

фильтровать сгруппированные строки спомощью HAVING;

объединять представления с другимипредставлениями или с таблицами по-средством JOIN;

сортировать результат с помощью OR-DER BY.

Считывание данных из представления

Введите:SELECT columns

FROM view

[JOIN joins]

[WHERE search_condition]

[GROUP BY group_columns]

[HAVING search_condition]

[ORDER BY sort_columns];

view – это название представления для за-проса. Предложения команды работают спредставлениями так же, как с таблицами.

LastName Title

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

Kells Ask Your System Administrator

Heydemark How About Never?

Heydemark I Blame My Mother

Heydemark Not Without My Faberge Egg

Heydemark Spontaneous, not Annoying

Рис. 12.1. Результат листинга 12.6

Листинг 12.6. Отобразить все строки и столбцыиз представления au_titles. Результатисполнения показан на рис. 12.1

SELECT *

FROM au_titles;

Листинг 12.7. Отобразить список городовв представлении cities. Результат исполненияпоказан на рис. 12.2

SELECT DISTINCT au_city

FROM cities;

au_city

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

New York

San Francisco

Рис. 12.2. Результат листинга 12.7

Считывание данных из представления

Page 418: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

418 Представления

За информацией о предложениях SELECT,FROM, ORDER BY и WHERE обратитесь к главе 4;за информацией о GROUP BY и HAVING –к главе 6; за информацией о JOIN – к главе 7.Листинги 12.6–12.11 и рис. 12.1–12.6 пока-зывают, как считывать данные из представ-лений, созданных листингами 12.1–12.5в разделе «Создание представления с помо-щью команды CREATE VIEW».

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

Чтобы удалить представление, обратитеськ разделу «Удаление представления с по-мощью команды DROP VIEW» далее в этойглаве.

Чтобы запустить листинг 12.9 в MicrosoftAccess, возьмите названия столбцов пред-ставления в двойные кавычки и скобки:

SELECT ["address3"]

FROM mailing_labels

WHERE ["address1"] LIKE '%Kell%';

Чтобы запустить листинг 12.9 в Oracle,возьмите названия столбцов представле-ния в двойные кавычки:

SELECT "address3"

FROM mailing_labels

WHERE "address1" LIKE '%Kell%';

MySQL 4.0 и более ранних версий не под-держивает представления и не сможет за-пустить листинги 12.6–12.11.

Листинг 12.8. Отобразить список типов книг,доход от продаж которых превышает 1 миллиондолларов США. Результат исполнения показан нарис. 12.3

SELECT BookType,

AVG(Revenue) AS "AVG(Revenue)"

FROM revenues

GROUP BY BookType

HAVING AVG(Revenue) > 1000000;

Листинг 12.9. Отобразить третью строку почтовогоадреса каждого автора, в имени которого есть словоKell. Результат исполнения показан на рис. 12.4

SELECT address3

FROM mailing_labels

WHERE address1 LIKE '%Kell%';

BookType AVG(Revnue)

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

biographi 18727318.50

computer 1025396.65

psychology 2320933.76

Рис. 12.3. Результат листинга 12.8

Рис. 12.4. Результат листинга 12.9

address3

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

New York, NY 10014

Palo Alto, CA 94305

Page 419: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

419

Изменение данныхчерез представлениеИзменяемое представление – это такоепредставление, к которому вы можетеприменять команды INSERT, UPDATE и DE-LETE, чтобы преобразовать данные в табли-цах низшего уровня. Любые изменения,вносимые в модифицируемое представле-ние, всегда применяются к базовым таб-лицам. Команды INSERT, UPDATE и DELETEработают с представлениями так же, какс таблицами (см. главу 9).Неизменяемое представление не поддержи-вает команды INSERT, UPDATE и DELETE,поскольку изменения не будут внесеныв базовые таблицы. Такие представленияпредназначены только для чтения. Чтобыизменить данные, которые отображаютсяв неизменяемом представлении, вам сле-дует напрямую изменить таблицы низше-го уровня.Каждая строка в изменяемом представле-нии связана с одной строкой в базовой таб-лице низшего уровня. Для того чтобы изме-нять данные в представлении, командаSELECT, которая задает представление, дол-жна отвечать следующим требованиям:

представление должно быть задано дляодной таблицы (без объединений);команда SELECT не может сопровождать-ся предложениями GROUP BY и HAVING;команда SELECT DISTINCT запрещена;запросы UNION, INTERSECT и EXCEPT за-прещены;предложение SELECT может задаватьтолько ссылки на простые столбцы, ноне на столбцы с расчетами, функциямии т.д.;если таблица низшего уровня являетсяпредставлением, оно тоже должно бытьизменяемым.

Листинг 12.10. Отобразить имена всех авторов,которые написали в соавторстве хотя бы однукнигу. Результат исполнения показан на рис. 12.5

SELECT DISTINCT an.au_fname, an.au_lname

FROM au_names an

INNER JOIN title_authors ta

ON an.au_id = ta.au_id

WHERE ta.au_order > 1;

Листинг 12.11. Отобразить список авторов,которые живут в Калифорнии. Результатисполнения показан на рис. 12.6

SELECT au_fname, au_lname

FROM au_names

WHERE state = 'CA';

au_fname au_lname

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

Hallie Hull

Klee Hull

Рис. 12.5. Результат листинга 12.10

Рис. 12.6. Результат листинга 12.11

ERROR: Invalid column name 'state'.

Изменение данных через представление

Page 420: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

420 Представления

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

внутренних объединениях «один-к-од-ному»;внешних объединениях «один-к-одно-му»;внутренних объединениях «один-ко-многим»;внешних объединениях «один-ко-мно-гим»;объединениях «многие-ко-многим»;запросах UNION и EXCEPT.

В этом разделе мы приведем пример изме-няемого представления, которое ссылает-ся только на одну таблицу низшего уров-ня (в соответствии со стандартом SQL).За информацией о том, поддерживает ливаша СУБД изменяемые представления,которые ссылаются на несколько таблицнизшего уровня, и если да, то каким об-разом они влияют на эти таблицы, обра-титесь к документации по вашей СУБД.

Добавление строки в представлении

Рассмотрим представление ny_authors, ко-торое состоит из информации об авторах,проживающих в штате Нью-Йорк (см. ли-стинг 12.12 и рис. 12.7). ny_authors ссылает-ся только на базовую таблицу authors.

Листинг 12.12. Создать и отобразитьпредставление ny_authors, которое состоит изинформации только об авторах, проживающих вштате Нью-Йорк. Результат исполнения листингапоказан на рис. 12.7

CREATE VIEW ny_authors

AS

SELECT au_id, au_fname, au_lname,

state

FROM authors

WHERE state = 'NY';

SELECT *

FROM ny_authors;

au_id au_fname au_lname state

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

A01 Sarah Buchman NY

A05 Christian Kells NY

Рис. 12.7. Результат листинга 12.12:режим просмотра ny_authors

Page 421: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

421

Листинг 12.13 добавляет новую строку впредставление. СУБД вставляет новую стро-ку в таблицу authors. Строка содержитзначение A08 в столбце au_id, Don в au_-fname, Dawson в au_lname и NY в state. Дру-гие столбцы в строке (phone, address, cityи zip) задаются как NULL (или значения поумолчанию, если есть ограничение DE-FAULT).

Листинг 12.14, как и листинг 12.13, добав-ляет новую строку в представление. Одна-ко новый автор из штата Калифорния, ане из Нью-Йорка, что делает ложнымусловие WHERE в описании представле-ния. Добавит СУБД новую строку или от-менит действие? Ответ на вопрос зависитот того, как создавалось представление.В нашем примере строка будет добавлена,так как команда CREATE VIEW (см. листинг12.12) не имеет предложения WITH CHECKOPTION, поэтому СУБД не должна сохранятьсоответствие с начальной установкой пред-ставления. За информацией о предложенииWITH CHECK OPTION обратитесь к примечаниюDBMS в разделе «Создание представленияс помощью команды CREATE VIEW» этойглавы. СУБД отменила бы вставку строки,если бы представление ny_authors было за-дано следующим образом:CREATE VIEW ny_authors

AS

SELECT au_id, au_fname, au_lname,

state

FROM authors

WHERE state = 'NY'

WITH CHECK OPTION;

Изменение строки в представлении

Листинг 12.15 изменяет существующуюстроку в представлении. СУБД преобразу-ет строку автора A01 в таблице authors пу-тем замены Сары Бухман (Sarah Buchman)

Листинг 12.13. Добавить новую строкув представление ny_authors

INSERT INTO ny_authors

VALUES('A08','Don','Dawson','NY');

Листинг 12.14. Добавить новую строкув представление ny_authors. СУБД отменитвставку, если при создании представления выиспользовали опцию WITH CHECK OPTION

INSERT INTO ny_authors

VALUES('A09','Jill','LeFlore','CA');

Изменение данных через представление

Page 422: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

422 Представления

на Ясмин Хаукамли (Yasmin Howcomely).Значения в других столбцах этой строки(au_id, phone, address, city, state и zip) неизменяются.Предположим, что листинг 12.15 имеетследующий вид:UPDATE ny_authors

SET au_fname = 'Yasmin',

au_lname = 'Howcomely',

state = 'CA'

WHERE au_id = 'A01';

Эта команда встречается с той же пробле-мой, что и листинг 12.14: при внесенииизменения строка Yasmin больше не будетсоответствовать условиям, заданным дляпредставления. СУБД примет или отверг-нет изменения в зависимости от того,была ли задана опция WITH CHECK OPTIONпри создании представления. Если онабыла задана, строка не может быть изме-нена.

Удаление строки в представлении

Листинг 12.16 удаляет строку в представле-нии. СУБД удалит строку с информациейоб авторе A05 из таблицы authors (это каса-ется всех столбцов в строке, а не толькотех, которые отображены в представлении).В свою очередь, строка будет удалена изсамого представления ny_authors.Разумеется, при изменении представлениямогут возникнуть нарушения в системессылок. СУБД отменит удаление, если приудалении будет нарушено ограничение,сохраняющее ссылочную целостность. См.раздел «Задание внешнего ключа с по-мощью ограничения FOREIGN KEY» в гла-ве 10. Удаление строки будет выполненотолько при условии, что ни одно из огра-ничений FOREIGN KEY в связанных таблицахне будет нарушено. Некоторые изменениямогут быть выполнены с помощью опции

Листинг 12.15. Изменить существующую строкув представлении ny_authors

UPDATE ny_authors

SET au_fname = 'Yasmin',

au_lname = 'Howcomely'

WHERE au_id = 'A01';

Листинг 12.16. Удалить строкув представлении ny_authors

DELETE FROM ny_authors

WHERE au_id = 'A05';

Page 423: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

423

CASCADE (если она была задана) в ограниче-нии FOREIGN KEY, а не с помощью описанияпредставления.Например, в листинге 12.16 СУБД отменитизменения, если предварительно не изме-нить или не удалить значения внешнегоключа в таблице title_authors, которыеуказывают на автора A05 в authors.

Чтобы удалить представление, обратитеськ разделу «Удаление представления с по-мощью команды DROP VIEW» далее вэтой главе.

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

Любой столбец, который не входит впредставление, должен иметь разрешениена содержание значения null или ограни-чение DEFAULT в базовой таблице, чтобыСУБД могла создать целую строку длядобавления.

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

Некоторые столбцы, получаемые как ре-зультат арифметических выражений, явля-ются (теоретически) изменяемыми. Напри-мер, в представлении с вычисляемымстолбцом bonus = 0.1 * salary вы може-те попробовать изменить значение bonusи использовать обратную функцию(bonus/0.1), чтобы изменить значениестолбца salary в базовой таблице. Одна-ко ваши усилия пропадут даром, так какSQL не будет реализовывать изменения ввычисляемых столбцах.

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

Чтобы запустить листинг 12.12 в MicrosoftSQL Server, уберите точку с запятой из ко-манды CREATE VIEW и запустите две ко-манды по отдельности.

MySQL 4.0 и более ранних версий не под-держивает представления и не запуститлистинги 12.12–12.16.

Чтобы допустить работу с представлени-ем, следует использовать систему правилPostgreSQL. Команда CREATE RULE, кото-рая задает правила, является расширениемPostgreSQL, а не частью ANSI SQL (обрати-тесь к документации по СУБД PostgreSQL).

Чтобы узнать, как СУБД работает с пред-ставлениями для столбцов, тип данныхв которых автоматически создает иденти-фикатор строк, обратитесь к документации.См. примечание DBMS в разделе «Первич-ные ключи» главы 2.

Изменение данных через представление

Page 424: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

424 Представления

Удаление представленияс помощью командыDROP VIEWЧтобы удалить представление, пользуй-тесь командой DROP VIEW. Так как представ-ление физически независимо от таблицнизшего уровня, вы можете удалить его влюбое время и это не повлияет на самитаблицы. Все программы SQL, приложенияи другие представления, которые ссыла-ются на удаленное представление, пере-станут нормально функционировать.

Удаление представления

Введите:DROP VIEW view;

view – это название представления, котороевы желаете удалить (см. листинг 12.17).

При удалении представления другие пред-ставления, которые ссылаются на него,удалены не будут. Поэтому вам придетсяпо отдельности удалять каждое представ-ление с помощью команды DROP VIEW; см.раздел «Удаление таблицы с помощью ко-манды DROP TABLE» в главе 10.

В SQL нет команды ALTER VIEW. Если выизменили таблицу низшего уровня илипредставление после его создания, следу-ет удалить его и создать заново (MicrosoftSQL Server и Oracle поддерживают коман-ду ALTER VIEW).

MySQL 4.0 и более ранних версий не под-держивает представления и не сможет за-пустить листинг 12.17.

Листинг 12.17. Удалить представлениеny_authors

DROP VIEW ny_authors;

Page 425: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

Транзакция (Transaction) – это последова-тельность из одной или нескольких ко-манд SQL, которые исполняются в видеединого логического целого. СУБД рас-сматривает транзакцию как неделимуюгруппу и исполняет либо все ее команды,либо ни одной.Давайте проиллюстрируем важность тран-закций на примере. Предположим, чтоклиент переводит 500 долларов США сосвоего сберегательного счета на свой че-ковый счет. Эта операция состоит из двухотдельных действий, которые выполняют-ся последовательно:

1. Уменьшить сберегательный счет на 500долларов США.

2. Увеличить чековый счет на 500 долларовСША.

На рис. 13.1 показаны две команды SQLдля данной транзакции. Представим те-перь, что СУБД откажет (по причине сбояпитания, нарушения в работе системы, не-исправности аппаратных средств) послеисполнения первой команды, но до того,как будет исполнена вторая команда. Ба-ланс на счетах нарушится, а вы не будетеоб этом знать. Это может привести кочень серьезным и неприятным послед-ствиям.

1313131313Транзакции

UPDATE saving_accounts

SET balance = balance – 500.00

WHERE account_number = 1009;

UPDATE checking_accounts

SET balance = balance + 500.00

WHERE account_number = 6482;

Рис. 13.1. Если клиент банка переводит деньгисо сберегательного счета на чековый,нужно исполнить две команды SQL

Page 426: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

426 Транзакции

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

Выполнение транзакцийЧтобы изучить принципы выполнениятранзакций, следует иметь представлениео некоторых понятиях:

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

откат (Roll back). Откат транзакцииотменяет все изменения, которые воз-никли в результате исполнения ее ко-манд. После отката транзакции данныесохранят свой начальный вид, как буд-то она никогда не была исполнена;

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

Хотя СУБД поддерживает физическую це-лостность каждой транзакции, вы должныначинать и завершать транзакции такимобразом, чтобы сохранять логическую це-лостность данных, в соответствии с требо-ваниями решаемой задачи. Операция дол-жна состоять только из тех команд SQL,которые необходимы для выполнения од-ного изменения (не больше и не меньше).Перед транзакцией и после нее данные вовсех связанных таблицах должны быть врабочем состоянии.При разработке и исполнении транзакцийнеобходимо учитывать следующие фак-торы:

команды SQL, работающие в транзакци-ях, изменяют данные, поэтому вам при-дется получить разрешение админист-ратора базы данных, чтобы запуститьих;обработка транзакций относится толь-ко к командам INSERT, UPDATE и DELETE(см. главу 9). Вы не сможете отменитьрезультат выполнения команд SELECT,CREATE, ALTER и DROP, если включите их втранзакцию;каждая команда INSERT, UPDATE и DELETEдолжна выполняться как часть транзак-ции;выполненная транзакция становитсяпостоянной. Это значит, что все изме-нения сохранятся даже в случае сбоясистемы;система восстановления данных СУБДзависит от транзакции. Когда вы вклю-чаете СУБД после сбоя, она проверяетжурнал транзакций, чтобы выяснить,были ли они выполнены. Если СУБДнайдет незавершенные транзакции, онаотменит их на основании журнала. Вамследует повторить все отмененныетранзакции (хотя некоторые СУБД ав-томатически повторяют незавершен-ные транзакции);

Page 427: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

427

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

При этом база данных восстанавлива-ется в том виде, в котором она была досбоя (вам опять-таки придется повто-рить все незаконченные транзакции);

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

Для исполнения транзакции ее границы(начальная и конечная точки) должныбыть четко обозначены. Эти границы по-зволяют СУБД исполнить все команды ввиде единой операции. В соответствии состандартом SQL транзакция всегда начина-ется с первой команды SQL и заканчивает-ся командой COMMIT или ROLLBACK.

Контроль параллелизма

Некоторые пользователи считают, что компьютеры выполняют несколько действийодновременно. На самом деле эти действия выполняются не одновременно, а после-довательно. Иллюзия одновременного исполнения возникает потому, что микропро-цессор работает с отрезками времени, намного меньшими, чем человек может пред-ставить. В СУБД контроль параллелизма представляет собой набор правил, которыепредотвращают нарушения целостности данных, вызванные тем, что несколькопользователей одновременно обращаются к данным или изменяют их.СУБД использует блокировку (самую распространенную методику), чтобы обеспечитьцелостность при исполнении операций, а также целостность базы данных. Блокировказапрещает доступ к данным при считывании и записи; таким образом, она не дает пользо-вателям считать данные, которые изменяются другими пользователями. При этом не-сколько пользователей не смогут одновременно изменять одни и те же данные. Без бло-кировки данные могут стать логически неверными и команды, использующие такиеданные, будут выдавать неправильные результаты. Механизмы блокировки очень слож-ны; обратитесь к документации по вашей СУБД.Прозрачность параллелизма – это механизм, благодаря которому при исполнениитранзакции создается впечатление, что это единственная операция, которая испол-няется с базой данных. СУБД изолирует изменения, выполненные транзакцией, отизменений, выполненных другими транзакциями. Транзакция никогда не используетданные в промежуточном состоянии; она работает с данными либо до изменения,либо после завершения других транзакций. Этот уровень изоляции позволяет пе-резагрузить начальные данные и выполнить серию транзакций, которые приведутданные в то состояние, в котором они были после исполнения оригинальных тран-закций.

Выполнение транзакций

Page 428: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

428 Транзакции

Oracle совместим со стандартом ANSI.В других СУБД вы можете (или должны)начинать операцию с помощью определен-ной команды. СУБД обычно используетключевое слово BEGIN, чтобы точно обо-значить начало транзакции. BEGIN не яв-ляется частью стандарта SQL, поэтомуего использование отличается для раз-ных СУБД (обратитесь к документации повашей СУБД).

Как начать транзакцию

В Microsoft Access или Microsoft SQL Ser-ver введите BEGIN TRANSACTION;илив MySQL или PostgreSQL введите BEGIN;.

Порядок выполнения транзакции

Введите COMMIT;.

Отмена транзакции

Введите ROLLBACK;.Команды SELECT в листинге 13.1 показыва-ют, что транзакции UPDATE исполняютсяСУБД, а затем отменяются командойROLLBACK. Результат исполнения листингапоказан на рис. 13.2.Листинг 13.2 демонстрирует более практи-ческое применение транзакций. Мы жела-ем удалить издательство P04 из таблицыpublishers без нарушения ссылочной цело-стности. Поскольку некоторые значениявторичного ключа в titles указывают наиздательство P04, сначала нам нужно уда-лить связанные строки из таблиц titles,titles_authors и royalties. Мы использу-ем транзакцию, чтобы убедиться, что всекоманды DELETE будут исполнены. Если невсе команды были исполнены успешно,то данные останутся неизмененными (заинформацией о проверках ссылочной

SUM(pages) AVG(price)

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

5107 18.3975

SUM(pages) AVG(price)

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

0 36.7750

SUM(pages) AVG(price)

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

5107 18.3975

Рис. 13.2. Результат листинга 13.1.Результаты исполнения команд SELECTпоказывают, что СУБД отменила транзакцию

Листинг 13.1. Команды UPDATE (как и командыINSERT и DELETE) в группе команд транзакцииникогда не являются конечными. Результатисполнения листинга показан на рис. 13.2

SELECT SUM(pages), AVG(price) FROM titles;

BEGIN;

UPDATE titles SET pages = 0;

UPDATE titles SET price = price * 2;

SELECT SUM(pages), AVG(price) FROM titles;

ROLLBACK;

SELECT SUM(pages), AVG(price) FROM titles;

Page 429: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

429

целостности обратитесь к разделу «Зада-ние внешнего ключа с помощью ограниче-ния FOREIGN KEY» в главе 10).

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

Вы можете располагать транзакции на не-скольких уровнях. Максимальное количе-ство уровней зависит от конкретной СУБД.

Большинство СУБД по умолчанию работа-ют в режиме автоматического исполнения,если только вы не ввели ключевое словодля транзакции. В режиме автоматическо-го исполнения каждая команда исполняет-ся как отдельная транзакция. Если коман-да была завершена успешно, СУБД испол-няет ее; если СУБД обнаруживает ошибку,команда будет отменена.

Работая с большими транзакциями, вы мо-жете задать промежуточные маркеры (точ-ки сохранения), чтобы разделить транзак-цию на несколько частей. Точки сохране-ния позволяют отменять изменения от те-кущей точки в транзакции до предыдущейточки (при условии, что транзакция покане была выполнена). Представьте себесессию, в которой вы ввели нескольконеисполненных команд INSERT, UPDATE иDELETE, а затем поняли, что последниеизменения неверны или не нужны. С помо-щью точек сохранения вы можете избежатьвосстановления всех команд. Microsoft SQLServer и Oracle поддерживают точки со-хранения.

Листинг 13.2. Используйте транзакцию, чтобыудалить издательство P04 из таблицыpublishers, а также связанные с ней строкив других таблицах

BEGIN;

DELETE FROM title_authors

WHERE title_id IN

(SELECT title_id

FROM titles

WHERE pub_id = 'P04');

DELETE FROM royalties

WHERE title_id IN

(SELECT title_id

FROM titles

WHERE pub_id = 'P04');

DELETE FROM titles

WHERE pub_id = 'P04';

DELETE FROM publishers

WHERE pub_id = 'P04';

COMMIT;

Выполнение транзакций

Page 430: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

430 Транзакции

Чтобы запустить листинги 13.1 и 13.2 в Mic-rosoft Access, вместо команды BEGIN ис-пользуйте BEGIN TRANSACTION. В Accessвы не можете исполнять транзакции по-средством пользовательского интерфейсаAccess SQL View или DAO; для этих целейнужен провайдер Jet OLE DB и ADO.

Чтобы запустить листинги 13.1 и 13.2 вMicrosoft SQL Server, замените BEGIN наBEGIN TRANSACTION.

Транзакции в Oracle начинаются без вводаключевого слова. Чтобы запустить листин-ги 13.1 и 13.2 в Oracle, пропустите ключе-вое слово BEGIN.

MySQL 4.0 и более ранних версий поддер-живает транзакции в таблицах InnoDB иBDB, но не в родных таблицах (обратитеськ документации по MySQL).

Page 431: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

В большинстве примеров данной книгииспользуется база данных books (см. раз-дел «Наша типовая база данных» в гла-ве 2). Чтобы работать с примерами, необ-ходимо создать в вашей СУБД базу дан-ных books.В этом приложении мы расскажем, как со-здать такую базу данных, построить еетаблицы и заполнить их данными. Всенужные файлы вы можете загрузить сWeb-страницы данной книги по адресуwww.peachpit.com/vqs/sql или с сайта изда-тельства ДМК Пресс www.dmkpress.ru. Фай-лы хранятся в архиве с расширением zip.Чтобы разархивировать их, используйтеспециальную утилиту, например WinZip(Windows – www.winzip.com), gunzip илиunzip (Mac OS – www.stuffit.com). Пос-ле разархивирования прочитайте файлreadme.txt.

Приложение ААААА

Page 432: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

432 Приложение А

Создание типовойбазы данных booksЕсли вы используете Microsoft Access, про-сто откройте файл books.mdb. Если вы ис-пользуете другую СУБД, вам необходимосоздать базу с именем books, а затем запус-тить SQL-скрипт, чтобы построить ее таб-лицы и заполнить их данными. В листингеA.1 показан скрипт ANSI SQL под названи-ем create_books.sql, который создает табли-цы и добавляет в них строки. Чтобы со-здать образец базы данных в СУБД, требу-ется выполнить следующие действия:1. Создать базу данных books.2. Если нужно, изменить create_books.sql

таким образом, чтобы работать с вашейСУБД.

3. Запустить create_books.sql для базы дан-ных books.

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

к ней доступ (см. советы в разделе «Вы-полнение программ SQL» в главе 1).

Все СУБД поддерживают команду CREATEDATABASE, которая создает новую базу дан-ных, но мы опишем, как использоватьдругие, более простые инструменты. Есливы предпочитаете работать с командойCREATE DATABASE, обратитесь к документа-ции по вашей СУБД. Эта команда не явля-ется стандартной командой SQL, поэтомуее структура и возможности различаютсяв разных СУБД (в стандарте SQL-92 бли-жайшей командой к CREATE DATABASE явля-ется CREATE SCHEMA).

Открытие базы данных booksв программе Microsoft Access

1. Выберите пункты меню File ⇒ Open(Файл ⇒ Открыть), а затем books.mdbи щелкните по кнопке Open (Открыть).

2. Чтобы изучить таблицы, нажмите кноп-ку Tables (Таблицы) под кнопкой Ob-jects (Объекты) в окне Database (Базаданных).

Листинг A.1. SQL-скрипт create_books.sql создает все таблицы в базе данных books и заполняетих данными. Этот скрипт написан на ANSI SQL для максимальной совместимости,но вам может понадобиться внести в него некоторые изменения, чтобы запустить его на вашей СУБД

DROP TABLE authors;

CREATE TABLE authors

(

au_id CHAR(3) NOT NULL ,

au_fname VARCHAR(15)NOT NULL ,

au_lname VARCHAR(15)NOT NULL ,

phone VARCHAR(12)NULL ,

address VARCHAR(20)NULL ,

city VARCHAR(15)NULL ,

state CHAR(2) NULL ,

zip CHAR(5) NULL ,

CONSTRAINT authors_pk PRIMARY KEY (au_id)

);

Page 433: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

433

Листинг A.1 (продолжение)

DROP TABLE publishers;

CREATE TABLE publishers

(

pub_id CHAR(3) NOT NULL ,

pub_name VARCHAR(20) NOT NULL ,

city VARCHAR(15) NOT NULL ,

state CHAR(2) NULL ,

country VARCHAR(15) NOT NULL ,

CONSTRAINT publishers_pk PRIMARY KEY (pub_id)

);

DROP TABLE titles;

CREATE TABLE titles

(

title_id CHAR(3) NOT NULL ,

title_name VARCHAR(40) NOT NULL ,

type VARCHAR(10) NULL ,

pub_id CHAR(3) NOT NULL ,

pages INTEGER NULL ,

price DECIMAL(5,2) NULL ,

sales INTEGER NULL ,

pubdate DATE NULL ,

contract SMALLINT NOT NULL ,

CONSTRAINT titles_pk PRIMARY KEY (title_id)

);

DROP TABLE title_authors;

CREATE TABLE title_authors

(

title_id CHAR(3) NOT NULL ,

au_id CHAR(3) NOT NULL ,

au_order SMALLINT NOT NULL ,

royalty_shareDECIMAL(5,2) NOT NULL ,

CONSTRAINT title_authors_pk PRIMARY KEY (title_id, au_id)

);

DROP TABLE royalties;

CREATE TABLE royalties

(

title_id CHAR(3) NOT NULL ,

advance DECIMAL(9,2) NULL ,

royalty_rate DECIMAL(5,2) NULL ,

CONSTRAINT royalties_pk PRIMARY KEY (title_id)

);

Создание типовой базы данных book

Page 434: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

434 Приложение А

Листинг A.1 (продолжение)

INSERT INTO authors VALUES('A01','Sarah','Buchman','718-496-7223',

'75 West 205 St','Bronx','NY','10468');

INSERT INTO authors VALUES('A02','Wendy','Heydemark','303-986-7020',

'2922 Baseline Rd','Boulder','CO','80303');

INSERT INTO authors VALUES('A03','Hallie','Hull','415-549-4278',

'3800 Waldo Ave, #14F','San Francisco','CA','94123');

INSERT INTO authors VALUES('A04','Klee','Hull','415-549-4278',

'3800 Waldo Ave, #14F','San Francisco','CA','94123');

INSERT INTO authors VALUES('A05','Christian','Kells','212-771-4680',

'114 Horatio St','New York','NY','10014');

INSERT INTO authors VALUES('A06','','Kellsey','650-836-7128',

'390 Serra Mall','Palo Alto','CA','94305');

INSERT INTO authors VALUES('A07','Paddy','O''Furniture','941-925-0752',

'1442 Main St','Sarasota','FL','34236');

INSERT INTO publishers VALUES('P01','Abatis Publishers','New York','NY','USA');

INSERT INTO publishers VALUES('P02','Core Dump Books','San Francisco','CA','USA');

INSERT INTO publishers VALUES('P03','Schadenfreude Press','Hamburg',NULL,'Germany');

INSERT INTO publishers VALUES('P04','Tenterhooks Press','Berkeley','CA','USA');

INSERT INTO titles VALUES('T01','1977!','history','P01',

107,21.99,566,DATE '2000-08-01',1);

INSERT INTO titles VALUES('T02','200 Years of German Humor','history','P03',

14,19.95,9566,DATE '1998-04-01',1);

INSERT INTO titles VALUES('T03','Ask Your System Administrator','computer','P02',

1226,39.95,25667,DATE '2000-09-01',1);

INSERT INTO titles VALUES('T04','But I Did It Unconsciously','psychology','P04',

510,12.99,13001,DATE '1999-05-31',1);

INSERT INTO titles VALUES('T05','Exchange of Platitudes','psychology','P04',

201,6.95,201440,DATE '2001-01-01',1);

INSERT INTO titles VALUES('T06','How About Never?','biography','P01',

473,19.95,11320,DATE '2000-07-31',1);

INSERT INTO titles VALUES('T07','I Blame My Mother','biography','P03',

333,23.95,1500200,DATE '1999-10-01',1);

INSERT INTO titles VALUES('T08','Just Wait Until After School','children','P04',

86,10.00,4095,DATE '2001-06-01',1);

INSERT INTO titles VALUES('T09','Kiss My Boo-Boo','children','P04',

22,13.95,5000,DATE '2002-05-31',1);

INSERT INTO titles VALUES('T10','Not Without My Faberge Egg','biography','P01',

NULL,NULL,NULL,NULL,0);

INSERT INTO titles VALUES('T11','Perhaps It''s a Glandular Problem','psychology','P04',

826,7.99,94123,DATE '2000-11-30',1);

INSERT INTO titles VALUES('T12','Spontaneous, Not Annoying','biography','P01',

507,12.99,100001,DATE '2000-08-31',1);

INSERT INTO titles VALUES('T13','What Are The Civilian Applications?','history','P03',

802,29.99,10467,DATE '1999-05-31',1);

Page 435: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

435

Листинг A.1 (окончание)

INSERT INTO title_authors VALUES('T01','A01',1,1.0);INSERT INTO title_authors VALUES('T02','A01',1,1.0);

INSERT INTO title_authors VALUES('T03','A05',1,1.0);INSERT INTO title_authors VALUES('T04','A03',1,0.6);

INSERT INTO title_authors VALUES('T04','A04',2,0.4);INSERT INTO title_authors VALUES('T05','A04',1,1.0);

INSERT INTO title_authors VALUES('T06','A02',1,1.0);INSERT INTO title_authors VALUES('T07','A02',1,0.5);

INSERT INTO title_authors VALUES('T07','A04',2,0.5);INSERT INTO title_authors VALUES('T08','A06',1,1.0);

INSERT INTO title_authors VALUES('T09','A06',1,1.0);INSERT INTO title_authors VALUES('T10','A02',1,1.0);

INSERT INTO title_authors VALUES('T11','A03',2,0.3);INSERT INTO title_authors VALUES('T11','A04',3,0.3);

INSERT INTO title_authors VALUES('T11','A06',1,0.4);INSERT INTO title_authors VALUES('T12','A02',1,1.0);

INSERT INTO title_authors VALUES('T13','A01',1,1.0);

INSERT INTO royalties VALUES('T01',10000,0.05);INSERT INTO royalties VALUES('T02',1000,0.06);

INSERT INTO royalties VALUES('T03',15000,0.07);INSERT INTO royalties VALUES('T04',20000,0.08);

INSERT INTO royalties VALUES('T05',100000,0.09);INSERT INTO royalties VALUES('T06',20000,0.08);

INSERT INTO royalties VALUES('T07',1000000,0.11);INSERT INTO royalties VALUES('T08',0,0.04);

INSERT INTO royalties VALUES('T09',0,0.05);INSERT INTO royalties VALUES('T10',NULL,NULL);

INSERT INTO royalties VALUES('T11',100000,0.07);INSERT INTO royalties VALUES('T12',50000,0.09);

INSERT INTO royalties VALUES('T13',20000,0.06);

Чтобы запустить команды SQL с books,обратитесь к разделу «Microsoft Access»в главе 1.

Создание базы данных booksв программе Microsoft SQL Server

1. Выберите пункты меню Start ⇒ Pro-grams ⇒ Microsoft SQL Server ⇒ Enter-prise Manager (Пуск ⇒ Программы ⇒Microsoft SQL Server ⇒ Enterprise Ma-nager).

2. Перейдите в папку Databases (Базы дан-ных) на сервере, с которым вы работае-те (см. рис. A.1).

3. Выберите пункты меню Tools ⇒ Wi-zards (Сервис ⇒ Мастер).

4. В разделе Database (База данных) выбе-рите пункт Create Database Wizard (Ма-стер создания базы данных) и нажмитена кнопку OK (см. рис. A.2).

5. Нажмите на кнопку Next (Далее), чтобыпропустить окно Welcome (Привет-ствие).

Создание типовой базы данных books

Page 436: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

436 Приложение А

6. Введите в поле Database Name (Имябазы данных) слово books, затем нажми-те на кнопку Next (Далее).

Если вы желаете изменить местополо-жение базы данных и файлов журналаопераций, воспользуйтесь кнопкамипоиска (см. рис. A.3).

7. Примине название по умолчанию, за-данное для файла базы данных, а такженачальный размер (см. рис. A.4). На-жмите на кнопку Next (Далее).

8. Воспользуйтесь заданными по умолча-нию установками размеров файлов вбазе данных (см. рис. A.5). Нажмите накнопку Next (Далее).

9. Согласитесь с заданными по умолчаниюразмерами файла журнала транзакций иначальным размером (см. рис. A.6). На-жмите на кнопку Next (Далее).

10. Согласитесь с заданными по умолчаниюустановками увеличения размеров файлажурнала транзакций (см. рис. A.7). На-жмите на кнопку Next (Далее).

11. Нажмите на кнопку Finish (Закончить),чтобы подтвердить выбор (см. рис. A.8).

12. Нажмите на кнопку OK, чтобы закрытьокно подтверждения.

13. Нажмите на кнопку NO (Нет), чтобыотказаться от создания плана обслужи-вания. Теперь на панели Details (Свой-ства) в окне Enterprise Manager отобра-зится база данных books.

14. Чтобы запустить скрипт create_books.sqlв SQL Server, используйте тип данныхстолбца pubdate в таблице titles не DATE,а DATETIME и удалите ключевое слово DATEв буквенных обозначениях дат во всех ко-мандах INSERT INTO titles. Например, за-мените DATE '2000-08-01' на '2000-08-01'.

Рис. A.1. SQL Server отображает списокбаз данных на панели Details (Свойства)

Рис. A.2. Мастер Create Database Wizardпоследовательно выполняет все действия,которые необходимы для создания базы данных

Рис. A.3. Введите название базы данных books

Page 437: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

437

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

Рис. A.5. Для этой базы данных разрешите SQLServer автоматически увеличивать размеры файлабазы данных, а не делайте это самостоятельно

Рис. A.6. Примите заданные по умолчаниюразмеры файла журнала транзакций и начальныйразмер

Рис. A.7. Для этой базы данных разрешите SQLServer автоматически увеличивать размеры файлажурнала транзакций, а не делайте этосамостоятельно

15. Выберите пункты меню Start ⇒ Pro-grams ⇒ Microsoft SQL Server ⇒ QueryAnalyzer (Пуск ⇒ Программы ⇒ Micro-soft SQL Server ⇒ Query Analyzer).

16. Укажите сервер и режим аутентифика-ции, затем нажмите на кнопку OK.

17. Выберите в поле на панели инструмен-тов базу данных books.

18. Выберите пункты меню File ⇒ Open(Файл ⇒ Открыть), затем в диало-говом окне Open (Открыть) файлcreate_books.sql и щелкните по кноп-ке OK.

19. Выберите пункты меню Query ⇒Execute (Запрос ⇒ Выполнить) либонажмите клавишу F5 (см. рис. A.9).

Создание типовой базы данных books

Page 438: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

438 Приложение А

Рис. A.8. SQL Server готов к созданию базыданных

20. Чтобы запустить SQL-скрипты и ин-терактивные команды с books, обрати-тесь к разделу «Microsoft SQL Server»в главе 1.

Создание базы данных books в Oracle

1. Запустите программу Database Configu-ration Assistant. Эта процедура отлича-ется для разных платформ. Например, вWindows вам следует выбрать пунктыменю Start ⇒ Programs ⇒ Oracle ⇒OraHome91 ⇒ Configuration andMigration Tools ⇒ Database Con-figuration Assistant (Пуск ⇒ Програм-мы ⇒ Oracle ⇒ OraHome91 ⇒ Configu-ration and Migration Tools ⇒ DatabaseConfiguration Assistant).

2. Нажмите на кнопку Next (Далее), чтобыпропустить окно Welcome (Привет-ствие).

3. Выберите пункт Create a Database (Со-здать базу данных) – см. рис. A.10, затемнажмите на кнопку Next (Далее).

4. Выберите пункт General Purpose (Об-щего назначения) – см. рис. A.11, затемнажмите на кнопку Next (Далее).

5. Введите в полях Global Database Name(Глобальное имя базы данных) и SID(System Identifier – системный иденти-фикатор) слово books (см. рис. A.12), за-тем нажмите на кнопку Next (Далее).

6. Выберите режим Dedicated Server Mode(Персонализированный режим) – см.рис. A.13, затем нажмите на кнопку Next(Далее).

7. Выберите пункт Typical (Типичные),примите другие установки инициализа-ции, заданные по умолчанию (см. рис.A.14), затем нажмите на кнопку Next(Далее).

Рис. A.9. Используйте программу Query Analyzer,чтобы создать таблицы и добавить в них данные.Игнорируйте предупреждения, которыепоявляются при первом запуске скрипта

Page 439: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

439

8. Примите заданные по умолчанию уста-новки Database Storage (Хранилищебазы данных) – см. рис. A.15, затем на-жмите на кнопку Next (Далее).

9. Выберите пункт Create Database (Со-здать базу данных) – см. рис. A.16, затемнажмите на кнопку Finish (Закончить).

10. Нажмите на кнопку OK, чтобы пропус-тить окно Summary (Итого) и создатьбазу данных.

11. Нажмите на кнопку Exit (Выход) в сооб-щении, которое появится, когда Oracleзавершит создание базы данных.

12. Для запуска скрипта create_books.sqlв Oracle у автора A06 в столбце au_fnameтаблицы authors замените пустую стро-ку ('') символом пробела (' '). Это изме-нение не позволяет Oracle интерпрети-ровать имя автора A06 как NULL (см. при-мечание DBMS в разделе «Значениеnull» главы 3).

13. Запустите SQL*Plus. Эта процедураотличается для разных платформ. На-пример, в Windows вам следует вы-брать пункты Start ⇒ Programs ⇒Oracle ⇒ OraHome91 ⇒ ApplicationDevelopment ⇒ SQL Plus (Пуск ⇒Программы ⇒ Oracle ⇒ OraHome91 ⇒Application Development ⇒ SQL Plus).

14. Введите имя пользователя, пароль иимя базы books, затем нажмите накнопку OK.

15. В строке SQL наберите:@create_books.sql

Затем нажмите клавишу Enter (см.рис. A.17).Вы можете добавить абсолютное илиотносительное имя пути (см. примеча-ние в разделе «Выполнение программSQL» главы 1).

Монитор SQL*Plus отобразит резуль-таты. Игнорируйте сообщения Oracleо том, что он не может удалить несуще-ствующие таблицы. Команды DROP TABLEнужны для того, чтобы при по следую-щем запуске скрипта create_books.sqlвосстановить таблицы в первоначаль-ном виде.

16. Чтобы запустить SQL-скрипты и инте-рактивные команды с books, обратитеськ разделу «Oracle» в главе 1.

Создание базы данных books в MySQL

1. В командной строке операционной сис-темы (оболочки) наберите mysqladmincreate books, затем нажмите клавишуEnter. MySQL создаст базу данныхbooks.

2. Введите в оболочке:

mysql –f books < create_books.sql

Опция –f заставляет mysql работать,даже если будут обнаружены ошибкиSQL (см. рис. A.18). Вам не нужно из-менять скрипт create_books.sql, чтобызапустить его в MySQL. Вы можете до-бавить абсолютное или относительноеимя пути (см. примечание в разделе«Выполнение программ SQL» главы 1).

mysql отобразит результаты. Игнори-руйте сообщения MySQL о том, чтопрограмма не может удалить несуще-ствующие таблицы. Команды DROP TABLEнужны для того, чтобы при последую-щем запуске скрипта create_books.sqlвосстановить таблицы в первоначаль-ном виде.

3. Чтобы запустить SQL-скрипты и инте-рактивные команды с books, обратитеськ разделу «MySQL» в главе 1.

Создание типовой базы данных books

Page 440: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

440 Приложение А

Рис. A.10. Программа Database Configuration Assistant последовательновыполняет все действия для создания базы данных

Рис. A.11. Мы выбрали пункт General Purpose (Общего назначения).Вместо него вы можете выбрать пункт New Database (Новая база данных),но при этом создание базы данных займет у Oracle больше времени

Page 441: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

441

Рис. A.12. Присвойте название базе данных books

Рис. A.13. Следует использовать режим Dedicated Server Mode,если база данных предназначается для небольшого числа клиентов

Создание типовой базы данных books

Page 442: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

442 Приложение А

Рис. A.15. Согласитесь с заданными по умолчанию установкамиDatabase Storage

Рис. A.14. Установка Typical используется для создания базы данныхс небольшим объемом данных

Page 443: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

443

Рис. A.16. Oracle готов к созданию базы данных

Рис. A.17. Используйте программу SQL*Plus,чтобы создать таблицы и добавить в них данные.Игнорируйте предупреждения, которыепоявляются при первом запуске скрипта

Рис. A.18. Cоздавая базу данных booksв MySQL, игнорируйте предупреждающиесообщения, которые появляются при первомзапуске скрипта

Создание типовой базы данных books

Page 444: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

444 Приложение А

Если вы запустили MySQL с удаленногокомпьютера в сети, необходимо указатьхост (имя сервера), имя пользователя и па-роль, чтобы подключиться к серверу. Дляэтого введите mysql –h host –u user–p dbname.

host – это название сервера, user – имяпользователя, а dbname – название базыданных, которая будет использоваться. My-SQL попросит вас ввести пароль. Обрати-тесь к администратору базы данных, что-бы узнать, какие настройки соединенияследует ввести.

Создание базы данных booksв PostgreSQL

1. В командной строке операционной систе-мы (оболочки) наберите createdb books,затем нажмите клавишу Enter. Postgre-SQL создаст базу данных books.

2. Введите в оболочке:psql –f create_books.sql books

Опция –f задает название файла SQL-скрипта (см. рис. A.19). Вам не нужно из-менять скрипт create_books.sql, чтобы за-пустить его в PostgreSQL. Можно доба-вить абсолютное или относительное имяпути (см. примечание в разделе «Выпол-нение программ SQL» главы 1).psql отобразит результаты. Игнорируйтесообщения PostgreSQL о том, что про-грамма не может удалить несуществую-щие таблицы. Команды DROP TABLE нужныдля того, чтобы при последующем запус-ке скрипта create_books.sql восстановитьтаблицы в первоначальном виде.

3. Чтобы запустить SQL-скрипты и инте-рактивные команды с books, обратитеськ разделу «PostgreSQL» главы 1.

Если вы запустили PostgreSQL с удаленно-го компьютера в сети, необходимо указатьхост, имя пользователя и пароль, чтобыподключиться к серверу. Для этого введите:

psql –h host –U user –W dbname

host – это название хоста, user – имяпользователя, а dbname – название базыданных, которая будет использоваться.PostgreSQL попросит ввести пароль. Обра-титесь к администратору базы данных,чтобы узнать, какие настройки соединенияследует ввести.

Рис. A.19. Создавая базу данных booksв PostgreSQL, игнорируйте предупреждающиесообщения, которые появляются при первомзапуске скрипта

Page 445: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

ААААААльтернативный ключ 50Аргумент 137

БББББ

База данныхнормализация 57типовая 23, 63

Битовый тип данных 79Блокировка 427

ВВВВВ

Внешний ключпростой 383сложный 383

Временная таблицаглобальная 395локальная 395

Выражение 74CASE 179DECODE() 183

ГГГГГГлагол 73Группа, повторяющаяся 58

ДДДДДДействительный числовой тип данных 82Декомпозиция

без потерь 57с сохранением зависимостей 57

Предметный указатель

Детерминант 60Домен 44, 75

ЗЗЗЗЗЗависимость

полная функциональная 60транзитивная 60частичная функциональная 58

Заголовок столбцаalias 96по умолчанию 96псевдоним 96

Замкнутостьтаблиц в реляционной модели 45

Заполнение таблицы 357Запрос

внешний 300внутренний 300

Значение 44null 53по умолчанию 376элементарное 58

ИИИИИИдентификатор 70Имя столбца, уточненное 220Индекс 405

простой 406сложный 406удаление 409уникальный 406

Интервальный тип данных 88

Page 446: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

446 SQL

ККККК

Календарный тип данных 84Каталог

базы данныхстандарт ANSI 47

системный, СУБД 46Ключ

альтернативный 50внешний 51, 383

свойства 51вторичный 383

простой 383сложный 383

первичный 47создание 379простой 379сложный 379

Ключевое словоCHECK 392ESCAPE 125

КомандаALTER TABLE 401COMMIT 427CREATE DATABASE 432CREATE INDEX 407CREATE TABLE 368CREATE VIEW 414DELETE 363DROP INDEX 409DROP TABLE 404DROP VIEW 424INSERT 352ROLLBACK 427SELECT 92

ALL 100AS 96DISTINCT 99FROM 93ORDER BY 101WHERE 109

SQL 70UPDATE 358

Комментарии 69, 74

ЛЛЛЛЛ

Лексема команды SQL 70Литерал 76

МММММ

Метаданные 46Метасимвол 122

непереключенный 125переключенный 125

Модель реляционная 41Мощность таблицы 47

ННННН

Нормализация 57Нормальная форма

первая (1НФ) 58вторая (2НФ) 58третья (3НФ) 60четвертая, пятая 57

ООООО

Объединение 224JOIN

CROSS 225, 233FULL OUTER 225INNER 225, 241LEFT OUTER 225, 265NATURAL 225, 236RIGHT OUTER 225SELF 225, 279

UNION 267внешнее 265синтаксис JOIN 228синтаксис WHERE 228табличная операция 219

Объектный тип 43Ограничение 368

check 392NOT NULL 374NULL 374PRIMARY KEY 379наименование 370

Page 447: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

447

столбца 369таблицы 369уникальности 389

простое 389сложное 389

Операнд 137Оператор 137

UNION 286бинарный арифметический 140конкатенации 145логический 114

AND 114NOT 116OR 115

отрицания 140, 143перегружаемый 166сравнения 110

BETWEEN 128IN 131IS NULL 134LIKE 122

тождества 140, 143унарный арифметический 140

Операциябинарная 44обработки наборов записей 227проекция 95унарная 44

Отличие 297Очередность операторов 143

ППППП

Первичный ключ 47простой 48составной 48

Перегрузка оператора 166Переключающий символ 124Пересечение 295Повторяющаяся группа 58Подзапрос 299Порядок таблицы 47Правило ассоциативности 143Предикат 109

ПредложениеAS 96, 222CONSTRAINT 370DISTINCT 202FROM 70, 93GROUP BY 206HAVING 215ORDER 70ORDER BY 101

ASC 102DESC 102

SELECT 70SQL 70USING 232WHERE 70

Представлениенеизменяемое 419создание 411удаление 424

Преобразование сужающее 176Преобразование типов неявное 173Прозрачность параллелизма 427Просмотр структуры таблицы

в Microsoft Access 349в Microsoft SQL Server 350в MySQL 350в Oracle 350в PostgreSQL 351

Псевдонимы таблиц 222

РРРРР

Режимавтоматического исполнения 429

Реляционная модель 41

ССССС

Свойствавнешнего ключа 51столбца таблицы базы данных 44

Связьведущий-ведомый 56многие-ко-многим 55один-к-одному 54один-ко-многим 55

Предметный указатель

Page 448: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

448 SQL

предок-потомок 56рефлексивная 279

Сессия 395Символ регистровый 153Синтаксис SQL 69Система кодирования 79Сканирование таблицы 408Слово

ключевое (зарезервированное) 70Сортировка

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

Cсылочная целостность 51, 383Столбец

агрегатный 206группирующий 206обнуляемый 91производный 138

Строкависячая 51пустая 78

Строки и столбцынеупорядоченные 45

Строковый тип данных 77СУБД 11

Microsoft Access 27режим запросов ANSI SQL 26

Microsoft SQL Server 30SQL Query Analyzer 30

MySQL 36Oracle 33PostgreSQL 38

Схемабазы данных 47в смысле стандарта ANSI 47

ТТТТТ

Таблицабазовая 395виртуальная 411

временная 395глобальная 395локальная 395

заимствованная 411изменение структуры 401копия 395низшего уровня 411образ 395пустая 44реляционной базы данных 43ссылающаяся на себя 51удаление 404

Таблица истинности 114Таблицы

пользователя 46системные 46

Терминынереляционных моделей 43реляционной модели 43стандарта ANSI SQL 43

Тип данных 75битовый 79

BIT 79BIT VARYING 79

действительный числовой 82DOUBLE PRECISION 83FLOAT 83REAL 83мантисса 82порядок 82

интервальный 88интервал day-time 88интервал year-month 88

календарный 84DATE 84, 85TIME 84, 85TIME WITH TIME ZONE 84, 85TIMESTAMP 84, 85TIMESTAMP WITH TIME ZONE 84, 85поля типа DATETIME 86

пользовательский 394строковый 77

CHARACTER 77CHARACTER VARYING 77

Page 449: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

449

NATIONAL CHARACTER 77NATIONAL CHARACTER VARYING 77

точный числовой 80DECIMAL 81INTEGER 81NUMERIC 81SMALLINT 81масштаб 80точность 80

Типовая база данныхтаблица authors 64таблица рublishers 65таблица royalties 68таблица title_authors 67таблица titles 66

Точка сохранения 429Точный числовой тип данных 80Транзакция 425

commit 426log 426roll back 426выполнение 426журнал 426откат 426точка сохранения 429

УУУУУУпорядочивание

лексикографическое 76Условие

объединенное 114поиска 109сравнения 110

ФФФФФ

Формавторая нормальная (2НФ) 58третья нормальная (3НФ) 60

Функция 137ADD_MONTHS() 168BIT_LENGTH() 160CAST() 173CHAR_LENGTH 160CHARACTER_LENGTH() 159COALESCE() 184

CONCAT() 148CURRENT_DATE() 169CURRENT_TIME() 169CURRENT_TIMESTAMP() 169CURRENT_USER 171DATE_ADD() 168DATE_SUB() 168DATEDIFF() 168DATEPART() 168EXTRACT() 166GETDATE() 170INSTR() 163LEN() 160LENGTH() 160LOCATE() 164LOWER() 153LPAD() 158NULLIF() 185OCTET_LENGTH() 160POSITION() 161RPAD() 158SESSION_USER 172SUBSTRING() 149SYS_CONTEXT 172SYSDATE 170SYSTEM_USER 172TRIM() 155

BOTH, опция 155LEADING, опция 155TRAILING, опция 155

UPPER() 153агрегатная 188

AVG() 198COUNT() 200MAX() 194MIN() 192SUM() 196опция DISTINCT 202

аргумент 137перегружаемая 166

ШШШШШ

Шаблонзначений 122переключенный 125

Предметный указатель

Page 450: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

450 SQL

ЯЯЯЯЯ

Языквстроенный 14естественный 12интерактивный 14непроцедурный 12неформальный 12программирования 11процедурный 12со свободным форматом предложений 73формальный 11

ААААА

ADD_MONTHS() 168ALL 100ALTER TABLE 401AND 114ANSI 15ANSI SQL 1992 15AS 96, 222ASC 102AVG() 198

BBBBB

BETWEEN 128BIT_LENGTH() 160

CCCCC

CASE 179CAST() 173CHAR_LENGTH 160CHARACTER_LENGTH() 159CHECK 392COALESCE() 184COMMIT 427CONCAT() 148CONSTRAINT 370COUNT() 200CREATE DATABASE 432CREATE INDEX 407CREATE TABLE 368

CREATE VIEW 414CURRENT_DATE() 169CURRENT_TIME() 169CURRENT_TIMESTAMP() 169CURRENT_USER 171

DDDDD

DATE_ADD() 168DATE_SUB() 168DATEDIFF() 168DATEPART() 168DECODE() 183DEFAULT 376DELETE 363DESC 102DISTINCT 99, 202DROP INDEX 409DROP TABLE 404DROP VIEW 424

EEEEE

ESCAPE 125EXCEPT 297EXTRACT() 166

FFFFF

FROM 93

GGGGG

GETDATE() 170GROUP BY 206

HHHHH

HAVING 215

IIIII

IN 131Index 405INNER JOIN 241INSERT 352INSTR() 163INTERSECT 295IS NULL 134

Page 451: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

451

JJJJJ

JOINCROSS 225, 233FULL OUTER 225INNER 225, 241LEFT OUTER 225, 265NATURAL 225, 236RIGHT OUTER 225SELF 225, 279синтаксис объединения 228

LLLLL

LEFT OUTER JOIN 265LEN() 160LENGTH() 160LIKE 122LOCATE() 164LOWER() 153LPAD() 158

MMMMM

MAX() 194MIN() 192

NNNNN

NATURAL JOIN 236NOT 114NOT NULL 374NULL 374NULLIF() 185

OOOOO

OCTET_LENGTH() 160OR 114ORDER BY 101

ASC 102DESC 102

PPPPP

POSITION() 161PRIMARY KEY 379

RRRRR

ROLLBACK 427RPAD() 158

SSSSS

SELECT 92SELF JOIN 279SESSION_USER 172SQL

семантика 12синтаксис 12

SUBSTRING() 149SUM() 196SYS_CONTEXT 172SYSDATE 170SYSTEM_USER 172

TTTTT

Transaction 425commit 426log 426roll back 426

TRIM() 155BOTH, опция 155LEADING, опция 155TRAILING, опция 155

UUUUU

UNION 286UPDATE 358UPPER() 153USING 232

VVVVV

View 411

WWWWW

WHEREв предложении SELECT 109синтаксис объединения 228

Предметный указатель

Page 452: SQL 01 03 - e-reading.life · Нэнси Олдридж-Рюнцель (Nancy Aldrich-Ruenzel) – за свежие идеи. Информация в примере с базой

Крис Фиайли

SQL

Главный редактор . .

Перевод с английского Хаванов А. В.

Научный редактор Нилов М. В.

Выпускающий редактор Космачева Н. А.

Верстка Пискунова Л. П.Графика Салимонов Р. В.

Дизайн обложки Дудатий А. М.

Подписано в печать 30.06.2003. Формат 70×100 1/16 .Гарнитура «Миниатюра». Печать офсетная.

Усл. печ. л. 37,05. Тираж 1000 экз. Зак. №

Web-сайт издательства: www.dmk-press.ru Internet-магазин: www.alians-kniga.ru

Отпечатано на ордена Трудового Красного ЗнамениГУП Чеховский полиграфический комбинат

Министерства Российской Федерации по делам печати,телерадиовещания и средств массовых коммуникаций

142300, г. Чехов Московской областиТел. (272) 71-336, факс (272) 62-536

Издательство «ДМК Пресс»

[email protected]

Мовчан Д А