Язык запросов SQL
Что такое база данных
Что такое база данныхПонятие база данных вошло в широкий обиход довольно-таки поздно и при этом потеряло многие из своих первоначальных значений. Для некоторых база данных является собранием элементов данных (телефонной книгой, перечнем прачечных, пергаментным свитком... да всем, чем угодно). Другие же определяют понятие более строго.
В этой книге база данных определяется как самоописательное собрание интегрированных записей. При этом она является компьютерной технологией, укомплектованной языками типа SQL .
Помни: Запись является представлением некоего физического или умозрительного объекта. Скажем, вы, например, собираетесь сохранять данные о своих клиентах. Каждый из них имеет свою запись. А в каждой записи имеется набор атрибутов, таких как имя, адрес и номер телефона. Имена, адреса и другие значения, соответствующие этим атрибутам, и представляют собой данные.
База данных состоит как из данных, так и из метаданных. Метаданные - это данные, которые являются описанием структуры данных, находящихся внутри базы. Зная, как расположены данные, можно их получить. Так как описание структуры базы данных находится в самой базе, то она является самоописательной. База данных является интегрированной, ибо содержит в себе не только элементы данных, но и существующие между ними взаимосвязи.
В базе данных метаданные хранятся в области, которая называется словарь данных. Он описывает таблицы, столбцы, индексы, ограничения и другие компоненты, из которых состоит база данных.
Так как в системе плоских файлов (описанной далее в этой главе) метаданных нет, то в приложениях, работающих с этими файлами, роль метаданных должна взять на себя часть приложения.
Что такое система управления базами данных
Что такое система управления базами данныхСистема управления базами данных (СУБД) — это набор программ, используемых для определения, администрирования и обработки баз данных и связанных с ними приложений. База данных, управляемая такой системой, является, в сущности, структурой, которую создают, чтобы хранить в ней нужные данные. А СУБД — это инструмент, используемый для создания этой структуры и работы с данными, которые в ней хранятся.
Сегодня на рынке имеется много программ СУБД. Некоторые из них работают только на мэйнфреймах, другие — только на мини-компьютерах, а есть такие, которые работают только на персональных компьютерах. Однако наблюдается тенденция к переносу СУБД на множество платформ с возможностью работы в сетях со всеми тремя классами машин.
Система СУБД, работающая на платформах нескольких классов, больших и малых, называется масштабируемой.
Каким бы ни был класс компьютера с базой данных — независимо от того, соединена ли машина с сетью или нет, — поток информации между базой данных и пользователем в принципе один и тот же. На Рисунок 1.1 показано, что пользователь соединяется с базой данных с помощью СУБД. Та скрывает физические детали хранения базы данных, так что приложению приходится иметь дело только с логическими характеристиками данных, а не с тем, каким образом эти данные хранятся.
Много лет назад один умник подсчитал, что если разложить любое человеческое существо на компоненты, такие как атомы углерода, водорода, кислорода и азота (плюс незначительное количество других), то их стоимость будет только 97 центов. Впрочем, это совершеннейшая глупость. Люди не состоят из простых изолированных наборов атомов. Наши атомы комбинируются в энзимы, протеины, гормоны и другие вещества, стоимость которых на фармацевтическом рынке обычно составляет миллионы долларов за унцию. Точная структура таких комбинаций атомов — вот что составляет их ценность. И, аналогично, структура баз данных позволяет интерпретировать данные, кажущиеся бессмысленными. Закономерности и тенденции, имеющиеся в данных, становятся известными благодаря структуре этих данных. Неструктурированные данные, как и неупорядоченные атомы, имеют малую ценность или совсем ее не имеют.
Домены
ДоменыАтрибут отношения (т.е. столбец таблицы) может допускать некоторое конечное число значений. Множество всех таких значений и является доменом атрибута.
Скажем, например, что вы являетесь дилером по продаже автомобилей и торгуете новинкой — спортивным Curarri GT 4000 с двухместным закрытым кузовом. Данные об автомобилях, находящихся на складе, вы держите в базе данных, а именно в таблице, которую называете INVENTORY (наличные товары). Один из столбцов этой таблицы вы назвали Color (цвет), и в нем находятся данные о наружном цвете каждого автомобиля. GT 4000 поступают только четырех цветов: огненно-малиновый ( blazing crimson ), насыщенный черный ( midnight black ), снежно-белый ( snowflake white ) и металлический серый ( metallic grey ). Эти четыре цвета и представляют собой домен атрибута Color.
Каждая строка базы данных содержит
Рисунок 1.3. Каждая строка базы данных содержит запись; каждый столбец этой базы содержит один атрибут
Помни: Понятие отношения в модели базы данных — это то же самое, что таблица в базе данных, основанной на определенной модели. Попробуйте повторить это определение быстро десять раз.
Компоненты реляционной базы данных
Компоненты реляционной базы данныхГибкость реляционных баз данных объясняется тем, что их данные находятся в таблицах, которые в значительной степени независимы друг от друга. В таблицу данные можно добавлять, удалять их из нее, вносить в них изменения и при этом не затрагивать данные из других таблиц — если только таблица не является родительской по отношению к этим другим таблицам. (Об отношениях родительских и дочерних таблиц рассказывается в главе 5, но там речь пойдет не о конфликте поколений.) В этом разделе будет показано из чего состоят таблицы и как они связаны с другими частями реляционной базы данных.
Модели баз данных
Модели баз данныхНезависимо от размеров баз данных все они относятся к одной из трех нижеприведенных моделей.
Новые системы управления базами данных, которые не являются реляционными, соответствуют, скорее всего, более новой, чем реляционная, объектной модели или гибридной объектно-реляционной модели.
Объектная модель бросает вызов реляционной
Объектная модель бросает вызов реляционнойРеляционная модель имела невероятный успех в большом количестве самых разных прикладных областей. Однако "беспроблемной" ее назвать нельзя. Проблемы сильнее проявились из-за роста популярности объектно-ориентированных языков программирования, таких как C++, Java и С#. Такие языки могут решать более сложные задачи, чем обычные языки программирования. Это достигается благодаря таким возможностям, как расширяемые пользователями системы типов, инкапсуляция, наследование, динамическое связывание методов, сложные и составные объекты и объектная целостность. Мы не собираемся в этой книге объяснять, что означают все эти понятия (хотя позднее затронем некоторые из них). Достаточно сказать, что со многими из этих особенностей классическая реляционная модель не совсем хорошо стыкуется. В результате были разработаны и появились на рынке системы управления базами данных, работающие на основе объектной модели. Впрочем, пока что их доля на рынке относительно невелика.
Объектнореляционная модель
Объектно-реляционная модельПроектировщики баз данных, как и все остальные люди, постоянно ищут самый лучший из всех возможных миров. Они размышляют: "Не правда ли, было бы прекрасно, чтобы у нас были преимущества систем объектно-ориентированных баз данных и одновременно сохранялась совместимость с реляционной системой, которую мы узнали и полюбили?" Такого рода размышления и привели к созданию гибридной объектно-реляционной модели. Объектно-реляционные СУБД расширяют реляционную модель настолько, чтобы в ней можно было поддерживать объектно-реляционное моделирование данных. Объектно-ориентированные модели были добавлены в международный стандарт SQL для того, чтобы поставщики реляционных СУБД могли перевести свои продукты в объектно-реляционные СУБД, сохраняя при этом совместимость со стандартом. Таким образом, в то время как стандарт SQL -92 описывает чисто реляционную модель баз данных, SQL : 1999 (ранее известный как SQL 3) описывает объектно-реляционную модель. A SQL :2003 описывает даже больше — объектно-ориентированные возможности.
В этой книге описывается международный стандарт SQL , подготовленный ISO / IEC . В основном он соответствует реляционной модели. Кроме того, в ней рассказывается и об объектно-ориентированных расширениях стандарта, которые появились благодаря SQL : 1999, и дополнительных расширениях, включенных в SQL :2 OO 3. Объектно-ориентированные возможности позволяют разработчикам решать с помощью баз данных SQL проблемы, которые не по зубам старой, чисто реляционной парадигме. (Чуть язык не сломал.)
Оцените представление
Оцените представлениеВ моем представлении часто возникает один из моих любимых пейзажей: вид на Йосе-митскую долину весенним вечером на выезде из туннеля Уовона. Золотой свет заливает отвесную поверхность скалы Эль-Капитан, вдали сияет пик Хаф-Доум, а водопад "Фата невесты" (Бридалвейл) создает серебряный каскад сверкающей воды, в то время как стайка легких облаков слегка закрывает небо. У баз данных также имеются виды, пусть даже не такие живописные. Называются они представлениями. Их красота заключена в реальной пользе, которую они приносят при работе с данными.
В таблицах может находиться достаточно много строк и столбцов. Иногда все эти данные могут вас интересовать, а иногда — нет. Вас, возможно, интересуют только некоторые из столбцов, имеющихся в таблице, или только строки, которые удовлетворяют определенному условию. Или могут интересовать некоторые столбцы из одной таблицы и некоторые другие — из другой, связанной с ней таблицы. Чтобы исключить данные, которые вам в данный момент не нужны, можно создать представление. Представление ( view ) — это подмножество базы данных, которое обрабатывается приложением. В представлении могут находиться части одной или множества таблиц.
Помни: Представления иногда называют виртуальными таблицами. Для приложения или пользователя они ведут себя точно так же, как и таблицы. Однако представления сами по себе не существуют. Они дают возможность просматривать данные, но сами частью данных не являются.
Скажем, вы работаете, например, с базой данных, в которой имеются таблицы CUSTOMER (клиент) и INVOICE (счет-фактура). В первой таблице имеются столбцы CustomerlD (идентификатор клиента), FirstName (имя), LastName (фамилия), Street (улица), City (город), State (штат), Zipcode (почтовый код) и Phone (телефон). А столбцами второй таблицы являются InvoiceNumber (номер счета-фактуры), CustomerlD (идентификатор клиента), Date (дата), TotalSale (всего продано), TotalRemitted (всего переведено денег) и FormOfPayment (форма платежа).
Главный менеджер по продажам хочет видеть на экране только такие реквизиты клиентов, как имя, фамилия и номер телефона. Если из таблицы CUSTOMER создать представление, в котором содержатся лишь три столбца с названной информацией, то менеджер будет просматривать только нужную ему информацию. А данные из тех столбцов, которые его не интересуют, он видеть не будет. На Рисунок 1.4 показано получение представления для главного менеджера по продажам.
Ограничения
ОграниченияОграничения являются важным, хотя часто и недооцениваемым компонентом базы данных. Ограничения — это правила, определяющие допустимые значения для атрибутов таблицы.
Предотвратить ввод в столбец неправильных данных можно, применяя к этому столбцу жесткие ограничения. Конечно, любое значение, правильно входящее в домен столбца, должно удовлетворять всем ограничениям, которые имеются для этого столбца. Как уже говорилось в предыдущем разделе, доменом столбца является множество всех тех значений, которые могут быть в этом столбце. Ограничение — это именно ограничение на то, что может находиться в столбце. Характеристики табличного столбца и применяемые к нему ограничения определяют домен этого столбца. Применяя ограничения, можно предотвратить ввод в столбец таких данных, которые не входят в его домен.
Что касается примера с автомобилями, то можно применить к таблице ограничение, при котором в столбец Color (цвет) можно будет вводить только одно из перечисленных значений. Если после этого оператор ввода данных попытается ввести в этот столбец такое значение, как, например, темно-зеленый, система это значение не примет. Ввод данных нельзя будет продолжить до тех пор, пока оператор не введет в поле Color правильное значение.
Плоские файлы
Плоские файлыПлоские файлы — самая простая разновидность структурированных данных. Нет, плоский файл — это не папка, придавленная стопкой книг. Плоские файлы называются так потому, что имеют минимальную структуру. Если бы они были зданиями, то их стены поднимались бы не от фундамента, а прямо от земли. Плоский файл — это собрание записей данных, записываемых в определенном формате одна за другой, — данные, одни только данные и ничего, кроме данных, т.е. список. На компьютерном языке плоский файл называется простым. В таком файле нет метаданных со структурной информацией, а есть лишь одни данные.
Скажем, вам нужно сохранить в системе плоских файлов имена и адреса клиентов вашей компании. У этой системы может быть примерно такая структура.
| Harold Percival | 26262 | S . Howards Mill Rd | Westminster | CA92683 |
| Jerry Appel | 32323 | S. River Lane Rd | Santa Ana | CA92705 |
| Adrian Hansen | 232 | Glenwood Court | Anaheim | CA92640 |
| John Baker | 2222 | Lafayette St | Garden Grove | CA92643 |
| Michael Pens | 77730 | S. New Era Rd | Irvine | CA92715 |
| Bob Michimoto | 25252 | S. Kelmstey Dr | Stanton | CA92610 |
| Linda Smith | 444 | S.E. Seventh St | Costa Mesa | CA92635 |
| Robert Funnell | 2424 | Shen Court | Anaheim | CA92640 |
| Bill Checkal | 9595 | Curry Dr | Stanton | CA92610 |
| Jed Style | 3535 | Randall St | Santa Ana | CA92705 |
Такая структура плоских файлов позволяет работать с ними очень быстро. Однако недостатком является то, что программная логика, которая предназначена для манипуляции данными из файлов, должна быть очень подробной. Приложение должно точно "знать", где и как в файле хранятся данные. Итак, что касается малых систем, то в них плоские файлы работают прекрасно. Но чем больше система плоских файлов, тем труднее с ней работать. Использование базы данных вместо системы плоских файлов позволяет этого избежать. Хотя файлы базы данных имеют больший "фундамент", приложения могут работать на большем количестве аппаратных платформ и операционных систем. Кроме того, базы данных позволяют легче писать прикладные программы, потому что программисту не нужно вникать в детали того, как в файлах физически расположены данные.
Базы данных облегчают работу программистов, потому что при работе с данными в детали "вникает" СУБД. А приложениям, написанным для работы с плоскими файлами, необходимо держать эти детали при себе, т.е. в собственном коде. Если нескольким приложениям приходится одновременно получать доступ к одним и тем же данным из плоских файлов, то в каждом из приложений обязательно должен быть код, предназначенный для работы с этими данными. Но когда используется СУБД, то такой код в приложениях вообще не нужен.
Кроме того, если в приложении имеется код для работы с данными из плоских файлов, причем работает он только на определенной аппаратной платформе, то перенос приложения на новую платформу — это довольно сложное дело. Ведь придется изменить весь код, связанный с аппаратным обеспечением. А вот перенос на другую платформу аналогичного СУБД-приложения проходит намного проще — с меньшим количеством проблем и выпитого аспирина.
Почему реляционная модель лучше
Почему реляционная модель лучшеВ приложениях, работающих с СУБД, которые следуют иерархической или сетевой модели, структура базы данных "зашита" в само приложение. Это означает, что приложение зависит от определенной физической реализации базы данных. При добавлении в базу данных нового атрибута вам, чтобы привести свое приложение в соответствие с изменением базы, придется это приложение изменить, причем неважно, будет ли оно использовать новый атрибут.
У реляционных баз данных более гибкая структура. Приложения для таких баз поддерживать легче, чем те, что написаны для иерархических или сетевых баз данных. Эта гибкость структуры дает возможность получать такие комбинации данных, которые, возможно, еще не были нужны при проектировании базы данных.
Представление для главного менеджера
Рисунок 1.4. Представление для главного менеджера по продажам, получаемое из таблицы CUSTOMER
Региональный менеджер по продажам, возможно, хочет видеть имена, фамилии и номера телефонов всех клиентов, с почтовым кодом в диапазоне между 90000 и 93999 (Южная и Центральная Калифорния). Эту работу выполняет представление, которое накладывает ограничение на получаемые с его помощью строки, а также на выводимые с его помощью столбцы. На Рисунок 1.5 показано получение представления для регионального менеджера по продажам.
Менеджеру по оплате счетов, возможно, требуется просматривать имена и фамилии клиентов из таблицы CUSTOMER и значения столбцов Date , TotalSale , TotalRemitted и FormOfPayment из таблицы INVOICE , причем только из таких записей, где значение TotalRemitted меньше значения TotalSale . Это ограничение на записи имеет место тогда, когда оплата еще не проведена полностью. Для такого просмотра требуется создать представление, которое собирает данные из обеих таблиц. На Рисунок 1.6 показаны потоки данных, идущие из обеих таблиц, CUSTOMER и INVOICE , в представление для менеджера по оплате счетов, которое называется ACCTS_PAY.
Работа с данными
Работа с даннымиС помощью компьютеров люди выполняют множество таких задач, которые раньше выполнялись с помощью других инструментов. Документы теперь создают и исправляют не на пишущих машинках, а на компьютерах. Электромеханические калькуляторы также заменены компьютерами — лучшим средством для выполнения математических вычислений. Компьютеры пришли на смену миллионам листов бумаги, миллионам папок и стеллажей для документов и являются главными хранилищами важной информации.
Конечно, в сравнении со старыми инструментами компьютеры выполняют намного больше работы, намного быстрее и, главное, — с большей точностью. Однако за все приходится платить. Пользователи компьютеров больше не имеют прямого физического доступа к своим данным.
Как только компьютеры внезапно перестают работать, у сотрудников учреждений тут же закрадываются сомнения— а действительно ли компьютеризация благо? Раньше папкам с документами угрожало лишь падение на пол, и достаточно было просто нагнуться, собрать выпавшие листы бумаги и снова положить их в папку, чтобы все встало на свои места. Если не считать землетрясений и других катаклизмов, то стеллажи с папками никогда не "удаляются" и никогда не отправляют сообщения об ошибке. А вот авария с жестким диском — это совсем другое дело: потерянные биты и байты "подобрать" нельзя. Отказы оборудования, вызванные механическими, электрическими и человеческими воздействиями, могут безвозвратно отправить ваши данные на тот свет.
Малое — это прекрасно В области хранения данных компьютеры действительно предстали во всей своей красе. Это произошло потому, что они могут хранить в двоичном виде любую информацию: текст, числа, звуки, графические изображения, телевизионные программы или анимацию. Причем компьютер может хранить данные с очень высокой плотностью, позволяя поместить большое количество информации. По мере совершенствования технологий все больше и больше данных занимают все меньше и меньше места. Где только ни используются сейчас компьютеры: и в газовых насосах, и в новых автомобилях, и в ошеломляющем количестве игрушек. Осталось недолго ждать появления компьютеризованных туфель, меняющих упругость своих подошв в зависимости от того, идете ли вы, бежите или прыгаете. А звезды баскетбола, может быть, вскоре смогут использовать обувь с возможностью хранения небольшой базы данных с игровой статистикой...
Большая скорость и точность компьютеров только тогда пойдут на пользу, если будут приняты необходимые меры по защите от случайной потери данных. При хранении важных данных возникают четыре главные задачи.
Размер и сложность базы данных
Размер и сложность базы данныхБазы данных бывают любых размеров, начиная от простого набора из нескольких записей до огромных систем с миллионами записей.
Помни: Персональная база данных предназначена для использования одним человеком на одном компьютере. У такой базы обычно достаточно простая структура и относительно небольшой размер. База данных отдела или рабочей группы используется сотрудниками одного отдела или членами одной рабочей группы в пределах одной организации. Такая база обычно больше персональной и, конечно же, более сложная. С ней должны иметь возможность работать несколько пользователей, которым одновременно нужен доступ к одним и тем же данным. База данных организации бывает громадных размеров. Она может полностью моделировать информационный обмен в крупной организации.
Реляционная модель
Реляционная модельВпервые реляционную модель баз данных сформулировал в 1970 году работавший в компании IBM доктор И.Ф. Кодд ( E . F . Codd ), а примерно десятилетие спустя эта модель начала появляться в готовых продуктах. По иронии судьбы первую реляционную СУБД разработала не IBM . Такая честь выпала на долю маленькой компании-новичка, назвавшей свой продукт Oracle .
Базы данных, созданные на основе предыдущих моделей, были заменены реляционными, потому что не имели тex ценных свойств, которые и отличают реляционные базы от баз других типов. Вероятно, самым важным из этих свойств является то, что в реляционной базе данных можно менять структуру, не внося изменений в приложения. Такого не скажешь о приложениях, созданных на основе старых структур. Предположим, например, что в таблицу базы данных вы добавили один или несколько новых столбцов. В этом случае нет необходимости менять никакое из уже написанных приложений, которые будут продолжать обрабатывать эту таблицу, — только если вы не изменили столбцы, с которыми работают эти приложения.
Родственники и таблицы что общего?
Родственники и таблицы - что общего?В праздничные дни многие родственники приходят ко мне в гости и сидят за моим столом. В базах данных также имеются отношения (но не родственные), и каждое из них имеет свою собственную таблицу (английское слово " table " может иметь два значения: "стол" или "таблица", a " relation " — "родство" или "отношение"). Реляционная база данных состоит из одного или нескольких отношений.
Помни: Отношение — это двумерный массив строк и столбцов, причем у каждого из этих элементов может быть только одно значение, а строки друг друга не дублируют. Каждая ячейка в массиве может содержать только одно значение, и никакие из двух строк не могут быть одинаковы.
Большинство людей знакомы с двумерными массивами строк и столбцов. Это электронные таблицы, с которыми можно работать в таких приложениях, как Microsoft Excel . Другой пример двумерного массива — это статистика на обратной стороне бейсбольной карточки игрока высшей лиги. В такой карточке имеются столбцы, указывающие год, команду, количество игр, ударов битой, попаданий, выигранных очков и т.п. Строки соответствуют годам, на протяжении которых игрок играл в высшей лиге. Эти данные также можно хранить в отношении (таблице), которое имеет ту же простую структуру. На Рисунок 1.2 показана таблица реляционной базы данных, содержащая статистику по одному из игроков высшей лиги. В действительности такая таблица может хранить статистику для всей команды или даже целой лиги.
Исторические перспективы Персональные базы данных впервые появились на персональных компьютерах в начале 1980-х годов. Самые первые продукты работали с системами плоских файлов, но чуть позже появились продукты, из которых некоторые пытались следовать реляционной модели. По мере развития некоторые популярные системы СУБД для персональных компьютеров достаточно близко подошли к тому, чтобы стать по-настоящему реляционными, т.е. удовлетворяющими определению доктора Кодца. Начиная с конца 1980-х годов в организациях все больше и больше ПК объединяются в рабочие группы или в сети отделов. Чтобы заполнить эту новую рыночную нишу, те СУБД, которые первоначально работали только на больших ЭВМ (мэйнфреймах), "спустились" на персональные компьютеры, а реляционные СУБД для ПК, наоборот, "поднялись" на большие ЭВМ.
Схема информационной системы работающей на основе СУБД
Рисунок 1.1. Схема информационной системы, работающей на основе СУБД
Схемы домены и ограничения
Схемы, домены и ограничения
База данных — это не только набор таблиц. В ней имеются и другие структуры; они помогают поддерживать на нескольких уровнях целостность данных. Схема базы данных дает таблицам общую организацию. Домен табличного столбца указывает, какие значения можно хранить в этом столбце. Ограничения на таблицу базы данных можно использовать для того, чтобы никто (включая и вас) не смог сохранять в таблице неправильные данные.
Схемы
СхемыСтруктура всей базы данных — это ее схема или концептуальное представление. Кроме того, эта структура иногда называется полным логическим представлением базы данных. Схема представляет собой метаданные и в качестве таковых является частью базы данных. Сами метаданные, которые описывают структуру базы данных, хранятся в таблицах, похожих на те таблицы, в которых хранятся обычные данные. Метаданные — тоже данные, и в этом их прелесть.
Соображения по поводу проектирования баз данных
Соображения по поводу проектирования баз данныхБаза данных является представлением физической или умозрительной структуры, такой, например, как организация, автосборочное производство или статистика выступлений всех бейсбольных клубов высшей лиги. Точность представления зависит от уровня детализации, достигнутого в проекте базы данных. Сколько усилий прикладывать к этому проекту — это должно зависеть от типа информации, которую вы собираетесь получать из базы данных. Слишком большая детализация — это напрасная трата усилий, времени, да и места на жестких дисках. А слишком малая детализация может свести ценность базы данных на нет. Решайте, какая степень детализации вам нужна сейчас, а какая может потребоваться в будущем, а затем воплотите этот уровень в свой проект (не больше и не меньше). Впрочем, не удивляйтесь, если придется этот уровень корректировать в соответствии с постоянно меняющимися условиям повседневной жизни.
Сегодняшние системы управления базами данных, снабженные привлекательными графическими пользовательскими интерфейсами и интуитивными инструментами проектирования, могут навеять претенденту на звание проектировщика ложное чувство безопасности. Такие системы приводят к тому, что проектирование базы данных кажется сравнимым с созданием электронной таблицы или с выполнением другой, относительно простой задачи. Но это не тот случай. Проектировать базу данных трудно. Если будете неправильно проектировать, то получите базу, которая со временем станет только хуже. Часто проблема не проявляется до тех пор пока вы не потратите немалые усилия на ввод данных. И к тому времени, когда проблема себя проявит, она станет достаточно серьезной. Во многих случаях решение состоит в том, чтобы полностью перепроектировать базу и заново ввести все данные. Единственным утешением при этом будет приобретаемый опыт работы с базами данных.
Таблица со статистикой игрока
Рисунок 1.2. Таблица со статистикой игрока
Столбцы в массиве являются постоянными, т.е. означают в каждой строке одно и то же. Если в одной строке столбец содержит фамилию игрока, то в остальных строках в том же столбце должны быть фамилии. Порядок расположения строк и столбцов в массиве не имеет значения. Что касается СУБД, то для нее не имеет значения, какой столбец будет первым, какой — следующим и какой — последним. Каким бы ни был порядок столбцов, СУБД будет обрабатывать таблицу одинаковым способом. То же верно и для строк. Для СУБД порядок расположения строк не имеет значения.
Каждый столбец в таблице из базы данных представляет один из атрибутов этой таблицы. Это означает, что столбец содержит однородные данные в каждой строке таблицы. Например, в таблице могут находиться имена, адреса и номера телефонов всех клиентов организации. Каждая табличная строка (также называемая записью, или кортежем) содержит данные по одному клиенту. А каждый столбец содержит один атрибут. Это, например, может быть один из следующих атрибутов: номер клиента, его имя, улица, на которой он живет, его город, штат, почтовый код или номер телефона. Столбцы и строки такой таблицы показаны на Рисунок 1.3.
В представлении для регионального
Рисунок 1.5. В представлении для регионального менеджера по продажам имеются только некоторые строки из таблицы CUSTOMER
к которому обращается имеющееся приложение,
ВниманиеКонечно, если вы удалили столбец, к которому обращается имеющееся приложение, то какая бы модель базы данных ни применялась, вы столкнетесь с трудностями. Один из лучших способов устроить аварийное завершение работы приложения базы данных — запросить у него такую информацию, которой нет в вашей базе данных.
Чем является SQL и чем он не является
Чем является SQL и чем он не являетсяПервое, что надо уяснить насчет SQL , — этот язык не является процедурным, как FORTRAN , Basic , С, COBOL , Pascal и Java . Чтобы решить задачу с помощью одного из этих процедурных языков, приходится писать процедуру, которая выполняет одну за другой указанные операции, пока выполнение задачи не будет закончено. Процедура может быть линейной последовательностью или содержать ветвление, но в любом случае программист указывает порядок выполнения.
Иными словами, SQL является непроцедурным языком. Чтобы с его помощью решить задачу, сообщите SQL , что именно вам нужно, как если бы вы говорили с джином из лампы Аладдина. И при этом не надо говорить, каким образом получить для вас то, что вы хотите. Система управления базами данных (СУБД) сама решит, как лучше всего выполнить ваш запрос.
Хорошо. Только что я сказал, что SQL не является процедурным языком. В сущности, это правда. Однако миллионы программистов вокруг (и вы, возможно, один из них) привыкли решать задачи процедурным путем, поэтому в последние годы оказывалось немалое давление, чтобы дополнить SQL некоторыми процедурными возможностями. Поэтому теперь в составе новой версии спецификации SQL , SQL:2003, имеются такие средства процедурного языка, как блоки BEGIN , условные операторы IF , функции и процедуры. Благодаря этим новым средствам, можно хранить программы на сервере с тем, чтобы их могли повторно использовать многие пользователи.
Для иллюстрации того, что я имел в виду, когда говорил "сообщите системе, что именно вам нужно", предположим, что у вас имеется таблица EMPLOYEE с данными о служащих и вы хотите выбрать из нее все строки, соответствующие всем "старшим" работникам. Под "старшими" работниками можно подразумевать каждого, кто старше 40 лет или кто получает более 60000 долларов в год. Нужную вам выборку можно сделать с помощью следующего запроса:
SELECT * FROM EMPLOYEE WHERE AGE >40 OR SALARY >60000 ;
Этот оператор выбирает из таблицы EMPLOYEE все строки, в которых или значение столбца AGE (возраст) больше 40 или значение в столбце SALARY (зарплата) больше 60000. SQL сам знает, каким образом надо выбирать информацию. Ядро базы данных проверяет базу и принимает для себя решение, каким образом следует выполнять запрос. Все, что от вас требуется, — указать, какие данные вам нужны.
Помни: Запрос — это вопрос, который вы задаете базе данных. Если какие-либо ее данные удовлетворяют условиям вашего запроса, то SQL передает их вам.
В современных реализациях SQL отсутствуют многие простые программные конструкции, которые являются фундаментальными для большинства других языков. В приложениях для повседневной жизни, как правило, требуются хотя бы некоторые из этих конструкций, поэтому SQL на самом деле представляет собой подъязык данных. Даже имея дополнения, появившиеся в SQL вместе со стандартом SQL: 1999 и дополнительные расширения, добавленные в SQL:2003, все равно для создания законченного приложения необходимо использовать вместе с SQL один из программных языков, такой, например, как С.
Выбирать информацию из базы данных можно одним из следующих способов.
Что делает клиент
Что делает клиентКлиентская часть СУБД выводит информацию на экран и реагирует на пользовательский ввод, переданный с помощью клавиатуры, мыши или другого устройства ввода. Кроме того, клиент также может обрабатывать данные, приходящие через линию связи или из других станций сети. Весь "интеллект" приложения сосредоточен в клиентской части СУБД. Эта клиентская часть как раз и интересует разработчиков. Ведь в серверной части всего лишь монотонно, механически обрабатываются запросы, пришедшие от клиента.
Что делает сервер
Что делает серверРабота у сервера относительно простая и понятная. Все, что ему нужно делать, — это читать, интерпретировать и выполнять команды, приходящие по сети от клиентов. Эти команды Должны быть написаны на одном из имеющихся подъязыков данных. Подъязык нельзя считать полным языком — он выполняет только часть функций языка. Подъязык данных занят только обработкой данных. В нем имеются операции добавления, обновления, удаления и выборки данных, но нет таких управляющих структур, как циклы, локальные переменные, Функции или операции ввода-вывода на различные устройства. Из используемых сегодня подъязыков данных самым известным является SQL, и он уже стал промышленным стандартом. SQL вытеснил патентованные подъязыки данных на всех классах машин. С появлением стандарта SQL: 1999 язык SQL пополнился новыми возможностями, которые отсутствуют У традиционных подъязыков. Однако SQL:2003 все еще не является полноправным языком Программирования общего назначения. Поэтому, чтобы создать приложение, работающее с базой данных, его следует использовать вместе с языком-оболочкой.
Что такое клиент
Что такое клиентОсновная работа клиента состоит в том, чтобы поддерживать пользовательский интерфейс. С точки зрения пользователя клиентской машиной является компьютер, а приложением — пользовательский интерфейс. Пользователь даже может и не знать, что в процессе участвует сервер. Обычно сервер не виден — он может находиться в другой комнате. Но кроме пользовательского интерфейса в состав клиента входят еще и прикладная программа, а также клиентская часть СУБД. Прикладная программа выполняет нужную вам специальную задачу, такую как работа со счетами дебиторов или оформление заказов. Клиентская часть СУБД выполняет команды прикладной программы и обменивается с серверной частью данными и командами SQL.
Что такое сервер
Что такое серверСервер (полное название — сервер базы данных) является той частью системы клиент/сервер, где находится база данных. Кроме того, на нем находится серверная часть системы управления базой данных. Команды, которые приходят от клиентов, интерпретируются в ней, а затем переводятся в операции, выполняемые в базе данных. Серверные программы преобразуют в определенный формат результаты, полученные при выполнении каждого запроса, и отправляют эти результаты клиенту, от которого пришел запрос.
Данные типа датывремени
Данные типа даты-времениВ SQL:2OO3 определяются пять различных типов данных, которые относятся к дате и времени. Они называются типами данных даты-времени или просто датой-временем. Некоторые из этих типов данных довольно сильно перекрывают друг друга, поэтому в отдельных реализациях, которые могут вам встретиться, все пять типов, возможно, и не поддерживаются.
Интервалы
ИнтервалыИнтервальные типы данных тесно связаны с типами данных даты-времени. Интервал — это разница между двумя значениями даты-времени. Во многих приложениях, имеющих дело со значениями даты, времени или с теми и другими, иногда требуется определить интервал между двумя датами или значениями времени. SQL поддерживает два различных типа интервалов: год-месяц и день-время. Интервал год-месяц — это количество лет и месяцев между двумя датами. А интервал день-время — это количество дней, часов, минут и секунд между двумя моментами в пределах одного месяца. Нельзя смешивать вычисления, где используется интервал год-месяц, с вычислениями, где используется интервал день-время, потому что в месяцах разное количество дней (28, 29, 30 или 31).
Использование SQL в Intenet/интернет
Использование SQL в Intenet/интернетРабота с базами данных через Internet или интранет кардинально отличается от работы в традиционной системе клиент/сервер. Основное различие между ними находится в клиентской части. В традиционной системе клиент/сервер большинство функций СУБД выполняется на клиентской машине. А в базе данных, работающей через Internet, большая часть системы СУБД (если не вся) находится на сервере. В клиентской же части может не быть ничего, кроме Web-браузера. В большинстве случаев в ней находится браузер вместе со своим расширением, таким как включаемый модуль Netscape или элемент управления ActiveX. Таким образом, "интеллект" системы концентрируется на сервере. Такая концентрация имеет несколько преимуществ, перечисленных ниже.
Клиент
КлиентКлиентская часть системы клиент/сервер (клиент) состоит из двух компонентов: аппаратного и программного. Аппаратным компонентом является клиентский компьютер и его интерфейс, предназначенный для соединения с локальной сетью. Эта аппаратура может быть очень похожа на ту, что используется на сервере, или даже идентична ей. Клиент отличается от сервера прежде всего программным обеспечением.
Команды SQL
Команды SQLЯзык SQL состоит из ограниченного числа команд, специально предназначенных для управления данными. Одни из этих команд служат для определения данных, другие — для их обработки, а остальные — для администрирования данных. О командах определения и обработки данных рассказывается в главах 4-12, а о командах администрирования данных — в главах 13 и 14.
Чтобы соответствовать стандарту SQL: 2003, в состав реализации должны входить все основные возможности. Кроме того, в ее состав могут входить и расширения этого основного набора (которые также описаны спецификацией SQL:2003). Расширения пока оставим, вернемся к основам. Ниже приведена таблица основных команд SQL:2003.
Если вы из тех программистов, кому нравится проверять новые возможности, то возрадуйтесь.
Таблица 2.1. Основные команды SQL:2003
| ALTER DOMAIN | CREATE CURSOR | FREE LOCATOR |
| ALTER TABLE | DECLARE TABLE | GET DIAGNOSTICS |
| CALL | DELETE | GRANT |
| CLOSE | DISCONNECT | HOLD LOCATOR |
| COMMIT | DROP ASSERTION | INSERT |
| CONNECT | DROP CHARACTER SET | OPEN |
| CREATE ASSERTION | DROP COLLATION | RELEASE SAVEPOINT |
| CREATE CHARACTER SET | DROP DOMAIN | RETURN |
| CREATE COLLATION | DROP ORDERING | REVOKE |
| CREATE DOMAIN | DROP ROLE | ROLLBACK |
| CREATE FUNCTION | DROP SCHEME | SAVEPOINT |
| CREATE METHOD | DROP SPECIFIC FUNCTION | SELECT |
| CREATE ORDERING | DROP SPECIFIC PROCEDURE | SET CONNECTION |
| CREATE PROCEDURE | DROP SPECIFIC ROUTINE | SET CONSTRAINTS |
| CREATE ROLE | DROP TABLE | SET ROLE |
| CREATE SCHEMA | DROP TRANSFORM | SET SESSION AUTHORIZATION |
| CREATE TABLE | DROP TRANSLATION | SET SESSION CHARACTERISTICS |
| CREATE TRANSFORM | DROP TRIGGER | SET TIME ZONE |
| CREATE TRANSLATION | DROP TYPE | SET TRANSACTION |
| CREATE TRIGGER | DROP VIEW | START TRANSACTION |
| CREATE TYPE | FETCH | UPDATE |
| CREATE VIEW |
Конструкторы
КонструкторыПри создании структурного UDT-типа СУБД автоматически создает для него функцию-конструктор, давая ей имя, аналогичное имени UDT-типа.
Логические данные
Логические данныеТип данных BOOLEAN (булев) имеет два определенных логических значения, true (истина) и false (ложь), а также неопределенное значение unknown (неизвестное). Если любое из первых двух значений сравнить с NULL или со значением unknown, то в результате получится unknown.
Мутаторы и наблюдатели
Мутаторы и наблюдателиПри создании структурированного UDT-типа СУБД также автоматически создает для него функцию-мутатор и функцию-наблюдатель. При применении функции-мутатора изменяется значение атрибута структурного типа. Функция-наблюдатель работает обратно функции-мутатора. Это позволяет отыскивать значение атрибута структурированного типа. Вы можете включить функции-наблюдатели в операторы SELECT, чтобы отыскать значения в базе данных.
Неопределенные значения
Неопределенные значенияПомни: Если в поле базы данных находятся какие-то данные, то в этом поле имеется определенное значение. А если поле не содержит никаких данных, то говорят, что у него неопределенное значение. Неопределенное значение (null) в числовом поле — это не одно и то же, что нуль. А в символьном поле неопределенное значение — это не одно и то же, что пустая строка. И нуль и пустая строка являются определенными значениями. Неопределенное же значение указывает на то, что имеющееся в поле значение неизвестно.
Встречаются случаи, когда поле может иметь неопределенное значение. В следующем списке приведены некоторые из этих случаев и даны примеры каждого из них.
Совет 3
Поле может иметь неопределенное значение по самым разным причинам. Так чад не торопитесь с выводами относительно того, что означает конкретное неопределенное значение.
Короткая история
(Очень) Короткая историяЯзык SQL , как и теория реляционных баз данных, берет свое начало в одной из исследовательских лабораторий компании IBM . В начале 1970-х годов исследователи из IBM выполняли первые разработки реляционных систем СУБД (или РСУБД), и тогда они создали подъязык данных, предназначенный для работы в этих системах. Пробная версия этого подъязыка была названа SEQUEL (Structured English QUEry Language - структурированный английский язык запросов). Однако, когда пришло время официально выпускать их язык запросов в качестве продукта, разработчики захотели сделать так, чтобы люди понимали, что выпущенный продукт отличается от пробной системы СУБД и превосходит ее. Поэтому они решили дать выпускаемому продукту имя, хотя и отличающееся от SEQUEL , но явно принадлежащее к тому же семейству. Так что они назвали его SQL.
О работе, которая велась в IBM над реляционными базами данных и над языком SQL , в информационной отрасли хорошо знали, причем еще до того, как эта компания представила в 1981 году РСУБД SQL / DS . К этому времени компания Relational Software , Inc . (ныне Oracle Corporation ) уже выпустила свою первую РСУБД. Эти первоначальные продукты туг же стали стандартом для нового класса систем, предназначенных для управления базами данных. В состав этих продуктов вошел SQL , который фактически стал стандартом для подъязыков данных. Производители других систем управления базами данных выпустили свои собственные версии SQL . В этих реализациях обычно имелись все основные возможности продуктов IBM, но, впрочем, не только они. Там также имелись расширения, введенные с целью дать преимущество сильным сторонам именно "своей" РСУБД. В результате, хотя почти все поставщики и использовали варианты одного языка SQL , платформенная совместимость была слабой.
Помни: Реализация — это конкретная СУБД, работающая на конкретной аппаратной платформе.
Вскоре началось движение за создание общепризнанного стандарта SQL , которого мог бы придерживаться каждый. В 1986 году организация ANSI выпустила официальный стандарт под названием SQL -86. Этот стандарт был обновлен той же организацией в 1989 году и получил название SQL -89, а затем, в 1992 году, был назван SQL -92. Поставщики СУБД, выпуская новые версии своих продуктов, всегда старались приблизить свои реализации к стандарту. Эти усилия и привели к тому, что мечта о настоящей переносимости SQL стала намного ближе к реальности.
Самой последней версией стандарта SQL является SQL:2003 ( ISO / IEX 9075 X:2003). В этой книге описан язык SQL , который определяется стандартом SQL:2003. Конечно, любая конкретная реализация SQL в определенной степени отличается от стандарта. Так как полный стандарт SQL:2003 является слишком всеобъемлющим, то от современных реализаций, видимо, не стоит ждать полного ему соответствия. Однако поставщики систем СУБД сейчас работают над тем, чтобы эти системы все же соответствовали основной части стандартного SQL. Полные спецификации стандартов ISO / IEC доступны в Internet по адресу webstore.ansi.org.
Ограничения
ОграниченияОграничения вы устанавливаете на данные, вводимые кем-либо в таблицу базы данных. Например, известно, что значения, вводимые в определенный числовой столбец, должны находиться в пределах определенного диапазона. А если кто-то вводит число, которое не попадает в этот диапазон, то такой ввод будет ошибочным. От таких ошибок и защищает установленное на столбец ограничение — вводить в него только значения из определенного диапазона.
Традиционно сложилось так, что если прикладная программа использует базу данных, то она и накладывает на эту базу любые ограничения. Однако в самых последних продуктах у вас есть возможность устанавливать ограничения на данные непосредственно из СУБД. Этот подход дает несколько преимуществ. Если одна и та же база данных используется множеством приложений, то вам придется устанавливать ограничения только один раз, а не столько, сколько имеется приложений. Кроме того, устанавливать ограничения на уровне базы данных обычно проще, чем на уровне приложения. Во многих случаях вам будет достаточно только добавить предложение в свой оператор CREATE (создать).
Об ограничениях и утверждениях (assertions), которые тоже являются ограничениями, но применяются к более чем одной таблице, подробно рассказывается в главе 5.
Использование SQL в системе клиент/сервер
SQL — это подъязык данных, который работает в одно- или многопользовательской системе. Особенно хорошо SQL работает в системе клиент/сервер. В такой системе пользователи работают на множестве клиентских машин, соединенных с серверным компьютером. И эти пользователи могут иметь доступ — через локальную сеть или другие каналы связи — к базе данных, расположенной на сервере, с которым соединены их машины. В прикладной программе, работающей на клиентском компьютере, создаются команды SQL. Та часть системы СУБД, которая находится на клиентском компьютере, передает эти команды на сервер по каналу связи, соединяющему сервер с клиентом. А та часть СУБД, которая находится на сервере, интерпретирует и выполняет полученную команду SQL, а затем по каналу связи отправляет результаты назад, клиенту. В виде SQL можно закодировать очень сложные операции, а затем на сервере декодировать их и выполнить. Такого рода система позволяет эффективнее всего использовать пропускную способность канала связи.
Если вы с помощью SQL получаете данные через систему клиент/сервер, то по каналу связи от сервера на клиентский компьютер попадут только нужные вам данные. И наоборот, простая система с разделением ресурсов и с минимальным "интеллектом" сервера должна гонять туда-сюда по каналу связи огромные блоки данных. И все это ради того, чтобы вы смогли получить крохотное количество нужной информации. Не приходится и говорить, что такого рода пересылки данных могут очень замедлить работу. Архитектура клиент/сервер, дополняя характеристики SQL, дает возможность в малых, средних и больших сетях получать хорошую производительность при умеренных расходах.
Определяемые пользователем типы
Определяемые пользователем типыОпределяемые пользователем типы (user-defined types, UDT) — это еще один пример новых возможностей, пришедших в SQL: 1999 из мира объектно-ориентированного программирования. Как программист на языке SQL вы больше не ограничены теми типами данных, которые определяются в спецификации SQL:2003. У вас есть возможность определять собственные типы, используя принципы абстрактных типов данных (Abstract Data Types, ADT), таких объектно-ориентированных языков программирования, как C++.
Одним из самых важных преимуществ UDT-типов является то, что их можно использовать для устранения "нестыковок" между SQL и базовым языком приложения баз данных. Застарелой проблемой SQL является то, что его заранее определенные типы данных могут не соответствовать типам, которые используются в базовом языке. Теперь же программист баз данных может создавать в SQL такие типы данных, которые не нарушают этого соответствия. UDT-типы имеют инкапсулированные атрибуты и методы. Снаружи можно видеть определение атрибутов и результаты выполнения методов, но конкретные механизмы этого выполнения скрыты от пользователя. Доступ к атрибутам и методам можно еще больше ограничить, указав, что они являются общими (public), приватными (private) или защищенными (protected). Общие атрибуты или методы доступны всем пользователям UDT, в то время как приватные — только самому UDT. Защищенные атрибуты или методы доступны только самому UDT-типу или его подтипам. Из этого можно видеть, что в SQL UDT-тип ведет себя во многом так же, как и класс из объектно-ориентированного программного языка. Существуют две формы определяемых пользователем типов: отдельный и структурный.
Отдельные типы данных
Отдельные типы данныхОтдельные типы данных — это простейшие формы определяемых пользователем типов. Отдельные типы определяются той особенностью, что они выражаются как единый тип данных. Они создаются на основе одного из ранее определенных типов данных, называющихся базовыми типами. Множество отдельных типов, которые все созданы на основе одного базового типа, отличаются друг от друга, и, таким образом, непосредственно сравнивать их между собой нельзя. Например, отдельные типы можно использовать, чтобы различать валюты разных стран. Проанализируйте следующее определение типа:
CREATE DISTINCT TYPE Usdollar AS DECIMAL (9,2) ;
В результате на основе заранее определенного типа DECIMAL создан новый тип данных предназначенный для долларов США. Аналогичным образом можно создать другой отдельный тип для евро:
CREATE DISTINCT TYPE Euro AS DECIMAL (9,2) ;
Теперь можно создать таблицы USInvoice (счет-фактура в долларах США) и Eurolnvoice (счет-фактура в евро), в которых используются эти новые типы. В столбцах обеих таблиц хранятся такие реквизиты: идентификатор счета-фактуры, идентификатор клиента, идентификатор сотрудника, общая сумма продажи, сумма налога, сумма поставки и общий итог.
| CREATE TABLE USInvoice ( | ||
| InvID | INTEGER | PRIMARY KEY, |
| CustID | INTEGER, | |
| EmpID | INTEGER, | |
| TotalSale | Usdollar, | |
| Tax | Usdollar, | |
| Shipping | Usdollar, | |
| GrandTotal | Usdollar | |
| ) ; |
| CREATE TABLE Eurolnvoice ( | ||
| InvID | INTEGER | PRIMARY KEY, |
| CustID | INTEGER, | |
| EmpID | INTEGER, | |
| TotalSale | Euro, | |
| Tax | Euro, | |
| Shipping | Euro, | |
| GrandTotal | Euro | |
| ) ; |
Подтипы и супертипы
Подтипы и супертипыМежду двумя структурированными типами могут существовать иерархические отношения. К примеру, тип MusicCDudt имеет подтипы RockCDudt и ClassicalCDudt. Для этих двух подтипов MusicCDudt является супертипом. Если для MusicCDudt нет подтипа, который является супертипом для RockCDudt, то RockCDudt является собственным подтипом MusicCDudt. Если RockCDudt имеет подтип с именем HeavyMetalCDudt, который, в свою очередь, также является подтипом для MusicCDudt, то тип HeavyMetalCDudt уже не будет являться собственным подтипом для MusicCDudt.
Структурированный тип, не имеющий супертипа, называется максимальным супертипом, а структурированный тип, не имеющий подтипа, — конечным подтипом.
Приблизительные числовые типы
Приблизительные числовые типыУ некоторых величин имеется такой большой диапазон возможных значений (большое количество порядков), что компьютер с данным размером регистра не может в точности представить все эти значения. (Размерами регистра являются, например, 32 бита, 64 бита и 128 бит.) Обычно в таких случаях точность не является необходимой, и поэтому будет достаточно иметь близкое приближение. Для работы с такими данными SQL :2003 определяет три приблизительных числовых типа.
Пример структурированного типа
Пример структурированного типаСтруктурированные UDT-типы могут быть созданы следующим способом:
| /* Создаем UDT-тип MusicCDudt */ | |
| CREATE TYPE MusicCDudt AS | |
| /* Задаем атрибуты */ | |
| Title | CHAR(40), |
| Cost | DECIMAL(9,2), |
| SuggestedPrice | DECIMAL(9,2), |
| /* Разрешаем подтипы */ | |
| NOT FINAL ; | |
| CREATE TYPE RockCDUdt UNDER MusicCDudt NOT FINAL ; |
CREATE TYPE HeavyMetalCDudt UNDER RockCDUdt FINAL ;
Создадим таблицы, использующие эти типы. Например:
| CREATE TABLE METALSKU ( | |
| Album | HeavyMetalCDudt, |
| SKU | INTEGER ) ; |
BEGIN
/* Объявляем временную переменную 'a' */
DECLARE a = HeavyMetalCDudt ;
/* Выполняем функцию-конструктор */
SET a = HeavyMetalCDudt () ;
/* Выполняем первую функцию-мутатор */
SET a = a.title('Edward the Great') ;
/* Выполняем вторую функцию-мутатор */
SET a = a.cost(7.50) ;
/* Выполняем третью функцию-мутатор */
SET a = a.suggestedprice(15/99) ;
INSERT INTO METALSKU VALUES (a, 31415926) ;
END
Сервер
СерверСервер не делает ничего, пока не получит запрос от клиента. Он только стоит и ждет. Но если требуется одновременно обслужить множество клиентов, то серверам приходится реагировать довольно оперативно. Они обычно отличаются от клиентских машин тем, что имеют быстрые дисковые массивы. Серверы настроены так, чтобы обеспечивать скорейший доступ к данным и скорейшую их передачу. Но так как им приходится обрабатывать трафик, идущий одновременно со множества клиентских машин, то на сервере должен быть очень быстрый процессор.
Символьные строки
Символьные строкиСейчас в базах данных хранятся данные многих разных типов, в том числе графические изображения звуки и анимация. Надеюсь, что вслед за ними появятся и запахи. Можете вы представить на своем экране трехмерное, размером 1600x1200 пикселей, выполненное в 24-битовом цвете изображение большого ломтя наперченной пиццы, в то время как через вашу супермультимедийную плату воспроизводится фрагмент запаха, записанный в "Ди Филиппи Пицца Гротто"? Такое мультимедиа может вызывать одно лишь разочарование — хотя бы до тех пор пока в систему нельзя будет вводить и данные вкусовых ощущений. Увы, возможно придется ждать очень долго, пока запах и вкус не станут стандартными типами данных SQL . А сейчас типами данных, которые вам придется использовать чаще всего, не считая, конечно, числовых, являются типы символьных строк.
Есть три главных типа символьных данных: фиксированных символьных данных ( CHARACTER или CHAR ), переменных символьных данных ( CHARACTER VARYING или VARCHAR ) и данных для больших символьных объектов ( CHARACTER LARGE OBJECT или CLOB ). Кроме того, есть еще три варианта этих типов данных: NATIONAL CHARACTER (строка с национальными символами), NATIONAL CHARACTER VARYING (переменная строка с национальными символами) и NATIONAL CHARACTER LARGE OBJECT (большой объект с национальными символами).
Структурированные типы
Структурированные типыВторая форма определяемого пользователем типа — структурированный тип. Он представляет собой перечень определений атрибутов и методов, а не базируется на отдельном предопределенном исходном типе.
Сводка типов данных
Сводка типов данныхВ таблице 2.2 перечислены различные типы данных и показаны литералы, которые соответствуют каждому из этих типов.
Типы данных
Таблица 2.2. Типы данных| Тип данных | Пример значения |
| CHARACTER (20) | 'Любительское радио' |
| VARCHAR (20) | 'Любительское радио' |
| CLOB (1000000) | 'В этой строке миллион символов..' |
| SMALLINT, BIGINT ИЛИ INTEGER | 7500 |
| NUMERIC или DECIMAL | 3425,432 |
| REAL, FLOAT ИЛИ DOUBLE PRECISION | 6,626E-34 |
| BLOB (1000000) | '1001001110101011010101010101.. . ' |
| BOOLEAN | 'true' |
| DATE | DATE '1957-08-14' |
| TIME (2) WITHOUT TIME ZONE (аргумент указывает количество цифр в дробной части) | TIME '12:46:02,43' WITHOUT TIME ZONE |
| TIME (3) WITH TIME ZONE | TIME '12:46:02,432-08:00' WITH TIME ZONE |
| TIMESTAMP WITHOUT TIME ZONE (0) | TIMESTAMP '1957-08-14 12:46:02' WITHOUN TIME ZONE |
| TIMESTAMP WITH TIME ZONE (0) | TIMESTAMP '1957-08-14 12:46:02-08:00' WITH TIME ZONE |
| INTERVAL DAY | INTERVAL '4' DAY |
| ROW | ROW (Street VARCHAR (25), City VARCHAR (20), Stat CHAR (2), PostalCode VARCHAR (9)) |
| ARRAY | INTEGER ARRAY [15] |
| MULTISET | Нет точного применения типа multiset |
| REF | Это не тип, а указатель |
| USER DEFINED TYPE | Тип валюты, созданный на основе decimal |
Тип BIGINT
Тип BIGINTТип данных BIGINT (большой целый) — это новый тип данных, появившийся вместе с SQL :2 OO 3. Он также предназначен для целых значений и определяется как тип, точность которого может быть не намного больше, чем точность данных типа INTEGER , или сильно превышать ее. Предел точности данных типа BIGINT зависит от реализации.
Тип CHARACTER LARGE OBJECT
Тип CHARACTER LARGE OBJECTтип данных CHARACTER LARGE OBJECT (CLOB) впервые появился в SQL: 1999. Как Указывает его имя (означает "большой символьный объект"), он используется вместе с громадными символьными строками, которые для типа CHARACTER слишком велики. Данные - LOB ведут себя во многом так же, как и обычные символьные строки, но на действия, которые можно с ними проводить, имеется ряд ограничений. Для типа CLOB нельзя использовать предикаты PRIMARY KEY (первичный ключ), FOREIGN KEY (внешний ключ), UNIQUE (уникальный). Более того, эти данные нельзя использовать для сравнения, за исключением равенства или неравенства. Из-за того, что у данных CLOB большие размеры, он как правило, всегда остаются на сервере. Вместо них на стороне клиента применяется специальный тип данных, который называется локатор LOB ( LOB locator ). Это параметр, значение которого идентифицирует большой символьный объект.
Тип CHARACTER VARYING
Тип CHARACTER VARYINGТип данных CHARACTER VARYING полезен тогда, когда вводимые в столбец значения имеют разную длину, но вы не хотите, чтобы поле заполнялось пробелами. Этот тип данных Дает возможность сохранять то количество символов, которое ввел пользователь. Для типа CHARACTER VARYING нет значения по умолчанию. Чтобы указать этот тип данных, используйте синтаксис CHARACTER VARYING (х) или VARCHAR (х), где х — это максимальное разрешенное количество символов.
Тип CHARACTER
Тип CHARACTERЕсли вы определяете для столбца тип данных CHARACTER или CHAR , количество символов, которое будет в нем находиться, можно укачать, используя синтаксис CHARACTER (х), где х и является нужным вам количеством. Если, например, тип данных столбца был определен как CHARACTER (16), то максимальная длина любых данных, которые можно будет ввести в этот столбец, равна 16 символам. Если аргумент не указан (т.е. нет значения в скобках), тогда для SQL это означает, что длина поля равна одному символу. Если вы вводите данные в поле типа CHARACTER , имеющего определенную длину, и при этом вводите символов меньше, чем может поместиться в поле, то позиции, оставшиеся свободными, будут заполнены пробелами.
Тип DATE
Тип DATEТип DATE (дата) предназначен для хранения значений даты в следующем порядке: год, и день. Значение года занимает четыре цифры, а месяца и дня — по две. Значения этого могут представлять любую дату, начиная с 0001 года и заканчивая 9999 годом. Длина DAТЕ равна десяти позициям, как, например, для 1957-08-14.
Технические подробности: Так как язык SQL, используя тип DATE, представляет в явном виде все четыре цифры года, то данные SQL никогда не вызывали опасения из-за пресловутой проблемы 2000 года.
Тип DECIMAL
Тип DECIMALТип данных DECIMAL (десятичный) похож на NUMERIC . В нем может быть дробная часть, и для него можно указать точность и масштаб. DECIMAL отличается от NUMERIC тем, что если точность имеющейся реализации SQL будет больше указанного значения, то в реализации будет использоваться большая точность. А если точность или масштаб не указаны, то, как и при использовании типа NUMERIC , в реализации применяются значения по умолчанию.
В столбце, тип которого NUMERIC (5,2), нельзя поместить числа, большие 999,99. Если же тип столбца DECIMAL (5,2), в него всегда можно поместить значения до 999,99. Кроме того, если точность реализации позволяет, то СУБД сможет поместить в этот столбец и значения, большие, чем 999,99.
Если в ваших данных имеются дробные части, тогда применяйте тип NUMERIC или DECIMAL , а если ваши данные всегда состоят из целых чисел, то используйте тип INTEGER или SMALLINT . Когда нужно добиться максимальной переносимости, следует использовать тип NUMERIC , потому что поле, тип которого вы определите, например, как NUMERIC (5,2), будет во всех системах иметь один и тот же диапазон значений.
Тип DOUBLE PRECISION
Тип DOUBLE PRECISIONТип данных DOUBLE PRECISION (двойная точность) дает возможность задавать числа двойной точности с плавающей запятой, точность которых опять-таки зависит от реализации Удивительно, что само значение слова DOUBLE (двойной) также зависит от реализации Арифметика двойной точности в основном применяется в научных целях. А для разных научных дисциплин требуется разная точность. Некоторые реализации SQL обслуживают одну категорию пользователей, а другие реализации — соответственно другие категории.
В некоторых системах тип DOUBLE PRECISION имеет и для мантиссы, и для экспоненты как раз в два раза большую вместимость, чем тип REAL . (Если вы забыли то, что учили в средней школе, то вспомним, что любое число можно представить в виде мантиссы, умноженной на число десять, возведенное в степень, показатель которой является экспонентой. Например, 6626 можно написать в виде 6.626ЕЗ. Число 6,626 является мантиссой, которую вы умножаете на десять, возведенное в третью степень (в данном случае 3 является экспонентой).
Вы не получите выигрыша, если будете с помощью приблизительного числового типа данных представлять числа, достаточно близкие к единице (такие как 6626 или даже 6626000). Точные числовые типы работают точно так же и занимают в памяти меньше места. Однако для чисел, которые слишком близко находятся от нуля или слишком далеко от единицы, таких как 6.626Е-34 (очень малое число), необходимо использовать приблизительный числовой тип. Для таких величин не подходят точные числовые типы. В других системах тип DOUBLE PRECISION дает побольше, чем двойная вместимость мантиссы, и поменьше, чем двойная вместимость типа REAL для экспоненты. В системах следующего вида тип DOUBLE PRECISION дает для мантиссы вместимость в два раза больше, чем у типа REAL , а для экспоненты — ту же, что и у REAL . В этом случае точность удваивается, а диапазон — нет.
Спецификация SQL :2 OO 3 не пытается жестко определить, что означает DOUBLE PRECISION . Она только требует, чтобы точность числа типа DOUBLE PRECISION была больше, чем точность типа REAL . Хотя это ограничение очень слабое, но, возможно, оно является наилучшим из возможных, если учитывать те немалые различия, которые имеются в оборудовании.
Тип FLOAT
Тип FLOATТип данных FLOAT (плавающий) является самым полезным, если вы считаете, что ваша база данных однажды должна перейти на аппаратную платформу, в которой размеры регистров отличаются от размеров регистров платформы, для которой вы первоначально спроектировали базу. Используя тип данных FLOAT , можно указывать точность— например, FLOAT (5). Если ваше оборудование поддерживает указанную точность, используя аппаратную одинарную точность, то ваша система использует арифметику с одинарной точностью. Если указанная точность требует арифметики с двойной точностью, то система использует ее.
Совет 2
Совет 2
Использование FLOAT вместо REAL или DOUBLE PRECISION облегчает перенос ваших баз данных на другие системы, так как тип данных FLOAT дает возможность указывать точность. Точность чисел типа REAL или DOUBLE PRECISION зависит от аппаратуры.
Если вы не уверены, использовать точные числовые типы данных (NUMERIC/DECIMAL) или приблизительные числовые типы ( FLOAT / REAL ), то выбирайте точные. Точным типам данных требуется меньше системных ресурсов, к тому же они дают точные, а не приблизительные результаты. Если же диапазон возможных значений ваших данных настолько большой, что требуется использовать приблизительные типы данных, то этот факт вы, скорее всего сможете определить для себя заранее.
Тип INTEGER
Тип INTEGERВ данных типа INTEGER (целый) нет дробной части, и их точность зависит от конкретной реализации SQL . Таким образом, точность не может быть установлена разработчиком базы данных.
Помни: Точностью числа является максимальное количество цифр, которое у него может быть.
Тип MULTISET
Тип MULTISETТип данных MULTISET (мультимножество) — это неупорядоченная коллекция. Определенные элементы мультимножества могут быть не связаны, поскольку им не назначается определенная порядковая позиция в мультимножестве.
Тип NUMERIC
Тип NUMERICВ данных типа NUMERIC (числовой), кроме целого компонента, может быть и дробный. Для этих данных можно указать точность и масштаб. Точность, как вы помните, — это максимально возможное количество цифр.
Масштаб — это количество цифр после запятой. Масштаб не может быть отрицательным или превышать точность числа.
При определении типа NUMERIC необходимо указать требуемые значения точности и масштаба. В определении можно указать только NUMERIC и получить значения по умолчанию. А если вы укажете NUMERIC (p), то получите требуемую точность и значение масштаба по умолчанию. Выражение NUMERIC (p,s) позволяет непосредственно задать и точность, и масштаб. При определении данных вместо параметров p и s нужно ввести соответственно требуемые значения точности и масштаба.
Скажем, например, что в вашей реализации SQL точность по умолчанию для типа данных NUMERIC равна 12, а масштаб по умолчанию равен 6. Если вы укажете, что столбец базы данных имеет тип NUMERIC , то в этом столбце смогут находиться числа вплоть до 999999,999999. Если же, с другой стороны, вы для столбца указываете тип данных NUMERIC (10), то в столбце смогут находиться только числа с максимальным значением 9999,999999. Параметр (10) указывает максимально возможное для числа количество цифр. Когда для столбца будет указан тип данных NUMERIC (10,2), то в столбце могут находиться числа с максимальным значением — 99999999,99. В этом случае хотя и останется всего десять цифр, но справа от десятичной запятой будут находиться только две из них. (Имейте в виду, что синтаксис SQL требует использовать для разделения целой и дробной частей числа не запятую, а десятичную точку, как это принято в США и странах Европы.)
Тип данных NUMERIC предназначен для значений, таких как 595,72. Точность этого значения равна 5 (общее количество цифр), а масштаб — 2 (количество цифр справа от десятичной запятой). Для чисел, таких, как это, подходит тип данных NUMERIC (5,2).
Тип REAL
Тип REALТип данных REAL (действительное число) дает возможность задавать числа однократно точности с плавающей запятой, точность которых зависит от реализации. Вообще-то, точность определяется используемым оборудованием. Например, 64-битовая машина дает большую точность, чем 32-битовая.
Число с плавающей запятой ( floating - point number ) — это число с десятичной запятой Десятичная запятая "плавает" или появляется в разных частях числа, в зависимости от значения этого числа. Примерами чисел с плавающей запятой являются 3,1, 3,14 и 3,14159.
Тип SMALLINT
Тип SMALLINTТип SMALLINT (малый целый) также предназначен для целых значений, но его точность в конкретной реализации не может быть больше точности типа INTEGER , имеющейся в данной реализации. В реализациях, работающих на компьютерах IBM System /370, типы SMALLINT и INTEGER обычно представлены соответственно 16- и 32-битовыми двоичными числами. Впрочем, во многих реализациях SMALLINT и INTEGER являются одним и тем же типом.
Если в таблице из базы данных вы определяете столбец для целых данных и известно, что значения в этом столбце никогда не превысят точность, установленную в вашей реализации для значений типа SMALLINT , то присвойте столбцу тип не INTEGER , a SMALLINT . Таким образом вы, возможно, позволите своей СУБД сэкономить место на диске.
Тип TIME WITH TIME ZONE
Тип TIME WITH TIME ZONEТип данных TIME WITH TIME ZONE (время вместе с часовым поясом) в точности такой же как и TIME WITHOUT TIME ZONE, за исключением того, что в нем еще имеется информация о разнице между местным и всемирным временем (UTC, Universal Time), ранее известным как среднее время по Гринвичу (Greenwich Mean Time, GMT). Значение этой разницы может находиться в диапазоне от -12:59 до +13:00. Такая дополнительная информация занимает за цифрами времени шесть позиций: дефис в качестве разделителя, знак "плюс" или "минус", а затем разница в часах (две цифры) и минутах (также две цифры) и двоеточие между часами и минутами. Значение типа TIME WITH TIME ZONE без дробной части (как установлено по умолчанию) занимает 14 позиций. Если же дробная часть указана, то длина поля равняется 15 позициям плюс количество цифр дробной части.
Тип TIME WITHOUT TIME ZONE
Тип TIME WITHOUT TIME ZONEТип TIME WITHOUT TIME ZONE (время без часового пояса) предназначен для хранения значений времени в следующем порядке: час, минута и секунда. Значения часа и минуты занимают в точности по две цифры. Секундное значение может занимать две цифры, но может быть и расширено, чтобы иметь необязательную дробную часть. Поэтому время 9 часов 32 минуты и 58,436 секунды утра представляется с помощью этого типа данных как 09:32:58,436.
Точность дробной части зависит от конкретной реализации, но имеет длину не менее шести символов. Значение типа TIME WITHOUT TTME ZONE без дробной части занимает восемь позиций (включая двоеточия), а с дробной частью — девять позиций (вместе с десятичной запятой) плюс число цифр дробной части. Этот тип данных задается или с помощью синтаксиса ТМЕ, в результате чего данные представляются в виде, установленном по умолчанию, т.е. без дробной части, или с помощью другого синтаксиса, TTME WITHOUT TIME ZONE (p), где вместо р должно стоять количество цифровых позиций, находящихся справа от десятичной запятой. В предыдущем абзаце представлен пример данных типа TTME WITHOUT TIME ZONE (3).
Тип TIMESTAMP WITH TIME ZONE
Тип TIMESTAMP WITH TIME ZONEТип данных TIMESTAMP WITH TIME ZONE (дата и время вместе с часовым поясом) работает точно так же, как и TIMESTAMP WITHOUT TIME ZONE, за исключением того, что в нем еще имеется информация о разнице между местным и всемирным временем. Дополнительная информация занимает за датой и временем шесть позиций (о формате данных часового пояса см. в предыдущем разделе). Для поля с данными часового пояса и без дробной части требуется 25 позиций, а для поля с дробной частью нужно 26 позиций плюс количество цифр дробной части (это количество по умолчанию равно шести).
Тип TIMESTAMP WITHOUT TIME ZONE
Тип TIMESTAMP WITHOUT TIME ZONEВ данных типа TIMESTAMP WITHOUT TIME ZONE (дата и время без часового пояса) хранится информация как о дате, так и о времени. У компонентов данных этого типа такие же значения длины и такие же ограничения, как и для данных типа DATE и TIME WITHOUT TIME ZONE, если не считать одного различия. Оно состоит в том, что по умолчанию в данных типа TIMESTAMP WITHOUT TIME ZONE длина дробной части равна шести цифрам, а не нулю. Если в значении типа TIMESTAMP WITHOUT TIME ZONE нет цифр дробной части, то длина этого значения равна 19 позициям, занимаемым в следующем порядке: десять позиций — датой, один пробел служит в качестве разделителя, и восемь позиций — временем. Если цифры дробной части все же имеются (по умолчанию их должно быть шесть), то Длина равна 20 позициям плюс количество этих цифр. Двадцатая позиция предназначена для Десятичной запятой. Устанавливать для поля тип TIMESTAMP WITHOUT TIME ZONE можно с помощью двух видов синтаксиса: TIMESTAMP WITHOUT TIME ZONE или IMESTAMP WITHOUT TIME ZONE (p), где вместо р должно стоять число позиций, предназначенных для цифр дробной части. Вместо р не может стоять отрицательное число, максимальное значение этого параметра зависит от конкретной реализации SQL.
Типы ARRAY
Типы ARRAYТип данных ARRAY (массив) нарушает первую нормальную форму (1НФ), но делает это иначе, чем тип ROW. Тип ARRAY, относящийся к типу коллекций, в действительности не является отдельным типом данных в том же смысле, что и типы CHARACTER или NUMERIC. Тип ARRAY просто дает возможность одному из других типов иметь множество значений внутри одного поля в таблице. Скажем, например, что для вашей организации важно поддерживать контакт со своими клиентами, находятся ли они на работе, дома или в дороге. Поэтому среди данных о клиенте может потребоваться и несколько телефонных номеров. Такую возможность можно реализовать, объявив атрибут Phone (телефон) массивом, как показано в следующем коде:
| CREATE TABLE CUSTOMER ( | ||
| CustID | INTEGER | PRIMARY KEY, |
| LastName | CHARACTER | VARYING (25), |
| FirstName | CHARACTER | VARYING (20), |
| Address | addr_typ | |
| Phone | CHARACTER | VARYING (15) ARRAY [3] |
| ) ; |
Типы данных
Типы данныхВ разных реализациях SQL поддерживаются различные исторически сложившиеся типы данных. В спецификации SQL:2003 признаны только пять заранее определенных общих типов:
Совет 1
Совет 1
Если вы используете реализацию SQL , в которой поддерживаются типы данных, не описанные в SQL:2003, то для большей переносимости вашей базы данных старайтесь этими типами данных не пользоваться. Перед тем как вы решите создать и использовать определяемый пользователем тип, убедитесь, что в любой СУБД, на которую вам, возможно, захочется перейти в будущем, также поддерживаются определяемые пользователем типы.
Типы коллекций
Типы коллекцийПосле того как с выходом SQL: 1999 была разрушена реляционная строгость, стало возможным использование таких типов данных, которые нарушают первую нормальную форму. Теперь поля могут содержать не один, а целую коллекцию объектов. Тип ARRAY (массив) впервые появился уже в SQL: 1999, а вот тип MULTISET (мультимножество) — это новинка SQL:2003.
Можно сравнить две коллекции между собой только в том случае, если они содержат один и тот же тип данных, либо ARRAY, либо MULTISET, и сравниваемые типы элементов. Поскольку массивы имеют определенный порядок элементов, соответствующие элементы массивов можно сравнить. Мультимножества такого порядка не имеют, однако их также можно сравнить, но только в том случае, если для одного из двух сравниваемых мультимножеств существует нумерация, которой должна соответствовать нумерация другого мультимножества.
Типы NATIONAL CHARACTER NATIONAL
Типы NATIONAL CHARACTER , NATIONAL CHARACTER VARYING и NATIONAL CHARACTER LARGE OBJECTВ разных языках используются символы, которые отличаются от любых символов другого языка. Например, в немецком имеются символы, которых нет в наборе символов английского языка. Имеются языки, такие, например, как китайский или японский, в которых набор иероглифов очень сильно отличается от наборов символов других языков. Если вы в своей системе сделаете английский языком по умолчанию, то все равно сможете использовать и другие символьные наборы, так как типы данных NATIONAL CHARACTER , NATIONAL CHARACTER VARYING и NATIONAL CHARACTER LARGE OBJECT работают точно так же, как и CHARACTER , CHARACTER VARYING и CHARACTER LARGE OBJECT , за исключением того, что определяемый вами набор символов отличается от того, который вы используете по умолчанию. Набор символов можно указывать при определении столбца в таблице. Если нужно, то в каждом столбце можно использовать отдельный набор символов. В следующем примере инструкции, создающей таблицу, используется несколько таких наборов:
CREATE TABLE XLATE
LANGUAGE_1 CHARACTER (40),
LANGUAGE_2 CHARACTER VARYING (40) CHARACTER SET GREEK,
LANGUAGE_3 NATIONAL CHARACTER (40),
LANGUAGE_4 CHARACTER (40) CHARACTER SET KANJI
) ;
Столбец LANGUAGE_1 (1-й язык) предназначен для символов из набора, используемого в данной реализации по умолчанию. В свою очередь, столбец LANGUAGE_3 (3-й язык) предназначен для символов национального набора данной реализации. А для греческих символов предназначен столбец LANGUAGE_2 (2-й язык). И, наконец, для иероглифов предназначен столбец LANGUAGE_4 (4-й язык).
Типы REF
Типы REFТипы REF в основную часть SQL не входят. Это значит, что какая-либо система СУБД, совсем не использующая эти типы данных, все равно может считаться соответствующей стандарту SQL:2003. Тип REF не является отдельным типом данных в том же смысле, что и типы CHARACTER и NUMERIC. На самом деле это указатель на элемент данных, на данные типа ROW или на данные абстрактного типа, размещаемого в строке таблицы. Разыменовывая указатель, можно получить требуемое значение. Не беспокойтесь, если запутались, — в этом вы не одиноки. Использование типов REF требует практического знания принципов объектно-ориентированного программирования (ООП). В книге я старался не слишком глубоко погружаться в эту муть. Поскольку типы REF не входят в основную часть SQL, вам будет лучше совсем их не использовать. Если нужна максимальная переносимость с одних платформ СУБД на другие, то не следует выходить за пределы основной части SQL.
Типы ROW
Типы ROWТип данных ROW (запись) впервые появился в SQL: 1999. Он не является легким для понимания, и вы на своем пути от начального до среднего уровня программирования на SQL, возможно, так никогда с этим типом данных не встретитесь. В конце концов, в 1986-1999 годах люди прекрасно без него обходились.
Кроме всего прочего, примечательно то, что тип данных ROW нарушает правила нормализации, которые доктор И.Ф. Кодд (E.F. Codd) объявил на начальной стадии теории реляционных баз данных. Об этих правилах более подробно рассказывается в главе 5. Одной из характеристик, определяющих первую нормальную форму, является то, что поле в табличной строке не может быть многозначным. В поле может находиться одно и только одно значение. Однако тип данных ROW дает возможность объявить целую строку данных находящейся в единственном поле единственной строки таблицы — другими словами, объявить строку, вложенную в строку.
Проанализируйте следующую команду SQL, которая для персональной адресной информации (улица, город, штат, почтовый код) использует тип ROW:
| CREATE ROW TYPE addr_typ ( | |
| Street | CHARACTER VARYING (25) |
| City | CHARACTER VARYING(20) |
| State | CHARACTER (2) |
| PostalCode | CHARACTER VARYING (9) |
| ) ; |
| CREATE TABLE CUSTOMER ( | ||
| CustID | INTEGER | PRIMARY KEY, |
| LastName | CHARACTER | VARYING (25), |
| FirstName | CHARACTER | VARYING (20), |
| Address | addr_typ | |
| Phone | CHARACTER | VARYING (15) |
| ) ; |
Точные числовые типы
Точные числовые типыКак вы, возможно, поняли из названия, точные числовые типы данных позволяют точно выразить значение числа. К этой категории относятся пять типов:
в которых не полностью поддерживаются
ВниманиеВ тех реализациях, в которых не полностью поддерживаются все пять типов данных для даты и времени, при переносе в них баз данных из других реализаций могут возникнуть проблемы. Если с переносом возникли сложности, то проверьте, какие имеются способы представления даты и времени в двух реализациях исходной и той, на которую требуется перейти.
Зарезервированные слова
Зарезервированные словаКроме команд, специальное значение в SQL имеют и некоторые другие слова. Вместе с командами они зарезервированы для специального использования, поэтому эти слова нельзя применять в качестве имен переменных или любым другим способом, для которого они не предназначены. Вы можете легко увидеть, почему таблицам, столбцам и переменным нельзя давать имена из списка зарезервированных слов. Представьте себе, какая путаница возникнет из-за такого оператора:
SELECT SELECT FROM SELECT WHERE SELECT = WHERE ;
Полный список зарезервированных слов стандарта SQL:2003 приводится в приложении А.
ALTER
ALTERТаблица не обязательно навсегда останется такой, какой ее создали. Как только ее начинают использовать, то вдруг обнаруживается, что в ней нет чего-то такого, что обязательно должно было быть. Чтобы изменить таблицу, добавив, изменив или удалив ее столбец, воспользуйтесь командой ALTER TABLE (изменить таблицу). Команду ALTER можно применять не только к таблицам, но также к столбцам и доменам.
AVG
AVGФункция AVG возвращает среднее арифметическое всех значений указанного столбца. к- и функция SUM, AVG применяется только к столбцам с числовым типом данных. Чтобы найти среднее арифметическое значение продаж, учитывая все финансовые операции, хранящиеся в базе, используйте функцию AVG следующим образом:
SELECT AVG(TotalSale) FROM INVOICE;
Имейте в виду, что неопределенные значения значениями не считаются, так что если в каких-либо строках в столбце TotalSale (всего продано) находятся неопределенные значения, то при подсчете средней продажи эти строки игнорируются.
COUNT
COUNTФункция COUNT возвращает число строк указанной таблицы. Чтобы в базе данных сред. ней школы, используемой в качестве примера, подсчитать число самых юных учеников выпускных классов, воспользуйтесь следующим оператором (название GRADE означает "класс"):
SELECT COUNT (*)
FROM STUDENT
WHERE Grade =12 AND AGE < 14 ;
CREATE
CREATEКоманда языка SQL CREATE может создавать объекты SQL нескольких видов, в том числе схемы, домены, таблицы и представления. С помощью оператора CREATE SCHEMA (создать схему) можно создать схему, идентифицировать ее владельца и указать символьный набор по умолчанию. Вот, например, как может выглядеть такой оператор:
CREATE SCHEMA SALES
AUTHORIZATION SALES_MGR
DEFAULT CHARACTER SET ASCII_FULL ;
С помощью оператора CREATE DOMAIN (создать домен) устанавливаются ограничения на те значения, которые могут быть в столбце, или указывается порядок сопоставления. Устанавливаемые на домен ограничения определяют, какие объекты могут и какие не могут в нем находиться. Создавать домены можно после того, как установлена схема. Следующий пример демонстрирует, как можно использовать эту команду:
CREATE DOMAIN AGE AS INTEGER
CHECK (AGE > 20) ;
Таблицы создаются с помощью оператора CREATE TABLE (создать таблицу), а представления — с помощью CREATE VIEW (создать представление). В этой главе уже приводились примеры использования операторов CREATE TABLE и CREATE VIEW. Когда с помощью оператора CREATE TABLE создается новая таблица, то в том же операторе на ее столбцы можно также установить ограничения. Впрочем, иногда требуется установить ограничения, которые относятся не только к таблице, но и ко всей схеме. В таких случаях используется оператор CREATE ASSERTION (создать утверждение).
Кроме того, имеются операторы CREATE CHARACTER SET (создать символьный набор), CREATE COLLATION (создать сопоставление) и CREATE TRANSLATION (создать трансляцию), которые предоставляют широкие возможности по созданию новых символьных наборов, последовательностей сопоставления или таблиц трансляции. (Последовательности сопоставления определяют порядок, в котором будут проводиться операции сравнения или сортировки. Таблицы трансляции управляют преобразованием символьных строк из одного символьного набора в другой.)
Делегирование ответственности за безопасность
Делегирование ответственности за безопасностьЕсли вы хотите сохранять свою систему в безопасности, то должны строго ограничить полномочия доступа, которые вы предоставляете, и круг тех людей, кому вы предоставляете эти полномочия. Однако те, кто не может работать из-за отсутствия доступа, скорее всего, будут постоянно вам надоедать. Чтобы иметь возможность сосредоточиться, вам придется кому-то делегировать часть своей ответственности за безопасность базы данных. В SQL такое Делегирование выполняется с помощью предложения WITH GRANT OPTION (с возможностью предоставления). Проанализируйте следующий пример:
GRANT UPDATE
ON RETAIL_PRICE_LIST
TO SALES_MANAGER WITH GRANT OPTION
Этот оператор похож на приведенный в предыдущем примере с GRANT UPDATE в том смысле, что дает возможность менеджеру по продажам обновлять розничный прайс - лист. Но, кроме того, новый оператор еще дает ему право предоставлять полномочия на обновление любому, кому он захочет. И если вы используете такую форму оператора GRANT, то обязаны не только быть уверены, что менеджер по продажам разумно использует предоставленные полномочия, но также должны быть уверены, что он будет осторожно предоставлять подобные полномочия другим пользователям.
DROP
DROPУдалить таблицу из схемы базы данных легко. Надо только использовать команду DROP TABLE <имя_таблицы> (прекратить поддержку таблицы). В результате стираются все данные этой таблицы, а также метаданные, которые определяют ее в словаре данных, — после чего таблицы как будто и не было.
Иерархическая структура типичной базы данных SQL
Рисунок 3.5. Иерархическая структура типичной базы данных SQL
Итоговые функции
Итоговые функцииИногда информация, которую вы хотите получить из таблицы, не связана с содержимым отдельных строк, но относится к данным таблицы, взятым в целом. Для таких ситуаций стандарт SQL: 2003 предусматривает пять итоговых функций: COUNT, MAX, MIN, SUM и AVG Каждая из этих функций выполняет действие по получению данных, относящихся к множеству строк, а не только к одной.
Язык манипулирования данными
Язык манипулирования даннымиКак уже говорилось в этой главе, DDL является частью языка SQL, предназначенной для создания, модификации или разрушения структур базы данных. Непосредственно с данными язык DDL не работает. Для этого предназначена другая часть SQL — язык манипулирования данными (Data Manipulation Language, DML). Некоторые операторы DML можно читать как обычные предложения на английском языке, и эти операторы легко понять. Однако другие операторы DML могут быть, наоборот, очень сложными — как раз из-за того, что SQL дает необъятные возможности работы с данными. Если в операторе DML имеется множество выражений, предложений, предикатов или подзапросов, то даже просто понять, для чего этот оператор предназначен, может оказаться по-настоящему трудным делом. Поработав с некоторыми из них, вы, возможно, захотите переключиться на что-нибудь более легкое, например, на хирургию мозга или квантовую электродинамику. Впрочем, все не так плохо. Дело в том, что такие сложные операторы SQL можно мысленно разбивать на простые части и анализировать одну за другой.
Можно использовать такие операторы DML: INSERT (вставить), UPDATE (обновить), DELETE (удалить) и SELECT (выбрать). Они могут состоять из разных частей, в том числе из множества предложений. А в каждом предложении могут быть выражения со значениями, логические связки, предикаты, итоговые функции и подзапросы. Все они позволяют точнее отделять друг от друга записи базы данных и получать из своих данных больше информации. В главе 6 рассказывается о том, как работают команды DML, а более подробно о самих командах речь пойдет в главах 7-12.
Язык определения данных
Язык определения данныхЯзык определения данных (DDL) — это часть языка SQL, которая используется для создания, изменения и уничтожения основных элементов реляционной базы данных. В число этих элементов могут входить таблицы, представления, схемы, каталоги, кластеры и, возможно, не только они. В этом разделе говорится о контейнерной иерархии, которая связывает между собой эти элементы, и рассматриваются команды, выполняемые с элементами базы данных.
В главе 1 упоминались таблицы и схемы, и говорилось, что схема — это общая структура, в состав которой входят таблицы. Таким образом, таблицы и схемы являются двумя элементами контейнерной иерархии реляционной базы данных. Эту иерархию можно представить таким образом.
Язык управления данными
Язык управления даннымиВ языке управления данными (Data Control Language, DCL) имеются четыре команды: COMMIT (завершить), ROLLBACK (откат), GRANT (предоставить) и REVOKE (отозвать). Все эти команды связаны с защитой базы от случайного или умышленного повреждения.
Логические связки
Логические связкиЛогические связки позволяют из простых предикатов строить сложные. Скажем вам нужно в базе данных по ученикам средней школы найти информацию о юных дарованиях. Два логических высказывания, которые относятся к этим ученикам, можно прочитать следующим образом:
"Ученик учится в выпускном классе".
"Ученику еще нет 14 лет".
Чтобы отделить нужные вам записи, можно с помощью логической связки AND (и) создать составной предикат, например, как этот:
CLASS = SENIOR AND AGE < 14
Если используется связка AND, то чтобы составной предикат был истинным, Должны быть оба входящих в него предиката. А если нужно, чтобы составной предикат был истинным тогда когда истинный какой-либо из входящих в него предикатов, то используйте логическую связку OR (или). Третьей логической связкой является NOT (отрицание). Строго говоря, эта связка не соединяет два предиката. Она применяется к единственному предикату и заменяет его логическое значение на противоположное. Возьмем, например, следующее выражение:
NOT (CLASS = SENIOR)
Это значение истинно только тогда, когда значение CLASS на самом деле не равно SENIOR
MAX
MAXФункция MAX используется для определения максимального значения столбца. Скажем, требуется найти самого старшего ученика вашей школы. Естественно, таких переростков может быть несколько. Строку с его данными возвращает следующий оператор:
SELECT FirstName, LastName, Age
FROM STUDENT
WHERE Age = (SELECT MAX(Age) FROM STUDENT);
В результате появляются данные по всем старшим ученикам, т.е. если возраст самого старшего ученика равен 23 годам, этот оператор возвращает данные по всем ученикам с возрастом 23 года.
В этом запросе используется подзапрос. Этот подзапрос, SELECT MAX(Age) FROM STUDENT, находится внутри главного запроса.
Место для представления
Место для представленияИногда из таблицы CUSTOMER (клиент) вам требуется получить определенную информацию. При этом не нужно просматривать все подряд, а только конкретные столбцы и строки. В таком случае требуется представление (view).
Представления — это виртуальные таблицы. В большинстве реализаций они не являются в таблицах метаданных, и данные на самом деле поступают из таблиц, на основе которых это представление создано. Его данные больше нигде не хранятся. Одни представления состоят из определенных столбцов и строк одной таблицы. Другие же, которые называются многотабличными представлениями, получаются не менее чем из двух таблиц.
MIN
MINФункция MIN работает точно так же, как и МАХ, за исключением того, что MIN ищет в указанном столбце не максимальное, а минимальное значение. Чтобы найти самых юных учеников школы, можно использовать следующий запрос:
SELECT FirstName, LastName, Age
FROM STUDENT
WHERE Age = (SELECT MIN(Age) FROM STUDENT);
В результате появляются данные по самым младшим ученикам вашей школы.
Однотабличное представление
Однотабличное представлениеИногда данные, которые дадут ответ на ваш вопрос, находятся в единственной таблице базы данных. А если вся необходимая вам информация находится в одной таблице, то можно создать однотабличное представление данных. Скажем, например, что нужно просмотреть имена (first name), фамилии (last name) и телефонные номера (phone) всех клиентов, которые живут в штате Нью-Хэмпшир (который обозначается аббревиатурой NH). Тогда на основе таблицы CUSTOMER можно создать представление, содержащее только те данные, которые вам нужны. Оно создается при выполнении следующей команды:
CREATE VIEW NH_CUST AS
SELECT CUSTOMER.FirstName,
CUSTOMER.LastName,
CUSTOMER.Phone
FROM CUSTOMER
WHERE CUSTOMER.State = 'NH' ;
Диаграмма на Рисунок 3.2 показывает, каким образом представление создается из таблицы CUSTOMER.
Ограничения ссылочной целостности угрожают вашим данным
Ограничения ссылочной целостности угрожают вашим даннымВозможно, вы думаете, что если можете контролировать функции просмотра, создания, изменения и удаления в таблице, то вы надежно защищены. В большинстве случаев это правда. Однако с помощью непрямого метода опытный хакер все равно имеет возможность сделать подкоп под вашу базу.
Правильно спроектированная реляционная база данных имеет ссылочную целостность, т.е. данные в одной таблице из базы данных согласуются с данными во всех других таблицах. Чтобы обеспечить ссылочную целостность, проектировщики баз данных применяют к таблицам такие ограничения, которые относятся к вводимым в таблицу данным. И если ваша база данных имеет ограничения ссылочной целостности, то какой-либо пользователь может создать новую таблицу, где в качестве внешнего ключа используется столбец из засекреченной таблицы вашей базы. Вполне возможно, что в таком случае этот столбец можно использовать в качестве канала кражи конфиденциальной информации.
Скажем, вы, например, являетесь знаменитым аналитиком с Уолл-Стрит. Многие верят в точность вашего биржевого анализа, и если вы рекомендуете подписчикам своего бюллетеня какие-либо ценные бумаги, то многие люди их покупают, и стоимость этих бумаг растет. Ваш анализ хранится в базе данных, в которой находится таблица FOUR_STAR. В этой таблице содержатся самые лучшие рекомендации, предназначенные для следующего выпуска вашего бюллетеня. Естественно, что доступ к FOUR_STAR ограничен, чтобы ни слова не просочилось в массу инвесторов, пока бюллетень не дойдет до ваших платных подписчиков.
Вы будете находиться в уязвимом положении, если кому-то удастся создать таблицу, в качестве внешнего ключа которой используется поле таблицы FOUR_STAR, содержащее названия ценных бумаг. Вот, например, команда, создающая такую таблицу:
CREATE TABLE HOT_STOCKS (
STOCK CHARACTER (30) REFERENCES FOUR_STAR
);
Теперь хакер может вставить в свою таблицу HOT_STOCKS названия всех ценных бумаг с Нью-йоркской фондовой биржи. Те названия, которые будут успешно вставлены, подскажут ему, что именно находится в вашей конфиденциальной таблице. Благодаря быстродействию компьютеров хакеру не потребуется много времени, чтобы вытащить весь ваш список ценных бумаг.
Вы сможете защитить себя от проделок, аналогичных показанной в предыдущем примере, если будете остерегаться вводить операторы такого рода:
REFERENCES (STOCK)
ON FOUR_STAR
TO IMASECRET_HACKER;
Совет 3
Совет 3
He предоставляйте полномочия тем, кто может ими злоупотребить. Конечно, гарантии у людей на лбу не написаны. Но если вы кому-либо не собираетесь давать свой новый автомобиль для дальней поездки, то, скорее всего, не должны также предоставлять этому человеку и полномочия REFERENCES на ценную таблицу.
Этот пример показывает первую уважительную причину, чтобы осторожно обращаться с полномочиями REFERENCES. А ниже указаны еще две причины, чтобы быть осторожными с этим видом полномочий.
Подзапросы
ПодзапросыПодзапросами (см. выше раздел "Итоговые функции") являются запросы, находящиеся внутри какого-либо запроса. В любом месте оператора SQL, где можно использовать выражение, можно также использовать и подзапрос. Подзапросы являются мощным инструментом для связывания информации из одной таблицы с информацией из другой. Дело в том, что запрос к одной из таблиц можно встроить в другой запрос. С помощью вложенных подзапросов можно иметь доступ к информации более чем из двух таблиц. Если правильно пользоваться подзапросами, то из базы данных можно получить почти любую нужную информацию.
Пользователи и полномочия
Пользователи и полномочияКроме повреждения данных, вызванного проблемами с оборудованием и программами или неумышленными совместными действиями двух пользователей, целостности данных угрожает и другая большая опасность. Это сами пользователи. Некоторым людям вообще нельзя иметь доступ к данным. Другим — только ограниченный доступ к некоторым данным и никакого доступа к остальным. А кое-кто должен иметь неограниченный доступ ко всем данным. Поэтому вам нужна система, предназначенная для классификации пользователей по категориям и присвоения этим пользователям в соответствии с их категорией определенных полномочий доступа.
Создатель схемы указывает, кого следует считать ее владельцем. Являясь владельцем схемы, вы можете предоставлять полномочия доступа пользователям. Любые полномочия, не предоставленные вами явно, являются недействительными. Вы также можете отозвать уже предоставленные вами полномочия. Пользователю, перед тем как получить предоставляемый вами доступ к файлам, необходимо подтвердить свою личность, пройдя для этого процедуру аутентификации. Что собой представляет эта процедура — зависит от конкретной реализации SQL.
SQL дает возможность защищать следующие объекты базы данных.
Доступ разрешается с помощью оператора GRANT (разрешить), а аннулируется с помощью тора REVOKE (отозвать). Управляя использованием команды SELECT, DCL позволяет определить тех, кто может видеть объекты базы данных, такие, например, как таблица, столбец или представление. В случае команды INSERT DCL позволяет определить тех, кто может добавлять в таблицу новые строки. То, что команда UPDATE может применяться только авторизованными пользователями, дает возможность назначать пользователей, ответственных за изменение табличных строк, и аналогично в случае команды DELETE — тех, кто может такие строки удалять.
Если в одной таблице базы данных имеется столбец, который для этой таблицы является внешним ключом, а для другой таблицы из этой базы — первичным, то для первой таблицы, если она ссылается на вторую, можно установить ограничение. Дело в том, что когда одна таблица ссылается на другую, то владелец первой из них, вероятно, сможет получать информацию о содержимом второй. Владельцу же второй таблицы, возможно, захочется этот доступ пресечь. Такая возможность дается в виде оператора GRANT REFERENCES (предоставить доступ по ссылке). В следующем разделе рассказывается о проблеме, связанной с "предательской" ссылкой, и о том, как оператор GRANT REFERENCES решает эту проблему. Применяя оператор GRANT USAGE (предоставить использование), можно назначать пользователей, которым позволено использование или просмотр содержимого домена, набора символов, сопоставления или трансляции. (Об этом рассказывается в главе 13.)
Операторы SQL, с помощью которых предоставляют или отзывают полномочия, приведены в табл. 3.4.
Предикаты
ПредикатыПредикаты- это используемые в SQL эквиваленты логических высказывании. Примером высказывания является следующее выражение:
"Ученик учится в выпускном классе".
В таблице, содержащей информацию об учениках, домен столбца CLASS (класс) может быть набором таких значений: SENIOR (выпускной), JUNIOR (предпоследний), SOPHOMORE (второй старший класс), FRESHMAN (первый старший класс) и NULL (неизвестен). Предикат CLASS = SENIOR можно использовать для отсева тех строк, для которых его значение ложно, оставляя, соответственно, только те строки, для которых значение этого предиката истинно. Иногда в какой - либо строке значение этого предиката не известно (т.е. представляет собой NULL). В таком случае строку можно отбросить или оставить в зависимости от конкретной ситуации.
CLASS = SENIOR - это пример предиката сравнения. В SQL имеется шесть операторов сравнения. В простом предикате сравнения используется только один из этих операторов. Предикаты сравнения и примеры их использования приведены в таблице 3.3.
Создание представления
Рисунок З.2. Создание представления NH_CUST из таблицы CUSTOMER
Совет 1
Совет 1
Этот код безупречно правильный, но немного громоздкий. Ту же самую операцию можно выполнить, набирая команды и покороче. Это возможно тогда, когда имеющаяся у вас реализация SQL допускает, что если в перечисленных атрибутах не указаны ссылки на таблицу, то все атрибуты относятся к таблице предложения FROM. Если ваша система в состоянии сделать это разумное допущение, то команду можно сократить до следующих строк:
CREATE VIEW NH_CUST AS
SELECT FirstName, LastName, Phone
FROM CUSTOMER
WHERE STATE = 'NH' ;
Хотя этот вариант записи проще, подобное представление может неправильно работать после применения команд ALTER TABLE. Конечно, если оператор JOIN (соединить) не используется, такого не случится. А для представлений с операторами JOIN лучше использовать полные имена. Об операторах JOIN рассказывается в главе 10.
Создание многотабличного
Рисунок З.4. Создание многотабличного представления с помощью оператора JOIN
Ниже приведены положения для четырех операторов CREATE VIEW.
Сборка таблиц в схемы
Сборка таблиц в схемыТаблица состоит из строк и столбцов и обычно соответствует какому-либо объекту, такому, например, как множество клиентов, товаров и счетов-фактур. Для полезной работы обычно требуется информация о нескольких (или многих) объектах, имеющих между собой какие-либо отношения. Таблицы, соответствующие этим объектам, вы располагаете вместе, согласно логической схеме. (Логическая схема — это организационная структура совокупности таблиц, связанных между собой отношениями.)
В системе, где может сосуществовать несколько несвязанных друг с другом проектов, можно соединить все таблицы, связанные друг с другом отношениями, в одну схему. А из таблиц, не вошедших в эту схему, можно образовать другие схемы. Чтобы таблицы из одного проекта не оказались случайно в другом, схемам следует дать имена. У каждого проекта имеется своя собственная схема, которую по имени можно будет отличать от других схем. Некоторые табличные имена (например, CUSTOMER, PRODUCT и т.д.) могут встречаться сразу в нескольких проектах. Если есть хоть малейший шанс, что возникнет путаница с именами, необходимо в именах таблиц указывать имя схемы (примерно так: ИМЯ_СХЕМЫ.ИМЯ_ТЛБЛИЦЫ). Если имя схемы не указано, SQL будет считать, что эта таблица относится к схеме, подразумеваемой по умолчанию.
Помни: У базы данных, кроме логической, есть еще и физическая схема. Физическая схема — это способ, с помощью которого данные и соответствующие им компоненты, например индексы, физически размещаются на диске компьютера. И когда в книге говорится о схеме базы данных, то имеется в виду логическая схема, а не физическая.
Создание многотабличного представления
Создание многотабличного представленияЧтобы получать ответы на имеющиеся вопросы, часто приходится выбирать данные не менее чем из двух таблиц. Скажем, вы работаете в магазине спорттоваров, и для рассылки рекламы по почте вам нужен список клиентов, купивших у вас в прошлом году лыжное снаряжение. Скорее всего, потребуется информация из следующих таблиц: CUSTOMER (клиент), PRODUCT (товар), INVOICE (счет-фактура) и INVOICE_LINE (строка счета-фактуры). На их основе можно создать многотабличное представление, которое покажет нужные данные. Создав представление, его можно использовать снова и снова. При каждом гаком использовании представление отображает последние изменения в таблицах, на основе которых это представление создано.
В базе данных магазина спорттоваров имеются четыре таблицы: CUSTOMER, PRODUCT, INVOICE и INVOICE_LINE. Структура каждой из них показана в табл. 3.1.
Создание таблиц
Создание таблицТаблица базы данных представляет собой двумерный массив, состоящий из строк и столбцов. Создать таблицу можно с помощью команды языка SQL CREATE TABLE (создать таблицу). В команде следует указать имя и тип данных каждого столбца.
После того как таблица создана, можно приступать к ее заполнению данными. (Впрочем, загружать данные — это дело языка DML, а не DDL.) Если требования меняются, то изменить структуру уже созданной таблицы можно с помощью команды ALTER TABLE (изменить таблицу). Со временем таблица может перестать быть полезной или устареть. И если час таблицы пробил, то уничтожить ее можно с помощью команды DROP (прекратить). Имеющиеся в SQL разные формы команд — CREATE (создать) и ALTER (изменить), а также DROP — как раз и представляют собой язык DDL.
Скажем, вы проектируете базы данных и не хотите, чтобы их таблицы постепенно, по мере обновления имеющихся в них данных, стали бы "неудобоваримыми". Чтобы обеспечить поддержку целостности данных, вы принимаете решение: структура таблиц этой базы должна быть наилучшим образом нормализована. Нормализация, которая сама по себе является широким полем для исследования, — это способ задания такой структуры таблиц баз данных, в которой обновление данных не создавало бы аномалий. В каждой создаваемой таблице столбцы соответствуют атрибутам, которые тесно связаны друг с другом.
Вы, например, можете создать таблицу CUSTOMER (клиент), в которой имеются такие атрибуты: CUSTOMER.CustomerlD (идентификатор клиента), CUSTOMER.FirstName (имя), CUSTOMER.LastName (фамилия), CUSTOMER.Street (улица), CUSTOMER.Sity (город), CUSTOMER.State (штат), CUSTOMER.Zipcode (почтовый код) и CUSTOMER.Phone (телефон). Все эти атрибуты имеют отношение к описанию клиента, а не любого другого объекта. В них находится более-менее постоянная информация о клиентах вашей организации.
В большинстве систем управления базами данных таблицы этих баз можно создавать с помощью графических инструментов. Однако такие таблицы можно создавать и с помощью команды языка SQL. Вот, например, команда, при выполнении которой создается таблица CUSTOMER:
| CREATE TABLE CUSTOMER ( | ||
| CustomerlD | INTEGER | NOT NULL, |
| FirstName | CHARACTER (15), | |
| LastName | CHARACTER (20) | NOT NULL, |
| Street | CHARACTER (25), | |
| City | CHARACTER (20), | |
| State | CHARACTER (2), | |
| Zipcode | INTEGER | |
| Phone | CHARACTER (13) ) ; |
На Рисунок 3.1 показана часть таблицы CUSTOMER с теми данными, которые могут быть введены в нее.
Структура базы данных магазина спорттоваров
Рисунок 3.3. Структура базы данных магазина спорттоваров
Таблица CUSTOMER поддерживает отношение с таблицей INVOICE, используя их общий столбец CustomerlD. А отношение таблицы INVOICE с таблицей INVOICE_LINE поддерживается с помощью общего столбца InvoiceNumber. Отношение же таблицы PRODUCT с таблицей INVOICE_LINE поддерживается с помощью общего столбца ProductDD. В сущности эти отношения и делают саму базу реляционной, т.е. работающей на основе отношений.
Чтобы получить информацию о тех клиентах, которые купили лыжное оборудование, необходимы данные из следующих полей: FirstName, LastName, Street, City, State и Zipcode из таблицы CUSTOMER; Category — из таблицы PRODUCT; InvoiceNumber — из таблицы INVOICE, а также LineNumber— из таблицы INVOICE_LINE. Нужное представление можно создавать поэтапно, используя для этого следующие команды:
CREATE VIEW SKI_CUST1 AS
SELECT FirstName,
LastName, Street,
City,
State,
Zipcode,
InvoiceNumber
FROM CUSTOMER JOIN INVOICE
USING (CUSTOMER_ID) ;
CREATE VIEW SKI_CUST2 AS
SELECT FirstName,
LastName,
Street,
City,
State,
Zipcode,
ProductID
FROM SKI_CUST1 JOIN INVOICE_LINE
USING (InvoiceNumber) ;
CREATE VIEW SKI_CUST3 AS
SELECT FirstName,
LastName,
Street,
City,
State,
Zipcode,
Category
FROM SKI_CUST2 JOIN PRODUCT
USING (ProductID) ;
CREATE VIEW SKI_CUST AS
SELECT DISTINCT FirstName,
LastName,
Street,
City,
State,
Zipcode
FROM SKI_CUST3
WHERE CATEGORY = 'Ski' ;
Эти операторы CREATE VIEW соединяют данные из множества таблиц, используя для этого оператор JOIN. Диаграмма всего этого процесса показана на Рисунок 3.4.
SUM
SUMФункция SUM складывает значения из указанного столбца. Столбец должен иметь один из числовых типов данных, а значение суммы не должно выходить за пределы диапазона, предусмотренного для этого типа. Таким образом, если столбец имеет тип данных SMALLINT, то полученная сумма не должна превышать верхний предел, имеющийся у этого типа данных-В таблице INVOICE (счет-фактура) из базы данных, о которой уже говорилось в этой главе, хранятся данные по всем продажам. Чтобы найти общую сумму в долларах для всех продаж, иные которых хранятся в базе, используйте функцию SUM следующим образом:
CELECT SUM(TotalSale) FROM INVOICE;
базы данных магазина спорттоваров
Таблица 3.1. Таблицы базы данных магазина спорттоваров| Таблица | Столбец | Тип данных | Ограничение |
| CUSTOMER | CustomeriD (идентификационный номер клиента) | INTEGER | NOT NULL (не может быть неопределенным значением) |
| FirstName (имя) | CHARACTER (15) | ||
| LastName (фамилия) | CHARACTER (20) | NOT NULL | |
| Street (улица) | CHARACTER (25) | ||
| City (ГОРОД) | CHARACTER (20) | ||
| State (штат) | CHARACTER (2) | ||
| Zipcode (ПОЧТОВЫЙ КОД) | INTEGER | ||
| Phone (телефон) | CHARACTER (13) | ||
| PRODUCT | Product id (идентификационный номер товара) | INTEGER | NOT NULL |
| Name (название) | CHARACTER (25) | ||
| Description (описание) | CHARACTER (30) | ||
| Сategory (категория) | CHARACTER (15) | ||
| Vendor id (идентификационный номер поставщика) | INTEGER | ||
| VendorName (наименование поставщика) | CHARACTER (30) | ||
| INVOICE | InvoiceNumber (номер счета-фактуры) | INTEGER | NOT NULL |
| CustomeriD (идентификационный номер покупателя) | INTEGER | ||
| InvoiceDate (дата выписки счета-фактуры) | DATE | ||
| Totalsale (всего продано на сумму) | NUMERIC (9 ,2) | ||
| TotalRemitted (всего оплачено) | NUMERIC (9 ,2) | ||
| Formof Payment (форма платежа) | CHARACTER (10) | ||
| INVOICE_LINE | LineNumber (номер строки) | INTEGER | NOT NULL |
| InvoiceNumber (номер счета-фактуры) | INTEGER | ||
| Product id (идентификационный номер товара) | INTEGER | ||
| Quantity (количество) | INTEGER | ||
| SalePrice (продано по цене) | NUMERIC (9 ,2) |
Таблицы связываются друг с другом посредством общих столбцов. Ниже описаны связи между таблицами. (Отношения таблиц представлены на Рисунок 3.3.)
Примеры конкатенации строк
Таблица 3.2. Примеры конкатенации строк| Выражение | Результат |
| 'военная' | | 'разведка' | 'военная разведка' |
| 'абра' | | 'кадабра' | 'абракадабра' |
| CITY| | ' ' | |STATE| | ' '| |ZIP | Общая строка с названиями города, штата и с почтовым кодом, которые отделены друг от друга пробелами |
Операторы и предикаты сравнения
Таблица 3.3. Операторы и предикаты сравнения| Оператор | Сравнение | Выражение |
| = | Равно | CLASS = SENIOR |
| <> | Не равно | CLASS <> SENIOR |
| < | Меньше | CLASS < SENIOR |
| > | Больше | CLASS > SENIOR |
| <= | Меньше или равно | CLASS <= SENIOR |
| >= | Больше или равно | CLASS >= SENIOR |
Виды защиты
Таблица 3.4. Виды защиты| Действие по защите | Оператор |
| Позволяет просматривать таблицу | GRANT SELECT |
| Не позволяет просматривать таблицу | REVOKE SELECT |
| Позволяет вставлять строки в таблицу | GRANT INSERT |
| Не позволяет вставлять строки в таблицу | REVOKE INSERT |
| Позволяет менять значения в строках таблицы | GRANT UPDATE |
| Не позволяет менять значения в строках таблицы | REVOKE UPDATE |
| Позволяет удалять строки из таблицы | GRANT DELETE |
| Не позволяет удалять строки из таблицы | REVOKE DELETE |
| Позволяет ссылаться на таблицу | GRANT REFERENCES |
| Не позволяет ссылаться на таблицу | REVOKE REFERENCES |
| Позволяет использовать домен, набор символов, сопоставление или трансляцию | GRANT USAGE ON DOMAIN, REVOKE USAGE ON CHARACTER SET, REVOKE USAGE ON COLLATION, REVOKE USAGE ON TRANSLATION |
| Не позволяет использовать домен, набор сопоставление или трансляцию | REVOKE USAGE ON DOMAIN, REVOKE USAGE ON CHARACTER SET, REVOKE USAGE ON COLLATION, REVOKE USAGE ON TRANSLATION |
GRANT SELECT
ON CUSTOMER
TO SALES_MANAGER;
В этом случае один пользователь, менеджер по продажам, получает возможность пи сматривать таблицу CUSTOMER (клиент).
В следующем примере показана команда, благодаря которой каждый пользователь имеющий доступ к системе, получает возможность просматривать розничный прайс-лист:
GRANT SELECT
ON RETAIL_PRICE_LIST
TO PUBLIC;
Ниже приведен пример команды, которая дает возможность менеджеру по продажам видоизменять розничный прайс-лист. Менеджер по продажам может менять содержимое имеющихся строк, но добавлять или удалять строки он не может.
GRANT UPDATE
ON RETAIL_PRICE_LIST
TO SALES_MANAGER;
В следующем примере приведена команда, позволяющая менеджеру по продажам добавлять в розничный прайс-лист новые строки:
GRANT INSERT
ON RETAIL_PRICE_LIST
TO SALES_MANAGER;
А теперь благодаря команде из следующего примера менеджер по продажам может также удалять из таблицы ненужные строки:
GRANT DELETE
ON RETAIL_PRICE_LIST
TO SALES_MANAGER;
CUSTOMER которую можно
Рисунок 3.1. Таблица CUSTOMER, которую можно создать с помощью команды Create Table
Помни: Если используемая вами реализация SQL не полностью соответствует SQL.2003, то синтаксис, которым вам придется пользоваться, может и не совпадать с приведенным в этой книге. За специальной информацией обратитесь к документации к СУБД.
Представьте, что перед вами стоит задача создать базу данных для своей организации. Вдохновленные перспективой создать полезную, прекрасную и совершенно безупречную структуру, представляющую огромную важность для будущего вашей компании, вы садитесь за компьютер и начинаете вводить команды CREATE из языка SQL. Так?
Не совсем. На самом деле это — неверный подход. Когда вдохновение и энтузиазм не дожидались, пока будет проведено тщательное планирование, многие проекты по созданию баз данных с самого начала получались ущербными. Пусть вы уверены, что имеете в уме четкое представление о том, какая структура должна быть у базы данных. Однако даже в этом случае запишите все на бумаге, а лишь после этого прикасайтесь к клавиатуре. Ниже приведены процедуры, о которых не стоит забывать при планировании базы данных.
Транзакции
ТранзакцииБазы данных наиболее уязвимы именно тогда, когда в них вносят изменения. Изменения могут быть опасными даже для однопользовательских баз. Аппаратный или программный сбой, происшедший во время изменения, может застать базу данных в переходном состоянии — между состоянием в момент начала изменений и состоянием, которое было бы в момент завершения этих изменений.
С целью защиты базы данных язык SQL ограничивает операции, которые могут ее изменить, так что они выполняются только в пределах транзакций. Во время транзакции SQL записывает каждую операцию с данными в файл журнала. Если транзакцию, перед тем как она будет завершена оператором COMMIT, что-то прервет, то можно восстановить первоначальное состояние системы с помощью оператора ROLLBACK. Этот оператор обрабатывает журнал транзакций в обратном порядке, отменяя все действия, имевшие место во время транзакции. Выполнив откат базы данных до состояния, в котором она была перед началом транзакции, можно выяснить, что вызвало неполадки, а затем попробовать еще раз выполнить транзакцию.
Повреждения базы данных или некорректные результаты возможны в многопользовательской системе даже тогда, когда нет никаких аппаратных или программных отказов. Серьезные неприятности могут быть вызваны совместными действиями двух или нескольких пользователей но отношению к одной и той же таблице, т.е. имеющих к ней доступ в одно и то же время. VL решает эту проблему, допуская внесение изменений только в пределах транзакций.
Совет 2
Совет 2
База данных может пострадать из-за сбоев в аппаратном или программном обеспечении. Современные СУБД стараются свести подобную возможность к минимуму. Для этого они все операции с базой данных выполняют в пределах транзакций. Выполнив операции, находящиеся в транзакции, СУБД завершают транзакци одним оператором COMMIT. В современных системах управления базами данных также ведутся журналы транзакций. Это делается для того, чтобы в случае неприятностей с аппаратным обеспечением, программами или персоналом гарантировать защиту данных. После завершения транзакции данные защищены от всех системных отказов, если только не считать самых катастрофических; в случае ее неудачи го проведения возможен откат транзакции назад к ее начальной фазе и — поел устранения причин неполадок — повторное выполнение этой транзакции.
Поместив все операции, воздействующие на базу данных, в транзакции, вы можете изолировать действия одного пользователя от действий другого. Если необходима уверенность, что результаты, получаемые из базы данных, являются точными, то такая изоляция очень важна.
Технические подробности: Возможно, вы удивитесь, что совместные действия двух пользователей могут при. вести к некорректным результатам. Скажем, Донна читает запись какой-либо таблицы из базы данных. Через мгновение Дэвид меняет в той записи значение числового поля. А теперь в то же иоле Донна записывает число, полученное на основе значения, прочитанного ею вначале. И так как она не знает об изменении, сделанном Дэвидом, то ее операция является некорректной.
Другая неприятность может произойти, когда Донна вносит в запись какие-то значения, а Дэвид затем эту запись читает. И если Донна проводит откат своей транзакции, то Дэвид не знает об этой операции и выполняет свои действия на основе прочитанного им значения, которое не совпадает со значением, имеющимся в базе после отката. То, что смешно в кинокомедии, не всегда приятно в реальной жизни.
В некоторых реализациях SQL вместо
ВниманиеВ некоторых реализациях SQL вместо || в качестве оператора конкатенации используют +. Есть реализации, в которых вместо конкатенации используются строковые операторы, но стандарт SQL:2OO3 эти операторы не поддерживает.
Внимание
В последнем примере только первые два выражения имеют смысл (CLASS = SENIOR и CLASS <> SENIOR). Это объясняется тем, что SOPHOMORE считается больше, чем SENIOR, потому что в последовательности сопоставления, установленной по умолчанию (т.е. когда сортировка выполняется по алфавиту), SO следует после SE. Однако такая интерпретация, по всей вероятности, — не то, что вам нужно.
Внимание
Крайняя доверчивость означает и крайнюю уязвимость. Будьте чрезвычайно осторожны, используя подобные операторы: GRANT ALL PRIVILEGES
ON FOUR_STAR
TO BENEDICT_ARNOLD WITH GRANT OPTION
Здесь пользователь В ENEDICT_ ARNOLD получает все полномочия FOUR_STAR с возможностью передачи этих полномочий другим лицам.
Выражения с числовыми значениями
Выражения с числовыми значениямиЧтобы комбинировать числовые значения, используйте операторы сложения (+), вычитания (-), умножения (*) и деления (/). В следующих строках приведено несколько примеров выражений с числовыми значениями:
12 - 7
15/3 - 4
6 * (8+2)
Значения из этих примеров являются числовыми литералами. Значениями могут быть также имена столбцов, параметры, базовые переменные или подзапросы — при условии, что их значения являются числовыми. Вот несколько примеров:
SUBTOTAL + TAX + SHIPPING
6 * MILES/HOURS
:months/12
Двоеточие в последнем примере говорит о том, что следующий за ним терм (months — месяцы) является или параметром, или базовой переменной.
Выражения с логическими значениями
Выражения с логическими значениямиВыражение с логическими, или булевыми, значениями проверяет, является ли значение предиката истинным. Примером такого выражения может быть
(CLASS = SENIOR) IS TRUE
Если это условие выбора строк из таблицы со списком учеников-старшеклассников, то будут выбраны только записи, соответствующие ученикам выпускных классов. А чтобы выбрать записи учеников других классов, можно использовать следующее выражение:
NOT (CLASS = SENIOR) IS TRUE
To же самое условие можно выразить и по-другому:
(CLASS = SENIOR) IS FALSE
Чтобы получить все строки, имеющие в столбце CLASS неопределенное значение, используйте
(CLASS = SENIOR) IS UNKNOWN
Выражения со строковыми значениями
Выражения со строковыми значениямиВ выражениях со строковыми значениями может находиться оператор конкатенации (||). С его помощью, как показано в табл. 3.2, две текстовые строки объединяются в одну.
Выражения со значениями датывремени
Выражения со значениями даты-времени и интервальными значениямиВыражения со значениями даты-времени, оказывается, работают (кто бы мог подумать!) со значениями даты и времени. В таких выражениях могут появляться данные типа DATE, TIME, TIMESTAMP и INTERVAL. Результатом выполнения выражений со значениями даты-времени всегда является другое значение даты-времени. К значению этого типа можно прибавить (или отнять от него) какой-либо интервал, а также задать часовой пояс.
Вот пример выражения со значениями даты-времени (название DUE_DATE означает "срок возврата", a INTERVAL 7' DAY — "интервал в 7 дней"):
DUE_DATE + INTERVAL '7' DAY
Такое выражение можно использовать в библиотечной базе данных, чтобы определять, когда отправить напоминание "должникам". А вот новый пример, где, правда, указывается не дата, а время:
TIME '18:55:48' AT LOCAL
Ключевые слова AT LOCAL означают, что указано местное время.
Выражения со значениями интервалов работают с промежутками времени между значениями даты-времени. Имеются два вида интервалов: год-месяц и день-время. Их нельзя использовать в одном и том же выражении.
Приведем пример использования интервалов. Скажем, кто-то возвращает в библиотеку книгу после истечения крайнего срока. Тогда, используя выражение со значениями интервалов, такое, например, как приведено в следующем примере, можно вычислить, сколько прошло дней задержки после крайнего срока, и соответственно назначить пеню (названия DATE_RETURNED и DATE_DLIE означают соответственно "дата возврата" и "крайний срок"):
(DATE_RETURNED-DATE_DUE) DAY
Так как интервал может быть типа год-месяц либо день-время, то следует указать, какой из них использовать. В последнем примере с помощью ключевого слова DAY (день) выбран второй.
Выражения со значениями определяемыми пользователем
Выражения со значениями, определяемыми пользователемО типах, определяемых пользователями, см. в главе 2. Благодаря такой возможности пользователи могут определять собственные типы данных, а не довольствоваться теми, которые есть на "складе" SQL. Если есть выражение, имеющее элементы данных какого-либо типа, определяемого пользователем, то значением этого выражения должен быть элемент того же типа.
Выражения со значениями типа коллекции
Выражения со значениями типа коллекцииЗначением выражения типа коллекции является массив.
Выражения со значениями типа ссылки
Выражения со значениями типа ссылкиЗначением выражения типа ссылки является ссылка на некоторый другой компонент базы данных, например столбец таблицы.
Выражения со значениями типа записи
Выражения со значениями типа записиВыражение со значениями типа записи, как это ни удивительно, определяет значение типа записи. Значение типа записи может состоять из одного выражения с каким-либо значением либо из множества таких выражений. Например:
('Джозеф Тайкосинер', 'заслуженный профессор в отставке', 1918)
Это строка таблицы сотрудников факультета, содержащей поля имени, фамилии, статуса и года начала работы на факультете.
Выражения со значениями
Выражения со значениямиЧтобы комбинировать два или несколько значений, можно использовать выражения со значениями. В соответствии с разными типами данных имеется девять видов таких выражений:
Заказ по каталогу
Заказ по каталогуДля по-настоящему больших баз данных даже множества схем может оказаться недостаточно. В больших распределенных средах таких баз дублирование встречается даже в именах схем. Чтобы этого не было, в SQL предусмотрен еще один уровень контейнерной иерархии — каталог. Каталог — это набор схем, имеющий свое специальное имя.
При указании имени таблицы можно также использовать имена ее каталога и схемы. Таким образом, гарантируется, что никто не перепутает две таблицы с одним и тем же именем, находящиеся в схемах с одинаковым именем. Имя таблицы с указанием каталога имеет следующий формат: ИМЯ_КАТАЛОГА.ИМЯ_СХЕМЫ.ИМЯ_ТАБЛИЦЫ.
Наивысшим уровнем контейнерной иерархии базы данных являются кластеры. Впрочем, редко в какой системе надо строить полную контейнерную иерархию. В большинстве случаев можно вполне ограничиться каталогами. В каталогах находятся схемы, в схемах — таблицы и представления, а в таблицах и представлениях — столбцы и строки.
В каталоге также находится информационная схема. В этой схеме находятся системные таблицы, а в них хранятся метаданные, относящиеся к другим схемам. В главе 1 база данных была определена как самоописательное собрание интегрированных записей. Метаданные в системных таблицах как раз и делают базу данных самоописательной.
Каталоги можно различать по именам. Поэтому в базе данных можно иметь множество каталогов. В каждом каталоге, в свою очередь, может быть множество схем, а в каждой схеме — таблиц. И, конечно же, в каждой таблице может быть множество столбцов и строк. Взаимоотношения внутри иерархии базы данных показаны на Рисунок 3.5.
Знакомство с командами DDL
Знакомство с командами DDLЯзык определения данных (DDL) работает со структурой базы данных, в то время как язык манипулирования (он будет описан позже) — с данными, которые находятся в этой структуре. DDL состоит из следующих трех команд.
Чтобы удалить таблицу выберите
Рисунок 4.12. Чтобы удалить таблицу, выберите ее имя и щелкните на пиктограмме
Приложение Access выдаст сообщение с вопросом, действительно ли вы хотите удалить выбранную таблицу. После подтверждения таблица будет немедленно удалена.
Помни: Если Access удаляет таблицу, то также удаляются все связанные с ней таблицы и все ее индексы.
Диалоговое окно Индексы
Рисунок 4.10. Диалоговое окно Индексы
Совет 2
Совет 2
Access автоматически создает индекс для поля PostalCode, поскольку это поле часто используется для поиска данных.
Вы можете заметить, что, в отличие от ProposalNumber, поле PostalCode может не являться первичным ключом, и его значения не обязательно уникальны. Можно создать индексы для полей LastName и HowKnown, так как они, вероятно, также будут использоваться для доступа к данным. На Рисунок 4.11 изображено, как выглядят эти новые индексы.
Диалоговое окно определения первичного ключа
Рисунок 4.6. Диалоговое окно определения первичного ключа
Сохранив таблицу, вы, возможно, решите, что первоначальный замысел нуждается в улучшении. (Об этом вы прочитаете в разделе "Изменение структуры таблицы".) Со своими заманчивыми предложениями к вам подходило столько людей, и в результате выяснилось, что некоторые из этих ребят одновременно и тезки и однофамильцы. Чтобы не было путаницы, вы решили к каждой записи из таблицы базы данных добавить уникальный номер предложения. Таким образом, вы сможете отличить одного Дэвида Ли от другого.
Диалоговое окно создания таблицы
Рисунок 4.2. Диалоговое окно создания таблицы
Рисунок 4.4. Диалоговое окно создания таблицы со всеми определенными полями

Использование языка SQL с приложением Microsoft Access
Использование языка SQL с приложением Microsoft AccessПриложение Access было разработано как инструмент быстрой разработки приложений (RAD), не требующий программирования. Несмотря на то что можно писать и выполнять команды на языке SQL непосредственно в Access, чтобы сделать это, можно зайти также с "черного хода". Для того чтобы открыть основной редактор, который используется для ввода SQL-кода, выполните следующие действия.
Изменение структуры таблицы
Изменение структуры таблицыКак правило, созданные вами таблицы не получатся с первого раза такими, как надо. Если вы работаете для кого-то, то будьте уверены — ваш клиент ждет, пока вы наконец-то создадите базу данных, чтобы кое-что вам сообщить. И тут вы узнаете, что руководство желает, чтобы вводилась информация еще по одному виду данных, а возможно, даже по нескольким.
Если вы создаете базу данных для себя, то недостатки в ее структуре, которых совсем не было видно до реального создания базы, уже после создания этой структуры неизбежно всплывут на поверхность. Возможно, что к вам, например, начинают поступать предложения не только из США. Тогда нужно добавить столбец Country (страна). Или вы решили, что будет полезен еще и адрес электронной почты. В любом случае придется вернуться назад и переделать то, что вы создали. Такая возможность— переделывать уже созданное— имеется во всех RAD-инструментах. Чтобы показать типичный пример, я, используя Access, внесу некоторые изменения в созданную мной таблицу. Другие инструменты работают подобным образом.
Предположим, нужно добавить уникальный номер предложения, чтобы можно было различать предложения от разных людей с одинаковыми именами и фамилиями. Если уж на то пошло, то можно также добавить еще два поля. Это, во-первых, еще одно поле Address2 (адрес 2), которое пригодится для тех, у кого несколько адресов, и, во-вторых, поле Country — для предложений из других стран.
Чтобы вставить новую строку и внести изменения, соответствующие требованиям, выполните следующие действия.
Изменение структуры таблицы
Для изменения структуры существующей таблицы можно использовать команду SQL ALTER TABLE (изменить таблицу). Интерактивные средства SQL, находящиеся на вашей клиентской станции, не такие удобные, как RAD-инструмент. Этот инструмент показывает табличную структуру, которую затем можно изменить. А используя SQL, необходимо заранее знать и структуру таблицы, и то, каким образом эту структуру следует изменять. В том месте экрана, где находится приглашение, необходимо для внесения изменения ввести соответствующую команду. Впрочем, если в прикладной программе нужно поместить операторы изменения таблицы, то обычно самый легкий способ это сделать — все-таки использовать SQL.
Чтобы добавить в таблицу PowerSQL второе поле для адреса, используйте следующую команду DDL:
ALTER TABLE PowerSQL
ADD COLUMN Address__2 CHAR (30);
Чтобы расшифровать этот код, не нужно быть гуру SQL. В действительности это может сделать даже профан со слабыми познаниями в английском. Эта команда изменяет таблицу с названием PowerSQL, добавляя в нее новый столбец, который называется Address2, имеет тип данных CHAR и длину 30 символов. Приведенный пример показывает, насколько легко менять структуру таблиц в базе данных, используя для этого команды DDL из SQL.
Стандарт SQL:2003 разрешает использовать этот оператор для добавления в таблицу столбца, а также удаления имеющегося столбца, как показано в следующем примере:
ALTER TABLE PowerSQL
DROP COLUMN Address2;
Экран появившийся при создании
Рисунок 4.1. Экран, появившийся при создании новой базы данных в Microsoft Access
Экран проекта дает полный контроль
Экран проекта дает полный контрольЭкран, представленный на Рисунок 4.1, содержит намного больше информации, чем выводили программы СУБД предыдущих поколений. В 1980-е годы общение с типичной системой СУБД начиналось с пустого экрана, оттеняемого подсказкой из одного символа. С тех пор управление базами данных прошло долгий путь, и теперь намного легче узнать о том, с чего надо начинать. В правой части рабочего пространства находится область задач Приступая к работе с несколькими разделами.
После выполнения всех действий отобразится окно POWER : база данных.
Дважды щелкните на опции, чтобы появилось окно для создания таблицы (Рисунок 4.2).
Определение индексов для полей LastName и HowKnom
Рисунок 4.11. Определение индексов для полей LastName и HowKnom
Создав все нужные вам индексы, можно сохранить новую табличную структуру, выбрав команду Файл - Сохранить или щелкнув на пиктограмме с изображением дискеты.
Совет 3
Совет 3
Конечно, если вы применяете не Microsoft Access, то все, что говорилось в этом разделе, к вам не относится. Впрочем, при создании таблицы базы данных и ее индексов с помощью другого, отличного от Access, RAD-инструмента выполняется примерно та же последовательность действий.
Определение первичного ключа
Определение первичного ключаПервичный ключ таблицы — это поле, которое однозначно определяет каждую строку.
Поле ProposalNumber (номер предложения) является хорошим кандидатом на роль первичного ключа таблицы PowerDesign, потому что значения этого поля однозначно определены для каждой строки таблицы. Это единственное поле, относительно которого вы можете быть уверены, что оно нигде в таблице не дублируется. Для того чтобы назначить это поле первичным ключом таблицы, поместите курсор в строке ProposalNumber, находящейся в диалоговом окне определения данных, а затем щелкните на пиктограмме Первичный ключ, которая расположена в центре панели инструментов Проектирование таблиц. В результате в самом левом столбце диалогового окна определения таблицы появится пиктограмма ключа. Это означает, что поле ProposalNumber теперь является первичным ключом таблицы PowerDesign. На Рисунок 4.9 показано, как выглядит окно определения таблицы после объявления первичного ключа.
Переделанное определение таблицы
Рисунок 4.8. Переделанное определение таблицы должно выглядеть примерно так
Переносимость
ПереносимостьВ любой из реализаций SQL, которую вы используете, могут быть расширения, дающие ей возможности, не предусмотренные стандартом SQL:2OO3. Одни из этих возможностей, скорее всего, появятся в следующем выпуске спецификации SQL. Другие же характерны только для конкретной реализации и, вероятно, никогда не войдут в стандарт.
Часто эти расширения могут упростить создание нужного вам приложения, и у вас возникнет искушение ими воспользоваться. Это тоже вариант, но учтите — не исключено, что в таком случае придется чем-то пожертвовать. Если когда-нибудь потребуется перенести ваше приложение на другую реализацию SQL, то вам, возможно, придется переписывать те части этого приложения, в которых используются расширения, не поддерживаемые в новой среде. Подумайте о возможности такого переноса, который может произойти когда-нибудь в будущем, а также о том, является ли используемое вами расширение уникальным или все-таки достаточно распространенным. При долгосрочном использовании приложения, наверное, будет лучше не пользоваться нестандартным расширением, даже если в результате получается некоторая экономия времени. С другой стороны, подобное решение может быть излишней перестраховкой. Тщательно обдумайте каждую из этих возможностей. Чем больше вам известно об имеющихся реализациях и о тенденциях, связанных с их разработкой, тем лучшими могут быть ваши решения.
Поле ProposalNumber объявлено первичным ключом
Рисунок 4.9. Поле ProposalNumber объявлено первичным ключом
Правдоподобный сценарий
Правдоподобный сценарийПервый ваш шаг по созданию базы данных — решить, какую информацию следует в нее заносить. Вот вам правдоподобный пример. Представьте, что вы только что выиграли 101 миллион долларов в лотерее Пауэрболл. (В реальной жизни в вас чаще попадет молния или метеорит.) И гут, откуда ни возьмись, начинают появляться люди, о которых вы не слышали годами, и даже друзья, о которых вы уже забыли. У некоторых из них имеются безошибочные и беспроигрышные деловые предложения, в которые требуются ваши инвестиции. У других есть достойные инициативы, которые могли бы выиграть от вашей поддержки. Как хороший распорядитель своего нового богатства вы понимаете, что не все деловые предложения одинаково хороши. Поэтому, чтобы не упустить ни одной из возможностей и сделать справедливый и беспристрастный выбор, вы принимаете решение — поместить все предложения в базу данных.
Вы решили, что в базу по каждому предложению будут заноситься данные таких видов.
Создание индекса
Создание индексаТак как количество получаемых вами предложений об инвестициях и благотворительности может легко дойти до нескольких тысяч, то нужен способ, с помощью которого можно быстро выбрать интересующие вас записи. Вы сможете выполнить эту задачу самыми разными способами. Например, просмотреть все предложения, сделанные вашими братьями. Эти предложения можно эффективно выбрать, если использовать содержимое поля LastName (фамилия), как показано в следующем примере:
SELECT * FROM PowerDesign
WHERE LastName = 'Marx' ;
Впрочем, такая стратегия не работает для предложений, сделанных всеми вашими шуринами, деверями, свояками (по-английски любой из них называется одинаково — "brower-in-law". — Примеч. пер.), но эти предложения можно получить, используя другое поле, HowKnown (кто таков), как показано в следующем примере:
SELECT * FROM PowerDesign
WHERE HowKnown = 'brother-in-law' ;
Что ж, запросы эти работают, но они, возможно, не будут работать очень быстро, если таблица PowerDesign достаточно большая (десятки тысяч записей). SQL перебирает всю таблицу построчно, отыскивая значения, которые удовлетворяют предложению WHERE. Работу можно значительно ускорить, применяя в таблице PowerDesign индексы. (Индекс — это таблица с указателями. Каждая строка в индексе указывает на соответствующую строку в таблице с данными.)
Для каждого из способов, которые требуются для доступа к вашим данным, можно определять свой индекс. И если вы в таблице с данными добавляете, изменяете или удаляете строки, то снова сортировать эту таблицу не нужно — следует только обновить ее индексы.
А индекс можно обновить намного быстрее, чем сортировать целую таблицу. Создав индекс, строки которого выстроены в нужном порядке, вы с помощью этого индекса можете почти мгновенно переходить к нужным строкам таблицы данных.
Совет 1
Совет 1
Так как поле ProposalNumber (номер предложения) одновременно и уникальное, и короткое, то с его помощью можно быстрее всего добраться к отдельной записи. Поэтому первичный ключ любой таблицы всегда должен быть индексирован. В Access это делается автоматически. Однако, чтобы использовать это поле, необходимо знать его значение в нужной вам записи. Вам могут потребоваться и дополнительные индексы, создаваемые на основе других полей, таких как LastName (фамилия), PostalCode (почтовый код) или HowKnown (кто таков). Если в таблице данных, проиндексированной по LastName, в результате поиска будет найдена первая строка, где значением этого поля является Marx, то будут найдены и все строки с таким же значением этого поля. В индексе ключи для всех этих строк идут друг за другом. Поэтому все строки, относящиеся к Chico, Groucho, Harpo, Zeppo и Karl, можно получить почти так же быстро, как и для одного только Chico.
В результате создания индекса возникает дополнительная нагрузка на вашу систему, от чего ее работа будет немного замедляться. Это замедление необходимо сравнивать с увеличением скорости доступа к записям в результате использования индекса. Если индексировать поля, часто используемые для доступа к записям из большой таблицы, то замедление работы оправдано. Однако если создавать индексы для полей, которые никогда не будут использоваться для доступа к записям, то потери времени во много раз превысят его экономию. Также не имеет смысла создавать индексы для полей, которые не позволяют отличить одну запись от другой. Например, поле BusiOrCharity (бизнес или благотворительность) просто разбивает все записи в таблице только на две категории, поэтому хороший индекс на основе этого поля создать нельзя.
Помни: Эффективность индекса в разных реализациях бывает разной. Если перенести базу данных с одной платформы на другую, то индексы, лучше всех работавшие в первой системе, могут плохо работать во второй. Случается, что база данных хуже работает с индексами, чем при их полном отсутствии. Индексы приходится заново настраивать для каждой конкретной конфигурации СУБД и аппаратного обеспечения. Какая из разных схем индексирования в общем работает лучше остальных, приходится определять опытным путем, принимая во внимание в каждом случае скорость получения данных и их обновления.
Чтобы создать индексы для таблицы PowerDesign, щелкните на пиктограмме Индексы, расположенной справа от пиктограммы Первичный ключ на панели инструментов Конструктор таблиц. Появится диалоговое окно Индексы, в котором уже есть поля Postal-Code и ProposalNumber. Диалоговое окно Индексы представлено на Рисунок 4.10.
Создание индекса
Индексы — очень важная часть любой реляционной базы данных. Они служат указателями в тех таблицах, в которых содержатся нужные данные. С помощью индекса можно прямо перейти к определенной записи, не выполняя для ее поиска последовательного, запись за записью, просмотра таблицы. Для больших таблиц индексы просто необходимы. Без индексов результат из действительно очень большой таблицы придется, возможно, ждать не секунды, а годы. (Ладно, предположим, что вам не придется ждать годами. Однако некоторые операции выборки данных, если их запустить на выполнение, могут действительно продолжаться достаточно долго. И если у вас в запасе нет ничего лучшего, что могло бы ускорить работу, то вы, вероятно, прервете операцию, не получив никаких результатов. Ведь жизнь все равно продолжается.)
Удивительно, но в спецификации SQL:2003 нет средств для создания индекса. У поставщиков СУБД должны быть собственные реализации индексов. А так как эти реализации не стандартизированы, то вполне могут отличаться друг от друга. Большинство поставщиков реализует средство создания индекса, расширяя SQL оператором CREATE INDEX (создать индекс). Но даже если в двух реализациях используются одни и те же операторы, то способы их выполнения могут быть разными. В различных реализациях используются разные варианты синтаксиса этой команды. Необходимо внимательно изучать документацию по имеющимся у вас СУБД, чтобы знать, как можно создавать индексы с помощью этих систем.
Создание простой базы данных с помощью RADинструмента
Создание простой базы данных с помощью RAD-инструментаЛюди пользуются базами данных потому, что им нужно сохранять важную информацию. Иногда такая информация является простой, а иногда — нет. Но в любом случае хорошая система управления базами данных должна предоставить ту информацию, которая вам нужна. В некоторых СУБД можно использовать только SQL. А в других, которые называются RAD-инструментами, имеется объектно-ориентированная графическая среда.
Также имеются СУБД, поддерживающие оба этих подхода. В следующих разделах с помощью графического инструмента, предназначенного для проектирования баз данных, будет создана простая база, состоящая из одной таблицы. Это делается для того, чтобы вы могли увидеть, из каких операций состоит изучаемый процесс. Хотя я буду использовать Microsoft Access, но и в других средах разработки, работающих в Windows, процедура создания базы данных почти такая же.
Создание таблицы Power Design с помощью DDL
Создание таблицы Power Design с помощью DDLВсе действия по определению базы данных, которые можно выполнять с помощью RAD-инструмента, такого как Access, можно также выполнять и с помощью SQL. В этом случае вместо щелчков мышью на элементах меню выполняется ввод команд с помощью клавиатуры. Те, кто предпочитает манипулировать графическими объектами, считают, что RAD-инструменты являются легкими и естественными для понимания и изучения. Другие же, кому больше нравится складывать слова в предложения, имеющие определенную логику, считают, что более легкими и естественными являются все-таки команды SQL. Так как некоторые вещи легко представить, используя объектную парадигму, а с другими легко справляться с помощью SQL, то полезно хорошо знать оба метода.
В следующих разделах будет применяться SQL, чтобы выполнять те же действия по созданию, изменению и удалению таблицы, для которых в предыдущем разделе использовался RAD-инструмент.
Создание таблицы
Создание таблицыПри работе с полнофункциональной СУБД, например, такой, как Microsoft SQL Server, Oracle 9i или IBM DB2, в процессе создания таблицы с помощью SQL необходимо вводить ту же информацию, что и при создании таблицы с помощью какого-либо RAD-инструмента. Разница здесь в том, что RAD-инструмент помогает это делать, предоставив в ваше распоряжение диалоговое окно создания таблицы (или какую-либо подобную структуру) и не позволяя вводить неправильные имена полей, типы или размеры. SQL столько внимания вам не уделит. Работая с SQL, следует точно знать с самого начала, что именно надо делать. Необходимо ввести целый оператор CREATE TABLE, прежде чем SQL обратит на него внимание, не говоря уже о том, чтобы сообщить, нет ли в операторе каких-либо ошибок.
Следующая команда создает таблицу, идентичную созданной ранее:
| CREATE TABLE PowerSQL ( | |
| ProposalNumber | SMALL INT |
| FirstName | CHAR (15), |
| LastName | CHAR (20), |
| Address | CHAR (30), |
| City | CHAR (25), |
| StateProvince | CHAR (2), |
| PostalCode | CHAR (10), |
| Country | CHAR (30), |
| Phone | CHAR (14), |
| HowKnown | CHAR (30), |
| Proposal | CHAR (50), |
| BusinOrCharity | CHAR (1) ; |
Помни: Любые усилия, вложенные в изучение SQL, будут оправдываться в течение долгого времени, потому что быстро сходить со сцены этот язык не собирается. А усилия, вложенные в то, чтобы стать экспертом в среде разработки, вероятно, принесут меньшую отдачу. И каким бы прекрасным ни был последний RAD-инструмент, будьте уверены — в течение двух-трех лет его заменит более совершенная технология. Замечательно, если за это время вы сможете возместить усилия, вложенные в изучение данного инструмента! Если сможете, то пользуйтесь им. А если не сможете, то будет мудрее придерживаться старого и испытанного средства. Знание SQL намного дольше будет приносить дивиденды.
Текстовое поле Имя таблицы в диалоговом окне Сохранить как
Рисунок 4.5. Текстовое поле Имя таблицы в диалоговом окне Сохранить как
Удаление индекса
Удаление индексаУдаление таблицы
Удаление таблицыПеред тем как таблица PowerDesign приобрела нужную вам структуру, вы, возможно, успели создать несколько промежуточных вариантов этой таблицы, которые не являются окончательными. Присутствие в системе этих вариантов может впоследствии запутывать пользователей. Поэтому, пока вы еще помните, что к чему, лучше всего эти таблицы удалить. Для удаления таблицы выберите ее имя в окне базы данных и щелкните на пиктограмме (Рисунок 4.12).
Удаление таблицы
Таблицу, которая вам не нужна и только зря занимает место на диске, удалить достаточно легко. Всего лишь воспользуйтесь командой DROP TABLE (удалить таблицу), такой, например, как следующая:
DROP TABLE PowerSQL;
Что может быть проще? Если такой командой удалить таблицу, то будут удалены все ее данные и метаданные. От таблицы не останется и следа.
Удаление таблицы
Удаление таблицыТаблицу, которая вам не нужна и только зря занимает место на диске, удалить достаточно легко. Всего лишь воспользуйтесь командой DROP TABLE (удалить таблицу), такой, например, как следующая:
DROP TABLE PowerSQL;
Что может быть проще? Если такой командой удалить таблицу, то будут удалены все ее данные и метаданные. От таблицы не останется и следа.
В определение таблицы PowerDesign вставлена новая строка
Рисунок 4.7. В определение таблицы PowerDesign вставлена новая строка
Там, где находится курсор, появится новая незаполненная строка, а все остальные строки сдвинутся вниз.
При попытке сохранить новую таблицу
ВниманиеПри попытке сохранить новую таблицу появляется еще одно диалоговое окно (Рисунок 4.6). В этом окне сообщается, что вы не определили первичный ключ, и задается вопрос, не хотите ли вы сделать это сейчас. О первичных ключах мы поговорим чуть позднее, в разделе "Определение первичного ключа", а сейчас щелкните на кнопке Нет. Первичный ключ сейчас определять не надо, поэтому не задерживайтесь на нем. Сейчас ваша задача — сохранить таблицу.
Внимание
Удаляя таблицу с помощью команды DROP TABLE, вы одновременно удаляете и все относящиеся к ней индексы. Впрочем, иногда требуется оставить таблицу, но удалить один из ее индексов. Стандарт SQL:2003 не определяет команду DROP INDEX (удалить индекс), но в большинстве реализаций она все-таки есть. Эта команда пригодится тогда, когда ваша система замедлит свою работу до черепашьей скорости и обнаружится, что таблицы в ней индексированы не лучшим образом. Исправление индексов должно привести к резкому увеличению производительности системы. Это, правда, может опечалить пользователей, привыкших в ожидании своих результатов устраивать перекур.
Значения по умолчанию для свойств
Рисунок 4.3. Значения по умолчанию для свойств поля FirstName, показанные в диалоговом окне создания таблицы
Технические подробности: В Access вместо названия столбец используется название поле. Первоначальные системы обработки файлов не были реляционными, и в них использовались термины "файл", "поле" и "запись", которые характерны для систем плоских файлов.
Не исключено, что вы захотите оставить или соответствующим образом изменить имеющиеся значения. Например, для создаваемого вами поля FirstName (имя) значение свойства Размер поля по умолчанию составляет 50 символов. Возможно, это значение больше, чем нужно. И если для этого свойства задать, например, значение 15 символов, то будет сэкономлен определенный объем дискового пространства. На Рисунок 4.4 приведено диалоговое окно создания таблицы после того, как введены значения свойств для всех полей.
Что такое индекс
Что такое индексДанные в таблице обычно отображаются в том порядке, в каком их в нее первоначально ввели. Однако такой порядок может не иметь ничего общего с тем порядком, в котором затем требуется эти данные обрабатывать. Скажем, что вы, например, хотите обрабатывать таблицу CLIENT в такой последовательности, чтобы значения в столбце ClientName располагались в алфавитном порядке. Но на такую сортировку записей таблицы требуется определенное время. И чем таблица больше, тем сортировка проходит дольше. Что если в вашей таблице сто тысяч записей? Или миллион? А ведь в некоторых приложениях таблицы такого размера не являются редкостью. Даже при выполнении лучших алгоритмов сортировки, чтобы выстроить записи таблицы в нужном порядке, все равно придется в таком случае проделать примерно двадцать миллионов сравнений и миллионы перестановок. И пусть у вас очень быстрый компьютер, но подождать все-таки придется.
Индексы могут оказаться прекрасным средством экономии времени. Индекс — это подчиненная таблица, или таблица поддержки, которая сопровождает свою таблицу данных.
Каждой строке таблицы данных соответствует определенная строка таблицы индекса. Однако порядок расположения строк в таблице индекса другой.
Небольшой пример таблицы данных показан в табл. 5.2.
Действие 1 определение объектов
Действие 1: определение объектовПервое действие, которое предстоит выполнить при проектировании базы данных, — это решить, какие аспекты системы являются достаточно важными, чтобы быть включенными в модель. Каждый такой аспект рассматривайте как объект и составьте список этих объектов — всех, которые только придут вам в голову. И не пытайтесь гадать, каким образом эти объекты связаны друг с другом. Всего лишь попробуйте внести их в список.
Совет 1
Совет 1
Было бы полезно иметь команду из нескольких человек, знакомых с системой, которую вы моделируете. Они могли бы проводить "мозговой штурм" и реагировать на высказанные друг другом идеи. Вместе вы, вероятно, разработаете более полный и точный набор объектов.
Когда вы решите, что получился достаточно полный набор объектов, можете приступать к следующему действию: определить, каким образом эти объекты связаны друг с другом. Некоторые из них являются главными и играют важную роль в получении нужных вам результатов. Другие же являются подчиненными объектами по отношению к главным. Кроме того, следует окончательно решить, какие объекты вообще не являются частью модели.
Действие 2 определение таблиц и столбцов
Действие 2: определение таблиц и столбцовГлавные объекты переводятся в таблицы базы данных. И у каждого из главных объектов имеется набор связанных между собой атрибутов, которые переводятся в столбцы соответствующей таблицы. Например, во многих базах данных, применяемых в бизнесе, имеется таблица CUSTOMER (клиент), в которой хранятся имена клиентов, адреса и другая информация о них. Каждый из атрибутов клиента, такой, например, как имя, улица, город, штат, почтовый код, номер телефона и адрес в Internet, становится столбцом в таблице CUSTOMER.
Имеются достаточно легкие правила, позволяющие быстро определить, что должно стать таблицами и какие из атрибутов системы должны принадлежать каждой такой таблице. Возможно, у вас есть причины, чтобы присвоить какой-либо атрибут одной таблице, но также есть причины, чтобы присвоить его другой таблице. Вам следует принять решение на основе той информации, которую нужно получать из базы данных, и того, каким образом должна использоваться эта информация.
Действие 3 точное определение таблиц
Действие 3: точное определение таблицТеперь для каждого объекта вам необходимо точно определить таблицу, а для каждого атрибута — столбец. В табл. 5.1 показаны таблицы базы данных VetLab.
Доменная целостность
Доменная целостностьОбычно вы не можете гарантировать, что конкретный элемент данных из базы правильно введен, но можете хотя бы определить, разрешено ли его использование. У многих элементов данных набор возможных значений является ограниченным. Если вводится значение, которое не входит в этот набор, то такой ввод должен считаться ошибочным. Например, Соединенные Штаты состоят из 50 штатов, округа Колумбия, Пуэрто-Рико и еще нескольких владений. У каждой из этих территорий имеется код, состоящий из двух символов и признанный почтовой службой США. И если в базе данных имеется столбец State (штат), то доменную целостность можно обеспечить, требуя, чтобы любой ввод в этот столбец был одним из разрешенных двух-символьных кодов. Если оператор вводит код, не входящий в список принятых кодов, он тем самым нарушает доменную целостность. Проверяя соблюдение доменной целостности, вы можете отказываться принимать любую нарушающую эту целостность операцию.
Опасения за доменную целостность возникают при вводе в таблицу новых данных, выполняемом с помощью оператора INSERT или UPDATE. Домен для столбца можно установить с помощью оператора CREATE DOMAIN (создать домен), причем до того, как использовать этот столбец в операторе CREATE TABLE. Это показано в следующем примере, где перед созданием таблицы TEAM (команда) со столбцами TeamName (имя команды) и League (лига) создается домен LeagueDom (домен значений лиги):
| CREATE DOMAIN LeagueDom | CHAR (8) | |
| CHECK (LEAGUE IN ('American', 'National')); | ||
| CREATE TABLE TEAM ( | ||
| TeamName | CHARACTER (20) | NOT NULL, |
| League | CHARACTER (8) | NOT NULL |
| ); |
Доменноключевая нормальная форма (ДКНФ)
Доменно-ключевая нормальная форма (ДКНФ)После того как база данных оказалась в третьей нормальной форме, большинство шансов на возникновение аномалий изменения было сведено на нет. Впрочем, большинство, но не все. Для исправления этих оставшихся неполадок как раз предназначены нормальные формы, находящиеся внутри третьей. Примерами таких форм являются нормальная форма Бойса-Кодда (НФБК), четвертая нормальная форма (4НФ) и пятая нормальная форма (5НФ). Каждая форма сводит на нет угрозу какой-либо аномалии изменения, но не дает гарантии защиты от всех таких аномалий. Такую гарантию дает только доменно-ключевая нормальная форма (ДКНФ).
Помни: Отношение находится в доменно-ключевой нормальной форме (ДКНФ), если каждое ограничение в этом отношении является логическим следствием определения ключей и доменов. Ограничением в этом определении называется любое правило, которое можно проверить. Ключ — это уникальный идентификатор табличной строки, а домен — набор разрешенных значений атрибута.
Снова посмотрим на базу данных (см. Рисунок 5.2), которая находится в 1НФ. Это необходимо, чтобы увидеть, каким образом привести эту базу в ДКНФ.
Таблица: SALES(CustomerlD,Product,Price)
Ключ: CustomerID
Ограничения: CustomerlD определяет Product
PRODUCT определяет Price
CustomerlD должен быть целым числом больше 1000
Как заставить работать ограничение 3 (атрибут CustomerlD должен быть целым числом больше 1000)? Можно всего лишь так определить домен CustomerlD, чтобы в него входило это ограничение. Таким образом, ограничение становится логическим следствием домена столбца CustomerlD. Product и зависит от CustomerlD, a CustomerlD — это ключ, так что трудностей с ограничением 1 не будет, поскольку оно является логическим следствием определения ключа. Однако трудность есть с ограничением 2: Price зависит от (является логическим следствием) Product, a Product не является ключом. Справиться с трудностью можно, разделив таблицу SALES на две. В одной из них в качестве ключа используется CustomerlD, а в другой — Product. Такая схема приведена на Рисунок 5.3. База данных на этом рисунке находится не только в ЗНФ, но и в ДКНФ.
Помни: Проектируйте базы данных так, чтобы они по возможности были в ДКНФ. В таком случае ключевые и доменные ограничения определяют все требуемые ограничения, и аномалии изменений исключены. А если структура базы данных спроектирована так, чтобы ее нельзя было привести в ДКНФ, то ограничения необходимо встроить в прикладную программу, которая использует базу данных. Сама база данных не дает гарантии, что ограничения будут соблюдаться.
Избыточность данных
Избыточность данныхВ тех базах, которые организованы иерархически, избыточность данных — это существенная проблема. Впрочем, она имеет место и в реляционных базах. Такая избыточность не только означает ненужный расход места на диске и замедление обработки, но также может привести и к серьезному повреждению данных. Если вы храните один и тот же элемент данных в двух разных таблицах базы данных, то одна его копия может быть изменена, а другая, что находится во второй таблице, может остаться прежней. Такая ситуация приводит к расхождениям, и может случиться так, что вы не сможете сказать, какой из двух вариантов является правильным. Желательно сводить избыточность данных к минимуму. Определенная избыточность необходима, чтобы первичный ключ одной таблицы служил внешним ключом другой. Однако любой другой избыточности старайтесь избегать. Самый распространенный метод уменьшения избыточности в проекте называется нормализацией. Он состоит в том, что одна таблица из базы данных разбивается на две или несколько простых.
Правда, после того как вы уберете большую часть избыточности из проекта базы данных, то, возможно, обнаружите, что ее производительность стала никуда не годной. Операторы очень часто используют избыточность специально для того, чтобы ускорить обработку данных. В предыдущем примере в таблице ORDERS для определения источника каждого заказа имеется только название фирмы-клиента. При оформлении заказа, чтобы получить адрес этой фирмы, вам необходимо соединить таблицу ORDERS с таблицей CLIENT. Если в результате такого соединения таблиц программа, которая распечатывает заказы, будет работать очень медленно, то вам, возможно, захочется выполнять избыточное хранение адреса фирмы-клиента, т.е. еще и в таблице ORDERS, а не только в CLIENT. Преимущество такой избыточности состоит в более быстрой распечатке заказов, но это происходит за счет замедления и усложнения любого обновления адреса клиента.
Совет 4
Совет 4
Распространенная среди пользователей практика состоит в том, чтобы вначале проектировать базу данных с малой избыточностью и с высокой степенью нормализации, а затем, обнаружив, что важные приложения работают медленно, выборочно добавлять избыточность и уменьшать нормализацию. Главное слово здесь — это "выборочно". Добавляемая избыточность предназначена для определенной цели, и так как она представляет определенный риск, предпринимайте соответствующие меры к тому, чтобы избыточность вызывала проблем не больше, чем решала.
Эта таблица SALES ведет к аномалиям изменения
Рисунок 5.2. Эта таблица SALES ведет к аномалиям изменения
Ваша компания продает моющие средства для дома и предметы личной гигиены, и за один и тот же товар все покупатели платят одинаково. Все данные содержатся в таблице SALES — например, о продажах стирального порошка, зубной пасты и отбеливателя. Теперь предположим, что покупатель 1001 уехал и больше ничего у вас не приобретает. И так как он не собирается больше ничего приобретать, то вам не интересно, что же он приобретал раньше. Поэтому вы хотите удалить его строку из таблицы. Однако если вы это сделаете, то не только потеряете данные о том, что покупатель 1001 приобретал стиральный порошок, но йотом, что стиральный порошок стоит 12 долларов. Такая ситуация называется аномалией удаления. Удаляя одни данные (о том, что покупатель 1001 приобретал стиральный порошок), вы нечаянно удалите другие (о том, что стиральный порошок стоит 12 долларов).
В той же таблице можно наблюдать и аномалию вставки. Скажем, вы хотите добавить к своим товарам еще и сухой дезодорант по цене 2 доллара. Но эти данные нельзя будет поместить в таблицу SALES до тех пор, пока сухой дезодорант не потребуется какому-нибудь покупателю.
Трудность с изображенной на рисунке таблицей SALES заключается в том, что эта таблица слишком универсальна. В ней есть данные и о том, что именно приобрели у вас покупатели, и о том, сколько стоят купленные товары. Эту таблицу необходимо разбить на две другие, и каждая из них будет посвящена только одной теме (Рисунок 5.3).
Механическое повреждение
Механическое повреждениеПри механическом повреждении носителя данных, на котором была открыта таблица базы данных, данные этой таблицы могут быть испорчены. Главная защита против подобной неприятности — резервное копирование.
Ненормальная форма
Ненормальная формаНенормальность иногда полезна. Возможно, вы увлеклись нормализацией, и вас занесло слишком далеко. Ведь базу данных можно разбить на такое количество таблиц, что вся она станет громоздкой и неэффективной. Ее работа может застопориться. Так что часто оптимальная структура должна быть в какой-то степени денормализованной. На самом деле базы данных, используемые в практической деятельности, никогда не нормализованы до уровня ДКНФ. Впрочем, чтобы исключить возможность повреждения данных, происходящего из-за аномалий изменений, максимально нормализуйте проектируемую вами базу данных.
После этого мосты еще не сожжены. Если производительность вас не удовлетворяет, проверьте свой проект — можно ли с помощью некоторой денормализации увеличить производительность, не жертвуя при этом целостностью. Выборочно добавляя избыточность и проводя денормализацию, вы получите базу данных, которая будет и эффективной, и защищенной от аномалий.
Нормализация базы данных
Нормализация базы данныхСреди способов организации данных есть такие, которые лучше всех остальных, а есть такие, которые более логичны, некоторые — проще. Кроме того, имеются и такие, которые, как только начинается использование базы данных, лучше других предотвращают несоответствие данных.
Если правильно не организовать структуру базы данных, то эта база станет жертвой множества разных неприятностей (которые называются аномалиями изменения). Чтобы их предотвратить, можно нормализовать структуру базы данных. Нормализация обычно влечет за собой разделение в базе данных одной таблицы на две или несколько простых.
Аномалии изменения так называются потому, что проявляются в таблице базы данных при добавлении в нее, изменении в ней или удалении из этой таблицы данных.
Иллюстрацией того, каким образом могут проявляться аномалии изменения, является таблица, приведенная на Рисунок 5.2.
Обеспечение целостности
Обеспечение целостностиБаза данных представляет ценность лишь тогда, когда вы в достаточной степени уверены, что находящиеся в этой базе данные правильные. Например, неправильные данные в медицинских, авиационных и космических базах данных могут привести к человеческим жертвам. Неправильные данные в других приложениях приводят не к таким плачевным последствиям, но нанести ущерб все же могут. Поэтому проектировщик базы данных должен гарантировать, что в нее неправильные данные никогда не попадут.
Некоторые неприятности нельзя остановить на уровне базы данных. Об их заблаговременном предотвращении (перед тем как они нанесут ущерб базе) должен позаботиться прикладной программист. Каждый, кто каким-либо образом отвечает за работу с базой данных, должен осознавать, какие опасности угрожают целостности ее данных, и принять все меры, чтобы свести эти опасности к нулю.
В базах данных целостность может быть нескольких видов, причем довольно-таки разных. Разными могут быть и неприятности, которые представляют угрозу целостности. В следующих разделах рассказывается о трех видах целостности: смысловой, доменной и ссылочной. Кроме того, рассматриваются некоторые угрозы целостности баз данных.
Области возможных трудностей
Области возможных трудностейПокушения на целостность данных приходится ждать с самых разных сторон. Некоторые из этих неприятностей возникают только в многотабличных базах, в то время как другие могут произойти даже в базах, в которых имеется только одна таблица. Необходимо уметь распознавать эти угрозы и сводить вероятность их появления к минимуму.
Ограничения столбцов
Ограничения столбцовПример ограничения столбца показан в следующем операторе языка определения данных DDL:
| CREATE TABLE CLIENT ( | ||
| ClientName | CHARACTER (30), | NOT NULL,, |
| Address1 | CHARACTER (30), | |
| Address2 | CHARACTER (30), | |
| City | CHARACTER (25), | |
| State | CHARACTER (2), | |
| PostalCode | CHARACTER (10), | |
| Phone | CHARACTER (13), | |
| Fax | CHARACTER (13), | |
| ContactPerson | CHARACTER (30) ) ; | |
| ) ; |
| CREATE TABLE TESTS ( | ||
| TestName | CHARACTER (30) | NOT NULL, |
| StandardCharge | NUMBER (6,2) | |
| CHECK (StandardCharge >= 0.0 | ||
| AND StandardCharge <= 200.0) | ||
| ); |
CHECK (StandardCharge BETWEEN 0.0 AND 2 00.0)
Ограничения таблиц
Ограничения таблицОграничение PRIMARY KEY указывает на то, что столбец, к которому оно применено, является первичным ключом. Таким образом, это ограничение относится ко всей таблице и эквивалентно комбинации двух ограничений столбца: NOT NULL и UNIQUE. Это ограничение, как показано в следующем примере, можно задавать в операторе CREATE:
| CREATE TABLE CLIENT ( | ||
| ClientName | CHARACTER (30), | PRIMARY KEY, |
| Address1 | CHARACTER (30), | |
| Address2 | CHARACTER (30), | |
| City | CHARACTER (25), | |
| State | CHARACTER (2), | |
| PostalCode | CHARACTER (10), | |
| Phone | CHARACTER (13), | |
| Fax | CHARACTER (13), | |
| ContactPerson | CHARACTER (30) ) ; | |
| ) ; |
Утверждения Утверждение (assertion) задает ограничение для более чем одной таблицы. В следующем примере для создания утверждения применяется условие поиска, составленное для столбцов из двух таблиц
| CREAE TABLE ORDERS ( | ||
| OrderNumber | INTEGER | NOT NULL, |
| ClientName | CHARACTER (30), | |
| TestOrdered | CHARACTER (30), | |
| Salesperson | CHARACTER (30) , | |
| OrderDate | DATE | |
| ); | ||
| CREATE TABLE RESULTS ( | ||
| ResultNumber | INTEGER | NOT NULL, |
| OrderNumber | INTEGER | |
| Result | CHARACTER(50) , | |
| DateReported | DATE, | |
| PrelimFinal | CHARACTER (1), | |
| ); | ||
| CREATE ASSERTION | ||
| CHECK (NOT EXISTS SELECT * FROM ORDERS, RESULTS | ||
| WHERE ORDERS.OrderNumber = RESULTS. OrderNumber | ||
| AND ORDERS.OrderDate > RESULTS.DateReported) ; |
Ограничения
ОграниченияВ этой главе об ограничениях говорилось как о механизме, благодаря которому в табличный столбец могут быть введены только данные из домена этого столбца. Ограничение (constraint) — это правило, за исполнением которого следит СУБД. После определения базы данных можно создавать определения таблиц с заданными в этих определениях ограничениями (такими, например, как NOT NULL). Благодаря СУБД вы никогда не сможете успешно выполнить никакой транзакции, если она нарушает какое-либо ограничение.
Помни: В вашем распоряжении имеются ограничения трех разных видов.
Ошибка оператора
Ошибка оператораВаши исходные данные могут быть правильными, но оператор при вводе поймет их неправильно. Ошибка такого рода может привести к тем же трудностям, что и ввод неправильных данных. И некоторые из средств для их преодоления такие же. Проверки с помощью диапазонов допустимых значений в этом случае полезны, но не являются панацеей. Есть еще одно решение. Оно состоит в том, чтобы все вводимые данные независимо проверялись еще и вторым оператором. Такой подход обходится дорого, потому что независимая проверка удваивает количество работников и затраченное на работу время. Хотя в некоторых случаях, когда целостность данных очень важна, дополнительные усилия и затраты себя оправдывают.
Первая нормальная форма
Первая нормальная формаЧтобы быть в первой нормальной форме (1НФ), таблица должна обладать такими качествами.
Первичные ключи
Первичные ключиЧтобы реализовать в базе данных VetLab идею ключей, при создании таблицы можно сразу указывать ее первичный ключ. В следующем примере будет достаточно одного столбца (при условии, что у фирм-клиентов VetLab разные названия):
| CREATE TABLE CLIENT ( | ||
| ClientName | CHARACTER (30), | PRIMARY KEY, |
| Address1 | CHARACTER (30), | |
| Address2 | CHARACTER (30), | |
| City | CHARACTER (25), | |
| State | CHARACTER (2), | |
| PostalCode | CHARACTER (10), | |
| Phone | CHARACTER (13), | |
| Fax | CHARACTER (13), | |
| ContactPerson | CHARACTER (30) ) ; |
Несмотря на то что большинство СУБД позволяет создавать таблицу без единого ключа, важно помнить, что все таблицы базы данных должны иметь первичный ключ. Поэтому нужно ввести ограничение NOT NULL в таблицы TESTS, EMPLOYEE, ORDERS и RESULTS вместе с ограничением PRIMARY KEY, как показано в следующем примере:
CREATE TABLE TESTS (
TestName CHARACTER (30) PRIMARY KEY,
StandardCharge CHARACTER (30) ) ;
Иногда в таблице ни один единичный столбец не может гарантировать уникальность строки. В таких случаях можно использовать составной ключ. Он является сочетанием столбцов, совместное использование которых гарантирует уникальность. Представьте, что некоторые клиенты VetLab — это фирмы, имеющие свои отделения в нескольких городах. В таком случае поля ClientName будет недостаточно, чтобы различить два разных отделения одного и того же клиента. Чтобы решить эту проблему, можно определить следующий составной ключ:
| CREATE TABLE CLIENT ( | ||
| ClientName | CHARACTER (30), | PRIMARY KEY, |
| Address1 | CHARACTER (30), | |
| Address2 | CHARACTER (30), | |
| City | CHARACTER (25), | PRIMARY KEY, |
| State | CHARACTER (2), | |
| PostalCode | CHARACTER (10), | |
| Phone | CHARACTER (13), | |
| Fax | CHARACTER (13), | |
| ContactPerson | CHARACTER (30) ) ; |
Внешние ключи
Внешний ключ — это столбец или группа столбцов в таблице, соответствующие первичному ключу (т.е. ссылающиеся на первичный ключ) из другой таблицы базы данных. Внешний ключ сам по себе может и не быгь уникальным, но должен однозначно называть столбец в той таблице, на которую он ссылается.
Если столбец ClientName — это первичный ключ таблицы CLIENT, то каждая строка этой таблицы должна иметь в столбце ClientName уникальное значение. В свою очередь, в таблице ORDERS ClientName является внешним ключом. Этот внешний ключ соответствует первичному ключу таблицы CLIENT, но в таблице ORDERS он может и не быть уникальным. На самом деле вы, конечно же, надеетесь, что внешний ключ не является уникальным. Ведь если бы каждая из фирм ваших клиентов сделала у вас только один заказ и больше этого не повторяла, то ваш бизнес довольно быстро бы прекратился. На самом деле вы надеетесь, что каждой строке таблицы CLIENT соответствует много строк таблицы ORDERS, показывая этим, что почти все ваши клиенты постоянно пользуются вашими услугами.
Следующее определение таблицы ORDERS показывает, каким образом в операторе CREATE можно задавать внешние ключи:
| CREATE TABLE ORDERS ( | ||
| OrderNumber | INTEGER | NOT NULL, |
| ClientName | CHARACTER (30), | |
| TestOrdered | CHARACTER (30), | |
| Salesperson | CHARACTER (30), | |
| OrderDate | DATE ) ; | |
| CONSTRAINT BRANCHFK FOREIGN KEY (ClientName) | ||
| REFERENCES CLIENT (ClientName), | ||
| CONSTRAINT TestFK FOREIGN KEY (TestOrdered) | ||
| REFERENCES TESTS (TestName), | ||
| CONSTRAINT SalesFK FOREIGN KEY (Salesperson) | ||
| REFERENCES EMPLOYEE (EmployeeName),) ; |
Поддержание индекса
Поддержание индексаЕсли индекс создан, то его необходимо поддерживать. К счастью, вместо вас индексы поддерживает ваша система СУБД, которая обновляет их каждый раз, когда вы обновляете соответствующие таблицы данных. На этот процесс требуется немного дополнительного времени, но эти затраты того стоят. После того как вы создали индекс и СУБД его поддерживает, индекс всегда готов ускорять обработку ваших данных. И неважно, сколько раз он будет для этого использоваться.
Совет 2
Совет 2
Ясно, что лучше всего создавать индекс тогда, когда создается соответствующая ему таблица данных. Если индекс создается в самом начале и тогда же начинает поддерживаться, то вам не придется мучиться позднее с его созданием. Тогда на эту операцию придется тратить целый сеанс, длящийся довольно долго. Попробуйте предвидеть все способы, которые вам потребуются для доступа к данным, а затем для каждой такой возможности создайте свой индекс.
В некоторых продуктах СУБД имеется возможность отключать поддержку индексов. Такое, возможно, придется делать в некоторых практических приложениях, где на обновление индексов уходит много времени, которое нужно экономить. Иногда даже приходится производить обновление индексов специальной операцией, выполняемой в промежутках между часами пик.
Превышение технических возможностей базы данных
Превышение технических возможностей базы данныхСистема базы данных может работать безукоризненно годами, а затем периодически начать выдавать ошибки, которые постепенно становятся все более серьезными. Это означает наступление одного из пределов емкости системы. Существуют пределы количества строк таблицы, а также предел столбцов, ограничений и других характеристик. Поэтому старайтесь контролировать соответствие текущего размера и содержимого вашей базы данных техническим характеристикам вашей СУБД. Если вы обнаружили, что предел какого-либо свойства уже близок, займитесь улучшением возможностей системы. Или заархивируйте старые данные, к которым вы больше не обращаетесь, а затем удалите их из базы данных.
Проектирование базы данных
Проектирование базы данныхПри проектировании базы данных выполните следующие основные действия (подробно о каждом из них рассказывается в последующих разделах).
В последующих разделах подробно описываются эти действия, а также рассматриваются некоторые другие вопросы, связанные с проектированием базы данных.
Работа с индексами
Работа с индексамиСпецификация SQL:2003 к теме индексов не обращается, но это не значит, что они являются редкой или даже необязательной частью системы баз данных. Индексы поддерживаются каждой реализацией SQL, но общего соглашения по их поддержке не существует. В главе 4 было показано, как создать индекс с помощью RAD-инструмента Microsoft Access. Чтобы разобраться, как индексы используются в конкретной системе управления базами данных, необходимо обратиться к ее документации.
Смысловая целостность
Смысловая целостностьКаждая таблица базы данных соответствует какому-либо объекту реального мира. Такой объект может быть физическим или умозрительным, но его существование в некотором смысле не зависит от базы данных. Если таблица полностью соответствует объекту, который она моделирует, то она обладает смысловой целостностью. Чтобы иметь такую целостность, таблица должна иметь первичный ключ. Этот ключ однозначно определяет каждую строку таблицы. Если его нет, то нет и уверенности, что вы получите нужную вам строку.
Чтобы поддержать целостность объекта, необходимо для столбца или группы столбцов, из которых состоит первичный ключ, указать NOT NULL. Кроме того, для первичного ключа еще необходимо ограничение UNIQUE. В некоторых реализациях SQL такое ограничение указывается непосредственно в определении таблицы. В других же реализациях его приходится задавать уже после того, как будет указано, каким образом данные следует добавлять в таблицу, изменять и удалять из нее. Проще всего добиться, чтобы первичный ключ был и NOT NULL, и UNIQUE, — это использовать ограничение PRIMARY KEY, как показано в следующем примере:
| CREATE TABLE CLIENT ( | ||
| ClientName | CHARACTER (30), | PRIMARY KEY, |
| Address1 | CHARACTER (30), | |
| Address2 | CHARACTER (30), | |
| City | CHARACTER (25), | |
| State | CHARACTER (2), | |
| PostalCode | CHARACTER (10), | |
| Phone | CHARACTER (13), | |
| Fax | CHARACTER (13), | |
| ContactPerson | CHARACTER (30) ) ; |
| CREATE TABLE CLIENT ( | ||
| ClientName | CHARACTER (30), | NOT NULL,, |
| Address1 | CHARACTER (30), | |
| Address2 | CHARACTER (30), | |
| City | CHARACTER (25), | |
| State | CHARACTER (2), | |
| PostalCode | CHARACTER (10), | |
| Phone | CHARACTER (13), | |
| Fax | CHARACTER (13), | |
| ContactPerson | CHARACTER (30) ) ; | |
| UNIQUE (ClientName)) ; |
Ссылочная целостность
Ссылочная целостностьДаже если в базе данных для каждой таблицы установлены целостность объекта и доменная целостность, то этой базе все равно грозят неприятности, происходящие из-за того, что связь одной таблицы с другой не согласована. В большинстве хорошо спроектированных баз данных в каждой таблице имеется как минимум один столбец, который ссылается на столбец из другой таблицы той же базы. Такие ссылки играют важную роль при поддержании общей целостности базы данных. Впрочем, те же ссылки делают возможными аномалии обновления.
Аномалии обновления — это неприятности, которые могут происходить после обновления значений в строке базы данных.
В целом отношения между таблицами не являются равноправными. Обычно одна таблица зависит от другой. Скажем, у вас, например, имеется база данных с таблицами CLIENT (фирма-клиент) и ORDERS (заказы). Вы можете намеренно ввести в таблицу CLIENT данные фирмы-клиента еще до того, как ею будут сделаны какие-либо заказы. Однако в таблицу ORDERS нельзя будет ввести ни одного заказа, если в первой, CLIENT, не будет записи для клиента, делающего этот заказ. Получается, что таблица ORDERS зависит от таблицы CLIENT. Такой порядок часто называют родительско-дочерним отношением таблиц, при котором CLIENT — это родительская, a ORDERS — дочерняя таблица. Дочерний элемент базы данных зависит от родительского. Обычно первичный ключ родительской таблицы — это столбец (или группа столбцов), который имеется и в дочерней таблице. И там он уже является внешним ключом. Во внешнем ключе могут находиться неопределенные значения, и ему не нужно быть уникальным.
Аномалии обновления возникают несколькими способами. Например, фирма-клиент не делает у вас заказов, и вы хотите удалить ее данные из базы. И если она уже сделала у вас некоторые заказы, данные о которых записаны в таблице ORDERS, то удаление ее данных из таблицы CLIENT может вызвать трудности. Дело в том, что тогда в дочерней таблице ORDERS остались бы записи, для которых не было бы соответствующих записей в главной таблице CLIENT. Аналогичные трудности могут возникнуть и тогда, когда запись в дочернюю таблицу добавляется, а соответствующее добавление в родительскую еще не сделано. Все изменения первичного ключа, происходящие в любой строке родительской таблицы, должны отражаться в соответствующих внешних ключах всех дочерних таблиц. Если этого не произойдет, образуются аномалии обновления.
Большинство трудностей, связанных со ссылочной целостностью, можно свести к минимуму, если тщательно управлять процессом обновления. В некоторых случаях необходимо каскадное удаление из родительской таблицы и ее дочерних таблиц. Чтобы при удалении строки из родительской таблицы произошли каскадные удаления, необходимо из всех дочерних таблиц удалить строки, значение внешнего ключа которых равно значению первичного ключа строки, удаляемой из главной таблицы. Рассмотрим следующий пример:
| CREATE TABLE CLIENT ( | ||
| ClientName | CHARACTER (30), | PRIMARY KEY, |
| Address1 | CHARACTER (30), | |
| Address2 | CHARACTER (30), | |
| City | CHARACTER (25), | NOT NULL, |
| State | CHARACTER (2), | |
| PostalCode | CHARACTER (10), | |
| Phone | CHARACTER (13), | |
| Fax | CHARACTER (13), | |
| ContactPerson | CHARACTER (30) ) ; | |
| CREATE TABLE TESTS ( | ||
| TestName | CHARACTER (30) | PRIMARY KEY, |
| StandardCharge | CHARACTER (30) ) ; | |
| CREATE TABLE EMPLOYEE ( | ||
| EmployeeName | CHARACTER (30) | PRIMARY KEY, |
| Address1 | CHARACTER (30), | |
| Address2 | CHARACTER (30), | |
| City | CHARACTER (25), | |
| State | CHARACTER (2), | |
| PostalCode | CHARACTER (10), | |
| HomePhone | CHARACTER (13), | |
| OfficeExtension | CHARACTER (4), | |
| HireDate | DATE, | |
| JobClassification | CHARACTER (10), | |
| HourSalComm | CHARACTER (1) ) ; | |
| CREATE TABLE ORDERS ( | ||
| OrderNumber | INTEGER | PRIMARY KEY, |
| ClientName | CHARACTER (30), | |
| TestOrdered | CHARACTER (30), | |
| Salesperson | CHARACTER (30), | |
| OrderDate | DATE ) ; | |
| CONSTRAINT NameFK FOREIGN KEY (ClientName) | ||
| REFERENCES CLIENT (ClientName) | ||
| ON DELETE CASCADE, | ||
| CONSTRAINT TestFK FOREIGN KEY (TestOrdered) | ||
| REFERENCES TESTS (TestName) | ||
| ON DELETE CASCADE, | ||
| CONSTRAINT SalesFK FOREIGN KEY (Salesperson) | ||
| REFERENCES EMPLOYEE (EmployeeName) | ||
| ON DELETE CASCADE ) ; |
Возможно, вы не хотите производить каскадное удаление, а вместо этого хотите заменить внешний ключ подчиненной таблицы значением NULL. Проанализируйте следующий вариант предыдущего примера:
| CREATE TABLE ORDERS ( | ||
| OrderNumber | INTEGER | PRIMARY KEY, |
| ClientName | CHARACTER (30), | |
| TestOrdered | CHARACTER (30), | |
| Salesperson | CHARACTER (30), | |
| OrderDate | DATE ) ; | |
| CONSTRAINT NameFK FOREIGN KEY (ClientName) | ||
| REFERENCES CLIENT (ClientName), | ||
| CONSTRAINT TestFK FOREIGN KEY (TestOrdered) | ||
| REFERENCES TESTS (TestName), | ||
| CONSTRAINT SalesFK FOREIGN KEY (Salesperson) | ||
| REFERENCES EMPLOYEE (EmployeeName),) ; | ||
| ON DELETE SET NULL ) ; |
Есть и другой способ предохранить базу от несогласованных данных. Он состоит в том, чтобы отказаться от разрешения добавлять строки в дочернюю таблицу, пока в родительской таблице не появится соответствующая им строка. Если вы откажетесь разрешать добавление строк в дочерней таблице, пока не будет нужной строки в родительской таблице, то таким образом предотвратите появление "строк-сирот" в дочерней таблице. Это позволит легко поддерживать согласованность таблиц. Еще одна возможность состоит в том, чтобы запретить изменять первичный ключ таблицы. В этом случае можно не беспокоиться об обновлении внешних ключей в других таблицах, которые зависят от этого первичного ключа.
базы данных VetLab
Таблица 5.1. Таблицы базы данных VetLab.| Таблица | Столбцы |
| CLIENT (фирма-клиент) | Client Name (название фирмы-клиента) |
| Address 1 (адрес 1) | |
| Address 2 (адрес 2) | |
| City (город) | |
| State (штат) | |
| Postal Code (почтовый код) | |
| Phone (телефон) | |
| Fax (факс) | |
| Contact Person (контактный представитель) | |
| TESTS (анализы) | Test Name (название анализа) |
| Standard charge (стандартная цена) | |
| EMPLOYEE (сотрудник) | Employee Name (фамилия сотрудника) |
| Address 1 (адрес 1) | |
| Address 2 (адрес 2) | |
| City (город) | |
| State (штат) | |
| Postal Code (почтовый код) | |
| Home Phone (домашний телефон) | |
| Office Extension (телефонный номер в офисе) | |
| Hire Date (дата приема на работу) | |
| Job classification (трудовая классификация) | |
| Hourly/Salary/Commission (почасовая оплата/зарплата/комиссионные) | |
| ORDERS (заказы) | Order Number (номер заказа) |
| Client Name (название фирмы-клиента) | |
| Test ordered (заказанный анализ) | |
| Responsible Salesperson (сотрудник, принявший заказ) | |
| Order Date (дата заказа) | |
| RESULTS (результаты) | Result Number (номер результата) |
| Order Number (номер заказа) | |
| Result (результат) | |
| Date Reported (сообщенная дата) | |
| Preliminary / Final (предварительный/окончательный) |
разработки приложений (Rapid Application Development, RAD), или с помощью языка определения
данных (Data Definition Language, DDL), входящего в состав SQL, как показано ниже.
| CREATE TABLE CLIENT ( | ||
| ClientName | CHARACTER (30), | NOT NULL, |
| Address1 | CHARACTER (30), | |
| Address2 | CHARACTER (30), | |
| City | CHARACTER (25), | |
| State | CHARACTER (2), | |
| PostalCode | CHARACTER (10), | |
| Phone | CHARACTER (13), | |
| Fax | CHARACTER (13), | |
| ContactPerson | CHARACTER (30) ) ; | |
| CREATE TABLE TESTS ( | ||
| TestName | CHARACTER (30) | NOT NULL, |
| StandardCharge | CHARACTER (30) ) ; | |
| CREATE TABLE EMPLOYEE ( | ||
| EmployeeName | CHARACTER (30) | NOT NULL, |
| Address1 | CHARACTER (30), | |
| Address2 | CHARACTER (30), | |
| City | CHARACTER (25), | |
| State | CHARACTER (2), | |
| PostalCode | CHARACTER (10), | |
| HomePhone | CHARACTER (13), | |
| OfficeExtension | CHARACTER (4), | |
| HireDate | DATE, | |
| JobClassification | CHARACTER (10), | |
| HourSalComm | CHARACTER (1) ) ; | |
| CREATE TABLE ORDERS ( | ||
| OrderNumber | INTEGER | NOT NULL, |
| ClientName | CHARACTER (30), | |
| TestOrdered | CHARACTER (30), | |
| Salesperson | CHARACTER (30), | |
| OrderDate | DATE ) ; | |
| CREATE TABLE RESULTS ( | ||
| ResultNumber | INTEGER | NOT NULL, |
| OrderNumber | INTEGER | |
| Result | CHARACTER (50), | |
| DateReported | DATE, | |
| PrelimFinal | CHARACTER (1) ) ; |
Таблица CLIENT
Таблица 5.2. Таблица CLIENT| ClientName | Address1 | Address2 | City | State |
| Butternut Animal Clinic | 5 Butternut Lane | Hudson | NH | |
| Amber Veterinary, Inc. | 470 Kolvir Circle | Amber | Ml | |
| Vets R Us | 2300 Geoffrey Road | Suite 230 | Anaheim | CA |
| Doggie Doctor | 32 Terry Terrace | Nutley | NJ | |
| The Equistrian Center | Veterinary Department | 7890 Paddock Parkway | Gallup | NM |
| Dolphin Institute | 1002 Marine Drive | Key West | FL | |
| J.C.Campbell, Credit Vet | 2500 Main Street | Los Angeles | CA | |
| Wenger's Worm Farm | 15 Bait Boulevard | Sedona | AZ |
Индекс для этой таблицы CLIENT может выглядеть примерно так, как табл. 5.3.
Индекс по названию клиента для таблицы CLIENT
Таблица 5.3. Индекс по названию клиента для таблицы CLIENT| ClientName | Указатель к таблице данных |
| Amber Veterinary, Inc. | 2 |
| Butternut Animal Clinic | 1 |
| Doggie Doctor | 4 |
| Dolphin Institute | 6 |
| J.C.Campbell, Credit Vet | 7 |
| The Equistrian Center | 5 |
| Vets R Us | 3 |
| Wenger's Worm Farm | 8 |
SALES разбита на две другие
Рисунок 5.3. Таблица SALES разбита на две другие
На Рисунок 5.3 изображено, что таблица SALES разделена на две новые таблицы.
Нормализацией называется процесс разделения одной таблицы на множество других, каждая из которых посвящена отдельной теме. Нормализация, которая решает одну проблему, может не оказывать никакого влияния на другие. И чтобы в конце концов получить такие таблицы, которые посвящены каждая единственной теме, может потребоваться несколько последовательных нормализации. В базе данных у каждой таблицы должна быть одна и только одна главная тема. Ведь если таблица посвящена хотя бы двум темам, то в такой таблице иногда бывает трудно что-то понять.
Таблицы можно классифицировать по видам тех аномалий изменения, которым эти таблицы подвержены. В своей статье, выпущенной в 1970 году (первой, где была описана реляционная модель), доктор И.Ф. Кодд (E.F. Codd) диагностирует три источника аномалий изменения и для "лечения" от этих аномалий выписывает три "лекарства". Это первая, вторая и третья нормальные формы (1НФ, 2НФ, ЗНФ). В последующие годы И.Ф. Кодд и другие специалисты открыли как другие виды аномалий, так и средства против них — новые нормальные формы. Нормальная форма Бойса-Кодда (НФБК) (Boyce-Codd normal form, BCNF), четвертая нормальная форма (4НФ) и пятая нормальная форма (5НФ) — каждая из них обеспечивала еще более высокую защиту от аномалий изменения, чем их предшественницы. В 1981 году появилась статья, написанная Р. Фейджином (R. Fegin), где описана доменно-ключевая нормальная форма (ДКНФ) (domain/key normal form, DKNF). Эта последняя нормальная форма гарантирует отсутствие аномалий изменения.
Нормальные формы являются вложенными в том смысле, что таблица, находящаяся в 2НФ, автоматически находится и в ШФ. Аналогично, таблица, которая находится в ЗНФ, находится ив 2НФ и т.д. Для большинства приложений приведения базы данных в ЗНФ вполне достаточно, чтобы обеспечить в этой базе высокую степень целостности. Впрочем, чтобы была абсолютная уверенность в целостности базы данных, необходимо привести ее в ДКНФ.
После проведения максимально возможной нормализации своей базы данных вам для увеличения ее производительности, вероятно, захочется выполнить выборочную денормализацию. В таком случае надо полностью отдавать себе отчет, с какими аномалиями вы, возможно, столкнетесь.
Таблицы и связи базы данных VetLab
Рисунок 5.1. Таблицы и связи базы данных VetLab
На Рисунок 5.1 показаны четыре различных отношения типа "один ко многим". В изображении отношения одна стрелка указывает на сторону "один", а двойная — на сторону "многие".
Домены, символьные наборы, сопоставления и трансляции
Хотя главными компонентами базы данных являются таблицы, но другие ее элементы также играют определенную роль. В главе 1 домен табличного столбца был определен как набор всех значений, которые допустимы для этого столбца. Создание с помощью ограничений четко определенных доменов табличных столбцов — это важная часть проектирования базы данных.
Реляционными базами данных пользуются не только те, кто разговаривает на американском варианте английского языка. С этими базами можно работать, используя и другие языки, даже те, у которых другие символьные наборы. Даже если база данных создана с использованием только английского языка, некоторые приложения могут запросить специальные символьные наборы. SQL:2003 позволяет точно определить тот набор, который вам нужно использовать. Фактически можно для каждого табличного столбца использовать отдельный символьный набор. В языках, отличных от SQL, подобной гибкости обычно нет.
Сопоставление, или последовательность сопоставления, — это набор правил, которые определяют, каким образом сравниваются друг с другом строки, состоящие из элементов определенного символьного набора. Каждый символьный набор имеет свое сопоставление по умолчанию. В сопоставлении по умолчанию для символьного набора ASCII В следует после А, а С — после В. Поэтому при сравнении считается, что А меньше В, а С больше В. С другой стороны, SQL:2OO3 дает возможность применять к символьному набору и другие сопоставления. Повторяю снова, что в других языках подобной степени гибкости обычно нет.
Иногда данные в базе кодируются с помощью одного символьного набора, но работать с ними нужно с помощью другого набора. У вас, например, есть данные, закодированные в немецком символьном наборе, но те немецкие символы, которые не входят в набор ASCII, на вашем принтере не печатаются. Трансляция — это функциональная возможность SQL:2003, позволяющая преобразовывать символьные строки из одного набора в другой. Трансляция, например, позволяет преобразовывать один символ в два, в частности немецкий u — в ue из ASCII, или может преобразовывать символы из нижнего регистра в верхний. Можно даже преобразовать один алфавит в другой, например алфавит языка иврит в символы ASCII.
Третья нормальная форма
Третья нормальная формаВсе-таки есть аномалии изменения, против которых таблицы во второй нормальной форме беззащитны. Эти аномалии связаны с транзитивными зависимостями.
Помни: Транзитивная зависимость имеет место тогда, когда один атрибут зависит от второго, а второй, в свою очередь, от третьего. Удаления в таблице, имеющей такие зависимости, могут вызвать ненужную потерю информации. Отношение в третьей нормальной форме — это отношение во второй нормальной форме, не имеющее транзитивных зависимостей.
Снова посмотрим на таблицу SALES (продажи) (см. Рисунок 5.2), которая, как вам известно, находится в первой нормальной форме. Пока для каждого значения CustomerlD (идентификатор покупателя) можно вводить только одну строку, то имеется первичный ключ, состоящий из одного атрибута, поэтому таблица находится во второй нормальной форме. Однако таблица все равно подвержена аномалиям. А что если покупателю 1010, к примеру, не повезет с отбеливателем и он вернет свою покупку, получив назад деньги? Вы собираетесь удалить из таблицы третью строку, в которой записаны данные о том, что покупатель 1010 приобрел отбеливатель. Но тут возникает проблема. Если строка будет удалена, то также будут удалены данные о том, что цена отбеливателя составляет 4 доллара. Такая ситуация является примером транзитивной зависимости. Атрибут Price (цена) зависит от атрибута Product (товар), который, в свою очередь, зависит от первичного ключа CustomerlD.
Проблема транзитивной зависимости решается с помощью разделения таблицы SALES на две. Две таблицы, CUST_PURCH (покупки) и PROD_PRICE (цена товара), составляют базу данных, находящуюся в третьей нормальной форме (см. Рисунок 5.3).
Ускорение работы базы данных с помощью ключей
Ускорение работы базы данных с помощью ключейВ проектировании баз данных используется хорошее правило: в таблице каждая строка отличается от любой другой, т.е. каждая строка должна быть уникальной. Иногда вам может потребоваться для определенной цели — например, для статистического анализа — извлечь из своей базы некоторые данные и создать на их основе таблицы, где строки не обязательно являются уникальными. Для данного конкретного случая правило отсутствия дублирования может не выполняться. Однако в общем его нужно строго придерживаться.
Ключ — это атрибут или сочетание атрибутов, которое однозначно определяет строку в таблице. Чтобы получить доступ к строке, необходимо иметь способ, позволяющий отличить эту строку от всех остальных. Так как ключи уникальны, они позволяют выполнять такой доступ. Более того, ключ никогда не должен содержать неопределенное значение. При использовании неопределенных ключей две строки, содержащие такие ключевые поля, были бы неотличимы друг от друга.
Что касается примера с ветеринарной лабораторией, то здесь в качестве ключей вы можете назначить подходящие для этого столбцы. В таблице CLIENT хороший ключ получается из столбца ClientName. Этот ключ может отличить любого клиента от всех остальных. Таким образом, ввод значения в этот столбец становится обязательным для каждой строки таблицы CLIENT. Из столбцов TestName и EmployeeName получаются хорошие ключи для таблиц TESTS и EMPLOYEE. To же относится к столбцам OrderNumber и ResultNumber таблиц ORDERS и RESULTS соответственно. В любом случае надо проверять, вводится ли в каждую строку уникальное значение ключа.
Ключи могут быть двух видов: первичные и внешние. Ключи, о которых говорилось в предыдущем абзаце, на самом деле являются первичными. Благодаря им гарантируется уникальность строк. О внешних ключах рассказывается в этом разделе чуть позже.
В таблице SALESJRACK составной
Рисунок 5.4. В таблице SALESJRACK составной ключ состоит из столбцов CustomerlD и Product
На Рисунок 5.4 столбец CustomerlD однозначно строку не определяет. В двух строках его значения равны 1001. Еще в двух равны 1010. Однако строку однозначно определяет комбинация столбцов CustomerlD и Product. Вместе эти столбцы и являются составным ключом.
Если бы не то условие, что одни покупатели имеют скидку, а другие нет, то таблица не была бы во второй нормальной форме, потому что столбец Price (цена), являющийся неключевым атрибутом, зависел бы только от столбца Product (товар). Но так как часть покупателей имеет скидку, то PRICE зависит и от CustomerlD, и от Product, так что таблица все же находится во второй нормальной форме.
При проектировании структуры базы данных
ВниманиеПри проектировании структуры базы данных важно учитывать интересы ее будущих пользователей, а также лиц, принимающих решения на основе ее информации. Если созданная вами "разумная" структура не будет соответствовать способу, каким эти люди работают с информацией, то в лучшем случае ваша система может разочаровать своих пользователей. Она может даже выдавать неверную информацию, что намного хуже, чем просто быть трудной в использовании. Такого не должно быть! Хорошо подумайте, перед тем как принимать решение о структуре таблиц. Рассмотрим пример, который показывает процесс создания многотабличной базы данных. Скажем, вы только что основали VetLab — клиническую микробиологическую лабораторию, где проводятся анализы проб, присланных из ветеринарных фирм. Конечно, вам бы хотелось иметь данные:
Внимание
Не попадитесь на удочку, создавая индексы для получения относящихся к заказам данных, которые вы вряд ли когда-нибудь будете использовать. С поддержкой индекса связана определенная потеря производительности, потому что эта поддержка является дополнительной операцией, которую компьютер должен выполнять каждый раз, когда модифицирует поле индекса или добавляет (удаляет) строку в таблице данных. Чтобы добиться оптимальной производительности, создавайте только те индексы, которые действительно собираетесь использовать в качестве ключей получения данных, и только для таблиц, имеющих большое количество строк. Ведь в противном случае индексы только ухудшат производительность. Совет 3
Совет 3
Вам, возможно, потребуется составить нечто похожее на месячный или квартальный отчет, где нужны данные, выстроенные в каком-либо необычном порядке. В таком случае, перед тем как генерировать отчет, создайте индекс. Затем, после того как отчет будет готов, удалите этот индекс. Он не должен обременять СУБД, которой пришлось бы его поддерживать в течение долгого времени между отчетами.
Вторая нормальная форма
Вторая нормальная формаЧтобы оценить вторую нормальную форму, необходимо понимать, что такое функциональная зависимость. Функциональная зависимость — это связь между атрибутами. Один атрибут функционально зависит от другого, если значение второго атрибута определяет значение первого. Значение первого атрибута можно определить, зная значение второго.
Предположим, например, что у таблицы имеются атрибуты (столбцы): StandardCharge (стандартная плата), NumberOfTests (число анализов) и TotalCharge (общая плата), которые связаны следующей формулой:
TotalCharge = StandardCharge * NumberOfTests
В таком случае столбец TotalCharge функционально зависим от двух других: Standard-Charge и NumberOfTests. Если известны значения StandardCharge и NumberOfTests, то можно определить значение TotalCharge.
Каждая таблица в первой нормальной форме должна иметь уникальный первичный ключ. Этот ключ может состоять из одного или множества столбцов. Ключ, состоящий из множества столбцов, называется составным. Чтобы таблица была во второй нормальной форме (2НФ), все ее неключевые атрибуты (столбцы) должны зависеть от всего ключа. Таким образом, каждое отношение в 1НФ, которое имеет ключ, состоящий из одного атрибута, автоматически находится во второй нормальной форме. Если у отношения имеется составной ключ, то все неключевые атрибуты должны зависеть от всех компонентов ключа. Пусть у вас есть таблица с неключевыми атрибутами, которые не выполняют это условие. Тогда вам, возможно, захочется разбить таблицу на не менее чем две новые, чтобы в каждой из них все неключевые атрибуты зависели от всех компонентов первичного ключа.
Звучит достаточно запутанно? Тогда для ясности рассмотрим пример. Пусть имеется таблица SALES_TRACK (данные о продажах), аналогичная таблице SALES (продажи) (см. Рисунок 5.2). Правда, вместо того чтобы записывать для каждого покупателя только одну покупку, вы вводите для него строку каждый раз, когда он впервые покупает какой-либо вид товара.
Кроме того, первые покупатели товара (те, у кого значения столбца CustomerlD лежат в диапазоне 1001-1009) получают скидку. Некоторые строки этой таблицы приведены на Рисунок 5.4.
Ввод неправильных данных
Ввод неправильных данныхВ документах или файлах с исходной информацией, которыми вы пользуетесь для заполнения своей базы, могут быть неправильные данные. Они могут быть неправильным вариантом правильных данных или быть совсем не теми данными, которые вам нужны. Проверки с помощью диапазонов допустимых значений показывают, имеют ли данные доменную целостность. Хотя такие проверки и помогают преодолеть некоторые трудности, но, откровенно говоря, далеко не все. Если в полях имеются неправильные значения, не выходящие из допустимого диапазона, то в результате проверок ничего подозрительного обнаружено не будет.
Зачем нужен индекс
Зачем нужен индексПредположим, мне нужно обрабатывать таблицу в том порядке, при котором значения из поля ClientName будут выстроены по алфавиту. При этом у меня есть индекс, в котором значения из этого поля выстроены именно по алфавиту. Тогда свою работу я смогу выполнять так же быстро, как если бы сами записи таблицы данных были выстроены в том же порядке. У меня есть возможность последовательно перемещаться по строкам индекса и при этом немедленно переходить, используя указатель индекса, на те строки таблицы данных, которые соответствуют строкам индекса.
При использовании индекса время обработки таблицы пропорционально N, где N — количество ее строк. А при выполнении той же операции, но без индекса, время обработки таблицы
пропорционально NlgN, где igN — логарифм N по основанию 2. Для небольших таблиц разница между этими значениями получается незначительная, но для больших — огромная. Некоторые операции с большими таблицами без помощи индексов требуют слишком много времени.
Например, у вас есть таблица с 1000000 записей (N = 1000000) и на обработку каждой записи уходит одна миллисекунда (одна тысячная секунды). Если у вас есть индекс, то на обработку всей таблицы уйдет только 1000 секунд, т.е. меньше 17 минут. Однако, чтобы получить тог же результат без индекса, таблицу придется обрабатывать 100000x20 раз. Таким образом, этот процесс должен занять 20000 секунд, т.е. больше пяти с половиной часов. Думаю, вы согласитесь, что разница между семнадцатью минутами и пятью с половиной часами довольно-таки существенная. Это и есть та разница, которая создается индексированием записей.
Злой умысел
Злой умыселНе следует исключать возможности умышленной порчи данных. Ваша первая линия обороны — это запрещение доступа к базе данных потенциально опасным пользователям, а также ограничение доступа всем остальным только тем, что им нужно. Вторая линия —- хранение резервных копий данных в безопасном месте. Периодически проверяйте, насколько защищена ваша база данных.
Добавление данных только в выбранные столбцы
Добавление данных только в выбранные столбцыИногда нужно где-то отметить, что объект существует, даже если по нему еще нет всех данных. Если у вас для таких объектов есть таблица базы данных, то строку по новому объекту можно вставить в нее, не заполняя значениями все столбцы этой строки. И если вы хотите, чтобы таблица была в первой нормальной форме, то необходимо вставить столько данных, чтобы можно было отличать новую строку от всех остальных строк этой таблицы. (О первой нормальной форме см. в главе 5.) Для этого в новой строке достаточно вставить первичный ключ. Кроме этого ключа вставляйте все остальные данные, которые известны об этом объекте. В тех столбцах, куда данные не вводятся, остаются значения NULL.
Ниже приведен пример такого частичного ввода строки.
INSERT INTO CUSTOMER (CustomerlD, FirstName, LastName)
VALUES (:vcustid, 'Tyson1, 'Tylor') ;
Вы вставляете только уникальный идентификационный номер клиента, а также его имя и фамилию. А в остальных столбцах этой строки будут находиться значения NULL.
Добавление данных в виде отдельных записей
Добавление данных в виде отдельных записейВ большинстве СУБД поддерживается ввод данных с помощью форм. Такая возможность позволяет создать экранную форму, в которой для каждого табличного столбца из базы данных имеется свое поле. Находящиеся в форме метки полей позволяют легко определить, какие данные следует вводить в каждое поле. Оператор вручную вводит в форму все данные, предназначенные для одной строки. После того как СУБД примет новую строку, она очищает форму, чтобы в нее можно было вводить данные для следующей строки. Таким образом можно легко добавить в таблицу данные в виде отдельных строк одну за другой.
Ввод данных с помощью форм легко использовать, кроме того, при таком вводе допускается меньше ошибок, чем при вводе списков значений, отделяемых друг от друга запятыми. Основная трудность ввода данных с помощью форм состоит в том, что он не является стандартным. В различных системах СУБД имеются собственные методы создания форм. Для оператора, занятого вводом данных, не имеет значения, каким образом создана форма ввода. Можно сделать та, что форма в любой СУБД будет выглядеть в основном одинаково. А вот разработчику приложений при переходе к новым инструментам разработки приходится учиться с самого начала. При вводе данных с помощью форм встречается и другая трудность. Она состоит в том, что в некоторых реализациях нельзя выполнять полную проверку правильности вводимых данных.
Самый лучший способ поддерживать на высоком уровне целостность данных в базе — это не вводить неправильные данные. Предотвратить неправильный ввод можно, применяя ограничения к полям формы ввода. Это гарантирует, что в базу данных попадут только те значения, которые имеют правильный тип данных и являются частью заранее определенного диапазона. Конечно, с помощью такого ограничения нельзя предотвратить все возможные ошибки, но "отловить" некоторые все же можно.
Если в таблицу из базы данных вводятся значения, предназначенные для отдельной строки, то в команде INSERT используется следующий синтаксис:
INSERT INTO таблица_1 [(столбец_1, столбвц_2, ..., столбец_n)]
VALUES (значение_1, значение_2, ..., значение_n) ;
Совет 2
Совет 2
Бывает так, что средства проектирования форм вашей СУБД не могут осуществить все нужные проверки, гарантирующие целостность данных. В таком случае вам придется создавать собственные процедуры ввода, в которых вводимые значения присваиваются переменным и проверяются с помощью кода самой прикладной программы. Затем, убедившись, что все значения, введенные для табличной строки, являются правильными, программа может добавить эту строку в таблицу с помощью команды SQL INSERT (вставить).
Квадратные скобки ([]) означают, что список имен столбцов не является обязательным. По умолчанию порядок расположения столбцов в списке является таким же, как и в таблице. Если расположить значения, находящиеся после ключевого слова VALUES (значения), в том же порядке, в каком столбцы находятся в таблице, то эти элементы попадут в нужные столбцы — неважно, указаны при этом столбцы явно или нет. А если требуется расположить эти значения в порядке, который не совпадает с расположением столбцов в таблице, то тогда имена столбцов необходимо перечислить в требуемом порядке.
Чтобы ввести, например, запись в таблицу CUSTOMER (покупатель), используйте следующий синтаксис:
INSERT INTO CUSTOMER (CustomerlD, FirstName, LastName,
Street, City, State, Zipcode, Phone)
VALUES (:vcustid, 'David1, 'Taylor', '235 Nutley Ave.',
'Nutley', 'NJ', '07110', '(201) 555-1963') ;
После ключевого слова VALUES первым стоит vcustid — базовая переменная-счетчик, значение которой с помощью программного кода увеличивается на единицу, как только в таблицу введена новая строка. Это дает гарантию, что не будет дублирования значений в столбце CustomerlD (идентификатор покупателя). CustomerlD является первичным ключом для этой таблицы и поэтому должен оставаться уникальным. Остальные значения в операторе являются не переменными с элементами данных, а самими элементами данных. Конечно, элементы данных для этих столбцов также можно, если хотите, поместить в переменные. Оператор INSERT работает одинаково хорошо с аргументами ключевого слова VALUES, выраженными как в форме переменных, так и в форме значений.
Добавление новых данных
Добавление новых данныхВ базе данных каждая таблица появляется на свет пустой, т.е. сразу после своего создания (или с помощью DDL из SQL, или с помощью RAD-инструмента) такая таблица является не чем иным, как структурной оболочкой, не содержащей данных. Чтобы таблица стала полезной, в нее необходимо поместить некоторые данные. Эти данные могут быть уже в цифровом виде, т.е. введенными в компьютер, или еще нет.
Добавление в таблицу группы строк
Добавление в таблицу группы строкДобавлять в таблицу строки одну за другой, используя для этого оператор INSERT, становится ужасно скучным — особенно если это растягивается на целый день. Даже вводить данные в экранную форму, тщательно продуманную с точки зрения эргономики, через некоторое время становится утомительным. Ясно, что если у вас есть надежный способ автоматического ввода данных, вы будете стараться использовать его вместо ввода вручную везде, где это возможно.
Автоматический ввод, например, можно использовать тогда, когда данные уже представлены в электронном виде, благодаря тому, что кто-то уже ввел вручную эти данные в компьютер. Незачем повторять эту рутинную работу. Перенести данные из одного файла в другой можно с минимальным участием человека. Если вам известны характеристики исходных данных и нужная форма таблицы, в которую должны быть перенесены данные, то компьютер может в принципе выполнить такой перенос данных автоматически.
Копирование из внешнего файла данных
Копирование из внешнего файла данныхПредположим, что вы создаете базу данных для нового приложения. Некоторые из нужных вам данных уже имеются в каком-либо файле. Это может быть плоский файл или таблица базы данных, работающей в СУБД, отличающейся от той, которую используете вы. Данные могут быть в коде ASCII, EBCDIC или в каком-нибудь другом закрытом внутреннем формате. Так что же делать?
Прежде всего — это надеяться и молиться, чтобы нужные вам данные были представлены в каком-нибудь широко используемом формате. Если это достаточно популярный формат, то у вас имеется хороший шанс достать утилиту преобразования формата, которая может преобразовать данные в один или несколько других популярных форматов. А затем как минимум один из этих форматов можно импортировать в вашу среду разработки. А если повезет, то можно будет преобразовать текущий формат данных с помощью встроенных средств среды. Вероятно, самыми распространенными на персональных компьютерах форматами являются Access, dBASE и Paradox. Если нужные вам данные находятся в одном из этих форматов, преобразование должно пройти легко. Ну а если формат данных является не таким распространенным, то, видимо, преобразование все-таки придется провести в два этапа.
И с последней надеждой можно всегда обратиться к специальным профессиональным службам преобразования данных. Они специализируются на оказании услуг по преобразованию компьютерных данных из одного формата в другой. У них есть возможность обрабатывать сотни разных форматов — о большинстве из которых никто никогда и не слышал. Передайте одной из этих служб ленту или диск с данными в первоначальном формате, и вы получите назад те желанные, но преобразованные в любой из форматов, который только укажете.
Обновление имеющихся данных
Обновление имеющихся данныхВсе течет, все изменяется. Если вам не нравится нынешнее положение дел, то надо немного подождать. Через некоторое время существующее положение изменится.
И так как мир постоянно преобразуется, то надо также обновлять и базы данных, с помощью которых моделируются его компоненты. Покупатель, возможно, поменял адрес. Может измениться количество какого-либо товара на складе, будем надеяться не в результате воровства, а потому что товар хорошо расходится. Это типичные примеры тех событий, из-за которых приходится обновлять базу данных.
В языке SQL для изменения данных, хранящихся в таблице, имеется оператор UPDATE (обновить). С помощью одного такого оператора можно изменить в таблице одну строку, несколько или все ее строки. В операторе UPDATE используется следующий синтаксис:
UPDATE имя_таблицы
SET столбец_1 - выражение_1, столбец_2 = выражение_2,
. . . , столбец_n = выражение__n
[WHERE предикаты] ;
Предложение WHERE (где) не является обязательным. Оно указывает, какие строки должны обновляться. Если это предложение не используется, то будут обновляться все строки таблицы. В свою очередь предложение SET (установить) указывает новые значения изменяемых столбцов.
Проанализируйте с помощью табл. 6.1 таблицу CUSTOMER (покупатель), имеющую столбцы Name (имя и фамилия), City (город), Area-Code (телефонный код региона) и Telephone (телефон).
Обновление представлений
Обновление представленийСозданные таблицы автоматически поддерживают возможности вставки, обновления И удаления данных. А вот к представлениям это относится не всегда. Обновляя представление, вы на самом деле обновляете исходную таблицу. Вот две потенциальные проблемы, возникающие при обновлении представлений.
CREATE VIEW COMP AS
SELECT EmpName, Salary+Comm AS Pay
FROM EMPLOYEE ;
Можно ли в представлении обновить столбец PAY (оплата), используя следующий оператор?
UPDATE COMP SET Pay = Pay + 100 ;
Нет, этот подход не сработает потому, что в таблице EMPLOYEE нет столбца Pay, и в ней он обновлен не будет, а следовательно, не будет обновлен и в представлении. Представление не может показать того, чего нет в исходной таблице.
Помни: Когда вы думаете об обновлении представлений, не забывайте следующее правило. Столбец представления обновлять нельзя, если он не соответствует столбцу таблицы этого представления.
Перемещение данных
Перемещение данныхПомимо команд INSERT и UPDATE, можно воспользоваться командой MERGE (слияние), чтобы добавить данные в таблицу или представление. Команда MERGE позволяет производить "слияние" данных исходных таблиц, представления — в нужные таблицы или сами представления. Эта же команда позволяет вставить новые строки в нужную таблицу или обновить существующие строки. Таким образом, команда MERGE представляет собой весьма удобный способ копирования уже существующих данных из одного местоположения в новое, необходимое пользователю.
Возьмем, к примеру, базу данных VetLab (см. главу 5). Предположим, что некоторые работники, занесенные в таблицу EMPLOYEE, — это продавцы, которые уже приняли заказы, а другие — это работники, не связанные напрямую с продажами, или продавцы, которые еще не взяли заказы. Только что закончившийся год был прибыльным, поэтому вы решили дать премии по 100 долларов каждому, кто принял по крайней мере один заказ, и по 50 долларов всем остальным. Для начала давайте создадим таблицу BONUS (бонус) и вставим в нее записи для каждого работника, который появляется хотя бы однажды в таблице ORDERS, задавая каждой записи значение премии по умолчанию 100 долларов.
Затем воспользуемся командой MERGE, чтобы вставить новые записи для тех работников, которые не имеют заказов, давая им премии 50 долларов. Ниже приведен программный код, который позволяет создать и заполнить таблицу BONUS.
| СREARE TABLE BONUS ( | ||
| EmployeeName | CHARACTER (30) | PRIMARY KEY |
| Bonus | NUMERIC | DEFAULT 100 ) ; |
(SELECT EmployeeName FROM EMPLOYEE, ORDERS
WHERE EMPLOYEE.EmployeeName = ORDERS.Salesperson
GROUP BY EMPLOYEE.EmployeeName) ;
Теперь сделаем запрос для таблицы BONUS и посмотрим, что она содержит.
| SELECT * FROM BONUS ; | |
| EmployeeName | BONUS |
| ---------------- | ------- |
| Brynna Jones | 100 |
| Chris Bancroft | 100 |
| Greg Bosser | 100 |
| Kyle Weeks | 100 |
MERGE INTO BONUS
USING EMPLOYEE
ON (BONUS.EmployeeName = EMPLOYEE.EmployeeName)
WHEN NOT MATCHED THEN INSERT
(BONUS.EmployeeName, BONUS,bonus)
VALUES (EMPLOYEE.EmployeeName, 50) ;
Записи для людей в таблице EMPLOYEE, которые не соответствуют записям для тех же людей, но уже в таблице BONUS, будут вставлены в таблицу BONUS. Теперь запрос таблицы BONUS дает следующее:
| SELECT * FROM BONUS ; | |
| EmployeeName | BONUS |
| ---------------- | ------- |
| Brynna Jones | 100 |
| Chris Bancroft | 100 |
| Greg Bosser | 100 |
| Kyle Weeks | 100 |
| Neth Doze | 50 |
| Matt Bak | 50 |
| Sam Saylor | 50 |
| Nic Foster | 50 |
Перенос всех строк из одной таблицы в другую
Перенос всех строк из одной таблицы в другуюМного проще, чем импортировать внешние данные, извлекать данные, уже находящиеся водной из таблиц вашей базы, и комбинировать их с данными из другой таблицы. В самом простом случае структура второй таблицы идентична структуре первой. Это означает, что каждый столбец первой таблицы имеет соответствующий столбец во второй, а типы данных соответствующих столбцов совпадают. В таком случае содержимое двух таблиц можно комбинировать с помощью реляционного оператора UNION (объединение). В результате получается виртуальная таблица, в которой содержатся данные исходных таблиц. О реляционных операторах, в том числе о UNION, рассказывается в главе 10.
Перенос выбранных столбцов и строк из одной таблицы в другую
Перенос выбранных столбцов и строк из одной таблицы в другуюЧасто бывает так, что данные исходной таблицы не соответствуют в точности структуре той таблицы, в которую вы собираетесь их поместить. Возможно, соответствуют друг другу только некоторые из столбцов — и это как раз те столбцы, которые вы хотите перенести. Комбинируя операторы SELECT с помощью оператора UNION, можно указать, какие столбцы из исходных таблиц должны войти в полученную в результате виртуальную таблицу. Используя в операторах SELECT предложения WHERE, можно помещать в виртуальную таблицу только те строки, которые удовлетворяют определенным условиям. Предложения WHERE достаточно подробно описываются в главе 9.
Предположим, у вас имеются две таблицы, PROSPECT (потенциальный клиент) и CUSTOMER (покупатель), и вам нужно составить список всех жителей штата Мэн, данные о которых находятся в обеих таблицах. Тогда можете создать виртуальную таблицу с нужной информацией, используя следующую команду:
SELECT FirstName, LastName
FROM PROSPECT
WHERE State = 'ME'
UNION
SELECT FirstName, LastName
FROM CUSTOMER
WHERE State = 'ME'
В этом коде заключено следующее:
INSERT INTO PROSPECT
SELECT * FROM CUSTOMER
WHERE State = 'ME' ;
Получение данных
Получение данныхЗадача, которую выполняют пользователи, манипулируя данными чаще всего состоит в том, чтобы получить из базы выбранную информацию. Допустим, вы хотите получить содержимое одной определенной строки, находящейся в таблице среди тысяч других. Или, возможно, требуется получить все строки, удовлетворяющие какому-либо условию или комбинации условий. А может быть, вы хотите получить из таблицы все ее строки. Для решения всех этих задач предназначен оператор SQL SELECT.
Проще всего с помощью оператора SELECT получить все данные, хранящиеся во всех строках определенной таблицы. Для этого используется следующий синтаксис:
SELECT * FROM CUSTOMER ;
Помни: Звездочка (*)— это символ-маска, который означает "все". В данном примере этот символ стоит вместо перечня всех имен столбцов из таблицы CUSTOMER. В результате выполнения этого оператора на экран выводятся все данные, находящиеся во всех строках и столбцах этой таблицы.
Операторы SELECT могут быть намного сложнее, чем тот, который приведен в примере. Некоторые из них могут быть настолько сложными, что в них становится почти невозможно разобраться. Это связанно с тем, что есть возможность к основному оператору присоединять еще и множество уточняющих предложений. Подробно об уточняющих предложениях рассказывается в главе 9. В этой же главе кратко говорится о предложении WHERE — самом распространенном способе ограничить количество строк, возвращаемых оператором
SELECT.
Оператор SELECT с предложением WHERE имеет такой общий вид:
SELECT список_столбцов FROM имя_таблицы
WHERE условие ;
Список столбцов указывает, какие столбцы таблицы следует отобразить при выводе. Этот оператор отобразит только те столбцы, которые вы запросите. Предложение FROM определяет имя той таблицы, столбцы которой требуется отобразить. А предложение WHERE исключает те строки, которые не удовлетворяют указанному условию. Условие может быть простым (например, WHERE CUSTOMER_STATB= 'NH', где CUSTOMER_STATE означает "штат, где проживает клиент", a NH — "штат Нью-Хэмпшир") или составным (например, WHERE CUSTOMER_STATE= 'NH' AND STATUS='Active', где STATUS означает "статус", a Active — "активный").
Следующий пример показывает, как выглядит составное условие внутри оператора SELECT:
SELECT FirstName, LastName, Phone FROM CUSTOMER
WHERE State= 'NH'
AND Status='Active' ;
Этот оператор возвращает фамилии и телефонные номера всех активных клиентов, живущих в штате Нью-Хэмпшир. Ключевое слово AND означает следующее: чтобы строка была возвращена, она должна соответствовать сразу двум условиям, State= 'NH' и Status='Active'.
Представление ORDERS_BY_STATE
Рисунок 6.1. Представление ORDERS_BY_STATE, предназначенное для менеджера по маркетингу
Представление для менеджера по маркетингу создается с помощью следующего оператора:
CREATE VIEW ORDERS_BY_STATE
(ClientName, State, OrderNumber)
AS SELECT CLIENT. ClientName, State, OrderNumber
FROM CLIENT, ORDERS
WHERE CLIENT.ClientName = ORDERS.ClientName;
В новом представлении имеются три столбца: ClientName (название фирмы-клиента), State (штат) и OrderNumber (номер заказа). ClientName находится как в CLIENT, так и в ORDERS и используется для связи между этими двумя таблицами. Новое представление получает информацию из столбца State таблицы CLIENT и берет для каждого заказа значение из столбца OrderNumber таблицы ORDERS. В приведенном примере имена столбцов нового представления объявляются явно. Впрочем, если имена точно такие же, как и у соответствующих столбцов исходных таблиц, то такое объявление не обязательно. Пример, приведенный в следующем разделе, демонстрирует похожий оператор CREATE VIEW, в котором имена столбцов для представления явно не указываются, а только подразумеваются.
Представление REPORTINGJAG (задержка
Рисунок 6.2. Представление REPORTINGJAG (задержка результатов), предназначенное для чиновника из службы контроля качества
Ниже приведен код, с помощью которого создается представление, приведенное на Рисунок 6.2.
CREATE VIEW REPORTING_LAG
AS SELECT ORDERS.OrderNumber, OrderDate, DateReported
FROM ORDERS, RESULTS
WHERE ORDERS. OrderNumber = RESULTS. OrderNumber
AND RESULTS.PreliminaryFinal = 'F' ;
В представлении REPORTTNG_LAG содержится информация из таблицы ORDERS по датам заказов и из таблицы RESULTS по датам окончательных результатов. В этом представлении появляются только строки, у которых в столбце PRELIMJFINAL (предварительный-окончательный), взятом из таблицы RESULTS, находится значение 'F (от слова "final" — окончательный).
З Представление созданное
Рисунок 6.З. Представление, созданное, чтобы показать скидки в честь дня рождения
Представления можно создавать как на основе множества таблиц, что и делалось в предыдущих примерах, так и на основе всего лишь одной таблицы. Если вам не нужны определенные столбцы и строки какой-либо таблицы, то создайте представление, в котором нет этих строк и столбцов, а затем работайте уже не с таблицей, а с представлением. Этот подход защищает пользователя от путаницы и не отвлекает внимание, которые бывают при просмотре участков таблицы, не относящихся к делу.
Совет 1
Совет 1
Еще одной причиной создания представлений является обеспечение безопасности исходных таблиц базы данных. Одни столбцы ваших таблиц следует открыть, а другие, наоборот, скрыть. В таком случае можно создать представление только с теми столбцами, которые вы хотите сделать доступными, затем предоставить к нему широкий доступ, а доступ к исходным таблицам этого представления— ограничить. В главе 13 объясняется, каким образом обеспечивается безопасность баз данных, и описывается, как предоставлять полномочия на доступ к данным и лишать их.
Создание представлений из таблиц
Создание представлений из таблицДля менеджера по маркетингу можно создать представление ORDERS_BY_STATE (заказы по штатам), приведенное на Рисунок 6.1.
Создание представлений
Создание представленийСтруктура базы данных, спроектированной в соответствии с разумными принципами — включая и подходящую нормализацию, — обеспечивает максимальную целостность данных. Однако такая структура часто не позволяет обеспечить лучший способ их просмотра. Одни и те же данные могут использоваться разными приложениями, и у каждого из них может быть своя специализация. Одним из самых сильных качеств SQL является возможность выводить данные в виде представлений, чья структура отличается от структуры тех таблиц базы, в которых реально хранятся эти данные. Таблицы, столбцы и строки которых используются при создании представления, называются базовыми. В главе 3 говорилось о представлениях как о части языка определения данных (Data Definition Language, DDL). А в этом разделе представления рассматриваются как одно из средств, предназначенных для получения данных и манипуляции ими.
Оператор SELECT всегда возвращает результат в виде виртуальной таблицы. Представление же принадлежит к особой разновидности этих таблиц. От остальных виртуальных таблиц оно отличается тем, что в метаданных базы хранится его определение. Представление других виртуальных таблиц похоже на реальную таблицу базы данных. Работать с представлением можно так же, как и с настоящей таблицей базы данных. Разница здесь в том, что совокупность данных, находящихся в представлении, не является физически независимой частью базы данных. Представление извлекает их из одной или множества таблиц, из которых и были выбраны его столбцы. В каждом приложении могут быть свои собственные, непохожие друг на друга представления, но созданные на основе одних и тех же данных.
Обратите внимание на базу данных VetLab (см. главу 5). Эта база состоит из пяти таблиц: CLIENT (фирма-клиент), TESTS (анализы), EMPLOYEE (сотрудник), ORDERS (заказы) и RESULTS (результаты). Предположим, что главному менеджеру по маркетингу компании VetLab необходимо посмотреть, из каких штатов в эту компанию приходят заказы. Часть этой информации находится в таблице CLIENT, а часть — в ORDERS. А чиновнику из службы контроля качества требуется сравнить дату оформления заказа на один из анализов и дату получения его окончательного результата. Для этого сравнения требуются некоторые данные из таблицы ORDERS и RESULTS. В каждом конкретном случае можно создать представления, предоставляющие в точности те данные, которые требуются.
Создание представления с модифицированным атрибутом
Создание представления с модифицированным атрибутомВ примерах из двух предыдущих разделов предложения SELECT содержат только имена столбцов. Впрочем, в любом предложении SELECT может находиться не только имя, но и выражение. Предположим, что владелец VetLab отмечает свой день рождения и хочет в честь этого события предоставить всем своим клиентам 10-процентную скидку. Он может на основе двух таблиц, ORDERS и TESTS, создать представление BIRTHDAY (день рождения).
Вполне возможно, что оно будет создано так, как показано в следующем примере:
CREATE VIEW BIRTHDAY
(ClientName, Test, OrderDate, BirthdayCharge)
AS SELECT ClientName, TestOrdered, OrderDate,
StandardCharge * .9
FROM ORDERS, TESTS
WHERE TestOrdered = TestName ;
Обратите внимание, что в представлении BIRTHDAY второй столбец — Test (анализ) — соответствует столбцу TestOrdered (заказанный анализ) из таблицы ORDERS, который также соответствует столбцу TestName (название анализа) из таблицы TESTS. Как создать это представление, можно увидеть на Рисунок 6.3.
Создание представления с условием выборки
Создание представления с условием выборкиКак видно в примере на Рисунок 6.2, для чиновника из службы контроля качества требуется представление, отличающееся от того, которое использует менеджер по маркетингу.
Таблица CUSTOMER
Таблица 6.1. Таблица CUSTOMER| Name | City | AreaCode | Telephone |
| Abe Abelson | Springfield | (714) | 555-1111 |
| Bill Bailey | Decatur | (714) | 555-2222 |
| Chuck Wood | Philo | (714) | 555-3333 |
| Don Stetson | Philo | (714) | 555-4444 |
| Dolph Stetson | Philo | (714) | 555-5555 |
UPDATE CUSTOMER
SET City = 'Kankakee1, Telephone = '666-6666'
WHERE Name = 'Abe Abelson1 ;
В результате его выполнения в записи произошли изменения, которые показаны в табл. 6.2.
Таблица CUSTOMER после
Таблица 6.2. Таблица CUSTOMER после обновления одной строки оператором update| Name | City | AreaCode | Telephone |
| Abe Abelson | Kankakee | (714) | 666-6666 |
| Bill Bailey | Decatur | (714) | 555-2222 |
| Chuck Wood | Philo | (714) | 555-3333 |
| Don Stetson | Philo | (714) | 555-4444 |
| Dolph Stetson | Philo | (714) | 555-5555 |
UPDATE CUSTOMER
SET AreaCode = '(619)'
WHERE City = 'Philo' ;
Теперь таблица CUSTOMER выглядит так, как показано в табл. 6.3.
Таблица CUSTOMER после
Таблица 6.3. Таблица CUSTOMER после обновления нескольких строк оператором update| Name | City | Area-Code | Telephone |
| Abe Abelson | Kankakee | (714) | 666-6666 |
| Bill Bailey | Decatur | (714) | 555-2222 |
| Chuck Wood | Philo | (619) | 555-3333 |
| Don Stetson | Philo | (619) | 555-4444 |
| Dolph Stetson | Philo | (619) | 555-5555 |
UPDATE CUSTOMER
SET City = 'Rantoul' ;
Результат показан в табл. 6.4.
Таблица CUSTOMER после
Таблица 6.4. Таблица CUSTOMER после обновления всех строк оператором update| Name | City | Area-Code | Telephone |
| Abe Abelson | Rantoul | (714) | 666-6666 |
| Bill Bailey | Rantoul | (714) | 555-2222 |
| Chuck Wood | Rantoul | (619) | 555-3333 |
| Don Stetson | Rantoul | (619) | 555-4444 |
| Dolph Stetson | Rantoul | (619) | 555-5555 |
Предположим, что вы оптовый продавец и в вашей базе данных находится таблица VENDOR (поставщик) с названиями всех фирм-производителей, у которых вы покупаете товары. У вас также есть таблица PRODUCT (товар) с названиями всех продаваемых вами товаров и ценами, которые вы за них назначаете. В таблице VENDOR имеются столбцы VendorlD (идентификатор поставщика), VendorName (название поставщика), Street (улица), City (город), State (штаг) и Zip (почтовый код). А в таблице PRODUCT имеются столбцы ProductID (идентификатор товара), ProductName (название товара), VendorlD (идентификатор поставщика) и SalePrice (цена при продаже).
Предположим, поставщик Cumulonimbus Corporation принял решение поднять цены на все виды товаров на 10%. И для того чтобы поддержать планку своей прибыли, вам также придется поднять на 10% цены продажи продуктов, получаемых от этого поставщика. Это можно сделать с помощью следующего оператора UPDATE:
UPDATE PRODUCT
SET SalePrice = (SalePrice * 1.1)
WHERE VendorlD IN
(SELECT VendorlD FROM VENDOR
WHERE VendorName = 'Cumulonimbus Corporation') ;
Подстрока находит то значение из столбца VendorlD, которое соответствует Cumulonimbus Corporation. Затем полученное значение можно использовать для поиска в таблице PRODUCT тех строк, которые следует обновить. Цены всех товаров, полученных от Cumulonimbus Corporation, повышаются на 10%, а цены остальных остаются прежними. О подвыборках более подробно рассказывается в главе 11.
Удаление устаревших данных
Удаление устаревших данныхС течением времени данные могут устаревать и становиться бесполезными. Ненужные данные, находясь в таблице, только замедляют работу системы, расходуют память и путают пользователей. Возможно, лучше перенести старые данные в архивную таблицу, а затем поместить архив с этой таблицей вне системы. Таким образом, в случае такого маловероятного события, когда потребуется снова взглянуть на эти данные, их можно будет восстановить. А в остальное время они не будут замедлять ежедневную обработку данных. Впрочем, независимо от того, было ли решено архивировать устаревшие данные или нет, все же наступит время, когда их надо будет удалить. Для удаления строк из таблицы, находящейся в базе данных, в языке SQL предназначен оператор DELETE (удалить).
С помощью единственного оператора DELETE можно удалить как все строки таблицы, так и некоторые из них. Строки, предназначенные для удаления, можно выбрать, используя в операторе DELETE необязательное предложение WHERE. Синтаксис оператора DELETE такой же, как и в операторе SELECT, за исключением того, что столбцы указывать не надо. При удалении строки удаляются все данные, находящиеся в ее столбцах.
Предположим, например, что ваш клиент Дэвид Тейлор переехал на Таити и больше ничего у вас покупать не собирается. Вы можете удалить его данные из таблицы CUSTOMER, используя следующий оператор:
DELETE FROM CUSTOMER
WHERE FirstName = 'David' AND LastName = 'Taylor' ;
Если у вас только один покупатель, которого зовут Дэвид Тейлор, то этот оператор будет выполнен безупречно. А если существует вероятность, что Дэвидом Тейлором зовут как минимум двух ваших покупателей? Чтобы удалить данные именно того из них, к кому вы потеряли интерес, добавьте в предложение WHERE дополнительные условия (для таких столбцов, как, например. Street, Phone или CustomerlD).
но зато увеличивается производительность выборок.
ВниманиеДаже если эта операция и создает избыточные данные — данные о покупателях теперь хранятся в обеих таблицах, в PROSPECT и CUSTOMER, — но зато увеличивается производительность выборок. Чтобы избежать избыточности и поддерживать согласованность данных, делайте так, чтобы строки в одной таблице не вставлялись, не изменялись и не удалялись без вставки, изменения и удаления соответствующих строк в другой таблице. Может возникнуть еще одна проблема. Возможно, что оператор INCERT продублирует первичные ключи. Если существует один-единственный потенциальный клиент, имеющий ключ ProspectID, который совпадает с соответствующим первичным ключом CustomerlD покупателя, введенного в таблицу PROSPECT, тогда операция вставки будет неудачной.
ABS
ABSФункция ABS возвращает абсолютное значение числового выражения.
ABS (-273)
Функция возвращает 273.
AVG
AVGФункция AVG (среднее) подсчитывает и возвращает среднее арифметическое всех значений, находящихся в определенном столбце. Конечно, эту функцию можно применять только к столбцам с числовыми данными, как в следующем примере:
SELECT AVG (Fat)
FROM FOODS ;
В результате получается среднее содержание жиров, равное 15,37. Это число достаточно высокое. Дело в том, что весь подсчет портит информация по сливочному маслу. Возможно, вы зададите себе вопрос, а каким было бы среднее содержание жиров, если бы не учитывалось масло. Чтобы ответить на него, в оператор можно поместить выражение WHERE:
SELECT AVG (Fat)
FROM FOODS
WHERE FOOD <> 'Butter' ;
В этом случае содержание жиров в 100 граммах пищевых продуктов в среднем падает до 10,32 грамма.
CARDINALITY
CARDINALITYЭта функция работает с коллекциями элементов, такими как массивы или мультимножества, где каждый элемент является значением определенного типа данных. Кардинальное число коллекции — это количество содержащихся в ней элементов. Рассмотрим один из примеров использования функции CARDINALITY:
CARDINALITY (TeamRoster)
Например, если в списке членов команды значится двенадцать человек, то эта функция возвращает значение 12. Столбец TeamRoster таблицы TEAM может быть как массивом, так и мультимножеством. В свою очередь, массив — это упорядоченная коллекция элементов, а мультимножество — неупорядоченная коллекция. Для списка команды, который может изменяться достаточно часто, разумней использовать мультимножество.
CEIL или CEILING
CEIL, или CEILINGДанная функция округляет числовое выражение до наименьшего целого числа, которое не меньше, чем данное выражение.
CEIL (3,141592)
Функция возвращает значение 4,0.
CHARACTER_LENGTH
CHARACTER_LENGTHФункция CHARACTER_LENGTH (длина в символах) возвращает количество символов, находящихся в символьной строке. Например, следующее выражение возвращает 15:
CHARACTER_LENGTH ( ' Жареный опоссум' )
Помни: То, что уже говорилось в этой главе по поводу функции SUBSTRING, относится и к CHARACTER_LENGTH — эта функция не особенно полезна, если ее аргументами являются литералы, например, такие как 'Жареный опоссум'. Вместо выражения CHARACTER_LENGTH ('Жареный опоссум') можно написать число 15. Действительно, написать '15' проще. Функция SUBSTRING становится более полезной, если ее аргумент является не литеральным значением, а выражением.
Числовые функции
Числовые функцииЧисловые функции значения могут принимать данные разных типов, но возвращают всегда числовое значение. В SQL имеется тринадцать таких функций.
Числовые выражения со значением
Числовые выражения со значениемВ числовых выражениях со значением к числовым данным можно применять операторы сложения, вычитания, умножения и деления. Такое выражение обязательно должно сводиться к числовому значению. Компоненты числового выражения со значением могут иметь разные типы данных или все могут быть числовыми. Тип данных результата зависит от типов данных компонентов, из которых получается этот результат. В стандарте SQL:2003 нет жесткого определения, каким образом тип данных результата, получаемого при выполнении выражения, должен зависеть от исходных компонентов этого выражения. Это объясняется различиями аппаратных платформ. Поэтому, если вы используете смешанные типы данных, обращайтесь к документации по той платформе, на которой работаете.
Вот некоторые примеры числовых выражений со значением.
COUNT
COUNTФункция COUNT (счет) сообщает, сколько строк находится в таблице или сколько строк таблицы удовлетворяют некоторые условия. Вот самое простое применение этой функции:
SELECT COUNT (*)
FROM FOODS ;
Функция возвращает результат, равный 15, так как она считает все строки таблицы FOODS. Тот же результат получается при выполнении следующего оператора:
SELECT COUNT (CALORIES)
FROM FOODS ;
Так как значение в столбец CALORIES (калории) было введено в каждой строке, то результат подсчета получается тот же. Правда, если в некоторых строках в этом столбце находятся неопределенные значения, функция такие строки не считает.
Следующий оператор возвращает значение, равное 11, так как в 4 из 15 строк таблицы FOODS в столбце CARBOHYDRATE (углеводы) находится значение NULL:
SELECT COUNT (CARBOHYDRATE)
FROM FOODS ;
Совет: В поле таблицы базы данных неопределенное значение может находиться по разным причинам. Самыми распространенными являются следующие: значение вообще не известно или пока не известно. Или значение, возможно, известно, но пока еще не введено. Иногда, если значение, предназначенное для какого-либо поля, равно нулю, оператор, вводящий данные, это поле обходит стороной и, таким образом, оставляет в нем значение NULL. Так поступать не надо, потому что нуль все же является определенным значением и его можно учитывать при подсчетах. A NULL определенным значением не является, и в SQL неопределенные значения не учитываются при расчетах.
Кроме того, чтобы узнать, сколько в столбце имеется различных значений, можно использовать функцию COUNT в сочетании с ключевым словом DISTINCT (различный). Проанализируйте следующий оператор:
SELECT COUNT (DISTINCT Fat)
FROM FOODS ;
Он возвращает значение, равное 12. Как видно в таблице, в 100-граммовой порции спаржи имеется столько жиров (0,2 грамма), сколько и в 100 граммах бананов, а в 100-граммовой порции фасоли лима — ровно столько жиров (0,5 грамма), сколько в 100 граммах желе. Таким образом, в таблице находится всего 12 разных значений, имеющих отношение к содержанию жиров.
ЕХР
ЕХРДанная функция возводит основание натурального логарифма е в степень, указанную числовым выражением.
ЕХР (2)
Функция возвращает значение, которое приближенно равно 7,389056. Количество знаков числа после запятой зависит от вашей реализации.
EXTRACT
EXTRACTФункция EXTRACT (извлечь) извлекает одиночное поле из значения типа даты-времени или интервала. Например, следующее выражение возвращает 08:
EXTRACT (MONTH FROM DATE '2000-08-20').
FLOOR
FLOORФункция FLOOR округляет числовое выражение до наибольшего целого числа, не превышающего данное выражение.
FLOOR (3,141592)
Функция возвращает значение 3,0.
Функции значения датывремени
Функции значения даты-времениВ языке SQL имеются три функции, которые возвращают информацию о текущей дате, текущем времени или о том и другом вместе. CURRENT_DATE возвращает текущую дату, CURRENT_TIME — текущее время, a CURRENTJTIMESTAMP — текущую дату и текущее время. Первая из этих функций не принимает аргументов, а вторые две — только один. Этот аргумент указывает точность для секундной части возвращаемого функцией значения времени. О типах данных даты-времени и о том, что такое точность, см. в главе 2.
Некоторые примеры функций значения даты-времени приведены в следующей таблице.
| Выражение | Результат |
| CURRENT_DATE | 2000-12-31 |
| CURRENT_TIME (1) | 08:36:57 .3 |
| CURRENT_TIMESTAMP (2) | 2000 12 31 08:36:57.38 |
В некоторых приложениях значения даты и времени требуется представлять в виде символьных строк. Преобразование типа данных можно выполнять с помощью выражения CAST (приведение), которое описывается в главе 8.
Функции значения
Функции значенияНекоторые операции применяются для самых разных целей. Так как эти операции приходится использовать достаточно часто, то было бы более чем оправданным включить их в SQL в виде функций значения. Конечно, если сравнивать с такими системами управления базами данных, как Access или dBASE, то в SQL этих функций довольно-таки мало, но те немногие, что есть, являются, вероятно, функциями, которые нужны вам чаще всего. В SQL используются три вида таких функций:
Функции
ФункцииФункция — это простая или достаточно сложная операция, которую обычные команды SQL выполнить не могут, но которая тем не менее достаточно часто встречается на практике. В SQL имеются функции, выполняющие работу, которую иначе пришлось бы выполнять приложению, написанному на базовом языке. В языке SQL есть две главные разновидности функций: итоговые функции и функции значения.
Интервальные выражения со значением
Интервальные выражения со значениемЕсли взять два значения даты-времени и от одного из них отнять другое, то получится интервал. Но сложение таких значений друг с другом не имеет смысла, поэтому SQL эту операцию не поддерживает. Если же сложить друг с другом два интервала или вычесть один из другого, то в результате снова получится интервал. Кроме того, интервал можно умножать или делить на числовую константу.
Вспомните, что в SQL имеются два типа интервалов: год-месяц и день-время. Чтобы избежать двусмысленности, необходимо в интервальном выражении со значением указывать, какой из этих типов в нем используется. Например, в следующем выражении вычисляется интервал в годах и месяцах от текущей даты до дня, когда вы достигнете пенсионного возраста (60 лет):
(BIRTHDAY_60 - CURRENT_DATE) YEAR TO MONTH
А это возвращает интервал в 40 дней:
INTERVAL '17' DAY + INTERVAL '23' DAY
Ниже приблизительно подсчитывается общее число месяцев, в течение которых мать пятерых детей была беременна при условии, что сейчас она не ждет шестого.
INTERVAL '9' MONTH * 5
Интервалы могут быть как положительные, так и отрицательные и состоять из любого выражения со значением или комбинации таких выражений, значением которой является интервал.
Литеральные значения
Литеральные значенияВ SQL значение может быть представлено или переменной, или константой. Было бы логично считать, что значение переменной время от времени меняется, а значение константы (т.е. постоянной величины) не меняется никогда. Важной разновидностью констант является литеральное значение. Литерал можно считать WYSIWYG-значением, потому что "то, что вы видите, то вы и получаете" (What Kou See /s What Кои Get). Представление литерального значения как раз и является этим самым значением.
Так как в языке SQL имеется много разных типов данных, то в нем имеется и много разных типов литералов. Некоторые примеры литералов разных типов данных приведены в табл. 7.1.
Обратите внимание, что литералы нечисловых типов заключены в апострофы. Эти знаки помогают избежать путаницы, хотя, впрочем, могут и привести к трудностям.
LN...
LNФункция LN возвращает натуральный логарифм от числового.
LN (9)
Возвращаемое значение будет приближенно равно 2,197224577. Количество знаков числа после запятой зависит от вашей реализации.
LOWER
LOWERДругая функция, LOWER (нижний регистр), преобразует все символы символьной строки в нижний регистр, как показано в следующей таблице, в примерах со строками 'TAXES' и 'E.E.Cummings'.
| Выражение | Результат |
| LOWER ('TAXES') | 'taxes' |
| LOWER ('Е. Е. Cummings') | 'е. е. cummings' |
МАХ
МАХФункция МАХ (максимум) возвращает максимальное значение, найденное в указанном столбце. Следующий оператор возвращает значение, равное 81 (количество жиров в 100 граммах сливочного масла):
SELECT MAX (Fat)
FROM FOODS ;
MIN
MINФункция MIN (минимум) возвращает минимальное значение, обнаруженное в указанном столбце. Следующий оператор возвращает значение, равное 0,4, потому что функция не учитывает неопределенные значения:
SELECT MIN (Carbohydrate)
FROM FOODS ;
MOD
MODФункция MOD возвращает остаток от деления нацело первого числового выражения на второе числовое выражение.
MOD (3,2)
Данная функция возвращает числовое значение 1 — остаток, получаемый при делении нацело числа 3 на число 2.
OCTET_LENGTH
OCTET_LENGTHЧто касается мира музыки, то в нем вокальный ансамбль, состоящий из восьми певцов, называется октетом. В этом ансамбле обычно есть первое и второе сопрано, первый и второй альт, первый и второй тенор, а также первый и второй бас. А что касается мира информатики, то в нем ансамбль, состоящий из восьми битов данных, называется байтом. Слово байт ясно показывает, что оно имеет отношение к биту, но при этом подразумевается нечто большее. Прекрасная игра слов, но, к сожалению, в слове "байт" ничто не напоминает о "восьмеричности". А если позаимствовать музыкальный термин, то тогда набор из восьми битов будет иметь более подходящее и более описательное название.
Практически во всех современных компьютерах для представления одного алфавитно-цифрового символа используется восемь битов. А в более сложных наборах символов (таких, например, как китайский) для этого требуется уже 16 битов. Функция OCTET_LENGTH (длина в октетах) подсчитывает и возвращает количество октетов (байтов), находящихся в строке. Если строка является битовой, то эта функция возвращает такое количество октетов, чтобы вместить находящееся в строке количество битов. А если строка состоит из символов англоязычного набора (с одним октетом на символ), то OCTET_LENGTH возвращает количество символов, имеющихся в строке. Если же строка состоит из символов китайского набора, то тогда число, возвращаемое функцией, в два раза превышает количество китайских символов. Например:
OCTET_LENGTH ('Beans, Lima')
Эта функция возвращает 11, потому что каждый символ помещается в октете.
В некоторых наборах символов для разных символов используется разное количество октетов. В частности, в тех из них, которые поддерживают смешанное использование символов канджи и латиницы, для перехода из одного набора символов в другой используются управляющие последовательности. Например, для строки, состоящий из 30 латинских символов, потребуется 30 октетов. А если все ее 30 символов взяты из канджи, то для этой строки нужно 62 октета (60 октетов плюс ведущий и замыкающий символы переключения). И наконец, если в этой строке символы латиницы и канджи попеременно чередуются друг с другом, то тогда для нее требуется 150 октетов. (Тогда для каждого символа канджи требуется два октета, а также по одному октету для ведущего и замыкающего символа переключения.) Функция OCTET_LENGTH возвращает то количество октетов, которое нужно, чтобы поместить в них имеющуюся строку.
Переменные
ПеременныеПрекрасно, когда при работе с базами данных можно манипулировать литералами и другими константами. Однако полезно иметь и переменные. Во многих случаях, не имея переменных, приходится делать намного больше работы. Переменная — это такая величина, значение которой может изменяться. Чтобы увидеть, почему переменные так полезны, рассмотрим следующий пример.
Предположим, что вы розничный продавец, у которого есть покупатели нескольких категорий. Тем из них, кто покупает товары в больших объемах, вы продаете эти товары по самым низким ценам. Тем же, кто покупает в средних объемах, вы продаете товары по ценам более высокого порядка. И наконец, те, кто ограничивается при покупках малыми объемами шаров, платят самую высокую цену. Вы хотите, чтобы все розничные цены имели определенные коэффициенты по отношению к той стоимости, в какую товары обошлись вам. Для своего товара F-117A вы решили, что покупатели товаров в больших объемах (покупатели класса С) будут за него платить в 1,4 раза больше, чем платите за этот товар вы. А покупатели товаров в средних объемах (покупатель класса В) будут уже платить в 1,5 раза больше. И наконец, покупатели товаров в малых объемах (покупатели класса А) — в 1,6 раза больше.
Вы храните значения стоимости товаров и назначаемых вами цен в таблице, которую вы швали PRICING (ценообразование). Среди ее полей имеются такие: PRICE (цена), COST (стоимость), PRODUCT (продукт) и CLASS (класс). Чтобы реализовать свою новую структуру ценообразования, вы отправляете на выполнение следующие команды языка SQL:
UPDATE PRICING
SET Price = Cost * 1.4
WHERE Product = 'F-117A'
AND Class = 'C ;
UPDATE PRICING
SET Price = Cost * 1.5
WHERE Product = 'F-117A'
AND Class = 'B' ;
UPDATE PRICING
SET Price = Cost * 1.6
WHERE Product = 'F-117A'
AND Class = 'A' ;
Этот код прекрасный и пока что подходит для ваших нужд. А что если энергичные усилия конкурентов начинают подрывать ваш сектор рынка? Чтобы остаться на плаву, вам, возможно, придется уменьшить установленные вами значения разницы в ценах. Тогда потребуется ввести нечто похожее на строки следующих команд:
UPDATE PRICING
SET Price = Cost * 1.25
WHERE Product = 'F-117A'
AND Class = 'C ;
UPDATE PRICING
SET Price = Cost * 1.35
WHERE Product = 'F-117A'
AND Class = 'B' ;
UPDATE PRICING
SET Price = Cost * 1.45
WHERE Product = 'F-117A'
AND Class = 'A' ;
Если ваш рынок изменчив, то вам придется время от времени переписывать свой SQL-код. Это может потребовать значительных усилий с вашей стороны, особенно если цены указаны во многих местах вашего кода. Эти усилия можно свести к минимуму, если заменить литералы (например, 1.45) переменными (такими, например, как -.multiplierА). Тогда свои операции обновления вы можете выполнять таким образом:
UPDATE PRICING
SET Price = Cost * :multiplierC
WHERE Product = 'F-117A'
AND Class = 'C ;
UPDATE PRICING
SET Price = Cost * :multiplierB
WHERE Product = 'F-117A'
AND Class = 'B' ;
UPDATE PRICING
SET Price = Cost * :multiplierA
WHERE Product = 'F-117A'
AND Class = 'A' ;
Теперь в любом случае, когда условия на рынке заставят вас менять ценообразование, остается только изменить значения переменных: :multiplierC, :multiplierB и :multiplierA. Эти переменные являются параметрами, передаваемыми SQL-коду, который затем использует полученные переменные, чтобы считать новые цены.
Технические подробности:Иногда переменные, используемые таким образом, называются параметрами, а иногда — базовыми переменными. Переменные называются параметрами, если они находятся в приложениях, написанных на модульном языке SQL, а базовыми переменными — если используются во встроенном SQL.
Помни: Встроенный SQL означает, что операторы SQL встроены в код приложения, написанного на процедурном базовом языке. Кроме того, SQL-код можно поместить в модуль SQL. Модуль вызывается приложением, написанным на базовом языке. Каждый из этих двух методов имеет собственные преимущества и недостатки. Какой из них выбрать — это зависит от используемой вами конкретной реализации SQL.
POSITION
POSITIONФункция POSITION (положение) ищет указанную целевую строку внутри указанной исходной и возвращает положение в ней начального символа целевой строки. Эта функция имеет такой синтаксис:
POSITION (целевая_строка IN исходная_строка)
В следующей таблице приведено несколько примеров использования POSITION для исходной строки 'Полностью пшеничный хлеб'.
| Выражение | Результат |
| POSITION ('П' IN 'Полностью пшеничный хлеб') | 1 |
| POSITION ('Пол' IN 'Полностью пшеничный хлеб') | 1 |
| POSITION ('пш' IN 'Полностью пшеничный хлеб') | 11 |
| POSITION ('пшо' IN 'Полностью пшеничный хлеб') | 0 |
| POSITION ('' IN 'Полностью пшеничный хлеб') | 1 |
POWER
POWERФункция POWER возводит первое числовое выражение в степень, указанную вторым числовым выражением.
POWER (2,8)
В этом примере функция возвращает значение 256, т.е. два в восьмой степени.
Специальные переменные
Специальные переменныеКак только пользователь на клиентской машине соединяется с базой данных, находящейся на сервере, устанавливается сеанс. Если пользователь соединяется с несколькими базами данных, то сеанс, связанный с самым последним соединением, называется текущим, а предыдущие сеансы считаются бездействующими. Стандарт SQL:2OO3 определяет несколько специальных переменных, применяемых в многопользовательских системах. Эти переменные содержат данные о различных пользователях. Например, специальная переменная SESSION_USER (пользователь сеанса) содержит значение пользовательского идентификатора авторизации для текущего сеанса SQL. Вы можете написать программу мониторинга, определяющую, кто отправляет на выполнение операторы SQL, с помощью переменной SESSION_USER.
У модуля SQL может быть связанный с ним идентификатор авторизации, который определяется пользователем. Его значение хранится в переменной CURRENT_USER (текущий пользователь). Если такого идентификатора у модуля нет, то переменная CURRENT_USER имеет такое же значение, что и SESSION_USER.
В переменной SYSTEM_USER (системный пользователь) хранится идентификатор пользователя операционной системы. Он может отличаться от идентификатора этого пользователя, хранящегося в модуле SQL. Например, пользователь может регистрироваться в системе как LARRY (Ларри), а в модуле — уже как PLANT_MGR (директор завода). Таким образом, в переменной SESSION_USER будет храниться значение PLANT_MGR. Если этот пользователь явно не указывает идентификатор модуля, то значение PLANT_MGR будет храниться и в переменной CL!RRENT_USER. А значение LARRY будет храниться в переменной SYSTEMJJSER.
Специальные переменные SYSTEMJJSER, SESSIONJJSER и CURRENTJJSER применяются для сбора данных о том, какие именно пользователи работают в системе. Вы можете поддерживать таблицу-журнал и периодически вставлять в нее значения, содержащиеся в этих переменных. Как это сделать, показано в следующем примере:
INSERT INTO USAGELOG (SNAPSHOT)
VALUES ('User1 SYSTEM_USER ||
'with ID ' || SESSION_USER ||
'active at ' || CURRENT_TIMESTAMP) ;
При выполнении этого оператора создаются примерно такие журнальные записи:
User LARRY with ID PLANT_MGR active at 1998-05-17-14:18:00
SQRT
SQRTЭта функция возвращает квадратный корень числового выражения.
SQRT (4)
Функция возвращает значение 2 — квадратный корень из четырех.
Ссылки к столбцам
Ссылки к столбцамВ столбцах находятся значения, по одному в каждой табличной строке. Ссылки к таким значениям часто используются в операторах SQL. Полностью определенная ссылка к столбцу состоит из имени таблицы, точки и имени столбца (например, PRICING.Product). Посмотрите на следующий оператор:
SELECT PRICING. Cost
FROM PRICING
WHERE PRICING. Product = 'F-117A' ;
где PRICING. Product— это ссылка на столбец, которая содержит значение 'F-117A'. PRICING.Cost — это также ссылка на столбец, но вы не будете знать ее значения, пока не выполнится предшествующий этой ссылке оператор SELECT.
Так как имеет смысл делать ссылки только к тем столбцам, которые находятся в текущей таблице, то обычно эти ссылки полностью определять не нужно. Например, следующий оператор равнозначен предыдущему:
SELECT Cost
FROM PRICING
WHERE Product = 'F-117A' ;
Иногда все же приходится работать одновременно с разными таблицами. В базе данных у каких-либо двух таблиц могут быть столбцы с одинаковыми именами. В таком случае ссылки к этим столбцам приходится определять полностью. Это нужно для того, чтобы получаемый столбец был действительно тем, который вам нужен.
Предположим, например, что ваша компания имеет филиалы, расположенные в Кингстоне и Джефферсоне, и вы отдельно для каждого из этих филиалов ведете данные по работающим там сотрудникам. Ваша таблица по сотрудникам, работающим в Кингстоне, называется EMP_KINGSTON, а по работающим в Джефферсоне — EMP_JEFFERSON. Вам необходим список всех сотрудников, которые работают в обоих местах, поэтому следует найти всех тех, у кого имя вместе с фамилией находятся в обеих таблицах. То, что нужно, дает следующий оператор SELECT:
SELECT EMP_KINGSTON.FirstName, EMP_KINGSTON.LastName
FROM EMP_KINGSTON, EMP_JEFFERSON
WHERE EMP_KINGSTON.EmpID = EMP_JEFFERSON.EmpID ;
Так как идентификационный номер сотрудника является уникальным и имеет одно и то же значение независимо от филиала, в котором сотрудник работает, то этот номер можно использовать для связи между таблицами (в каждой из них он находится в столбце EmpID). В результате выполнения последнего оператора возвращаются имена и фамилии только тех сотрудников, чьи данные находятся в обеих таблицах. Имена и фамилии берутся соответственно из столбцов FirstName и LastName таблицы EMP_KINGSTON.
Строковые функции
Строковые функцииСтроковые функции принимают значение одной символьной или битовой строки и возвращают другую символьную или битовую строку. В SQL имеется шесть таких функций:
Строковые выражения со значением
Строковые выражения со значениемСамым простым строковым выражением со значением является одиночное строковое значение. В более сложных выражениях могут быть также ссылки на столбцы, итоговые функции, скалярные подзапросы, выражения с использованием ключевых слов CASE и CAST или составные строковые выражения со значением. О выражениях со значением, использующих CASE и CAST, рассказывается в главе 8. В строковых выражениях со значением можно применять только один оператор — оператор конкатенации. Его можно применять к любым выражениям, чтобы, соединив их вместе, получить более сложное строковое выражение со значением. Оператор конкатенации представлен двумя вертикальными линиями (||). Некоторые примеры строковых выражений со значением показаны в следующей таблице.
| Выражение | Результат |
| 'Хрустящий ' || 'арахис' | 'Хрустящий арахис' |
| 'Шарики' || ' ' || 'из желе' | 'Шарики из желе' |
| FIRST_NAME || ' ' || LAST_NAME | 'Джо Смит' |
| В'1100111' || В'01010011' | В'110011101010011' |
| ' ' || 'Спаржа' | 'Спаржа' |
| 'Спаржа' || ' ' | 'Спаржа' |
| 'C' || ' ' || 'пар' || ' ' || 'ж' || ' ' || 'а' | 'Спаржа' |
SUBSTRING
SUBSTRINGФункция SUBSTRING (подстрока) используется для того, чтобы из исходной строки выделить подстроку. Выделенная функцией подстрока имеет тот же тип, что и исходная. Например, если исходная строка является символьной, то и подстрока также является символьной. Вот синтаксис функции SUBSTRING:
SUBSTRING (строковое_значение FROM начало [FOR длина]).
Предложение в квадратных скобках ([]) не является обязательным. Подстрока, которую следует выделить из строкового_значения, начинается с символа, порядковый номер которого, если считать с самого первого символа, представлен значением начало. Кроме того, подстрока состоит из определенного количества символов, представленного значением длина. Если предложение FOR отсутствует, то подстрока выделяется, начиная от символа, соответствующего значению начало, до самого конца строки. Проанализируйте следующий пример:
SUBSTRING ('Полностью пшеничный хлеб' FROM 11 FOR 11)
Выделенной подстрокой является 'пшеничный х'. Она начинается с одиннадцатого символа исходной строки и имеет длину в одиннадцать символов. С первого взгляда SUBSTRING не представляется такой уж ценной функцией. Для литерала 'Полностью пшеничный хлеб' не требуется функция нахождения подстроки. Впрочем, функция SUBSTRING действительно представляет ценность, потому что строковое значение не обязательно должно быть литералом. Это значение может быть любым выражением, в результате выполнения которого получается символьная строка. Например, это может быть переменная fooditem, которая каждый раз может принимать разные значения. Следующее выражение может извлекать нужную подстроку независимо от того, какую символьную строку представляет переменная fooditem:
SUBSTRING (:fooditem FROM 11 FOR 11).
Все функции значения объединяет то, что они могут оперировать как со значениями, так и с выражениями, после выполнения которых получаются значения требуемого типа.
Внимание: При использовании функции SUBSTRING не следует забывать о следующем. Выбираемая вами подстрока действительно должна быть частью исходной строки. Если вам нужна подстрока, которая начинается с одиннадцатого символа, а в исходной строке всего только четыре символа, то вы получите значение NULL. Поэтому необходимо иметь некоторое представление о структуре своих данных, перед тем как задавать значения для функции SUBSTRING. Кроме того, нельзя указывать отрицательную длину подстроки.
Если у столбца тип данных VARCHAR, ширина поля конкретной строки не будет известна. Если вы укажете слишком большую длину подстроки, при которой та выйдет за правый край поля, функция SUBSTRING возвратит конец исходной строки и не будет сообщать об ошибке.
Скажем, у вас имеется оператор
SELECT * FROM FOODS
WHERE SUBSTRING (Food FROM 7 FOR 7) = 'хлеб' ;
И даже если значение, находящееся в столбце FOOD таблицы FOODS, имеет длину меньше 14 символов, этот оператор все равно возвращает табличную строку с данными, относящимися к белому хлебу.
Совет: Если какой-либо оператор функции SUBSTRING имеет значение NULL, то эта функция возвращает результат NULL.
Суммирование с помощью итоговых функций
Суммирование с помощью итоговых функцийИтоговые функции применяются к наборам строк из таблицы, а не только к ее отдельным строкам. Эти функции в текущем наборе строк "суммируют" некоторые характеристики, т.е. получают по ним определенные итоги. В такой набор могут входить все строки таблицы или только те из них, которые определяются предложением WHERE. (Подробно о предложениях WHERE рассказывается в главе 9.)
Программисты используют название итоговые функции, потому что те берут информацию из целого набора строк, определенным образом ее обрабатывают и выдают результат в виде единичной строки. Кроме того, эти функции еще называются функциями наборов.
Чтобы показать применение итоговых функций, проанализируйте табл. 7.2, в которой представлены питательные компоненты, содержащиеся в 100 граммах некоторых продуктов питания.
SUM
SUMФункция SUM (сумма) возвращает сумму всех значений, обнаруженных в указанном столбце. Следующий оператор возвращает число 3924, которое является общим количеством калорий во всех 15 продуктах:
SELECT SUM (Calories)
FROM FOODS ;
Примеры литералов различных типов данных
Таблица 7.1. Примеры литералов различных типов данных| Тип данных | Пример литерала |
| BIGINT | 8589934592 |
| INTEGER | 186282 |
| SMALLINT | 186 |
| NUMERIC | 186282,42 |
| DECIMAL | 186282,42 |
| REAL | 6.02257E23 |
| DOUBLE PRECISION | 3,1415926535897E00 |
| FLOAT | 6.02257E23 |
| CHARACTER (15) Примечание: в строке в одинарные кавычки заключено пятнадцать символов и пробелов | 'GREECE' |
| VARCHAR (CHARACTER VARYING) | 'lepton' |
| NATIONAL CHARACTER(15) Примечание: в строке в одинарные кавычки заключено пятнадцать символов и пробелов | 'E??A?' Примечание: Этот термин является словом, которым греки называют Грецию на своем языке. (Если написать его по-английски, то получится "Hellas", а по-русски — "Эллада".) |
| NATIONAL CHARACTER VARYING | '??????' Этот термин является словом "lepton " (лептон), написанным буквами греческого алфавита. |
| CHARACTER URGE OBJECT (CLOB) | Очень длинная символьная стока |
| BINARY LARGE OBJECT (BLOB) | Очень длинная строка, состоящая из нулей и единиц (0и1) |
| DATE | DATE'1969-07-20' |
| TIME(2) | TIME '13.41.32.50' |
| TIMESTAMP(O) | TIMESTAMP'1998-05-17-13.03.16.000000' |
| TIME WITH TIMEZONE(4) | TIME'13.41.32.5000-08.00' |
| TIMESTAMP WITH TIMEZONE(4) | TIMESTAMP'1998-05-17-13.03.16.0000+02.00' |
| INTERVAL DAY | INTERVAL 7' DAY |
Питательные компоненты
Таблица 7.2. Питательные компоненты некоторых продуктов питания (в 100 граммах)| Продукт питания (Food) | Калории (Calories) | Белки (Protein), г | Жиры (Fat), г | Углеводы (Carbogidrate), г |
| Жареные миндальные орехи | 627 | 18,6 | 57,7 | 19,6 |
| Спаржа | 20 | 2,2 | 0,2 | 3,6 |
| Сырые бананы | 85 | 1,1 | 0,2 | 22,2 |
| Гамбургер с нежирной говядиной | 219 | 27,4 | 11,3 | |
| Нежное мясо цыплят | 166 | 31,6 | 3,4 | |
| Жареный опоссум | 221 | 30,2 | 10,2 | |
| Свиной окорок | 394 | 21,9 | 33,3 | |
| Фасоль лима | 111 | 7,6 | 0,5 | 19,8 |
| Кола | 39 | 10,0 | ||
| Белый хлеб | 269 | 8,7 | 3,2 | 50,4 |
| Пшеничный хлеб | 243 | 10,5 | 3,0 | 47,7 |
| Брокколи | 26 | 3,1 | 0,3 | 4,5 |
| Сливочное масло | 716 | 0.6 | 81,0 | 0,4 |
| Шарики из желе | 367 | 0,5 | 93,1 | |
| Хрустящий арахис | 421 | 5,7 | 10,4 | 81,0 |
TRANSLATE и CONVERT
TRANSLATE и CONVERTФункции TRANSLATE (перевести) и CONVERT (преобразовать) выбирают исходную строку, составленную из символов одного набора, и переводят ее в строку, составленную из символов другого набора. Примерами могут быть переводы символов из английского набора в армянский или символов иврита во французский. Функции преобразования, выполняющие эти действия, зависят от реализации SQL. Подробности можно узнать в документации по имеющейся у вас реализации.
Помни: Если бы перевод с одного языка на другой был таким легким, как вызов в SQL функции TRANSLATE, то это было бы прекрасно. К сожалению, такая задача — не из легких. Все, что осуществляет TRANSLATE, — это перевод символа из первого символьного набора в соответствующий символ из второго набора. Она может, например, перевести 'ELLAS' (Греция) в 'Ellas'. Однако функция TRANSLATE не может перевести 'ELLAS' в 'Greece' (Греция).
TRIM
TRIMЧтобы из символьной строки удалить ведущие, замыкающие или одновременно и те и другие пробелы (и не только пробелы), используйте функцию TRIM (обрезать). Следующие примеры показывают, как ее использовать, например, применительно к строкам, где находится слово treat.
| Выражение | Результат |
| TRIM (LEADING ' ' FROM ' treat ') | 'treat ' |
| TRIM (TRAILING ' ' FROM ' treat ') | ' treat ' |
| TRIM (BOTH ' ' FROM ' treat ') | 'treat' |
| TRIM (BOTH 't' FROM 'treat') | 'rea' |
TRIM (BOTH FROM ' treat ').
В этом случае получается тот же результат, что и в третьем примере из таблицы, а именно 'treat'.
UPPER
UPPERДругая функция, UPPER (верхний регистр), преобразует все символы символьной строки в верхний регистр, как показано в следующей таблице, в примерах со строками 'e.e.cummings' и Isaac Newton, Ph.D.'.
| Выражение | Результат |
| UPPER ('е.е.cummings') | 'E.E.CUMMINGS' |
| UPPER ('Isaac Newton, Ph.D.') | 'ISAAC NEWTON, PH.D.' |
Условные выражения со значением
Условные выражения со значениемЗначение условного выражения со значением зависит от условия. Такие выражения, как CASE, NULLIF и COALESCE, значительно сложнее, чем другие выражения со значением. Эти три вида условных выражений настолько сложны, что заслуживают отдельного рассмотрения. Подробно о них речь пойдет в главе 8.
Выражения со значением датывремени
Выражения со значением даты-времениВыражения со значением даты-времени выполняют операции с данными, относящимися к дате и времени. Компоненты этих выражений могут иметь типы данных DATE, TIME, TTMESTAMP и INTERVAL. Результат выполнения выражения со значением даты-времени всегда относится к одному из типов даты-времени (DATE, TIME или TIMESTAMP). Например, после выполнения следующего выражения будет получена дата, которая наступит ровно через неделю:
CURRENT_DATE + INTERVAL '7' DAY
Значения времени поддерживаются в координатах Всемирного времени (Universal Time Coordinates, UTC), ранее известных как время по Гринвичу. Однако можно указывать и смещение, чтобы время соответствовало текущему часовому поясу. Для местного часового пояса, применяемого в вашей системе, можно использовать простой синтаксис, пример которого приведен ниже.
TIME '22.55.00' AT LOCAL
Кроме того, это значение можно указать и более развернуто:
TIME '22.55.00' AT TIME ZONE INTERVAL '-08.00' HOUR TO MINUTE
Последнее выражение определяет местное время часового пояса, в котором находится город Портленд, штат Орегон. Этот часовой пояс отстоит от Гринвича на восемь часов.
Выражения со значением
Выражения со значением.Выражение может быть простым или очень сложным. В нем могут находиться литеральные значения, имена столбцов, параметры, базовые переменные, подзапросы, логические связки и арифметические операторы. Впрочем, каким бы сложным выражение ни было, оно обязательно должно сводиться к одиночному значению.
Поэтому выражения SQL обычно называются выражениями со значением. Комбинирование множества таких выражений в одно возможно тогда, когда эти выражения-компоненты сводятся к значениям, имеющим совместимые типы данных.
В SQL определяется пять разных типов выражений со значением:
WIDTH_BUCKET
WIDTH_BUCKETФункция WTDTHJBUCKET используется при выполнении процессов в режиме реального времени (online application processing, OLAP). Эта функция имеет четыре аргумента и возвращает целое число между 0 (нулем) и значением последнего аргумента плюс 1 (один). Для первого apгумента она назначает область в разделенном на равновеликие части диапазоне чисел между вторым и третьим аргументами функции. Для значений, находящихся за пределами заданного диапазона, функция возвращает значение 0 (нуль) либо значение последнего аргумента плюс 1 (один).
Например:
WIDTH_BUCKET (PI, 0, 9, 5)
Предположим, что PI— числовое выражение со значением— это 3,141592. Интервал значений между нулем и девятью (0 и 9 — второй и третий аргументы функции соответственно) нужно разделить на пять равных отрезков (5 — четвертый аргумент функции), каждый шириной в две единицы. В этом случае функция возвращает значение 2, поскольку число 3,141592 находится во втором отрезке, который является диапазоном значений от двух до четырех.
Введение операторов языка SQL в базу данных приложения Microsoft Access
Access не позволяет ввести операторы SQL в базу данных, поэтому все операторы должны быть введены как запросы. Многие программные продукты, например SQL Server, Oracle, MySQL или PostgreSQL, имеют редакторы, предназначенные для ввода операторов языка SQL. Так, в приложении SQL Server это редактор Query Analyzer. Для других приложений такие редакторы описаны в документации.
Для Access также существует возможность ввода операторов SQL, однако этот путь весьма сложен и зачастую запутан. Подробные пошаговые инструкции относительно ввода операторов SQL в приложение Access см. в главе 4.
Значения типа записи
Значения типа записиСамыми заметными значениями в базе данных являются табличные значения типа записи. Это значения, которые являются содержимым каждой строки, находящейся в таблице базы данных. Значение этого типа обычно состоит из множества компонентов, ведь в каждом столбце каждой строки всегда находится какое-либо значение. Поле — это пересечение столбца и строки. В поле содержится скалярное, или атомарное, значение. У этого значения имеется только один компонент.
Значения
ЗначенияВ SQL имеется несколько видов значений:
В девятнадцатом веке ученые верили, что атом является той минимальной частью материи, какая только возможна. Поэтому они и назвали эту часть атомом — словом, происходящим от греческого "атомос", что означает "неделимый". А теперь ученым известно, что атомы не являются неделимыми и состоят из протонов, нейтронов и электронов. Протоны и нейтроны, в свою очередь, состоят из кварков, глюонов и виртуальных кварков. Кто знает, может быть, и их нельзя назвать неделимыми?
Значение поля таблицы базы данных называется атомарным, хотя многие поля совсем не являются неделимыми. У значения типа DATE имеются следующие компоненты: месяц, год и день.
А компонентами значения типа TIMESTAMP являются час, минута, секунда и т.д. Значения типов REAL и FLOAT в качестве компонентов имеют экспоненту и мантиссу. В значении типа CHAR есть компоненты, к которым можно получить доступ с помощью SUBSTRING. Поэтому, по аналогии с атомами материи, название "атомарные" для значений полей баз данных все-таки правильно. Впрочем, если исходить из первоначального значения этого слова, то ни одно из современных применений термина "атомарный" правильным не является.
Еще одно специальное выражение CASE — COALESCE
Еще одно специальное выражение CASE — COALESCECOALESCE (объединять), как и NULLIF, является упрощенной формой специального выражения CASE. COALESCE работает со списками значений, которые могут быть как определенными, так и неопределенными. Если в списке только одно из значений не является NULL, то оно и становится значением выражения COALESCE. Если же в списке таких значений больше, чем одно, то значением выражения становится первое из них. Когда все значения списка — NULL, то значением выражения COALESCE также становится NULL.
Выражение CASE, выполняющее те же действия, имеет следующий вид:
CASE
WHEN значение 1 IS NOT NULL
THEN значение1
WHEN значение2 IS NOT NULL
THEN значение2
...
WHEN значение_n IS NOT NULL
THEN значение_n
ELSE NULL
END
А соответствующий упрощенный синтаксис COALESCE выглядит так: COALESCE(значение1, значение2, ..., значение_n)
Возможно, вам придется использовать выражение COALESCE после выполнения операции OUTER JOIN (о которой рассказывается в главе 10). Этот оператор позволяет сократить объем вводимого кода.
Использование выражения CASE с условиями поиска
Использование выражения CASE с условиями поискаЭффективным способом использования выражения CASE является проводимый по всей таблице поиск тех строк, в которых выполняется определенное условие поиска. Если использовать выражение CASE таким способом, то у него должен быть следующий синтаксис:
CASE
WHEN условие 1 THEN результат1
WHEN условие2 THEN результат2
...
WHEN условие_n THEN результат_n
ELSE результат_х
END
Выражение CASE проверяет, является ли истинным условие1 в первой оцениваемой строке (т.е. в первой из тех строк, которые соответствуют условиям предложения WHERE, если только оно имеется). Если да, то выражение CASE принимает значение результата1. А если условие1 не выполняется, строка проверяется на выполнение условия2. Если оно выполняется, то выражение CASE принимает значение результата1 и т.д. А если ни одно из имеющихся условий не выполнено, то CASE принимает значение результат_х. Предложение ELSE не является обязательным. В том случае, если этого предложения нет и не выполняется ни одно из указанных условий, выражение принимает значение NULL. После того как оператор SQL, в котором находится выражение CASE, выполнится по отношению к первой оцениваемой строке таблицы и выполнит соответствующее действие, он приступает к следующей строке. Такая последовательность действий продолжается до тех пор, пока не будет закончена обработка всей таблицы.
Использование выражения CASE со значениями
Использование выражения CASE со значениямиПри сравнении проверяемого значения с набором других можно использовать более компактную форму выражения CASE. Эту форму полезно использовать внутри оператора SELECT или UPDATE, когда в столбце таблицы содержится ограниченное число разных значений и нужно связать с каждым из них соответствующее значение результата CASE. Если использовать выражение CASE таким образом, то оно будет иметь следующий синтаксис:
CASE значение_n
WHEN значенив1 THEN результат1
WHEN значение2 THEN результат2
...
WHEN значение_n THEN результат_n
ELSE результат_х
END
Если проверяемое значение (значение_n) равно значению1, то выражение принимает значение результат1. А если значение_n не равно значению1, а значению2, то выражение принимает значение результат2. Все значения, предназначенные для сравнения, проверяются сверху вниз, по направлению к значению_n, пока не будет найдено то из них, которое равно значению_n. Если же такое значение найдено не будет, то выражение принимает значение результат_х. И опять, если необязательное предложение ELSE отсутствует и ни одно из значений, предназначенных для сравнения, не равно проверяемому, то выражение принимает неопределенное значение.
Чтобы понять, как работает форма CASE со значениями, проанализируйте пример с таблицей, в которой находятся фамилии и звания офицеров. Требуется получить их список, в котором перед фамилиями офицеров стояли бы аббревиатуры их званий. Для сравнения будут использоваться такие звания: генерал (general), полковник (colonel), подполковник (lieutenant colonel), майор (major), капитан (captain), старший лейтенант (first lieutenant), лейтенант (second lieutenant). И наконец, тот, у кого какое-либо другое звание, в списке будет просто назван "господином" (Mr.). Список создается с помощью следующего оператора:
SELECT CASE RANK
WHEN ' general' THEN 'Gen.'
WHEN 'colonel' THEN 'Col.'
WHEN 'lieutenant colonel' THEN 'Lt. Col.
WHEN 'major' THEN 'Maj.'
WHEN ' captain' THEN 'Capt. '
WHEN 'first lieutenant' THEN '1st. Lt.
WHEN 'second lieutenant' THEN '2nd. Lt.
ELSE 'Mr. '
END,
LAST_ NAME
FROM OFFICERS ;
Результат должен быть примерно такой:
Capt. Midnight
Col. Sanders
Gen. Schwarzkopf
Maj. Disaster
Mr. Nimitz
Честер Нимиц был адмиралом во флоте Соединенных Штатов во время Второй мировой войны. Так как в выражении CASE его звания нет, то оно определяется предложением ELSE.
Вот еще пример. Предположим, что капитан Миднайт получает повышение в звании и становится майором. Требуется сделать соответствующие изменения в базе данных OFFICERS (офицеры). Предположим, что в переменной officerjastjiame (фамилия офицера) находится значение 'Midnight', а в переменной new_rank (новое звание) — целое значение (4), которое, согласно следующей таблице, соответствует новому званию Миднайта.
Тогда ввести данные о повышении можно с помощью следующего кода SQL:
UPDATE OFFICERS
SET RANK = CASE :new_rank
WHEN 1 THEN 'general'
WHEN 2 THEN 'colonel'
WHEN 3 THEN ' lieutenant colonel'
WHEN 4 THEN 'major'
WHEN 5 THEN 'captain'
WHEN 6 THEN 'first lieutenant'
WHEN 7 THEN 'second lieutenant'
WHEN 8 THEN'Mr. '
END
WHERE LAST_NAME = :officer_last_name ;
| new_rank | Звание |
| 1 | general |
| 2 | colonel |
| 3 | lieutenant colonel |
| 4 | major |
| 5 | captain |
| 6 | first lieutenant |
| 7 | second lieutenant |
| 8 | Mr. |
CASE
WHEN значение_n = значение1 THEN результат1
WHEN значвние_n = значение2 THEN результат2
...
WHEN значение_n = значвние_n THEN результат_n
ELSE результат_х
END
Использование выражения CAST при
Использование выражения CAST при взаимодействии SQL и базового языкаГлавное предназначение выражения CAST состоит в том, чтобы работать с такими типами данных, которые есть в SQL, но отсутствуют в базовом языке. Вот некоторые примеры таких типов.
SELECT CAST(Salary AS CHAR(10)) INTO :salary_var
FROM EMP
WHERE EmpID = :emp_id_var ;
Затем приложение проверяет появившуюся в переменной :salary_var символьную строку и присваивает этой переменной новое значение, а затем с помощью следующего кода SQL обновляет базу данных:
UPDATE EMP
SET Salary = CAST(:salary_var AS DECIMAL(5,3))
WHERE EmpID = :emp_id_var ;
С символьными строками, такими как '000198.37', работать в языках Fortran и Pascal очень трудно, но для выполнения в этих языках нужных операций можно написать набор специальных процедур. В принципе, в любом базовом языке можно получать и обновлять любые SQL-данные, а также получать и задавать точные значения.
Вообще говоря, выражение CAST лучше всего подходит для преобразования типов данных базового языка в типы данных базы и наоборот, а не для преобразования одних типов данных базы в другие.
Использование выражения CAST внутри SQLкода
Использование выражения CAST внутри SQL-кодаПредположим, что вы работаете для торговой компании, которая собирает данные о своих предполагаемых сотрудниках, а также о сотрудниках, которых вы уже наняли. Данные о предполагаемых сотрудниках вы разместили в таблице PROSPECT (потенциальный сотрудник), а различаете каждого из них по его номеру социального страхования (Social Security Number, SSN), который вы храните в виде данных типа CHAR(9). Данные о работающих сотрудниках вы разместили в другой таблице, EMPLOYEE (сотрудник), и различаются они также по своему номеру социального страхования, но имеющему уже тип INTEGER. Теперь вам требуется получить список людей, данные о которых содержатся в обеих таблицах. Чтобы выполнить эту задачу, используйте выражение CAST:
SELECT * FROM EMPLOYEE
WHERE EMPLOYEE.SSN =
CAST(PROSPECT.SSN AS INTEGER) ;
Обход условий вызывающих ошибки
Обход условий, вызывающих ошибкиДругим ценным применением выражения CASE является обход исключений — проверка условий, которые вызывают ошибки. Проанализируйте выражение CASE, которое определяет размер "компенсации" для продавцов. В компаниях, где работникам компенсируют недополученные комиссионные, часто к комиссионным своих новых работников дают еще и "компенсацию". В следующем примере такую "компенсацию" к своим комиссионным получают новые продавцы, причем по мере роста комиссионных ее размер довольно сильно уменьшается:
UPDATE SALES_COMP
SET COMP = COMMISION + CASE
WHEN COMMISSION <> 0
THEN DRAW/COMMISSION
WHEN COMMISSION = 0
THEN DRAW
END ;
Если у продавца комиссионных нет, то структура этого примера позволяет избежать операции деления на нуль, которая, как известно, приводит к ошибке. А если продавец все же заработал какие-то комиссионные, то ему выплатят эти комиссионные плюс "компенсацию", которая уменьшается пропорционально их размеру.
Все выражения THEN внутри общего выражения CASE должны быть одного и того же типа — или все числовые, или символьные, или даты-времени. Результат выражения CASE имеет тот же тип.
Обновление значений на основе условия
Обновление значений на основе условияВыражение CASE можно поместить почти в любом месте оператора SQL, где только может находиться значение. Поэтому использование этого выражения раскрывает перед вами огромные возможности. Можно использовать CASE внутри оператора UPDATE (обновить), чтобы, например, на основе определенного условия по-разному изменять табличные значения. Проанализируйте следующий пример:
UPDATE FOODS
SET RATING = CASE
WHEN FAT < 1
THEN 'очень мало жиров'
WHEN FAT < 5
THEN 'мало жиров'
WHEN FAT < 20
THEN 'среднее количество жиров'
WHEN FAT < 50
THEN 'высокое количество жиров'
ELSE 'сплошные жиры'
END ;
Этот оператор проверяет по порядку условия WHEN, пока не встретится первое истинное значение, после чего он игнорирует оставшиеся условия.
В табл. 7.2 было показано содержимое жиров в 100 граммах некоторых продуктов питания. Таблица из базы данных, содержащая эту информацию, может также иметь столбец RATING (оценка), который дает быструю оценку величины содержания жиров. Если запустить предшествующий оператор UPDATE в таблице FOODS (продукты питания) из главы 7, то у спаржи будет оценка "очень мало жиров", у цыплят — "мало жиров", а жареные миндальные орехи попадут в категорию "сплошные жиры".
Преобразование типов данных с помощью выражения CAST
Преобразование типов данных с помощью выражения CASTВ главе 2 рассказывалось о различных типах данных, используемых при работе с SQL. В идеальном случае каждый столбец таблицы должен иметь подходящий тип. Однако в действительности не всегда ясно, каким же он должен быть. Предположим, определяя для базы данных таблицу, вы присваиваете столбцу тип данных, который замечательно подходит для вашего нынешнего приложения. Однако позднее вам, возможно, потребуется расширить поле деятельности вашего приложения или написать полностью новое приложение, в котором данные используются по-другому. Для этого нового использования может потребоваться тип данных, который отличается от выбранного вами ранее.
Возможно, вам потребуется сравнить столбец одного типа, находящийся в одной таблице, со столбцом другого типа из другой таблицы. Например, в одной таблице даты могут храниться в виде символьных данных, а в другой — в виде значений типа DATE. Даже если в обоих столбцах находятся одни и те же элементы данных, например даты, их разные типы могут не позволить сделать сравнение. Для SQL-86 и SQL-89 несовместимость типов данных представляет большую проблему. Однако с появлением SQL-92 появилось и удобное ее решение — выражение CAST (приведение).
Выражение CAST преобразует табличные данные или базовые переменные одного типа в другой. После такого преобразования можно выполнять необходимые операцию или анализ.
Используя выражение CAST, вы, естественно, столкнетесь с некоторыми ограничениями. Нельзя без разбора преобразовать данные одного типа в любой другой. Преобразуемые данные должны быть совместимы с новым типом. Например, можно использовать выражение CAST для преобразования в тип DATE символьной строки '1998-04-26', имеющей тип данных CHAR(IO). Однако символьную строку 'rhinoceros' (носорог), также имеющую тип данных CHAR(IO), преобразовывать с помощью CAST в тип DATE уже нельзя. Нельзя преобразовать значение типа INTEGER в значение типа SMALLINT, если размер первого из них превышает размер, максимально допустимый для SMALLINT.
Элемент данных любого из символьных типов можно преобразовать в любой другой тип (например, числовой или даты) при условии, что значение этого элемента имеет вид литерала нового типа. И наоборот, элемент данных любого типа можно преобразовать в любой из символьных типов в виде литерала исходного типа.
Другие возможные преобразования перечислены ниже.
Специальное выражение CASE — NULLIF
Специальное выражение CASE — NULLIFВремя от времени предметы переходят из одного известного состояния в другое. Иногда вам кажется, что вы что-то знаете, но в конце концов выясняется, что это не так. Классическая термодинамика, как и современная теория хаоса, утверждает, что системы легко переходят из хорошо известного, упорядоченного состояния в состояние хаоса, которое никто не может предсказать. Кто когда-либо видел состояние комнаты подростка через неделю после того, как в ней была генеральная уборка, тот может поручиться за точность этих теорий.
В таблицах из базы данных точно определенные значения находятся в тех полях, в которых имеются известные данные. А если значение поля неизвестно, то в этом поле обычно находится неопределенное значение (NULL). SQL дает возможность с помощью выражения CASE менять определенное значение табличного поля на неопределенное. Значение NULL означает, что значение поля вам больше не известно.
Представьте, что вы владеете небольшой авиакомпанией, которая выполняет рейсы между Южной Калифорнией и штатом Вашингтон. До недавних пор во время некоторых из рейсов делалась промежуточная посадка в международном аэропорту Сан-Хосе для дозаправки. Затем вы лишились разрешения на полеты в Сан-Хосе. Теперь дозаправку приходится проводить в одном из двух международных аэропортов: или Сан-Франциско, или Окленда. Так что сейчас вам точно не известно, во время какого рейса в каком из аэропортов будут садиться ваши самолеты, ясно лишь, что не в Сан-Хосе. У вас имеется база данных FLIGHT (полет), в которой находится важная информация о каждом из ваших рейсов (в том числе данные в столбце RefuelStop
(остановка для дозаправки)). Теперь эту базу нужно обновить, чтобы удалить из нее все упоминания о Сан-Хосе. Один из способов это сделать показан в следующем примере:
UPDATE FLIGHT
SET RefuelStop = CASE
WHEN RefuelStop = 'San Jose1
THEN NULL
ELSE RefuelStop
END ;
Так как подобные ситуации, когда нужно заменить известное значение неопределенным (NULL), бывают часто, то для выполнения этой задачи в SQL имеется специальный упрощенный синтаксис. Вот как выглядит предыдущий пример, переписанный в таком упрощенном виде:
UPDATE FLIGHT
SET RefuelStop = NULLIF(RefuelStop, 'San Jose') ;
Это выражение можно прочитать следующим образом: "Обновить базу данных FLIGHT, заменяя в столбце RefuelStop значение San Jose на NULL. Другие значения менять не надо".
Синтаксис NULLIF еще более удобен, если нужно преобразовать данные, накопленные в таком виде, который позволял с ними работать с помощью программы, написанной на стандартном языке программирования, например на COBOL или Fortran. В стандартных языках программирования значение NULL не используется, поэтому очень часто для выражения понятия "не известен" или "не применяется" используются специальные значения. Например, значение "не известен" в поле SALARY (зарплата) может быть представлено числом -1, а значение "не известен" или "не применим" в поле JOBCODE (код задания) — символьной строкой "***". Если необходимо в SQL-совместимой базе данных представить состояния "не известен" или "не применим" с помощью значения NULL, то специальные значения придется преобразовать в неопределенные. В следующем примере эта операция выполняется для таблицы с данными о сотрудниках, в которой некоторые значения окладов неизвестны:
UPDATE EMP
SET Salary = CASE Salary
WHEN -1 THEN NULL
ELSE Salary
END ;
Это преобразование удобнее выполнять, используя выражение NULLIF:
UPDATE EMP
SET Salary = NULLIF(Salary, -1) ;
Условные выражения GASC
Условные выражения GASCВ каждом полноценном компьютерном языке имеется какой-либо условный оператор или условная команда. На самом же деле у большинства языков таких операторов или команд имеется несколько. Вероятно, самой распространенной среди них является структура IF...THEN...ELSE...ENDIF. Если вычисленным значением условия, следующего за ключевым словом IF (если), является True (истина), то выполняется блок команд, который следует за ключевым словом THEN (тогда). А если вычисленное значение условия не равно True, то выполняется блок команд, следующий за ключевым словом ELSE (иначе). О завершении структуры свидетельствует ключевое слово ENDIF (конец IF). Эта структура является прекрасным средством представления в виде кода любой операции, которая может выполняться одним из двух способов. С другой стороны, структура IF...THEN...ELSE...ENDIF меньше подходит для операций, имеющих больше двух вариантов выполнения.
Помни: В большинстве компьютерных языков имеется оператор CASE, предназначенный для ситуаций, когда требуется в зависимости от некоторых условий направлять выполнение программы по одному из многих имеющихся вариантов.
В SQL:2OO3 CASE является выражением, а не оператором. Поэтому CASE в этом языке является только частью оператора, а не оператором как таковым. В SQL выражение CASE можно поместить почти в любом месте, где только может находиться какое-либо значение. Во время выполнения программы для этого выражения вычисляется значение. А для операторов CASE из других языков никакое значение не вычисляется; вместо этого они управляют выполнением программы.
Выражение CASE можно использовать двумя способами.
Выражения со значением типа записи
Выражения со значением, типа записиВ SQL-86 и SQL-89 большинство операций выполнялось с одиночным значением или с одиночным столбцом из табличной строки. Чтобы работать с множеством значений, необходимо с помощью логических связок создать сложные выражения. (О логических связках вы узнаете в главе 9.)
С появлением SQL-92 в языке SQL появились выражения со значением типа записи, которые работают не с одиночными значениями или столбцами, а со списками значений или столбцов. Такое выражение является списком выражений со значением, заключенных в апострофы и отделяемых друг от друга запятыми. Можно работать сразу со всем значением типа записи или только с нужным его подмножеством.
В главе 6 рассказывалось, как с помощью оператора INSERT добавлять в таблицу новую строку. Чтобы это сделать, используется выражение со значением типа записи. Рассмотрите следующий пример. Данные сыра чеддер вводятся в поля FOODNAME (название продукта питания), CALORIES (калории), PROTEIN (белки), FAT (жиры), CARBOHYDRATE (углеводы) из таблицы FOODS (продукты питания).
INSERT INTO FOODS
(FOODNAME, CALORIES, PROTEIN, FAT, CARBOHYDRATE)
VALUES
('Сыр чеддер', 398, 25, 32.2, 2.1) ;
В этом примере выражением со значением типа записи является ('Сыр чеддер', 398, 25, 32.2, 2.1). Если таким образом в операторе INSERT использовать выражение со значением типа записи, в этом выражении могут быть значения NULL и значения по умолчанию. (Значением по умолчанию называется то, которое должно быть в табличном столбце, если явно не указано другое значение.) Вот, например, вполне соответствующее правилам выражение со значением типа записи:
('Сыр чеддер', 398, NULL, 32.2, DEFAULT).
Можно добавить в таблицу сразу множество строк, вставив в предложение VALUES множество выражений со значением типа записи. Например, так выглядит ввод в таблицу FOODS данных о таких продуктах: салат-латук, маргарин, горчица и спагетти:
INSERT INTO FOODS
(FOODNAME, CALORIES, PROTEIN, FAT, CARBOHYDRATE)
VALUES
('Салат-латук', 14, 1.2, 0.2, 2.5),
('Маргарин', 720, 0.6, 81.0, 0.4),
('Горчица', 75, 4.7, 4.4, 6.4),
('Спагетти', 148, 5.0, 0.5, 30.1) ;
Выражения со значением типа записи можно использовать, чтобы сэкономить место при вводе кода, предназначенного для сравнения. Предположим, у вас имеются две таблицы сданными о продуктах питания, одна из которых составлена на английском языке, а другая— на испанском. Требуется найти те строки первой таблицы, которые точно соответствуют строкам второй. Если не использовать выражения со значением типа записи, то, возможно, придется создавать такой код:
SELECT * FROM FOODS
WHERE FOODS.CALORIES = COMIDA.CALORIA
AND FOODS.PROTEIN = COMIDA.PROTEINA
AND FOODS.FAT = COMIDA.GORDO
AND FOODS.CARBOHYDRATE = COMIDA.CARBOHIDRATO ;
А вот код, выполняющий те же действия, но составленный с помощью выражения со значением типа записи:
SELECT * FROM FOODS
WHERE (FOODS.CALORIES, FOODS.PROTEIN, FOODS.FAT,
FOODS.CARBOHYDRATE)
=
(COMIDA.CALORIA, COMIDA.PROTEINA, COMIDA.GORDO,
COMIDA.CARBOHIDRATO) ;
В этом примере при вводе кода было сэкономлено не слишком много места. Больший выигрыш будет, если число сравниваемых столбцов будет больше. В крайнем случае (как, в частности, в этом примере), видимо, лучше придерживаться старого синтаксиса, так как он более понятен.
Впрочем, использование выражения со значением типа записи имеет дополнительное преимущество перед его аналогом с развернутым синтаксисом. Дело в том, что это выражение работает намного быстрее. В принципе, какая-либо очень умная реализация может анализировать развернутый синтаксис и выполнять его как выражение со значением типа записи. Однако на практике на такую хитроумную оптимизацию пока что не способна ни одна из имеющихся СУБД.
ALL SOME ANY
ALL, SOME, ANYТысячелетия назад греческий философ Аристотель сформулировал систему логики, ставшей основой значительной части западной мысли. Сущность этой логики состоит в следующем. Следует начать с набора посылок, о которых известно, что они истинные, затем применить к ним операции и вывести, таким образом, новые истины. Вот пример такой процедуры.
Посылка 1. Все греки —люди.
Посылка 2. Все люди смертны.
Заключение. Все греки смертны.
А вот еще пример.
Посылка 1. Некоторые греки — женщины.
Посылка 2. Все женщины — люди.
Заключение. Некоторые греки — люди.
Другой способ выражения идеи второго примера состоит в следующем.
Если какие-либо греки — женщины и все женщины — люди, то некоторые греки люди.
В первом примере в обеих посылках используется квантор всеобщности ALL (все), который дает возможность сделать в заключении разумный вывод обо всех греках. Во втором примере в одной из посылок используется квантор существования SOME (некоторые), который также позволяет сделать в заключении вывод обо всех греках. А в третьем примере, чтобы сделать то же заключение, что и во втором примере, используется квантор существования ANY (какие-либо) — синоним квантора SOME.
Посмотрите, как кванторы SOME, ANY и ALL применяются в SQL.
Рассмотрим пример с бейсбольной статистикой. Бейсбол — это вид спорта, требующий значительных физических нагрузок, особенно у питчера, т.е. игрока, подающего мяч. Ему за время игры приходится 90-150 раз бросать мяч со своей возвышенности до основной базы — места, где находится игрок с битой. Такие нагрузки очень утомляют, и часто получается так, что к концу игры питчера на подаче приходится заменять. Бессменно подавать мячи в течение всей игры — это уже выдающееся достижение, причем неважно, привели такие попытки к победе или нет.
Предположим, что вы собираете данные о количестве тех игр, в которых питчеры высшей лиги бессменно подавали мяч. В одной из ваших таблиц перечислены все питчеры Американской лиги, а в другой — Национальной лиги. В каждой из этих таблиц обо всех игроках хранятся следующие данные: имя игрока, его фамилия и количество игр, бессменно проведенных им на подаче
Возможная двусмысленность квантора any
Первоначально в SQL в качестве квантора существования использовалось слово ANY. Это использование оказалось достаточно запутанным и приводило к ошибкам, так как в английском языке слово any иногда означает всеобщность, а иногда — существование."Do any of you know where Baker Street is?" (Кто-нибудь из вас знает, где находится улица Бейкер-Стрит?) "I can eat more eggs than any of you." (Я могу съесть больше яиц, чем любой из вас.) В первом предложении, скорее всего, задается вопрос, есть ли хотя бы один человек, который знает, где находится улица Бейкер-Стрит. Any используется как квантор существования. Второе предложение — это хвастливое заявление о том, что я могу съесть больше яиц, чем самый большой едок из окружающих. В этом случае any используется как квантор всеобщности. Поэтому разработчики стандарта SQL-92 хотя и оставили в нем от предыдущих версий SQL слово ANY, чтобы была совместимость с предшествующими продуктами, но в то же время добавили его менее запутанный синоним — слово SOME. SQL:2OO3 также поддерживает оба квантора существования.
В Американской лиге разрешается, чтобы назначенный хиттер (designated hitter, DH) мог бить битой по мячу вместо одного из девяти игроков, играющих в обороне. (Кто такой хиттер? Это игрок с битой. А назначенный хиттер — это тот хиттер, которому не требуется играть в оборонительной позиции.) Обычно DH делают это вместо питчеров, потому что те, как известно, плохие хиттеры. Питчерам приходится тратить слишком много времени и усилий на совершенствование своего броска. Поэтому у них остается мало времени, чтобы тренироваться с битой, как это делают остальные игроки.
Скажем, вам теоретически известно, что в среднем у стартовых питчеров (питчеров, играющих с самого начала игры) Американской лиги насчитывается больше игр, в которых они бессменно подавали мяч, чем у стартовых питчеров Национальной лиги. Такой вывод основан на тех ваших наблюдениях, что назначенные хиттеры дают возможность хорошо бросающим, но слабым в обращении с битой питчерам Американской лиги оставаться на подаче до завершения игры. Так как DH уже работают с битой вместо них, то становится не важным, что питчеры — слабые хиттеры. Однако в Национальной лиге питчер, как правило, заменяется в конце игры хиттером, у которого обычно больше шансов подготовить лучший удар. Чтобы проверить свою теорию, вы составляете следующий запрос:
SELECT FirstName, LastName
FROM AMERICAN_LEAGUER
WHERE CompleteGames > ALL
(SELECT CompleteGames
FROM NATIONAL_LEAGUER) ;
Подзапрос (внутренний оператор SELECT) возвращает список, показывающий для каждого питчера Американской лиги количество тех игр, в которых он бессменно подавал мяч. Внешний же запрос возвращает имена и фамилии всех питчеров Американской лиги, которые, бессменно подавая мяч, сыграли больше игр, чем каждый питчер Национальной лиги. В этом запросе используется квантор всеобщности ALL. Таким образом, возвращаются имена и фамилии тех питчеров Американской лиги, которые имеют больше игр с бессменной подачей мяча, чем самый лучший по этому показателю питчер Национальной лиги.
Посмотрите на следующий похожий оператор:
SELECT FirstName, LastName
FROM AMERICAN_LEAGUER
WHERE CompleteGames > ANY
(SELECT CompleteGames
FROM NATIONAL_LEAGUER) ;
В этом случае вместо квантора всеобщности ALL используется квантор существования ANY. Подзапрос (внутренний, вложенный запрос) здесь такой же, как и в предыдущем примере. В результате выполнения этого подзапроса получается полный список игр, в течение которых питчеры Национальной лиги бессменно подавали мяч. А внешний подзапрос возвращает имена и фамилии всех тех питчеров Американской лиги, которые, бессменно подавая мяч, сыграли больше игр, чем какой-либо из питчеров Национальной лиги. Вот в этом-то запросе и используется квантор существования ANY. Вы можете быть уверены, что хотя бы один из питчеров Национальной лиги ни одной игры не провел бессменно на подаче. Поэтому результат, скорее всего, будет включать в себя всех питчеров Американской лиги, которые бессменно провели хотя бы одну игру.
Если заменить ключевое слово ANY эквивалентным ему SOME (какой-то), то результат будет точно такой же. И если истинно утверждение, что "хотя бы один питчер Национальной лиги ни одной игры бессменно не провел на подаче", то тогда можно сказать, что "какой-то питчер Национальной лиги ни одной игры не провел на подаче бессменно".
AND
ANDЕсли для получения строки необходимо, чтобы все условия из какого-либо их набора имели значение True, используйте логическую связку AND (и). Проанализируйте следующий пример, в нем используются поля InvoiceNo (номер счета-фактуры), SaleDate (дата продажи), Salesperson (продавец), TotalSale (всего продано) из таблицы SALES (продажи):
SELECT InvoiceNo, SaleDate, Salesperson, TotalSale
FROM SALES
WHERE SaleDate >= '2003-05-18'
AND SaleDate <= '2003-05-24' ;
Предложение WHERE (где) должно соответствовать следующим двум условиям.
BETWEEN
BETWEENИногда нужно выбрать ту строку, в которой значение какого-либо столбца входит в определенный диапазон. Один из способов это сделать — использовать предикаты сравнения. Можно, например, составить предложение WHERE, предназначенное для выбора всех строк таблицы FOODS, в которых значение, хранящееся в столбце CALORIES, больше 100 и меньше 300:
WHERE FOODS.Calories > 100 AND FOODS.Calories < 300
В это сравнение не включены продукты питания, содержащие в точности 100 или 300 калорий, — в нем имеются только те значения, которые находятся в промежутке между этими числами. Чтобы в сравнение попали и эти два значения, можно написать следующий оператор:
WHERE FOODS.Calories >= 100 AND FOODS.Calories <= 300
Другой способ указать диапазон, включая его границы, — использовать предикат BETWEEN (между):
WHERE FOODS.Calories BETWEEN 100 AND 3 00
Это предложение выполняет абсолютно те же функции, что и оператор в предыдущем примере, в котором используются предикаты сравнения. Как видите, эта формулировка позволяет ввести меньшее количество кода, а также она чуть более наглядная, чем та, в которой используются два предиката сравнения, соединенные логической связкой AND.
DISTINCT
DISTINCTПредикат DISTINCT (отличающийся) похож на UNIQUE, за исключением отношения к значениям NULL. Если в таблице, полученной в результате выполнения подзапроса, все значения являются уникальными, тогда они отличаются друг от друга. Однако, в отличие от результата предиката UNIQUE, если к такой таблице применить ключевое слово DISTINCT, а в ней, кроме двух неопределенных, больше никаких строк нет, то предикат DISTINCT является ложным. Два значения NULL не считаются отличающимися друг от друга, хотя и считаются уникальными. Такая странная ситуация выглядит противоречиво, но этому есть свое объяснение. Дело в том, что в некоторых ситуациях два значения NULL должны считаться отличными друг от друга, а в некоторых — одинаковыми. Тогда в первом случае надо использовать UNIQUE, а во втором — DISTINCT.
EXISTS
EXISTSДля определения того, возвращает ли подзапрос какие-либо строки, вместе с ним можно использовать предикат EXISTS (существует). Если подзапрос возвращает хотя бы одну строку, то этот результат удовлетворяет условию EXISTS и выполняется внешний запрос. Ниже приведен пример использования предиката EXISTS.
SELECT FirstName, LastName
FROM CUSTOMER
WHERE EXISTS
(SELECT DISTINCT CustomerlD
FROM SALES
WHERE SALES.CustomerID = CUSTOMER.CustomerlD)
В таблице SALES (продажи) хранятся данные обо всех продажах, выполненных компанией. В этой таблице в поле CustomerID находятся идентификаторы покупателей, которые участвовали в какой-нибудь из сделок. В таблице CUSTOMER (покупатель) хранятся имя и фамилия каждого покупателя, но нет никакой информации о конкретных сделках.
Имеющийся в последнем примере подзапрос возвращает строку для каждого покупателя который сделал хотя бы одну покупку. А внешний запрос возвращает имена и фамилии тех кто участвовал в сделках, указанных в таблице SALES.
Предикат EXISTS, как показано в следующем запросе, эквивалентен сравнению
COUNT с нулем:
SELECT FirstName, LastName
FROM CUSTOMER
WHERE 0 <>
(SELECT COUNT(*)
FROM SALES
WHERE SALES.CustomerID = CUSTOMER.CustomerlD);
Для каждой строки таблицы SALES, в которой значение CustomerlD равно какому-либо значению CustomerlD из таблицы CUSTOMER, этот оператор выводит столбцы FirstName (имя) и LastName (фамилия) из таблицы CUSTOMER. Поэтому для каждой сделки, отмеченной в таблице SALES, этот оператор выводит имя и фамилию того покупателя, который в ней участвовал.
IN и NOT IN
IN и NOT INПредикаты IN (в) и NOT IN (не в) используются для работы с любыми указанными значениями, такими, например, как OR (Орегон), WA (Вашингтон) и ГО (Айдахо), из определенного набора значений, а именно штатов США. Например, у вас имеется таблица, в которой перечислены поставщики товаров, регулярно закупаемых вашей компанией. Вам нужно узнать телефонные номера тех поставщиков, которые размещаются в северной части Тихоокеанского побережья. Эти номера можно найти с помощью предикатов сравнения, например таких, которые показаны в следующем примере:
SELECT Company, Phone
FROM SUPPLIER
WHERE State = 'OR' OR State = 'WA' OR State = 'ID' ;
Впрочем, для выполнения той же самой задачи можно также использовать предикат IN:
SELECT Company, Phone
FROM SUPPLIER
WHERE State IN ('OR1, 'WA1, 'ID') ;
Это чуть более компактная форма записи, чем та, в которой используются предикаты сравнения и логические OR.
Таким же образом работает и второй вариант этого предиката — NOT IN. Скажем, у вас есть представительства в штатах Калифорния, Аризона и Нью-Мексико. Чтобы избежать уплаты налога с продаж, вы обдумываете возможность работы только с теми поставщиками, чьи представительства находятся за пределами этих трех штатов. Используйте следующую конструкцию:
SELECT Company, Phone
FROM SUPPLIER
WHERE State NOT IN CCA1, 'AZ', 'NM') ;
Используя таким образом ключевое слово IN, можно вводить чуть меньше кода. Впрочем, это не такое уж и большое преимущество. Как показано в первом примере этого раздела, примерно столько же придется потрудиться над кодом, если использовать предикаты сравнения.
Совет 1
Совет 1
Даже если предикат IN дает возможность вводить не намного меньше кода, то у вас все равно есть еще одна веская причина, чтобы использовать именно IN, а не предикаты сравнения. Ваша СУБД, скорее всего, поддерживает оба этих метода, и один из них может работать значительно быстрее другого. Эти два метода включения или исключения можно испытать в работе, а затем использовать тот из них, который покажет самый быстрый результат. А если в СУБД имеется хороший оптимизатор, то, независимо от вида используемых вами предикатов, во время работы, скорее всего, будет выбран самый оптимальный метод. Испытание этих двух методов как раз покажет, насколько хорошо оптимизирует код используемая СУБД. Если между временем выполнения двух операторов имеется значительная разница, то качество оптимизации под вопросом.
Кроме того, ключевое слово IN представляет ценность и в другой области. Если оно является частью подзапроса, то для получения результатов, которые нельзя добыть в одной таблице, ключевое слово IN позволяет выбрать информацию уже из двух таблиц. О подзапросах рассказывается в главе 11, а сейчас мы рассмотрим пример того, как в них используется ключевое слово IN.
Предположим, что вам нужно вывести имена всех тех, кто за последние 30 дней купил товар F-117A. Имена и фамилии покупателей находятся в таблице CUSTOMER (покупатель), а данные о сделках— в таблице TRANSACT (заключение сделок). Для этого вы сможете использовать следующий запрос:
SELECT FirstName, LastName
FROM CUSTOMER
WHERE CustomerlD IN
(SELECT CustomerlD
FROM TRANSACT
WHERE ProductID = 'F-117A'
AND TransDate >= (CurrentDate - 30)) ;
В первой из этих таблиц используются поля CustomerlD (идентификатор покупателя), FirstName (имя), LastName (фамилия), а во второй — CustomerlD, ProductID (идентификатор товара) и TransDate (дата сделки). Кроме того, используется переменная CurrentDate (текущая дата). Внутренний оператор SELECT, работающий с таблицей TRANSACT, вложен во внешний оператор SELECT, работающий с таблицей CUSTOMER. Первый из них ищет в столбце CustomerlD номера всех тех, кто за последние 30 дней купил товар F-l 17A. А внешний оператор SELECT выводит имена и фамилии всех покупателей, номера которых получены с помощью внутреннего оператора SELECT.
LIKE и NOT LIKE
LIKE и NOT LIKEДля сравнения двух символьных строк, чтобы выяснить их частичное соответствие друг другу, можно использовать предикат LIKE (похожий). Частичное соответствие представляет ценность тогда, когда о разыскиваемой строке что-то все-таки известно, но не известно, как она в точности выглядит. Кроме того, частичные соответствия можно использовать, чтобы получить из таблицы множество строк, в которых один из столбцов содержит похожие друг на друга символьные строки.
Чтобы указать частичные соответствия, в SQL используются два символа-маски (wildcard character). Знак процента (%) означает любую строку, состоящую из любого количества символов (в том числе и равного нулю). Символ подчеркивания О означает любой одиночный символ. Некоторые примеры того, как можно использовать предикат LIKE, показаны в табл. 9.3.
Логические связки
Логические связкиКак видно из массы предыдущих примеров, чтобы из таблицы получить нужные строки, одного условия в запросе часто бывает недостаточно. В некоторых случаях условий, которым должны удовлетворять строки, должно быть не меньше двух. В других же случаях, чтобы быть выбранной, строка должна удовлетворять одному из нескольких условий. А иногда нужно получить только те строки, которые указанному условию не удовлетворяют. Для выполнения этих требований в SQL имеются логические связки AND, OR и NOT.
MATCH
MATCHВ главе 5 шла речь о ссылочной целостности, которая включает в себя поддержание согласованности в многотабличной базе данных. Целостность может быть нарушена, если в дочернюю таблицу добавить строку, у которой нет соответствующей строки в родительской таблице. Можно вызвать похожие сложности, удалив из родительской таблицы строку и оставив в дочерней те строки, которые соответствуют удаленной.
Скажем, вы, ведя свой бизнес, собираете данные о своих покупателях в таблицу CUSTOMER (покупатель), а данные о продажах заносите в таблицу SALES (продажи). Вам не хочется добавлять строку в SALES до тех пор, пока данные о покупателе, участвующем в соответствующей сделке, не появятся в таблице CUSTOMER. Вам также не хочется удалять из CUSTOMER данные о покупателе, если он участвовал в сделках, информация о которых все еще хранится в таблице SALES. Перед тем как выполнять вставку или удаление, вам, возможно, захочется проверить, не приведет ли к нарушениям целостности выполнение со строкой какой-либо из этих операций. Такую проверку может выполнить предикат MATCH (соответствие).
Как используется предикат MATCH, можно узнать с помощью примера, где применяются опять же таблицы CUSTOMER и SALES. CustomerED (идентификатор покупателя) — это первичный ключ таблицы CUSTOMER, и работает он как внешний ключ таблицы SALES. В каждой строке таблицы CUSTOMER должно быть уникальное значение CustomerlD, не равное NULL. А в таблице SALES ключ CustomerlD не является уникальным, потому что в ней повторяются его значения, относящиеся к тем, кто покупал больше одного раза. Это нормальная ситуация, которая не угрожает целостности, потому что в этой таблице CustomerlD является не первичным, а внешним ключом.
Совет 3
Совет 3
По-видимому, в столбце CustomerlD таблицы SALES могут быть и значения NULL, потому что кто-то может зайти к вам с улицы, купить что-то и выйти еще до того, как вы сможете ввести его или ее имя, фамилию и адрес в таблицу CUSTOMER. Тогда в дочерней таблице может появиться строка, у которой нет соответствующей строки в родительской таблице. Чтобы справиться с этой трудностью, можно включить в таблицу CUSTOMER строку для "общего" пользователя и заносить все эти анонимные продажи в базу на его идентификатор.
Скажем, к кассиру подходит покупательница и утверждает, что 18 мая 2003 года она купила истребитель-невидимку F-117A "Стеле". Теперь же она хочет вернуть самолет, потому что его, словно авианосец, видно на вражеских радарах. Ее заявление может быть подтверждено с помощью проверки вашей базы SALES с помощью MATCH. Прежде всего необходимо найти в столбце CustomerlD идентификатор покупательницы и присвоить его значение переменной .vcustid, а затем можно использовать следующий синтаксис, в котором применяются столбцы CustomerlD, ProductID (идентификатор товара), SaleDate (дата продажи):
... WHERE (-.vcustid, 'F-117-A1, '2003-05-18')
MATCH
(SELECT CustomerlD, ProductID, SaleDate
FROM SALES).
Если есть запись о продаже с этим идентификатором пользователя, товаром и датой, то предикат MATCH возвращает значение True. А вы возвращаете покупательнице деньги. (Примечание: если какое-либо значение в первом аргументе предиката MATCH будет неопределенным, то всегда будет возвращаться значение True.)
Технические подробности: Разработчики языка SQL добавили в него предикаты MATCH и UNIQUE по одной и той же причине — эти предикаты дают возможность явно выполнять проверки, которые определены для неявных ограничений, связанных со ссылочной целостностью и уникальностью.
Предикат MATCH имеет такой общий вид:
Значение_типа_записи ROW MATCH [UNIQUE] [SIMPLE| PARTIAL |
FULL ] Подзапрос
Ключи UNIQUE (уникальный), SIMPLE (простой), PARTIAL (частичный) и FULL (полный) связаны с правилами обработки выражения типа записи, имеющего столбцы с неопределенными значениями. Правила для предиката MATCH являются точной копией соответствующих правил ссылочной целостности.
NOT
NOTДля отрицания условия служит связка NOT (не). Если к условию, которое возвращает значение True, добавить NOT, то после этого условие будет возвращать значение False. А если до изменения условие возвращало False, то после добавления к нему NOT оно будет возвращать True. Посмотрите на следующий пример:
SELECT InvoiceNo, SaleDate, Salesperson, TotalSale
FROM SALES
WHERE NOT (Salesperson = 'Ford') ;
Этот запрос возвращает строки для всех сделок по продажам, совершенных всеми продавцами, кроме Форда.
NULL
NULLС помощью предиката NULL выполняется поиск всех тех строк, в которых выбранный столбец содержит неопределенное значение. Именно такие значения имелись в столбце Carbohydrate (углеводы) в нескольких строках таблицы FOODS (продукты питания) (см. главу7). Названия продуктов из этих строк можно получить с помощью такого оператора:
SELECT (FOOD)
FROM FOODS
WHERE Carbohydrate IS NULL ;
Этот запрос возвращает следующие значения:
Гамбургер с нежирной говядиной
Нежное мясо цыплят
Жареный опоссум
Свиной окорок
Как вы, возможно, и предполагаете, если вставить ключевое слово NOT (нет), то получится совершенно противоположный результат:
SELECT (FOOD)
FROM FOODS
WHERE Carbohydrate IS NOT NULL ;
Этот запрос возвращает все строки таблицы FOODS, за исключением тех четырех, которые были выведены предыдущим запросом.
Общие продажи по каждому продавцу за исключением Фергюсона
Рисунок 9.4. Общие продажи по каждому продавцу за исключением Фергюсона
Чтобы показать таблицу, выводимую запросом, в алфавитном порядке или в обратном алфавитном порядке, используйте предложение ORDER BY (по порядку). В то время как предложение GROUP BY собирает строки в группы и сортирует группы по алфавиту, ORDER BY сортирует отдельные строки. ORDER BY должно быть последним предложением в запросе. Если в запросе также имеется и предложение GROUP BY, то оно вначале собирает строки вывода в группы. Затем предложение ORDER BY сортирует строки, находящиеся внутри каждой группы. А если предложения GROUP BY нет, то оператор рассматривает всю таблицу как группу и предложение ORDER BY сортирует все ее строки таким образом, чтобы были упорядочены значения в столбцах, указанных в этом предложении.
Это можно проиллюстрировать с помощью данных из таблицы SALES (продажи). В ней имеются столбцы InvoiceNo (номер счета-фактуры), SaleDate (дата продажи), Salesperson (продавец), TotalSale (всего продано). Все данные SALES можно увидеть, но в произвольном порядке, если воспользоваться следующим простым примером:
SELECT * FROM SALES ;
В одних реализациях это порядок, в котором строки вставлялись в таблицу, а в других реализациях строки могут быть выстроены в зависимости от времени самого последнего обновления каждой из них. Кроме того, порядок может неожиданно измениться, если кто-то физически реорганизует базу данных. В большинстве случаев порядок расположения получаемых строк приходится указывать. Возможно, вам нужно увидеть строки, которые расположены по порядку, задаваемому значениями из столбца SaleDate:
SELECT * FROM SALES ORDER BY SaleDate ;
При выполнении этого примера все строки таблицы SALES будут возвращены именно втом порядке, который задан значениями SaleDate.
А порядок расположения тех строк, у которых в столбце SaleDate одинаковые значения, зависит от используемой реализации. Впрочем, и для строк с одинаковыми значениями Sale-Date можно также указать порядок сортировки. Возможно, вам, например, нужно для каждого значения SaleDate видеть строки таблицы SALES, расположенные по порядку, которые заданы значениями InvoiceNo:
SELECT * FROM SALES ORDER BY SaleDate, InvoiceNo ;
В этом примере таблица SALES вначале упорядочивается по значениям SaleDate; затем для каждого такого значения строки этой таблицы располагаются по порядку, задаваемому InvoiceNo. Однако не путайте этот пример со следующим запросом:
SELECT * FROM SALES ORDER BY InvoiceNo, SaleDate ;
При выполнении этого запроса SALES упорядочивается по столбцу InvoiceNo. Затем для каждого значения InvoiceNo строки таблицы SALES располагаются по порядку, задаваемому столбцом SaleDate. Скорее всего, нужный вам результат вы не получите, потому что мало вероятно, чтобы для одного номера счета-фактуры было множество дат продажи.
Следующий запрос является очередным примером того, как SQL может возвращать данные:
SELECT * FROM SALES ORDER BY Salesperson, SaleDate ;
В этом примере упорядочивание сначала идет по столбцу Salesperson, а затем — по SaleDate. Просмотрев данные, расположенные в таком порядке, вы, возможно, захотите его изменить:
SELECT * FROM SALES ORDER BY SaleDate, Salesperson ;
Теперь упорядочивание сначала идет по столбцу SaleDate, а затем — по Salesperson.
Во всех этих примерах упорядочивание идет в порядке возрастания (ASC), который является порядком сортировки по умолчанию. Последний оператор SELECT вначале показывает самые ранние продажи (строки таблицы SALES), а в пределах определенной даты ставит продажи, проведенные Адамсом, перед продажами, проведенными Бейкером. А если вы предпочитаете порядок убывания (DESC), можете указать его для одного или множества столбцов из предложения ORDER BY:
SELECT * FROM SALES
ORDER BY SaleDate DESC, Salesperson ASC ;
В этом примере для данных продаж указывается порядок убывания дат, в результате чего записи о самых недавних продажах будут показаны первыми, но для продавцов указывается порядок возрастания, т.е. их фамилии будут располагаться в обычном алфавитном порядке.
Общие продажи по каждому продавцу
Рисунок 9.3. Общие продажи по каждому продавцу
Как и в случае среднего уровня продаж, Фергюсон имеет также самый высокий уровень общих продаж.
OR...
ORЕсли для возвращения строки необходимо, чтобы из нескольких условий для этой строки было верно хотя бы одно, используйте логическую связку OR (или):
SELECT invoiceNo, SaleDate, Salesperson, TotalSale
FROM SALES
WHERE Salesperson = 'Ford'
OR TotalSale > '200' ;
В результате выполнения этого запроса будут получены данные обо всех продажах, которые были сделаны на любую сумму, но только Фордом, или были сделаны кем угодно, но при этом превышали 200 долларов.
OVERLAPS
OVERLAPSПредикат OVERLAPS (перекрывает) применяется для того, чтобы определить, не перекрывают ли друг друга два промежутка времени. Он полезен тогда, когда нужно избежать "накладок" в расписании. Когда два промежутка времени перекрываются, то этот предикат возвращает значение True. Если они не перекрываются, то будет возвращено значение False.
Промежуток времени можно указать двумя способами: в виде начального и конечного времени или в виде начального времени и длительности. Вот несколько примеров:
(TIME '2:55:00', INTERVAL 'I' HOUR)
OVERLAPS
(TIME '3:30:00' , INTERVAL '2' HOUR)
В только что приведенном примере будет возвращено значение True, так как 3:30 наступает после 2:55 меньше чем через час.
(TIME '9:00:00', TIME '9:30:00')
OVERLAPS
(TIME '9:29:00', TIME '9:31:00')
Во втором примере будет возвращено значение True, потому что два промежутка времени перекрываются в течение одной минуты.
(TIME '9:00:00', TIME '10:00:00')
OVERLAPS
(TIME '10:15:00', INTERVAL '3' HOUR)
В третьем примере будет возвращено значение False, так как два промежутка времени не перекрываются.
(TIME '9:00:00', TIME '9:30:00')
OVERLAPS
(TIME '9:30:00', TIME '9:35:00')
И наконец, в последнем примере будет возвращено значение False — хотя два промежутка времени и являются смежными, но они не перекрываются.
Правила ссылочной целостности
Правила ссылочной целостностиПравила ссылочной целостности требуют, чтобы значения в столбце (или столбцах) одной таблицы соответствовали значениям в столбце (или столбцах) другой. Столбцы в первой таблице называются внешним ключом, а во второй — первичным, или уникальным, ключом. Например, столбец EmpDeptNo (номер отдела, где работает сотрудник) из таблицы EMPLOYEE (сотрудник) можно объявить внешним ключом, который ссылается на столбец DeptNo (номер отдела) из таблицы DEPT (отдел). Это соответствие дает гарантию, что когда в таблицу EMPLOYEE о сотруднике заносится информация, что он работает в отделе 123, то в таблице DEPT появляется запись, в которой значением столбца DeptNo является 123.
Такая ситуация является довольно простой, если внешний и первичный ключи состоят из одного столбца каждый. Однако оба эти ключа могут состоять также из множества столбцов. Например, значение в столбце DeptNo может быть уникальным только для одного и того же значения в столбце Location (представительство). Поэтому, чтобы однозначно определить строку из таблицы DEPT, необходимо указать значение и для столбца Location, и для столбца DeptNo. Если, например, отдел 123 имеется в двух представительствах, расположенных соответственно в Бостоне и в Тампе, то отделы необходимо указывать как ('Boston', '123') и ('Tampa', '123'). В таком случае для указания в таблице EMPLOYEE строки из таблицы DEPT необходимо использовать два столбца. Их можно назвать EmpLoc (представительство, где работает сотрудник) и EmpDeptNo. Если сотрудник работает в каком-либо отделе, расположенном в Бостоне, то значениями столбцов EmpLoc и EmpDeptNo будут соответственно Boston' и '123'. Таким образом, объявление внешнего ключа в EMPLOYEE будет следующим:
FOREIGN KEY (EmpLoc, EmpDeptNo)
REFERENCES DEPT (Location, DeptNo)
Вывод правильных заключений из ваших данных в громадной степени усложняется, если в этих данных содержатся неопределенные значения. Иногда данные с такими значениями надо интерпретировать одним способом, а иногда — другим. Разные интерпретации данных, в которых встречаются значения NULL, можно задавать с помощью ключевых слов UNIQUE, SIMPLE, PARTIAL и FULL. Если в ваших данных нет неопределенных значений, то вы в значительной степени избавитесь от необходимости ломать голову, просто возьмете и перейдете к следующему разделу "Логические связки". Ну а если в ваших данных такие значения есть, то тогда от режима скорочтения сейчас лучше отказаться и начать медленно и внимательно читать последующие абзацы. В каждом из них описана отдельная ситуация, связанная со значениями NULL, и рассказывается, как с ней справляется предикат MATCH.
Если значения EmpLoc и EmpDeptNo вместе являются или не являются неопределенными, то правила ссылочной целостности будут такие же, как и для ключей, состоящих из одного столбца с неопределенными или определенными значениями. Но если значение EmpLoc неопределенное, a EmpDeptNo — нет или наоборот, то тогда нужны новые правила. И какие же правила нужны, когда в таблицу EMPLOYEE при вставке или обновлении ее строк вводятся значения EmpLoc и EmpDeptNo как (NULL, '123') или ('Boston', NULL)? Существует шесть вариантов, при которых используются SIMPLE, PARTIAL и FULL вместе с ключевым словом UNIQUE или без него. Присутствие этого ключевого слова означает следующее. Чтобы предикат был истинным, значение типа записи, найденное с помощью MATCH в таблице-результате выполнения подзапроса, должно быть уникальным. И если в значении выражения R, имеющем тип записи, оба компонента являются неопределенными, то предикат MATCH возвращает значение True, каким бы ни было содержимое сравниваемой таблицы, полученной при выполнении подзапроса.
Если в значении выражения R типа записи с ключевым словом SIMPLE, но без UNIQUE, ни один из компонентов не является неопределенным и при этом хотя бы одна строка в таблице, полученной при выполнении подзапроса, соответствует R, то предикат MATCH возвращает значение True. В противном случае он возвращает значение False.
Если в значении выражения R типа записи с ключевыми словами SIMPLE и UNIQUE ни один из компонентов не является неопределенным и при этом хотя бы одна строка в таблице, полученной при выполнении подзапроса, уникальна и соответствует R, то тогда предикат MATCH возвращает значение True. В противном случае он возвращает значение False.
Если в значении выражения R типа записи с ключевым словом SIMPLE какой-нибудь из компонентов является неопределенным, то предикат MATCH возвращает значение True.
Если в значении выражения R типа записи с ключевым словом PARTIAL, но без UNIQUE, какой-нибудь из компонентов не является неопределенным и при этом определенные значения хотя бы одной строки в таблице, полученной при выполнении подзапроса, соответствуют R, то тогда предикат MATCH возвращает значение True. В противном случае он возвращает значение False.
Если в значении выражения R типа записи с ключевыми словами PARTIAL и UNIQUE какой-нибудь из компонентов не является неопределенным и при этом определенные части R соответствуют определенным частям хотя бы одной уникальной строки в таблице, полученной при выполнении подзапроса, то тогда предикат MATCH возвращает значение True. В противном случае он возвращает значение False.
Если ни один из компонентов значения выражения R типа записи с ключом FULL, но без UNIQUE, не является неопределенным и при этом хотя бы одна строка в таблице, полученной при выполнении подзапроса, соответствует R, то тогда предикат MATCH возвращает значение True. В противном случае он возвращает значение False.
Если ни один из компонентов значения выражения R типа записи с ключами FULL и UNIQUE не является неопределенным и при этом хотя бы одна строка в таблице, полученной при выполнении подзапроса, уникальна и соответствует R, то тогда предикат MATCH возвращает значение True. В противном случае он возвращает значение False.
Если какой-либо из компонентов выражения R является неопределенным и указано ключевое слово FULL, то предикат MATCH возвращает значение False.
Правила комитета по стандартам
С появлением SQL-89 стало подразумеваться, что по умолчанию используется правило UNIQUE. Это случилось еще до того, как кто-либо успел предложить или обсудить другие варианты. Но такие предложения появились уже во время разработки SQL-92. Кто-то упорно предпочитал правила PARTIAL и считал, что они должны быть единственными. С этой точки зрения правила SQL-89 (UNIQUE) были настолько нежелательны, что рассматривались как ошибка, которую необходимо исправить с помощью правил PARTIAL. Были и те, кому больше нравились правила UNIQUE, а правила PARTIAL для них были непонятными, двусмысленными и неэффективными. Впрочем, были и те, кто предпочитал еще более строгие правила FULL. В конце концов этот спор был решен следующим образом: пользователи получили в свое распоряжение все три ключевых слова и теперь могли выбирать тот подход, который им нужен. А потом, с появлением SQL: 1999, добавились еще и правила SIMPLE. Впрочем, рост числа правил делает работу с неопределенными значениями какой угодно, но только не простой (simple). Итак, если не указаны ключевые слова SIMPLE, PARTIAL или FULL, то будут выполняться правила SIMPLE.
Предикаты сравнения
Предикаты сравненияПримеры, приведенные в предыдущем разделе, демонстрируют обычное использование предикатов сравнения, в которых одно значение сравнивается с другим. Каждая строка, где в результате сравнения получается значение True, выполняет условие предложения WHERE, и с ней выполняется определенная операция (SELECT, UPDATE, DELECT и т.д.). Строки, где в результате сравнения получается значение False, пропускаются. Проанализируйте, например, следующий оператор SQL:
SELECT * FROM FOODS
WHERE Calories < 219 ;
Этот оператор выводит все строки таблицы FOODS, в которых значение, хранящееся в столбце Calories, меньше 219.
В табл. 9.2 приведены шесть предикатов сравнения.
Предложение HAVING
Предложение HAVINGПредложение HAVING позволяет еще лучше анализировать сгруппированные данные. Предложение HAVING (при условии) — это фильтр, который по своему действию похож на предложение WHERE, но, в отличие от предложения WHERE, HAVING работает не с отдельными строками, а с их группами. Проиллюстрируем действие предложения HAVING, используя следующий пример. Предположим, что менеджеру по продажам нужно сосредоточиться на работе других продавцов. Для этого ему необходимо исключить из общих данных количество продаж Фергюсона, поскольку его продажи находятся "в другой весовой категории". Чтобы сделать это, воспользуемся предложением HAVING:
SELECT Salesperson, SUM(TotalSale)
FROM SALES
GROUP BY Salesperson
HAVING Salesperson <> 'Ferguson';
Результат этого запроса приведен на Рисунок 9.4. Строки, которые относятся к продавцу по фамилии Фергюсон, на экран не выводятся.
Предложения From
Предложения FromПредложение FROM легко понять, если в нем указана только одна таблица, как, например, в следующем примере:
SELECT * FROM SALES ;
Этот оператор возвращает все данные, находящиеся во всех строках каждого столбца таблицы SALES (продажи). Впрочем, в предложении FROM можно указывать больше, чем одну таблицу. Например:
SELECT *
FROM CUSTOMER, SALES ;
Этот оператор создает виртуальную таблицу, в которой данные из таблицы CUSTOMER (покупатель) объединены с данными из таблицы SALES. Для создания новой таблицы каждая строка из CUSTOMER объединяется с каждой строкой из SALES. Поэтому в создаваемой таким способом новой виртуальной таблице количество строк равно количеству строк первой таблицы, умноженному на количество строк второй. И если в CUSTOMER десять строк, а в SALES — сто, то в новой таблице их будет тысяча.
Такая операция называется декартовым произведением двух исходных таблиц. Декартово произведение на самом деле является разновидностью операции объединения (JOIN). Об операциях объединения подробно рассказывается в главе 10.
В большей части приложений большинство тех строк, которые созданы в результате применения к двум таблицам декартова произведения, не имеют никакого смысла. Что касается виртуальной таблицы, созданной из CUSTOMER и SALES, то в ней представляют интерес только строки, в которых значение CustomerlD (идентификатор покупателя) из таблицы CUSTOMER равно значению CustomerlD из таблицы SALES. Все остальные строки можно отфильтровать с помощью предложения WHERE.
Предложения GROUP BY
Предложения GROUP BYИногда вместо того, чтобы получить отдельные записи, вам может понадобиться узнать что-либо о группе записей. В этом случае вам поможет предложение GROUP BY (группировать по).
Предположим, что вы менеджер по продажам и хотите посмотреть эффективность ваших продаж. Для этого вы можете воспользоваться оператором SELECT, как показано в следующем примере:
SELECT InvoiceNo, SaleDate, Salesperson, TotalSale
FROM SALES;
Полученный результат приведен на Рисунок 9.1.
Предложения WHERE
Предложения WHEREВ этой книге предложение WHERE использовалось много раз без всякого объяснения, потому что его значение и способ использования очевидны. Оператор выполняет операцию (такую как SELECT, DELETE или UPDATE) только с теми табличными строками, для которых определенное условие истинно. У предложения WHERE такой синтаксис:
SELECT список_столбцов
FROM имя_таблицы
WHERE условие ;
DELETE FROM имя_таблицы
WHERE условие ;
UPDATE имя_таблицы
SET столбец1=значение1, столбец2=значение2, ...,
столбец_n=значение_n
WHERE условие ;
Во всех случаях условие в предложении WHERE может быть или простым, или сколь угодно сложным. Чтобы из множества условий создать одно, их можно соединить друг с другом при помощи логических связок AND, OR и NOT. В этой главе мы еще вернемся к ним.
Вот некоторые типичные примеры предложений WHERE.
WHERE CustomerlD = SALES.CustomerlD
WHERE FOODS.Calories = COMIDA.Caloria
WHERE FOODS.Calories < 219
WHERE FOODS.Calories > 3 * base_value
WHERE FOODS.Calories < 219 AND FOODS.Protein > 27.4
Условия, выражаемые предложениями WHERE, называются предикатами. Предикат — это выражение, которое утверждает факт, относящийся к значениям из этого выражения.
Например, предикат FOODS.Calories < 219 является истинным, если в текущей строке значение столбца FOODS.Calories меньше 219. Если утверждение является истинным, то оно удовлетворяет условию. Утверждение может быть истинным (т.е. его значение равно True), ложным (его значение равно False) или с неопределенным логическим значением. Последний случай бывает тогда, когда в утверждении какие-либо элементы имеют значение NULL. Наиболее распространенными являются предикаты сравнения (=, <, >, О, <= и >=), но в SQL имеются и некоторые другие, которые значительно увеличивают возможности "отфильтровывать" требуемые данные от всех остальных. Ниже приведен список с предикатами, предоставляющими такую возможность.
Результат выбора информации о
Рисунок 9.1. Результат выбора информации о продажах с 07.01.2001 по 07.07.2001
Этот результат дает вам лишь некоторое представление о том, как работают ваши продавцы, поскольку здесь выводится информация о небольшом количестве продаж. Однако в реальной жизни продажи компаний очень велики, и в этом случае уже непросто будет определить, как были достигнуты результаты. Чтобы проверить, была ли коммерчески эффективной работа продавцов, вы можете скомбинировать предложение GROUP BY с одной из функций агрегирования (также называемыми итоговыми функциями), чтобы получить количественную картину о выполнении продаж. К примеру, вы можете просмотреть, кто из продавцов работает с более дорогостоящими и прибыльными позициями, используя функцию среднего значения (AVG):
SELECT Salesperson, AVG(TotalSale)
FROM SALES
GROUP BY Salesperson;
Полученный результат приведен на Рисунок 9.2.
SIMILAR
SIMILARВместе с SQL: 1999 появился и предикат SIMILAR (подобный), который позволяет находить частичное соответствие более эффективно, чем это делает предикат LIKE. С помощью предиката SIMILAR можно сравнить символьную строку с регулярным выражением. Скажем, например, вы просматриваете в таблице программной совместимости столбец OperatingSys-tem (операционная система), чтобы проверить совместимость с Microsoft Windows. Можно составить примерно такое предложение WHERE:
WHERE OperatingSystem SIMILAR TO
'(Windows (3.1195|98|Millenium Edition|CE|NT|2 000|XP)) '
В результате выполнения этого предиката будут возвращены все строки, у которых в столбце OperatingSystem содержится любая из указанных операционных систем Microsoft.
Средний уровень продаж по каждому продавцу
Рисунок 9.2. Средний уровень продаж по каждому продавцу
Как видно из Рисунок 9.2, средние продажи Фергюсона значительно выше, чем у двух других продавцов. Чтобы сравнить общие продажи по каждому продавцу, выполните следующий запрос:
SELECT Salesperson, SUM(TotalSale)
FROM SALES
GROUP BY Salesperson;
Результат этого запроса приведен на Рисунок 9.3.
Уточняющие предложения и их назначение
Таблица 9.1. Уточняющие предложения и их назначение| Уточняющее предложение | Для чего предназначено |
| from | Указывает, из каких таблиц брать данные |
| where | Фильтрует строки, которые не соответствуют условию поиска |
| group by | Группирует строки в соответствии со значениями в столбцах группирования |
| having | Фильтрует группы, которые не соответствуют условию поиска |
| order by | Сортирует результаты предыдущих предложений перед получением окончательного вывода |
SELECT список_столбцов
FROM список_таблиц
[WHERE условие_поиска]
[GROUP BY столбец_группирования]
[HAVING условие_поиска]
[ORDER BY условие_упорядочивания] ;
Ниже описывается, как работают уточняющие предложения.
SQL выполняет эти предложения в следующем порядке: FROM, WHERE, GROUP BY, HAVING и SELECT. Предложения работают по принципу конвейера, когда каждое из них получает результат выполнения предыдущего предложения, обрабатывает этот результат и передает то, что получилось, следующему предложению. Если этот порядок выполнения переписать в виде функций, то он будет выглядеть следующим образом:
SELECT (HAVING (GROUP BY (WHERE (FROM...) ) ) )
Предложение ORDER BY выполняется уже после SELECT. Оно может обращаться только к тем столбцам, которые перечислены в списке, находящемся после SELECT. К другим же столбцам из таблиц, перечисленных в предложении FROM, предложение ORDER BY обращаться не может.
Предикаты сравнения языка SQL
Таблица 9.2. Предикаты сравнения языка SQL| Сравнение | Символ |
| Равно | = |
| Не равно | <> |
| Меньше | < |
| Меньше или равно | <= |
| Больше | > |
| Больше или равно | >= |
Предикат like используемый в SQL
Таблица 9.3. Предикат like, используемый в SQL| Выражение | Возвращаемые значения |
| WHERE WORD LIKE 'intern%' | intern |
| internal | |
| international | |
| internet | |
| interns | |
| WHERE WORD LIKE '%Peace%' | Justice of the Peace |
| Peaceful Warrior | |
| WHERE WORD LIKE 't_p_' | tape |
| taps | |
| tipi | |
| tips | |
| tops | |
| type |
WHERE PHONE NOT LIKE '503%'
В этом случае будут возвращены все строки таблицы, в которых телефонный номер, содержащийся в столбце PHONE (телефон), не начинается с 503.
Совет 2
Совет 2
Возможно, вам потребуется выполнить поиск строки, в которой находится знак процента или символ подчеркивания. В таком случае необходимо, чтобы SQL интерпретировал, например, знак процента как знак процента, а не как символ-маску. Проводить такой поиск можно, если перед символом, который при поиске должен восприниматься буквально, ввести управляющий символ. В качестве такого символа можно назначить любой символ, лишь бы его не было в проверяемой строке. Как это сделать, показано в следующем примере:
SELECT Quote
FROM BARTLETTS
WHERE Quote LIKE '20#%'
ESCAPE '#' ;
Символ % превращается в обычный из символа-маски с помощью стоящего перед ним символа #. Точно таким же способом можно отключить и символ подчеркивания, а также сам управляющий символ. Например, предшествующий запрос должен найти такую цитату из "Bartlett's Familiar Quotations" (Известные цитаты Бартлетта): 20% of the salespeople produce 80% of the results.
Данный запрос найдет также следующее:
20%
UNIQUE
UNIQUEВместе с подзапросом, как и предикат EXISTS, можно также использовать предикат UNIQUE (уникальный). Если первый из этих предикатов является истинным тогда, когда подзапрос возвращает хотя бы одну строку, то второй из них будет истинным тогда, когда среди возвращенных подзапросом строк нет двух одинаковых. Другими словами, предикат UNIQUE будет истинным, если все возвращаемые подзапросом строки будут уникальными. Проанализируйте следующий пример:
SELECT FirstName, LastName
FROM CUSTOMER
WHERE UNIQUE
(SELECT CustomerID FROM SALES
WHERE SALES.CustomerID = CUSTOMER.CustomerID);
Этот оператор возвращает только имена и фамилии всех новых покупателей, которые участвовали лишь в одной из сделок, указанных в таблице SALES. Два значения NULL считаются не равными друг другу и, следовательно, уникальными. И когда ключевое слово UNIQUE применяется к таблице, полученной в результате выполнения подзапроса, а в этой таблице никаких строк, кроме двух неопределенных, больше нет, то и тогда предикат UNIQUE является истинным.
Уточняющие предложения
Уточняющие предложенияВ SQL имеются следующие уточняющие предложения: FROM, WHERE, HAVING, GROUP BY и ORDER BY. Предложение FROM (из) сообщает ядру базы данных, с какой таблицей (или таблицами) он должен работать. Что касается WHERE (где) и HAVING (при условии), то эти предложения указывают характеристику, определяющую необходимость выполнения текущей операции над конкретной строкой. И наконец, предложения GROUP BY (группировать по) и ORDER BY (упорядочивать по) указывают, каким образом следует выводить строки, полученные из базы данных. Основные сведения по уточняющим предложениям приведены в табл. 9.1.
Ключевое слово BETWEEN может привести
ВниманиеКлючевое слово BETWEEN может привести к путанице, потому что неочевидно, включены ли в предложение границы диапазона. На самом деле границы в предложение включены. Кроме того, первая граница обязательно должна быть не больше второй. Если, например, в FOODS.Calories содержится значение 200, то следующее предложение возвращает значение True: WHERE FOODS.Calories BETWEEN 100 AND 300
Однако предложение, казалось бы, эквивалентное предыдущему примеру, на самом деле возвращает противоположный результат False:
WHERE FOODS.Calories BETWEEN 300 AND 100
Помни:Для ключевого слова BETWEEN первая граница обязательно должна быть не больше второй.
Предикат BETWEEN можно использовать со следующими типами данных: символьными, битовыми, даты-времени, а также с числовыми. Вам могут встретиться примеры, похожие на следующий:
SELECT FirstName, LastName
FROM CUSTOMER
WHERE CUSTOMER.LastName BETWEEN 'A' AND 'Mzzz' ;
При его выполнении возвращаются данные обо всех покупателях, фамилии которых находятся в верхней половине списка по алфавиту.
Естественное объединение
Естественное объединениеЧастным случаем объединения, основанного на равенстве, является естественное объединение (natural join). В предложении WHERE из объединения, основанного на равенстве, проверяется равенство значения из столбца первой исходной таблицы значению из столбца второй. У двух столбцов должны быть одинаковые тип и длина, как, впрочем, у сравниваемых столбцов должно быть одно и то же имя. На самом же деле при естественном объединении равенство проверяется для всех столбцов из первой таблицы, имеющих те же имена, что и соответствующие им столбцы из второй.
Представьте, что в таблице COMPENSATION из предыдущего примера также имеются столбцы Salary и Bonus, но Employ заменен на ЕтрШ. В таком случае можно выполнить естественное объединение таблиц COMPENSATION и EMPLOYEE. Традиционный синтаксис объединения должен выглядеть примерно так:
SELECT E.*, С.Salary, С.Bonus
FROM EMPLOYEE E, COMPENSATION С
WHERE E.EmpID = C.EmpID ;
Этот запрос является естественным произведением. Для той же самой операции есть и альтернативный синтаксис:
SELECT E.*, С.Salary, С.Bonus
FROM EMPLOYEE E NATURAL JOIN COMPENSATION С ;
EXCEPT
EXCEPTОператор UNION выполняется с двумя таблицами и возвращает все строки, которые имеются как минимум в одной из них. Другой же оператор, INTERSECT, возвращает все те строки, которые имеются одновременно в первой и второй таблицах. А оператор EXCEPT (за исключением), наоборот, возвращает все строки, которые имеются в первой таблице, но не имеются во второй.
Теперь вернемся к примеру с базой данных, в которой находится информация о муниципальных пейджерах. Скажем, группа пейджеров, объявленных неработающими, была возвращена поставщику для ремонта, но к настоящему времени эти пейджеры уже исправлены и используются снова. В таблицу PAGERS данные о возвращенных пейджерах уже занесены, но из таблицы OUT их данные по некоторой причине не удалены, хотя это надо было сделать. С помощью оператора EXCEPT можно вывести все номера пейджеров, находящиеся в столбце PagerlD таблицы OUT, за исключением тех номеров, которые принадлежат уже исправленным пейджерам:
SELECT *
FROM OUT
EXCEPT CORRESPONDING (PagerlD)
SELECT *
FROM PAGERS;
При выполнении этого запроса возвращаются все строки из таблицы OUT, у которых значения PageiTD не находятся в таблице PAGERS.
INTERSECT
INTERSECTВ результате выполнения оператора UNION создается таблица, где появляются все строки, которые могут находиться в какой-либо из исходных таблиц. А если нужны только те строки, каждая из которых находится одновременно во всех исходных таблицах, то можно использовать оператор INTERSECT (пересечь). Он является реализацией в SQL оператора пересечения из реляционной алгебры. Выполнение INTERSECT будет показано на примере из воображаемого мира, в котором Боб Тарли был в середине сезона "продан" команде "Доджерс".
| SELECT * FROM NATIONAL ; | ||
| FirstName | LastName | СompleteGames |
| ----------- | ----------- | ------------------ |
| Sal | Maglie | 11 |
| Don | Newcombe | 9 |
| Sandy | Koufax | 13 |
| Don | Drysdale | 12 |
| Bob | Turley | 8 |
| SELECT * FROM AMERICAN ; | ||
| FirstName | LastName | СompleteGames |
| ----------- | ----------- | ------------------ |
| Whitey | Ford | 12 |
| Don | Larson | 10 |
| Bob | Turley | 8 |
| Allie | Reynolds | 14 |
SELECT *
FROM NATIONAL
INTERSECT
SELECT *
FROM AMERICAN;
| FirstName | LastName | СompleteGames |
| ----------- | ----------- | ------------------ |
| Bob | Turley | 8 |
Роль ключевых слов ALL и CORRESPONDING в операторе INTERSECT такая же, как и в операторе UNION. Если используется ALL, то получится таблица, в которой повторяющиеся строки остаются. А когда используется CORRESPONDING, то исходные таблицы не обязательно должны быть совместимыми для объединения, хотя у соответствующих столбцов должны быть одинаковые тип и длина.
Проанализируем следующий пример. В муниципалитете хранят данные о пейджерах, используемых полицейскими, пожарниками, уборщиками улиц и другими работниками городского хозяйства. Данные обо всех ныне используемых пейджерах находятся в таблице PAGERS (пейджеры) из базы данных. А данные обо всех пейджерах, которыми по той или иной причине не пользуются, находятся в другой таблице, OUT (вышедший из строя), имеющей такую же структуру, что и PAGERS. Информация ни по одному из пейджеров не может одновременно быть в двух таблицах. Выполнив оператор INTERSECT, можно проверить, не произошло ли такое ненужное дублирование строк:
SELECT *
FROM PAGERS
INTERSECT CORRESPONDING (PagerlD)
SELECT *
FROM OUT ;
В результате появляется таблица, и если в ней будут находиться какие-либо строки, то это будет означать, что база данных обновлена некорректно. Необходимо проверить все значения, которые появляются в этой таблице в столбце PagerlD (идентификатор пейджера). Ведь пейджер, соответствующий этому идентификатору, либо используется, либо не работает. Одновременно и того и другого не бывает. Обнаружив противоречивые данные, можно провести работы по восстановлению целостности базы данных — выполнить в одной из двух таблиц операцию DELETE (удалить).
Левое внешнее объединение
Левое внешнее объединениеВ запросе, имеющем объединение, левая таблица — это та, которая в операторе запроса предшествует ключевому слову JOIN, а правая — та, которая следует за ним. При левом внешнем объединении (left outer join) несоответствующие строки, имеющиеся в левой таблице, в выводе сохраняются, а имеющиеся в правой — из него, наоборот, удаляются.
Чтобы понять работу внешних объединений, представьте себе корпоративную базу данных, в которой хранятся записи о сотрудниках компании, ее отделах и представительствах. Примеры данных этой компании приведены в табл. 10.1-10.3.
Объединение основанное на равенстве
Объединение, основанное на равенствеОбъединение, основанное на равенстве, — это простое объединение с предложением WHERE, в котором находится условие, определяющее, что значение из одного столбца первой таблицы должно быть равно значению из соответствующего столбца второй таблицы. Если применить такое объединение к таблицам, имеющимся в примере из предыдущего раздела, то можно получить намного более содержательный результат:
SELECT *
FROM EMPLOYEE, COMPENSATION
WHERE EMPLOYEE.EmpID = COMPENSATION.Employ ;
И вот что вышло:
| EmpID | FName | LName | City | Phone | Employ | Salary | Bonus |
| --------- | --------- | -------- | ------ | -------- | --------- | -------- | -------- |
| 1 | Whitey | Ford | Orange | 555-1001 | 1 | 33000 | 10000 |
| 2 | Don | Larson | Newark | 555-3221 | 2 | 18000 | 2000 |
| 3 | Sal | Maglie | Nutley | 555-6905 | 3 | 24000 | 5000 |
| 4 | Bob | Turley | Passaic | 555-8908 | 4 | 22000 | 7000 |
SELECT EMPLOYEE.*, COMPENSATION.SALARY, COMPENSATION.Bonus
FROM EMPLOYEE, COMPENSATION
WHERE EMPLOYEE.EmpID = COMPENSATION.Employ ;
В результате получилось следующее:
| EmpID | FName | LName | City | Phone | Salary | Bonus |
| --------- | --------- | -------- | ------ | -------- | -------- | -------- |
| 1 | Whitey | Ford | Orange | 555-1001 | 33000 | 10000 |
| 2 | Don | Larson | Newark | 555-3221 | 18000 | 2000 |
| 3 | Sal | Maglie | Nutley | 555-6905 | 24000 | 5000 |
| 4 | Bob | Turley | Passaic | 555-8908 | 22000 | 7000 |
Можно облегчить труд по вводу кода SQL, если использовать псевдонимы (или имена корреляции). Псевдоним — это другое, более короткое имя таблицы. Если переделать предыдущий запрос с помощью псевдонимов, то получится примерно следующее:
SELECT Е.*, С.Salary, С.Bonus
FROM EMPLOYEE E, COMPENSATION С
WHERE E.EmpID = С.Employ ;
В этом примере Е — это псевдоним для EMPLOYEE, а С — для COMPENSATION. Действие псевдонима ограничено только тем оператором, в котором он определен. После объявления псевдонима в предложении FROM его необходимо использовать в пределах оператора. При этом нельзя одновременно использовать и длинную форму имени таблицы, и псевдоним.
Смешение полных имен с псевдонимами приводит к путанице. Проанализируйте следующий пример:
SELECT T1.C, T2.С
FROM Т1 Т2, Т2 Т1
WHERE T1.C > Т2.С ;
В этом примере псевдонимом для Т1 является Т2, а для Т2 — Т1. Конечно, такой выбор неразумен, однако формально он не противоречит никаким правилам. Если допустить возможность совместного использования полных имен и псевдонимов, невозможно определить, о какой таблице идет речь.
Предыдущий пример с псевдонимами эквивалентен следующему оператору SELECT без них:
SELECT T2.C, T1.C
FROM T1, T2
WHERE T2.С > T1.C ;
Стандарт SQL:2003 позволяет объединять больше двух таблиц. Их максимальное количество зависит от конкретной реализации. Синтаксис, используемый при таких объединениях, аналогичен тому, который применяется в случае двух таблиц:
SELECT Е.*, С.Salary, С.Bonus, Y.TotalSales
FROM EMPLOYEE E, COMPENSATION C, YTD_SALES Y
WHERE E.EmpID = С Employ
AND C.Employ = Y.EmpNo ;
Этот оператор проводит с тремя таблицами объединение, основанное на равенстве. При выполнении этого оператора извлекаются данные, хранящиеся в соответствующих столбцах каждой из таблиц. Это делается для того, чтобы можно было получить таблицу, в которой будут имена и фамилии продавцов, число проведенных каждым из них продаж и полученная ими компенсация. Менеджер по продажам сможет быстро увидеть, заслужил ли продавец свое вознаграждение.
Совет 2
Совет 2
Если данные о продажах, проведенных продавцами за последние 12 месяцев до текущей даты, будут храниться в отдельной таблице YTD_SALES (продажи за предшествующий год), то производительность и надежность будут выше, чем при хранении этих данных в таблице EMPLOYEE. Данные в EMPLOYEE относительно стабильные. Имя и фамилия человека, его адрес и номер телефона меняются не слишком часто. А данные о продажах за год меняются, наоборот, достаточно часто. Так как в таблице YTD_SALES столбцов меньше, чем в EMPLOYEE, то таблица YTD_SALES, скорее всего, сможет обновляться быстрее. И если при обновлении итогов продаж можно не трогать таблицу EMPLOYEE, то уменьшается риск случайного изменения хранящихся в ней данных.
Объединение по именам столбцов
Объединение по именам столбцовОбъединение по именам столбцов похоже на естественное, но является более гибким. При естественном объединении проверяется равенство значений из всех одноименных столбцов, которые только имеются в исходных таблицах. А что касается объединения по именам столбцов, то в нем можно выбирать, какие одноименные столбцы должны проверяться, а какие — нет. Если хотите, то можете выбрать их все, фактически превращая объединение по именам столбцов в естественное. Можете также выбрать и меньшее количество этих столбцов. Таким образом, есть возможность в большей степени определить, какие строки из перекрестного произведения должны оказаться в полученной вами итоговой таблице.
Скажем, вы изготовитель шахмат и имеете инвентарную таблицу, в которой хранятся данные о белых фигурах, а также другую такую же таблицу, но с данными о черных фигурах. Эти таблицы называются WHITE (белая) и BLACK (черная), и в каждой из них имеются следующие поля: Piece (фигура), Quant (количество), Wood (дерево). В таблицах хранятся такие данные:
| WHITE | BLACK | ||||
| -------- | -------- | ||||
| Piece | Quant | Wood | Piece | Quant | Wood |
| ------ | ------- | ------- | ------ | ------- | ------- |
| King | 502 | Oak | King | 502 | Ebony |
| Queen | 398 | Oak | Queen | 397 | Ebony |
| Rook | 1020 | Oak | Rook | 1020 | Ebony |
| Bishop | 985 | Oak | Bishop | 985 | Ebony |
| Knight | 950 | Oak | Knight | 950 | Ebony |
| Pawn | 431 | Oak | Pawn | 431 | Ebony |
При естественном объединении проверяется равенство значений во всех одноименных столбцах. В таком случае получится пустая таблица, потому что в таблице WHITE нет таких строк, где значение в столбце Wood бьло бы равно какому-либо значению из столбца Wood таблицы BLACK. Таблица, полученная в результате естественного объединения, не позволит определить, пропало что-нибудь или нет. Поэтому надо использовать объединение по именам столбцов, в котором столбец Wood исключается из рассмотрения. Это объединение может быть представлено в таком виде:
SELECT *
FROM WHITE JOIN BLACK
USING (Piese, Quant) ;
В результате объединения получается таблица только с теми строками, в которых количество белых и черных фигур, имеющихся на складе, совпадает:
| Piece | Quant | Wood | Piece | Quant | Wood |
| ------ | ------- | ------- | ------ | ------- | ------- |
| King | 502 | Oak | King | 502 | Ebony |
| Rook | 1020 | Oak | Rook | 1020 | Ebony |
| Bishop | 985 | Oak | Bishop | 985 | Ebony |
| Knight | 950 | Oak | Knight | 950 | Ebony |
Объединениеслияние
Объединение-слияниеВ отличие от других видов объединения, объединение-слияние (union join) не пытается искать для строки из левой исходной таблицы хотя бы одну соответствующую строку из правой исходной таблицы. Это объединение создает виртуальную таблицу, в которой находятся все столбцы обеих исходных таблиц. В созданной виртуальной таблице столбцы, взятые из левой исходной таблицы, содержат все строки этой исходной таблицы. В этих строках все столбцы, взятые из правой исходной таблицы, содержат неопределенные значения. И, аналогично, столбцы, взятые из правой исходной таблицы, содержат все строки этой исходной таблицы. А в этих строках все столбцы, взятые из левой исходной таблицы, содержат неопределенные значения. Таким образом, таблица, получившаяся в результате объединения-слияния, содержит все столбцы из обеих исходных таблиц, причем количество ее строк равно сумме количества строк, имеющихся в обеих исходных таблицах.
В большинстве случаев сам по себе результат объединения-слияния лишь промежуточный. В процессе обработки данных он является таблицей с большим количеством неопределенных значений. Впрочем, для получения полезной информации эту таблицу можно использовать вместе с выражением COALESCE (соединение) (см. главу 8).
Предположим, что вы работаете в компании, которая проектирует и производит ракеты, предназначенные для экспериментальных запусков. У вас в работе имеется несколько проектов. Под вашим руководством работают несколько инженеров-проектировщиков, каждый из которых имеет квалификацию в нескольких видах деятельности. Как менеджера вас интересует, какие инженеры в каких видах деятельности имеют квалификацию и над какими проектами работали. В настоящее время эти данные разбросаны по трем таблицам: EMPLOYEE (сотрудник), PROJECTS (проекты) и SKILLS (области квалификации).
В таблице EMPLOYEE хранятся данные о сотрудниках, и ее первичным ключом является EMPLOYEE.EmpID. Каждый проект, над которым работал сотрудник, занимает одну строку в другой таблице — PROJECTS. PROJECTS.EmpID — это внешний ключ, который ссылается на таблицу EMPLOYEE. В таблице SKILLS для каждого сотрудника перечислены те виды деятельности, в которых он имеет квалификацию. SKILLS.EmpID— внешний ключ, который ссылается на таблицу EMPLOYEE.
В таблице EMPLOYEE для каждого сотрудника имеется в точности одна строка. А в таблицах PROJECTS и SKILLS таких строк может быть сколько угодно, в том числе и ни одной.
Примеры данных, хранящихся в трех указанных таблицах, приведены в табл. 10.4-10.6.
ON или WHERE
ON или WHEREРоль, которую в объединениях разных видов играют предложения ON и WHERE, бывает достаточно запутанной. Прояснить ситуацию можно с помощью следующих фактов.
Операторы объединения
Операторы объединенияОператоры UNION, INTERSECT и EXCEPT представляют ценность в тех многотабличных базах данных, таблицы которых являются совместимыми. Однако во многих случаях приходится брать данные из наборов таблиц, имеющих между собой мало общего. Мощными реляционными операторами являются операторы объединения JOIN, в результате выполнения которых данные, взятые из множества таблиц, объединяются в одну. Таблицы из этого множества могут иметь мало общего друг с другом.
Стандарт SQL:2003 поддерживает разные типы операторов объединения. Какой из них лучше всего подходит в конкретной ситуации — это зависит от того результата, который требуется получить.
Перекрестное объединение
Перекрестное объединениеCROSS JOIN (перекрестное объединение) — это ключевое слово для простого объединения, не имеющего предложение WHERE. Поэтому оператор
SELECT *
FROM EMPLOYEE, COMPENSATION ;
также может быть записан как
SELECT *
FROM EMPLOYEE CROSS JOIN COMPENSATION ;
В результате получается декартово произведение (также известное как перекрестное произведение) двух исходных таблиц. CROSS JOIN редко дает тот окончательный результат, который вам нужен, но его применение может быть полезно в качестве первого шага в той цепочке манипуляций данными, которая в конце концов приведет к нужному результату.
Полное внешнее объединение
Полное внешнее объединениеПолное внешнее объединение (full outer join) соединяет в себе функции левого и правого внешних объединений. В результате выполнения в выводе остаются несоответствующие строки как из левой, так и из правой таблицы. Проанализируем теперь самый общий вариант корпоративной базы данных, которая уже рассматривалась в предыдущих примерах. В этой базе могут быть:
SELECT *
FROM LOCATION L FULL JOIN DEPT D
ON (L.LocationID = D.LocationID)
FULL JOIN EMPLOYEE E
ON (D.DeptID = E.DeptID) ;
Совет 4
Совет 4
Так как полного внутреннего объединения не существует, то полное внешнее объединение можно называть, полное объединение (в коде SQL это ключевые слова FULL JOIN).
Правое внешнее объединение
Правое внешнее объединениеГотов поклясться, что вы уже знаете, как ведет себя правое внешнее объединение. И вы правы! Правое внешнее объединение (right outer join) сохраняет в выводе несоответствующие строки, взятые из правой таблицы, но удаляет из него несоответствующие строки, взятые из левой. Это внешнее объединение можно использовать с теми же таблицами, что в левом внешнем объединении, и получить при этом те же результаты. Для этого надо, заменив в операторе ключевые слова левого внешнего объединения на ключевые слова правого, поменять порядок следования таблиц на обратный:
SELECT *
FROM EMPLOYEE E RIGHT OUTER JOIN DEPT D
ON (D.DeptID = E.DeptID)
RIGHT OUTER JOIN LOCATION L
ON (L.LocationID = D.LocationID) ;
В такой формулировке первое объединение создает таблицу, в которой находятся все отделы, с персоналом или без него. А второе объединение создает таблицу со всеми представительствами, независимо от наличия в них отделов.
Так как правого внутреннего объединения не существует, то правое внешнее объединение можно называть правое объединение (в коде SQL это ключевые слова RIGHT JOIN).
Простой оператор объединения
Простой оператор объединенияЛюбой многотабличный запрос является оператором объединения. Исходные таблицы объединяются в том смысле, что в таблице, полученной в результате этого объединения, будет находиться информация из всех исходных таблиц. Самым простым оператором объединения является оператор SELECT, выполняемый с двумя таблицами и не имеющий никаких ограничителей из предложения WHERE. Так что каждая строка из первой таблицы объединяется с каждой строкой из второй. В результате получается таблица, которая является декартовым произведением двух исходных таблиц. Понятие декартова произведения обсуждалось в главе 9 в связи с использованием предложения FROM. Количество строк в полученной таблице равно произведению числа строк в первой исходной таблице и числа строк во второй.
Представьте, что вы, например, являетесь в какой-либо компании менеджером по персоналу и что часть вашей работы состоит в том, чтобы вести учет сотрудников. Большинство данных о сотруднике, таких, например, как домашний адрес и номер телефона, не являются особо засекреченными. Однако доступ к некоторым данным, таким, например, как зарплата, должен быть только утех, кто имеет соответствующее разрешение. Чтобы защитить секретную информацию, держите ее в отдельной таблице, имеющей парольную защиту. Посмотрите на следующие две таблицы:
| EMPLOYEE (сотрудник) | COMPENSATION (компенсация) |
| -------------------------- | --------------------------------- |
| EmpID (идентификатор сотрудника) | Employ (сотрудник) |
| FName (имя) | Salary (зарплата) |
| LName (фамилия) | Bonus (премиальные) |
| City (город) | |
| Phone (телефон) |
| EmpID | FName | LName | City | Phone | Employ | Salary | BONUS |
| -------- | -------- | -------- | ---- | ------- | -------- | ------- | -------- |
| 1 | Whitey | Ford | Orange | 555-1001 | 1 | 33000 | 10000 |
| 2 | Don | Larson | Newark | 555-3221 | 2 | 18000 | 2000 |
| 3 | Sal | Maglie | Nutley | 555-6905 | 3 | 24000 | 5000 |
| 4 | Bob | Turley | Passaic | 555-8908 | 4 | 22000 | 7000 |
SELECT *
FROM EMPLOYEE, COMPENSATION ;
Вот что вышло:
| EmpID | FName | LName | City | Phone | Employ | Salary | Bonus |
| ------- | -------- | -------- | ---- | ------- | -------- | ------- | ------- |
| 1 | Whitey | Ford | Orange | 555-1001 | 1 | 33000 | 10000 |
| 1 | Whitey | Ford | Orange | 555-1001 | 2 | 18000 | 2000 |
| 1 | Whitey | Ford | Orange | 555-1001 | 3 | 24000 | 5000 |
| 1 | Whitey | Ford | Orange | 555-1001 | 4 | 22000 | 7000 |
| 2 | Don | Larson | Newark | 555-3221 | 1 | 33000 | 10000 |
| 2 | Don | Larson | Newark | 555-3221 | 2 | 18000 | 2000 |
| 2 | Don | Larson | Newark | 555-3221 | 3 | 24000 | 5000 |
| 2 | Don | Larson | Newark | 555-3221 | 4 | 22000 | 7000 |
| 3 | Sal | Maglie | Nutley | 555-6905 | 1 | 33000 | 10000 |
| 3 | Sal | Maglie | Nutley | 555-6905 | 2 | 18000 | 2000 |
| 3 | Sal | Maglie | Nutley | 555-6905 | 3 | 24000 | 5000 |
| 3 | Sal | Maglie | Nutley | 555-6905 | 4 | 22000 | 7000 |
| 4 | Bob | Turley | Passaic | 555-8908 | 1 | 33000 | 10000 |
| 4 | Bob | Turley | Passaic | 555-8908 | 2 | 18000 | 2000 |
| 4 | Bob | Turley | Passaic | 555-8908 | 3 | 24000 | 5000 |
| 4 | Bob | Turley | Passaic | 555-8908 | 4 | 22000 | 7000 |
Когда вы пытаетесь получить из множества таблиц полезную информацию, то декартово произведение, созданное с помощью простого объединения, почти никогда не бывает тем, что вам нужно. Впрочем, почти всегда первым шагом к тому, что вам нужно, бывает декартово произведение. Отфильтровывать из объединения ненужные строки можно с помощью ограничений, указываемых в предложении WHERE. Самым распространенным объединением, использующим фильтрующее предложение WHERE, является объединение, основанное на равенстве.
LOCATION (представительство)
Таблица 10.1. LOCATION (представительство)| LOCATION_ID (идентификатор представительства) | CITY (город) |
| 1 | Boston |
| 3 | Tampa |
| 5 | Chicago |
Усовершенствованный
Таблица 10.10. Усовершенствованный результат применения операции union join вместе с предложением coalesce| ID | Name | Type | ProjectName | Skill |
| 1 | Ferguson | Project | X-63 Structure | NULL |
| 1 | Ferguson | Project | X-64 Structure | NULL |
| 1 | Ferguson | Skill | NULL | Mechanical Design |
| 1 | Ferguson | Skill | NULL | Aerodynamic Loading |
| 2 | Frost | Project | X-63 Guidance | NULL |
| 2 | Frost | Project | X-64 Guidance | NULL |
| 2 | Frost | Skill | NULL | Analog Design |
| 2 | Frost | Skill | NULL | Gyroscope Design |
| 3 | Toyon | Project | X-63 Telemetry | NULL |
| 3 | Toyon | Project | X-64 Telemetry | NULL |
| 3 | Toyon | Skill | NULL | Digital Design |
| 3 | Toyon | Skill | NULL | R/F Design |
Если учесть количество имеющихся сейчас разных операций объединения (JOIN), то связывание данных из разных таблиц не должно создавать проблему, какой бы ни была структура этих таблиц. Поверьте, что если только в вашей базе имеются какие-либо сырые данные, то в SQJL2OO3 найдугся средства, чтобы их оттуда извлечь, а затем показать в каком-либо содержательном виде.
DEPT (отдел)
Таблица 10.2. DEPT (отдел)| DEPT_ID (идентификатор отдела) | LOCATION_ID | NAME (название) |
| 21 | 1 | Sales |
| 24 | 1 | Admin |
| 27 | 5 | Repair |
| 29 | 5 | Stock |
EMPLOYEE (сотрудник)
Таблица 10.3. EMPLOYEE (сотрудник)| EMP_ID (идентификатор сотрудника) | DEPT_ID | NAME (фамилия) |
| 61 | 24 | Kirk |
| 63 | 27 | McCoy |
SELECT *
FROM LOCATION L, DEPT D, EMPLOYEE E
WHERE L.LocationlD a D.LocationID
AND D.DeptID = E.DeptID ;
Результат выполнения этого оператора следующий:
| 1 | Boston | 24 | Admin | 61 | 24 | Kirk |
| 5 | Chicago | 27 | Repair | 63 | 27 | McCoy |
А теперь предположим, что вам требуются данные как о представительствах, так и связанные с представительствами данные об отделах и сотрудниках. Это "уже совсем другая история", потому что в представительстве может не быть никаких отделов. Поэтому для получения нужных данных используйте, как показано в следующем примере, внешнее объединение:
SELECT *
FROM LOCATION L LEFT OUTER JOIN DEPT D
ON (L.LocationID = D.LocationID)
LEFT OUTER JOIN EMPLOYEE E
ON (D.DeptID = E.DeptID);
В этом объединении данные берутся из трех таблиц. Сначала объединяются таблицы LOCATION и DEPT. Затем получившаяся таблица объединяется с таблицей EMPLOYEE. Даже если строки из таблицы, расположенной левее оператора LEFT OUTER JOIN, и не имеют соответствующих строк в таблице, расположенной правее этого оператора, они все равно входят в результат. Таким образом, при первом объединении в результат войдут все представительства, даже без отделов. А при втором объединении — войдут все отделы, даже без персонала. И вот какой получается результат:
| 1 | Boston | 24 | 1 | Admin | 61 | 24 | Kirk |
| 5 | Chicago | 27 | 5 | Repair | 63 | 27 | McCoy |
| 3 | Tampa | NULL | NULL | NULL | NULL | NULL | NULL |
| 5 | Chicago | 29 | 5 | Stock | NULL | NULL | NULL |
| 1 | Boston | 21 | 1 | Sales | NULL | NULL | NULL |
SELECT *
FROM LOCATION L LEFT OUTER JOIN DEPT D
ON (L.LocationID = D.LocationID)
LEFT OUTER JOIN EMPLOYEE E
ON (D.DeptID = E.DeptID) ORDER BY L.LocationID, D.DeptID, E.EmpID;
Совет 3
Совет 3
Так как левого внутреннего объединения не существует, то левое внешнее объединение можно назвать покороче —левое объединение (в коде SQL это ключевые слова LEFT JOIN).
Таблица EMPLOYEE
Таблица 10.4. Таблица EMPLOYEE| EmpID | Name (фамилия) |
| 1 | Ferguson |
| 2 | Frost |
| 3 | Toyon |
Таблица PROJECTS
Таблица 10.5. Таблица PROJECTS| ProjectName (название проекта) | EmpID |
| X-63 Structure (устройство ракеты Х-63) | 1 |
| X-64 Structure (устройство ракеты Х-64) | 1 |
| X-63 Guidance (система управления Х-63) | 2 |
| X-64 Guidance (система управления Х-64) | 2 |
| X-63 Telemetry (телеметрия Х-63) | 3 |
| X-64 Telemetry (телеметрия X-64) | 3 |
Теперь предположим, что вы как менеджер хотите увидеть всю информацию обо всех своих сотрудниках. Для этого вы решили применить к таблицам EMPLOYEE, PROJECTS и SKILLS объединение, основанное на равенстве:
SELECT *
FROM EMPLOYEE E, PROJECTS P, SKILLS S
WHERE E.EmpID = P.EmpID
AND E.EmpID = S.EmpID ;
Таблица SKILLS
Таблица 10.6. Таблица SKILLS| Skill (квалификация) | EmpID |
| Mechanical Design (механическое проектирование) | 1 |
| Aerodynamic Loading (расчеты аэродинамической нагрузки) | 1 |
| Analog Design (проектирование аналоговых устройств) | 2 |
| Gyroscope Design (проектирование гироскопов) | 2 |
| Digital Design (проектирование цифровых устройств) | 3 |
| R/F Design (проектирование РЛС) | 3 |
SELECT *
FROM EMPLOYEE E INNER JOIN PROJECTS P
ON (E.EmpID = P.EmpID)
INNER JOIN SKILLS S
ON (E.EmpID = S.EmpID) ;
Обе формулировки дают одинаковый результат, показанный в табл. 10.7.
Результаты внутреннего объединения
Таблица 10.7. Результаты внутреннего объединения| Е.EmpID | E.Name | P.EmpID | ProjectName | S.EmpID | S.Skill |
| 1 | Ferguson | 1 | X-63 Structure | 1 | Mechanical Design |
| 1 | Ferguson | 1 | X-63 Structure | 1 | Aerodynamic Loading |
| 1 | Ferguson | 1 | X-64 Structure | 1 | Mechanical Design |
| 1 | Ferguson | 1 | X-64 Structure | 1 | Aerodynamic Loading |
| 2 | Frost | 2 | X-63 Guidance | 2 | Analog Design |
| 2 | Frost | 2 | X-63 Guidance | 2 | Gyroscope Design |
| 2 | Frost | 2 | X-64 Guidance | 2 | Analog Design |
| 2 | Frost | 2 | X-64 Guidance | 2 | Gyroscope Design |
| 3 | Toyon | 3 | X-63 Telemetry | 3 | Digital Design |
| 3 | Toyon | 3 | X-63 Telemetry | 3 | R/F Design |
| 3 | Toyon | 3 | X-64 Telemetry | 3 | Digital Design |
| 3 | Toyon | 3 | X-64 Telemetry | 3 | R/F Design |
SELECT *
FROM EMPLOYEE E
UNION JOIN PROJECTS P UNION JOIN SKILLS S ;
Обратите внимание, что в объединении-слиянии нет предложения ON. Дело в том, что сейчас данные не фильтруются, поэтому предложение ON не нужно. Результат, полученный при выполнении этого оператора, приведен в табл. 10.8.
Результат операции union join
Таблица 10.8. Результат операции union join| E.EmpID | E.Name | P.EmpID | ProjectName | S.EmpID | S.Skill |
| 1 | Ferguson | NULL | NULL | NULL | NULL |
| NULL | NULL | 1 | X-63 Structure | NULL | NULL |
| NULL | NULL | 1 | X-64 Structure | NULL | NULL |
| NULL | NULL | NULL | NULL | 1 | Mechanical Design |
| NULL | NULL | NULL | NULL | 1 | Aerodynamic Loading |
| 2 | Frost | NULL | NULL | NULL | NULL |
| NULL | NULL | 2 | X-63 Guidance | NULL | NULL |
| NULL | NULL | 2 | X-64 Guidance | NULL | NULL |
| NULL | NULL | NULL | NULL | 2 | Analog Design |
| NULL | NULL | NULL | NULL | 2 | Gyroscope Design |
| 3 | Toyon | NULL | NULL | NULL | NULL |
| NULL | NULL | 3 | X-63 Telemetry | NULL | NULL |
| NULL | NULL | 3 | X-64 Telemetry | NULL | NULL |
| NULL | NULL | NULL | NULL | 3 | Digital Design |
| NULL | NULL | NULL | NULL | 3 | R/F Design |
Обратите внимание, что для идентификатора сотрудника в таблице есть три столбца, но в любой из строк определенным является только один из них. Вид выводимой таблицы можно улучшить, если использовать для этих столбцов выражение COALESCE (соединить). Как уже говорилось в главе 8, это выражение выбирает из переданного ему списка значений первое, не являющееся неопределенным. В данном случае COALESCE выбирает из списка столбцов единственное значение:
SELECT COALESCE (E.EmpID, P.EmpID, S.EmpID) AS ID,
E.Name, P.ProjectName, S.Skill
FROM EMPLOYEE E UNION JOIN PROJECTS P
UNION JOIN SKILLS S
ORDER BY ID ;
Предложение FROM здесь такое же, как и в предыдущем примере, но теперь три столбца EmpID соединяются с помощью выражения COALESCE в один, который называется ID. Кроме того, результат упорядочивается как раз по этому столбцу ID. Что в итоге получилось, показано в табл. 10.9.
В каждой строке этой таблицы имеются данные или о проекте, или о квалификации, но не о том и другом вместе. При чтении результата необходимо вначале определить, какого типа данные в каждой строке. Если в строке столбец ProjectName является определенным, то в ней указан проект, над которым работал сотрудник. А если определенным является столбец Skill, то в строке указаны навыки сотрудника.
Результат применения
Таблица 10.9. Результат применения операции union join вместе с предложением| ID | Name | ProjectName | Skill |
| 1 | Ferguson | X-63 Structure | NULL |
| 1 | Ferguson | X-64 Structure | NULL |
| 1 | Ferguson | NULL | Mechanical Design |
| 1 | Ferguson | NULL | Aerodynamic Loading |
| 2 | Frost | X-63 Guidance | NULL |
| 2 | Frost | X-64 Guidance | NULL |
| 2 | Frost | NULL | Analog Design |
| 2 | Frost | NULL | Gyroscope Design |
| 3 | Toyon | X-63 Telemetry | NULL |
| 3 | Toyon | X-64 Telemetry | NULL |
| 3 | Toyon | NULL | Digital Design |
| 3 | Toyon | NULL | R/F Design |
SELECT COALESCE (E.EmpID, P.EmpID, S.EmpID) AS ID,
E.Name, COALESCE (P.Type, S.Type) AS Type,
P.ProjectName, S.Skill
FROM EMPLOYEE E
UNION JOIN (SELECT "Project" AS Type, *
FROM PROJECTS) P
UNION JOIN (SELECT "Skill" AS Type, *
FROM SKILLS) S
ORDER BY ID, Type ;
В первом предложении UNION JOIN таблица PROJECTS заменена вложенным предложением SELECT, которое добавляет к столбцам, взятым из этой таблицы, еще один столбец, Р.Туре, с постоянным значением "Project" (проект). И, аналогично, во втором предложении UNION JOIN таблица SKILLS заменена другим вложенным предложением SELECT, которое добавляет к столбцам, взятым из этой таблицы, еще один столбец, S.Type, с постоянным значением "Skill" (квалификация). В каждой строке значением Р.Туре является или NULL, или "Project", а значением S.Type — или NULL, или "Skill".
В списке внешнего предложения SELECT указано выполнение операции COALESCE, при которой два столбца Туре должны стать одним, также имеющим имя Туре. Затем этот новый столбец Туре указывается в предложении ORDER BY, которое таким образом сортирует все строки, чтобы вначале шли строки с проектами, а затем — с квалификационными навыками. Результат показан в табл. 10.10.
UNION
UNIONОператор UNION (объединение) — это реализация в языке SQL оператора объединения из реляционной алгебры. Оператор UNION дает возможность получать информацию из нескольких таблиц, имеющих одинаковую структуру. Одинаковая структура означает следующее.
Скажем, вы создаете базу данных по бейсбольной статистике (см. главу 9). Она состоит из двух таблиц, совместимых для объединения, которые называются AMERICAN (Американская лига) и NATIONAL (Национальная лига). В каждой из них имеются по три столбца, и типы у всех соответствующих столбцов совпадают. На самом деле даже имена у таких столбцов одинаковые, хотя для объединения это условие не является обязательным.
В таблице NATIONAL перечислены имена, фамилии питчеров Национальной лиги и количество тех игр, в которых они все время были на подаче. Эти данные находятся в столбцах FirstName (имя), LastName (фамилия) и CompleteGames (полностью сыгранные игры). Та же информация, но только о питчерах Американской лиги, содержится в таблице AMERICAN. Если объединить таблицы NATIONAL и AMERICAN с помощью оператора UNION, то в результате получится виртуальная таблица со всеми строками из первой и второй таблиц. В этом примере, чтобы показать работу оператора UNION, я вставил в каждую таблицу всего лишь по несколько строк:
| SELECT * FROM NATIONAL ; | ||
| FirstName | LastName | СompleteGames |
| ----------- | ----------- | ------------------ |
| Sal | Maglie | 11 |
| Don | Newcombe | 9 |
| Sandy | Koufax | 13 |
| Don | Drysdale | 12 |
| SELECT * FROM AMERICAN ; | ||
| FirstName | LastName | СompleteGames |
| ----------- | ----------- | ------------------ |
| Whitey | Ford | 12 |
| Don | Larson | 10 |
| Bob | Turley | 8 |
| Allie | Reynolds | 14 |
| SELECT * FROM NATIONAL | ||
| UNION | ||
| SELECT * FROM AMERICAN ; | ||
| FirstName | LastName | СompleteGames |
| ----------- | ----------- | ------------------ |
| Allie | Reynolds | 14 |
| Bob | Turley | 8 |
| Don | Drysdale | 12 |
| Don | Larson | 10 |
| Don | Newcombe | 9 |
| Sal | Maglie | 11 |
| Sandy | Koufax | 13 |
| Whitey | Ford | 12 |
Условное объединение
Условное объединениеУсловное объединение похоже на объединение, основанное на равенстве, но в проверяемом условии присутствие равенства не обязательно (хотя и не исключается). Проверяемым условием может быть любой правильно составленный предикат. Если условие в проверяемой строке выполняется, то эта строка станет частью полученной таблицы. Синтаксис условного объединения немного отличается от того, который вы видели до сих пор. Это отличие состоит в том, что условие содержится в предложении ON (в), а не в WHERE (где).
Скажем, бейсбольному статисту надо знать, какие питчеры из Национальной лиги провели полностью на подаче столько игр, сколько это сделал хотя бы один питчер Американской лиги. Этот вопрос предназначен для объединения, основанного на равенстве, а также может быть выражен с помощью синтаксиса условного объединения:
SELECT *
FROM NATIONAL JOIN AMERICAN
ON NATIONAL.СompleteGames = AMERICAN.CompleteGames ;
Внешнее объединение
Внешнее объединениеПри объединении двух таблиц в первой из них (назовем ее левой) могут быть строки, которых нет во второй (правой) таблице. И наоборот, в правой таблице могут быть строки, которых нет в левой. При выполнении внутреннего объединения этих таблиц все несоответствующие строки из вывода удаляются. Однако при внешнем объединении (outer join) такие строки остаются. На самом деле любое внешнее объединение бывает трех видов: левое, правое и полное.
используется для обозначения всех столбцов,
ВниманиеЗвездочка ('*') используется для обозначения всех столбцов, имеющихся в таблице. Это сокращенное обозначение работает в большинстве случаев прекрасно, но если реляционные операторы используются во встроенном или модульном коде SQL, то это обозначение может доставить массу неприятностей. Что если в одну из таблиц или сразу во все будут добавлены дополнительные столбцы? Тогда эти таблицы больше не будут совместимыми для объединения, и программа перестанет работать. И даже если во все таблицы, для обеспечения совместимости по операции объединения, будут добавлены одни и те же столбцы, то программа скорее всего не будет готова работать с этими дополнительными данными. Таким образом, лучше явно перечислять нужные столбцы, а не полагаться на сокращение '*'. Но при вводе с консоли "одноразовой" команды SQL звездочка работает прекрасно. Если вдруг запрос не сработает, всегда можно быстро вывести структуру таблицы. Операция UNION обычно убирает любые повторяющиеся строки, которые появляются в результате ее выполнения. В большинстве случаев это желаемый результат. Впрочем, иногда повторяющиеся строки требуется сохранять. В таких случаях используйте UNION ALL (объединение всех).
Обратимся к нашему примеру и предположим, что Боб Тарли по кличке "Буллит" ("пуля") был "продан" в середине сезона из команды "Нью-Йорк Янкиз", входящей в Американскую лигу, в "Бруклин Доджерс" из Национальной лиги. А теперь предположим, что в каждой команде за сезон у этого питчера было по восемь игр, в течение которых он бессменно подавал мяч. Обычный оператор UNION, показанный в примере, отбросит одну из двух строк с данными об этом игроке. И хотя будет казаться, что за сезон он полностью провел на подаче мяча только восемь игр, но ведь на самом деле таких игр — замечательный результат — было 16. Корректные данные можно получить с помощью следующего запроса:
SELECT * FROM NATIONAL
UNION ALL
SELECT * FROM AMERICAN ;
Иногда оператор UNION можно применять и к двум таблицам, которые не являются совместимыми для объединения. Если в таблицу, которая должна получиться, войдут столбцы, имеющиеся в обеих исходных таблицах и являющиеся совместимыми, то можно использовать оператор UNION CORRESPONDING (объединение соответствующих). В этом случае учитываются только указанные столбцы, и только они войдут в получившуюся таблицу.
Полностью отличаются друг от друга данные, которые бейсбольные статистики собирают по питчерам и игрокам в дальней части поля. Однако каждый раз и в том и в другом случае записываются имя (first name), фамилия (last name) игрока, выходы на поле (putouts), ошибки (errors) и доля принятых мячей (fielding percentage). Конечно, по игрокам в дальней части поля не собирают данные о выигрышах/проигрышах (won/lost record), остановленных мячах (saves) или другие сведения, относящиеся только к подаче мяча. Но все равно, чтобы получить некоторую общую информацию об умении играть в защите, можно выполнять оператор UNION, который будет брать данные из двух таблиц — OUTFIELDER (игрок в дальней части поля) и PITCHER (питчер):
SELECT *
FROM OUTFIELDER
UNION CORRESPONDING
(FirstName, LastName, Putouts, Errors, FieldPct)
SELECT *
FROM PITCHER ;
В результате получается таблица, где для каждого питчера или игрока в дальней части поля указаны имя и фамилия, а также количество выходов на поле, ошибок и доля принятых мячей. Здесь, как и при использовании простого оператора UNION, повторяющиеся строки удалены. Таким образом, если игрок некоторое время играл в дальней части поля, а также был питчером, то при выполнении оператора UNION CORRESPONDING часть статистики этого игрока будет потеряна. Чтобы этого не случилось, используйте UNION ALL CORRESPONDING (объединение всех соответствующих).
Совет 1
Совет 1
В списке, находящемся сразу за ключевым словом CORRESPONDING (соответствующие), должны быть только те имена столбцов, которые имеются во всех объединяемых таблицах. Если этот список имен будет пропущен, то будет неявно использован полный список имен. Однако, если в одну или несколько таблиц будут добавлены новые столбцы, этот неявный список может измениться. Так что имена столбцов лучше не пропускать, а указывать явно.
Внутреннее объединение
Внутреннее объединениеОбъединения — мистические операторы, и для правильного обращения с ними требуется недюжинная внутренняя сила. Возможно, вы даже слышали о внутреннем объединении (inner join), — оно-то и является квинтэссенцией реляционных операций. Я вас разыграл! Во внутренних объединениях вовсе нет ничего таинственного. На самом деле внутренними являются все объединения, о которых уже говорилось в этой главе. Объединение по именам столбцов из последнего примера можно сформулировать и как внутреннее, если воспользоваться следующим синтаксисом:
SELECT *
FROM WHITE INNER JOIN BLACK
USING (Piese, Quant) ;
Результат при этом получится тот же самый.
Внутреннее объединение называется "внутренним", чтобы его можно было отличить от внешнего объединения. Из таблицы, получаемой при внутреннем объединении, выбрасываются все строки, у которых нет соответствующих строк одновременно в обеих исходных таблицах. А при внешнем объединении несоответствующие строки сохраняются. Такая вот между ними разница, и нет в ней ничего метафизического.
Что делают подзапросы
Что делают подзапросыПодзапросы находятся в предложении WHERE внешнего оператора. Их роль состоит в том, чтобы задавать для этого предложения условия поиска. Разные виды подзапросов дают разные результаты. Некоторые подзапросы создают список значений, который затем передается замыкающему оператору. Другие подзапросы создают единственное значение, которое затем проверяется замыкающим оператором с помощью оператора сравнения. Существуют также и подзапросы, возвращающие логические значения.
Другие коррелированные подзапросы
Другие коррелированные подзапросыКак уже говорилось в предыдущем разделе, подзапросы с ключевым словом IN или оператором сравнения не обязательно должны быть коррелированными, хотя, с другой стороны, такой вариант вполне возможен.
EXISTS
EXISTSДопустим, вы являетесь продавцом из Zetec Corporation и хотите позвонить контактным представителям всех калифорнийских организаций, покупающих продукцию Zetec. Попробуйте использовать следующий запрос:
SELECT *
FROM CONTACT
WHERE EXISTS
(SELECT *
FROM CUSTOMER
WHERE CustStat" - 'CA'
AND CONTACT.CuSl.? = CUSTOMER.CustID) ;
Обратите внимание на такую ссылку, как CONTACT.CuslTD. Она указывает на столбец из внешнего запроса. Этот столбец сравнивается с другим столбцом, CUSTOMER.CustID, находящемся в таблице внутреннего запроса. Для каждой строки внешнего запроса вы проверяете внутренний запрос, т.е. в предложении WHERE внутреннего запроса используется значение столбца CustID из текущей строки таблицы CONTACT. Эта таблица указана во внешнем запросе.
Столбец CustID связывает таблицу CONTACT с таблицей CUSTOMER. SQL переходит в первую строку таблицы CONTACT, затем находит строку в таблице CUSTOMER, имеющую то же значение CustID, и проверяет в этой строке значение столбца CustState. Если CUSTOMER.CustState = 'СА, то в выводимую таблицу добавляется текущая строка таблицы CONTACT. Точно так же обрабатывается и следующая запись этой таблицы. Так как запрос указывает SELECT * FROM CONTACT, то возвращаются все поля таблицы с данными контактных представителей, в том числе поля с фамилиями и телефонными номерами представителей.
Коррелированные подзапросы перед
Коррелированные подзапросы, перед которыми стоит ключевое слово INВыше, в разделе "Подзапросы, перед которыми стоит ключевое слово IN", рассказывалось, каким образом некоррелированный подзапрос можно использовать вместе с предикатом IN. А чтобы увидеть, каким образом этот предикат может использоваться, наоборот, коррелированным подзапросом, задайте тот же самый вопрос, что и в случае с предикатом EXISTS. Итак, какие фамилии и телефонные номера у представителей для контакта во всех организациях-покупателях продукции Zetec в Калифорнии? Ответ можно получить с помощью коррелированного подзапроса с IN:
SELECT *
FROM CONTACT
WHERE 'CA' IN
(SELECT CustState
FROM CUSTOMER
WHERE CONTACT.CustID = CUSTOMER.CustID) ;
Оператор выполняется с каждой записью таблицы CONTACT. Если значение столбца CustID этой записи совпадает с соответствующим значением столбца таблицы CUSTOMER, то значение CUSTOMER.CustState сравнивается со значением 'СА. Результатом выполнения подзапроса является список, в котором содержится не более одного элемента. Ваш этот единственный элемент представляет собой 'СА', то выполняется условие предложения WHERE из замыкающего оператора и строка добавляется в выводимую запросом таблицу.
Коррелированные подзапросы, перед которыми стоят операторы сравнения
Как будет показано в следующем примере, перед коррелированным подзапросом может стоять также любой из шести операторов сравнения.
Компания Zetec выплачивает каждому своему продавцу премию, которая зависит от общей суммы, вырученной им от продаж за месяц. Чем выше эта сумма, тем выше процент премии. Список этих процентов хранится в таблице BONUSRATE (ставка премии) со столбцами MIN_AMOUNT (нижняя граница), МАХ_AMOUNT (верхняя граница) (процент премии).
| MIN_AMOUNT | MAX_AMOUNT | BONUS_PCT |
| ---------------- | ---------------- | -------------- |
| 0.00 | 24999.99 | 0 |
| 25000.00 | 49999.99 | 0.001 |
| 50000.00 | 99999.99 | 0.002 |
| 100000.00 | 249999.99 | 0.003 |
| 250000 .00 | 499999.99 | 0.004 |
| 500000.00 | 749999.99 | 0.005 |
| 750000.00 | 999999.99 | 0.006 |
Продажи записываются в главную таблицу сделок TRANSMASTER.
| TRANSMASTER | ||
| ----------------- | ||
| Столбец | Тип | Ограничения |
| --------- | ---- | ---------------- |
| TRANSID (идентификатор сделки) |
INTEGER | PRIMARY KEY |
| CUSTID (идентификатор покупателя) |
INTEGER | FOREIGN KEY |
| EMPID (идентификатор сотрудника) |
INTEGER | FOREIGN KEY |
| TRANSDATE (дата сделки) |
DATE | |
| NET_AMOUNT (облагаемая налогом сумма) |
NUMERIC | |
| FREIGHT (стоимость перевозки) |
NUMERIC | |
| TAX (налог) |
NUMERIC | |
| INVOICETOTAL (итоговая сумма счета-фактуры) |
NUMERIC |
Премии начисляются на основе суммы значений из столбца NET_AMOUNT для всех сделок, которые совершены продавцом за месяц. Размер премии (в процентах) для любого продавца можно найти с помощью коррелированного подзапроса, в котором используются операторы сравнения:
SELECT BONUS_PCT
FROM BONUSRATE
WHERE MIN_AMOUNT <=
(SELECT SUM (NET_AMOUNT)
FROM TRANSMASTER
WHERE EMPID = 133)
AND MAX_AMOUNT >=
(SELECT SUM (NET_AMOUNT)
FROM TRANSMASTER
WHERE EMPID =133) ;
Этот запрос интересен тем, что в нем содержатся два подзапроса, для которых приходится использовать логическую связку AND. В подзапросах применяется итоговый оператор SUM, и он возвращает единственное значение — общую сумму продаж за месяц для сотрудника с идентификационным номером 133. Затем это значение сравнивается со значениями в столбцах MIN_AMOUNT и MAX_AMOUNT из таблицы BONUSRATE, и в результате получается процент премии для этого сотрудника.
Если идентификатор продавца, хранящийся в столбце EMPID, вам не известен, но известна фамилия, то такой же ответ можно получить, используя более сложный запрос:
SELECT BONUS_PCT
FROM BONUSRATE
WHERE MIN_AMOUNT <=
(SELECT SUM (NET_AMOUNT)
FROM TRANSMASTER WHERE EMPID =
(SELECT EMPID
FROM EMPLOYEE
WHERE EMPLNAME = 'Coffin'))
AND MAX_AMOUNT >=
(SELECT SUM (NET_AMOUNT)
FROM TRANSMASTER WHERE EMPID =
(SELECT EMPID
FROM EMPLOYEE
WHERE EMPLNAME = 'Coffin'));
В этом примере, чтобы получить процент премии для сотрудника по фамилии Коффин, используются подзапросы, вложенные в другие подзапросы, а те, в свою очередь, вложены в замыкающий запрос. Эта структура работает только тогда, когда вам наверняка известно, что в компании работает один-единственный сотрудник с этой фамилией. А если вы знаете, что имеются несколько сотрудников с фамилией Коффин? Тогда в предложение WHERE из подзапроса самого нижнего уровня можно добавлять все новые и новые условия, пока не появится уверенность, что будет выбрана единственная строка таблицы EMPLOYEE.
Кванторы ALL SOME и ANY
Кванторы ALL, SOME и ANYДругой способ сделать так, чтобы подзапрос возвращал единственное значение, — поставить перед этим подзапросом оператор сравнения с квантором. В сочетании с оператором сравнения квантор общности ALL (все) и кванторы существования SOME (некоторый) и ANY (какой-либо) обрабатывают список, возвращенный подзапросом, и в результате этот список сводится к единственному значению.
Воздействие этих кванторов на сравнение я проиллюстрирую примером, использующим базу данных из главы 10. В этой базе хранятся данные об играх, во время которых бейсбольные питчеры не менялись на подаче.
Содержимое двух таблиц получено с помощью следующих двух запросов:
| SELECT * FROM NATIONAL ; | ||
| FirstName | LastName | СompleteGames |
| ----------- | ----------- | ------------------ |
| Sal | Maglie | 11 |
| Don | Newcombe | 9 |
| Sandy | Koufax | 13 |
| Don | Drysdale | 12 |
| SELECT * FROM AMERICAN ; | ||
| FirstName | LastName | СompleteGames |
| ----------- | ----------- | ------------------ |
| Whitey | Ford | 12 |
| Don | Larson | 10 |
| Bob | Turley | 8 |
| Allie | Reynolds | 14 |
SELECT *
FROM AMERICAN
WHERE CompleteGames > ALL
(SELECT CompleteGames FROM NATIONAL) ;
Вот его результат:
| FirstName | LastName | СompleteGames |
| ----------- | ----------- | ------------------ |
| Allie | Reynolds | 14 |
А что если ваше первоначальное допущение ошибочно? Что если лидером высшей лиги по количеству бессменных игр был все-таки питчер Национальной лиги, несмотря на то, что в Национальной лиге нет назначенного хиттера? Если это так, то запрос
SELECT *
FROM AMERICAN
WHERE CompleteGames > ALL
(SELECT CompleteGames FROM NATIONAL) ;
возвращает предупреждение о том, что нет строк, удовлетворяющих условиям запроса. А это означает, что в Американской лиге нет такого питчера, которых бессменно пробыл бы на подаче в течение большего количества игр, чем питчер-рекордсмен Национальной лиги.
NОТ EXISTS
NОТ EXISTSВ предыдущем примере продавец из Zetec хотел узнать имена и телефонные номера представителей для контакта из всех калифорнийских организаций, покупающих продукцию его компании. Предположим, что другой продавец работает со всеми остальными штатами, кроме Калифорнии. Данные о контактных представителях из других штатов можно получить с помощью запроса, похожего на предыдущий, но с предикатом NOT EXISTS:
SELECT *
FROM CONTACT
WHERE NOT EXISTS
(SELECT *
FROM CUSTOMER
WHERE CustState = 'CA'
AND CONTACT.CustID = CUSTOMER.CustID) ;
В выводимую таблицу добавляются только те строки из таблицы CONTACT, для каждой из которых подзапрос не возвращает ни одной строки.
Операторы UPDATE DELETE и INSERT
Операторы UPDATE, DELETE и INSERTКроме операторов SELECT, предложения WHERE могут быть и в операторах UPDATE, DELETE и INSERT. А в этих предложениях, в свою очередь, могут быть такие же подзапросы, как и в предложениях WHERE, используемых в операторе SELECT.
Например, Zetec только что заключила с Olympic Sales соглашение о партнерстве, согласно которому Zetec "задним числом" предоставляет Olympic Sales десятипроцентную скидку на весь прошлый месяц. Информацию об этой скидке можно ввести в базу данных, используя оператор UPDATE:
UPDATE TRANSMASTER
SET NET_AMOUNT = NET_AMOUNT * 0.9
WHERE CUSTID =
(SELECT CUSTID
FROM CUSTOMER
WHERE COMPANY = 'Olympic Sales')
В операторе UPDATE можно также использовать и коррелированный подзапрос. Предположим, что в таблице CUSTOMER имеется столбец LAST_MONTHS_MAX (максимум за последние месяцы), а руководство Zetec хочет предоставить скидку для всех сделок, которые превышают значение LAST_MONTHS_MAX данного клиента:
UPDATE TRANSMASTER ТМ
SET NET__AMOUNT = NET_AMOUNT * 0.9
WHERE NET_AMOUNT >
(SELECT LAST_MONTHS_MAX
FROM CUSTOMER С
WHERE C.CUSTID = TM.CUSTID) ;
Обратите внимание, что этот подзапрос является коррелированным. Дело в том, что предложение WHERE, расположенное в последней строке оператора, обращается одновременно и к значению CUSTID из строки, полученной с помощью подзапроса из таблицы CUSTOMER, и к значению CUSTID из текущей строки-кандидата на обновление, которая находится в таблице TRANSMASTER.
Подзапрос в операторе UPDATE может обращаться и к обновляемой таблице. Предположим, что руководство Zetec хочет дать десятипроцентную скидку покупателям, купившим товаров на сумму более 10 000 долларов:
UPDATE TRANSMASTER TM1
SET NET_AMOUNT = NET_AMOUNT * 0.9
WHERE 10000 < (SELECT SUM(NET_AMOUNT)
FROM TRANSMASTER TM2
WHERE TM1.CUSTID = TM2.CUSTID);
Во внутреннем подзапросе для всех строк таблицы TRANSMASTER, которые относятся к одному и тому же покупателю, вычисляется (с помощью функции SUM) сумма значений из столбца NET_AMOUNT. Что это означает? Предположим, что в таблице TRANSMASTER к покупателю со значением CUSTID, равным 37, относятся четыре строки, в которых столбец NET_ AMOUNT имеет такие значения: 3000, 5000, 2000 и 1000. Для этого значения CUSTID сумма значений NET_AMOUNT равна 11000.
Обратите внимание, что порядок, в котором оператор UPDATE обрабатывает строки, определяется конкретной реализацией и обычно является непредсказуемым. Этот порядок может зависеть от того, каким образом строки хранятся на диске. Предположим, что в имеющейся реализации для значения столбца CUSTID, равного 37, строки таблицы TRANSMASTER обрабатываются в следующем порядке. Первой — строка со значением NET_AMOUNT, равным 3000, затем — с NET_AMOUNT, равным 5000, и т.д. После обновления первых трех строк со значением CUSTID, равным 37, у них в столбце NET_AMOUNT будут такие значения: 2700 (90% от 3000), 4500 (90% от 5000) и 1800 (90% от 2000). А затем, когда в TRANSMASTER идет обработка последней строки, в которой значение CUSTID равно 37, a NET_AMOUNT равно 1000, то значение функции SUM, возвращенное подзапросом, должно быть равно 10000. Это значение получается как сумма новых значений NET_AMOUNT из первых трех строк со значением CUSTID, равным 37, а также старого значения из последней строки, имеющей то же значение CUSTID. Таким образом, может показаться, что последняя строка для значния CUSTID, равного 37, не должна обновляться — ведь сравнение с этим значением SUM не будет истинным (10000 не меньше SELECT SUM(NET_AMOUNT)). Но при обращении подзапроса к обновляемой таблице оператор UPDATE работает уже по-другому. В этом операторе при всех проверках подзапросов используются старые значения обновляемой таблицы. В предыдущем операторе UPDATE для столбца CUSTID, равного 37, подзапрос возвращает 11000, т.е. первоначальное значение SUM.
Подзапрос в предложении WHERE работает точно так же, как оператор SELECT или UPDATE. To же самое верно для DELETE или INSERT. Чтобы удалить записи обо всех сделках Olympic Sales, используйте такой оператор:
DELETE TRANSMASTER
WHERE CUSTID =
(SELECT CUSTID
FROM CUSTOMER
WHERE COMPANY = 'Olympic Sales') ;
Как и в случае с UPDATE, подзапросы DELETE также могут быть коррелированными и также могут обращаться к изменяемой таблице (у которой в данном случае удаляют строки). Здесь действуют правила, похожие на те, что используются для подзапросов оператора UPDATE. Предположим, вы хотите удалить из таблицы CUSTOMER все строки тех пользователей, для которых итог NET_AMOUNT больше 10000 долларов:
DELETE TRANSMASTER TM1
WHERE 10000 < (SELECT SUM(NET_AMOUNT))
FROM TRANSMASTER TM2
WHERE TM1.CUSTID = TM2.CUSTID) ;
Этот запрос удаляет из таблицы TRANSMASTER все строки, в которых столбец CUSTID содержит 37, а также строки, относящиеся к другим пользователям, сумма покупок которых превышает 10000 долларов. Все обращения к TRANSMASTER, имеющиеся в подзапросе, указывают на содержимое этой таблицы, которое было перед любыми удалениями, уже выполненными текущим оператором. Поэтому даже при удалении из таблицы TRANSMASTER последней строки, в которой значение столбца CUSTID равно 37, подзапрос все равно выполняется на этой таблице таким образом, как если бы не было никаких удалений. В итоге подзапрос возвращает значение 11000.
При обновлении, удалении или вставке записей базы данных есть риск сделать так, что данные в изменяемой таблице не будут соответствовать данным в других таблицах из этой базы. Такое несоответствие называется аномалией изменения (см. главу 5). Если из таблицы TRANSMASTER удаляются записи, а от нее зависит другая таблица, TRANSDETAIL (подробности сделок), то записи, соответствующие удаленным записям из первой таблицы, необходимо удалять и из второй. Эта операция называется каскадным удалением, поскольку удаление родительской записи должно вызывать каскад удалений связанных с ней дочерних записей. В противном случае неудаленные дочерние записи становятся "записями-призраками".
В операторе INSERT может находиться предложение SELECT. Такие операторы применяются для заполнения таблиц с текущей информацией. Ниже приведен запрос для создания таблицы с содержимым TRANSMASTER за 27 октября.
CREATE TABLE TRANSMASTER_1027
(TRANSID INTEGER, TRANSDATE DATE,
. . . ) ;
INSERT INTO TRANSMASTER_1027
(SELECT * FROM TRANSMASTER
WHERE TRANSDATE = 2000-10-27) ;
Если требуется информация лишь о крупных сделках, то запрос будет таким:
INSERT INTO TRANSMASTER_102 7
(SELECT * FROM TRANSMASTER TM
WHERE TM.NET_AMOUNT > 10000
AND TRANSDATE 2000-10-27) ;
Подзапросы перед которыми стоит ключевое слово IN
Подзапросы, перед которыми стоит ключевое слово INОдин из видов вложенных запросов работает по следующему принципу: одиночное значение сравнивается с набором значений, возвращаемым SELECT. В этом случае используется предикат IN (в):
SELECT слисок_столбцов
FROM таблица
WHERE выражение IN (подзапрос) ;
Проверяется значение выражения, которое находится в предложении WHERE. Если это значение есть в списке, возвращенном подзапросом, то предложение WHERE возвращает логическое значение True, а перечисленные табличные столбцы обрабатываются и добавляются в выводимую таблицу. В подзапросе можно указать или ту же таблицу, что и во внешнем запросе, или же какую-нибудь другую.
Чтобы показать, как работает подобный запрос, я воспользуюсь базой данных компании Zetec. Предположим, что в компьютерной отрасли образовался дефицит мониторов. Под вопросом оказывается выпуск готовых товаров, в состав которых должны входить мониторы. Вы хотите знать, что это за товары. Введите следующий запрос:
SELECT Model
FROM COMP_USED
WHERE CompID IN
(SELECT CompID
; FROM COMPONENT
WHERE CompType = 'Monitor') ;
Вначале SQL выполняет запрос самого нижнего уровня, т.е. обрабатывает таблицу COMPONENT, возвращая значения CompID из тех строк, в которых значением СотрТуре является 'Monitor'. В результате появляется список идентификационных номеров всех мониторов. Затем внешний запрос сравнивает с полученным списком значение CompID из каждой строки таблицы COMP_USED. Если сравнение бьло успешным, то значение Model из той же строки добавляется в виртуальную таблицу, создаваемую внешним оператором SELECT. В результате появляется список всех моделей ваших товаров, в состав которых входит монитор. Следующий пример показывает, что получится, если этот запрос действительно запустить на выполнение:
Model
--------
СХ3000
СХ3010
СХ3020
МХ3030
МХ3020
МХ3030
Теперь известно, каких товаров в скором времени не будет у вас на складе. Рекламу этих товаров следует на время, по возможности, свернуть.
Этот вид вложенного запроса предполагает, что подзапрос возвращает единственный столбец, и тип данных для этого столбца совпадает с типом данных аргумента, находящегося перед ключевым словом IN.
Подзапросы, перед которыми стоит ключевое слово NOT IN
Запрос с ключевым словом IN, приведенный в предыдущем разделе, помог руководству фирмы узнать, какие товары нельзя будет продавать. Хотя это и ценная информация, но на ней много не заработаешь. А вот что действительно надо знать руководству Zetec — какие товары молено будет активно продавать. Руководство фирмы хочет продвигать именно те товары, в состав которых мониторы не входят. Такую информацию можно получить с помощью подзапроса, перед которым стоит ключевое слово NOT IN:
SELECT Model
FROM COMP_USED
WHERE Model NOT IN
(SELECT Model
FROM COMP_USED
WHERE CompID IN
(SELECT CompID
FROM COMPONENT
WHERE CompType = 'Monitor')) ;
В результате выполнения этого оператора получаем следующее:
Model
--------
РХ3040
РВ3050
РХ3040
РВ3050
Здесь надо сказать о двух моментах.
SELECT DISTINCT Model
FROM COMP_USED
WHERE Model NOT IN
(SELECT Model
FROM COMP_USED
WHERE CompID IN
(SELECT CompID
FROM COMPONENT
WHERE CompType = 'Monitor')) ;
Как и ожидалось, результат получился следующий:
Model
--------
РХ3040
РВ3050
Подзапросы в предложении HAVING
Подзапросы в предложении HAVINGКоррелированный подзапрос можно задавать не только в предложении WHERE, но и в предложении HAVING. Как уже говорилось в главе 9, перед этим предложением обычно находится предложение GROUP BY. Предложение HAVING действует как фильтр, который должен ограничивать группы, созданные предложением GROUP BY. Группы, которые не удовлетворяют условию предложения HAVING, в результат не попадут. Если предложение HAVING используется таким образом, то оно проверяется для каждой группы, созданной
предложением GROUP BY. Если же предложения GROUP BY нет, то предложение HAVING проверяется для всего набора строк, переданного предложением WHERE. Тогда этот набор считается одной группой. А если нет ни предложения WHERE, ни предложения GROUP BY, то условие предложения HAVING проверяется уже для всей таблицы:
SELECT TM1.EMPID
FROM TRANSMASTER TM1
GROUP BY TM1.EMPID
HAVING MAX (TM1.NET_AMOUNT) >= ALL
(SELECT 2 * AVG (TM2.NET_AMOUNT)
FROM TRANSMASTER TM2
WHERE TM1.EMPID <> TM2.EMPID) ;
В этом запросе для одной и той же таблицы используются два псевдонима. В результате можно получить идентификаторы всех тех продавцов, у кого размер максимальной сделки как минимум в два раза превысил средний размер сделок остальных продавцов. Запрос работает следующим образом.
Таблица PRODUCT
Таблица 11.1. Таблица PRODUCT| Столбец | Тип | Ограничения |
| Model (модель) | Char (6) | PRIMARY KEY |
| ProdName (название товара) | Char (35) | |
| ProdDesc (описание товара) | Char (31) | |
| ListPrice (цена) | Numeric (9,2) |
Таблица COMPONENT
Таблица 11.2. Таблица COMPONENT| Столбец | Тип | Ограничения |
| CompiD (идентификатор компонента) | char (6) | PRIMARY KEY |
| CompType (тип компонента) | char (10) | |
| CompDesс (описание компонента) | char (31) |
Таблица COMPOSED
Таблица 11.3. Таблица COMPOSED| Столбец | Тип | Ограничения |
| Model (модель) | char (6) | FOREIGN KEY (ДЛЯ PRODUCT) |
| CompiD (идентификатор компонента) | char (б) | FOREIGN KEY (ДЛЯ COMPONENT) |
Таблица CUSTOMER
Таблица 11.4. Таблица CUSTOMER| Столбец | Тип | Ограничения |
| CustiD (идентификатор покупателя) | INTEGER | PRIMARY KEY |
| Company (компания) | CHAR (40) | |
| СustAddress (адрес покупателя) | CHAR (30) | |
| Custcity (из какого города покупатель) | CHAR (20) | |
| Custstate (из какого штата) | CHAR (2) | |
| Сustzip (почтовый код покупателя) | CHAR (10) | |
| CustPhone (телефон покупателя) | CHAR (12) | |
| ModLevel | INTEGER |
Таблица CONTACT
Таблица 11.5. Таблица CONTACT| Столбец | Тип | Ограничения |
| CustID | INTEGER | FOREIGN KEY |
| ContFName (имя представителя) | CHAR (10) | |
| СontLName (фамилия представителя) | CHAR (16) | |
| ContPhone (телефон представителя) | CHAR (12) | |
| Continfo (информация о представителе) | CHAR (50) |
SELECT *
FROM CONTACT
WHERE CustiD =
(SELECT CustiD
FROM CUSTOMER
WHERE Company = 'Olympic Sales') ;
Результат его выполнения примерно следующий:
| CustiD | ContFName | ContLName | ContPhone | Contlnfo |
| ------- | ------------- | -------------- | ------------ | ---------- |
| 118 | Jerry | Attwater | 505-876-3456 | Will play |
| major role in | ||||
| coordinating | ||||
| the | ||||
| wireless | ||||
| Web. |
Если в сравнении '=' используется подзапрос, то в списке SELECT этого подзапроса должен находиться один столбец (CustiD в этом примере). Подзапрос должен возвратить только одну строку. Это необходимо для того, чтобы в сравнении было одно значение.
В этом примере я предполагаю, что в таблице CUSTOMER находится только одна строка, в которой столбец Company содержит значение 'Olympic Sales'. Если в операторе CREATE TABLE, с помощью которого была создана таблица CUSTOMER, для столбца Company было установлено ограничение UNIQUE (уникальный), то это дает гарантию, что подзапрос в предыдущем примере возвратит только одно значение (или вообще ни одного). Однако подзапросы, похожие на тот, что используется в примере, обычно используются со столбцами, для которых это ограничение не установлено. В этих случаях, чтобы значения в столбце не повторялись, приходится полагаться на другие средства.
А если окажется, что в столбце Company таблицы CUSTOMER находится больше одного значения 'Olympic Sales' (филиалы в разных штатах), то выполнение подзапроса вызовет ошибку.
С другой стороны, если ни один покупатель из CUSTOMER не работает в Olympic Sales, то подзапрос возвратит значение NULL и результатом сравнения будет значение "unknown" (неизвестно). В этом случае итоговая виртуальная таблица будет пустой. Дело в том, что предложение WHERE возвращает только строки, для которых было получено значение True, а строки со значениями False и "unknown" будут отфильтрованы. Такое может, скорее всего, произойти, если по чьей-то ошибке в столбце Company окажется неправильное название, например 'Olumpic Sales'.
Хотя в таких структурах оператор равенства (=) и является самым распространенным, но в них можно использовать и пять остальных операторов сравнения. Для каждой строки из таблицы, которая указана в предложении замыкающего оператора, единственное значение, возвращаемое подзапросом, сравнивается с выражением из предложения WHERE того же оператора. Если результатом сравнения является значение True, то строка добавляется в выводимую виртуальную таблицу.
Если в состав подзапроса будет включена итоговая функция, то он гарантированно возвратит единственное значение. Эти функции всегда возвращают единственное значение. (Об итоговых функциях см. в главе 3.) Естественно, такой подзапрос будет полезен только тогда, когда требуется получить значение именно итоговой функции.
Скажем, вы торговый представитель компании Zetec и, чтобы оплатить неожиданно свалившиеся на вас счета, должны заработать довольно большие комиссионные. У вас не остается другого выхода, кроме как перестать тратить время на мелочевку и сосредоточиться на продаже только самых дорогих товаров. Самый дорогой товар вы определяете с помощью вложенного запроса:
SELECT Model, ProdName, ListPrice
FROM PRODUCT
WHERE ListPrice =
(SELECT MAX(ListPrice)
FROM PRODUCT) ;
Это пример вложенного запроса, в котором подзапрос и замыкающий оператор работают с одной и той же таблицей. Подзапрос возвращает единственное значение — максимальную цену из столбца ListPrice таблицы PRODUCT. А внешний запрос возвращает все строки из той же таблицы, имеющие максимальное значение в столбце ListPrice.
В следующем примере показан подзапрос сравнения, в котором используется оператор сравнения, отличный от '=':
SELECT Model, ProdName, ListPrice
FROM PRODUCT
WHERE ListPrice <
(SELECT AVG(ListPrice)
FROM PRODUCT) ;
Подзапрос возвращает единственное значение — среднее значение цен, находящихся в столбце ListPrice таблицы PRODUCT. А внешний запрос возвращает все строки из той же таблицы, в которых значение столбца ListPrice меньше этого среднего значения.
Первоначально стандарт языка SQL разрешал иметь в сравнении только один подзапрос, который должен был находиться в правой части запроса. Согласно стандарту SQL: 1999 подзапросом может быть любой из двух операндов сравнения и даже оба сразу. А стандарт SQL:2OO3 поддерживает эту возможность.
Вложенные подзапросы которые возвращают наборы строк
Вложенные подзапросы, которые возвращают наборы строкПредположим, что вы работаете на фирме по сборке компьютерных систем. В вашей компании, Zetec Corporation, из покупаемых комплектующих собирают системы, которые затем продают другим компаниям и правительственным агентствам. Информацию о своем бизнесе вы храните в реляционной базе данных. Она состоит из множества таблиц, но сейчас вас интересуют только три: PRODUCT (товар), COMP_USED (использованные компоненты) и COMPONENT (компонент). В таблице PRODUCT содержится список всех выпускаемых вашей фирмой стандартных товаров (табл. 11.1). В таблице COMPONENT перечисляются производственные компоненты товаров (табл. 11.2), а в таблице COMP_USED хранятся данные о том, из каких компонентов состоят произведенные товары (табл. 11.3).
Вложенные запросы которые являются
Вложенные запросы, которые являются проверкой на существованиеЗапрос возвращает данные из всех табличных строк, которые удовлетворяют его условиям. Иногда возвращается много строк, а иногда — только одна. Бывает так, что в таблице ни одна строка не удовлетворяет условиям и поэтому ни одна из них не возвращается. Перед подзапросом можно ставить предикаты EXISTS (существует) и NOT EXISTS (не существует). Такая структура, в которой сочетаются подзапрос и один из этих предикатов, сообщает, имеются ли в таблице, указанной в предложении FROM (из) подзапроса, какие-нибудь строки, соответствующие условиям предложения WHERE (где) того же подзапроса.
Подзапросы, перед которыми ставится один из предикатов EXISTS или NOT EXISTS, принципиально отличаются от тех подзапросов, о которых уже говорилось в этой главе. Во всех предыдущих случаях SQL вначале выполняет подзапрос, а затем применяет результат этой операции по отношению к замыкающему оператору. А подзапросы с предикатами EXISTS и NOT EXISTS — это коррелированные подзапросы, и выполняются они по-другому.
Коррелированный подзапрос вначале находит таблицу и строку, указанные замыкающим оператором, а затем выполняет подзапрос в той строке его таблицы, которая коррелирует (соотносится) с текущей строкой таблицы замыкающего оператора.
Подзапрос или возвращает одну, или несколько строк, или вообще не возвращает ни одной. Если он возвращает хотя бы одну строку, то предикат EXISTS является истинным и свое действие выполняет замыкающий оператор. В тех же условиях предикат NOT EXISTS является ложным, и замыкающий оператор свое действие не выполняет. После обработки строки в таблице внешнего оператора та же операция выполняется со следующей строкой. Это действие повторяется до тех пор, пока не будут обработаны все строки из таблицы, указанной замыкающим оператором.
Вложенные запросы возвращающие одно значение
Вложенные запросы, возвращающие одно значениеЧасто перед подзапросом полезно ставить один из шести операторов сравнения (=, о, <, <=, >, >=). Это можно делать тогда, когда у выражения, стоящего перед оператором, вычисляется единственное значение, а подзапрос, стоящий после оператора, также выдает одно значение. Исключением является оператор сравнения, сразу после которого находится квантор (ANY, SOME или ALL).
Чтобы проиллюстрировать случай, когда вложенный подзапрос возвращает единственное значение, вернемся к базе данных корпорации Zetec. В ней имеется таблица CUSTOMER (покупатель), содержащая информацию о компаниях, которые покупают товары Zetec. Кроме того, в ней имеется еще другая таблица, CONTACT (представитель для контакта), с личными данными о контактных представителях каждой компании-клиента. Структура этих таблиц приведена в табл. 11.4 и 11.5.
Зачем использовать подзапрос
Зачем использовать подзапросВо многих случаях с помощью подзапроса можно получить тот же результат, что и с помощью объединения (JOIN). Как правило, сложность синтаксиса подзапроса сопоставима со сложностью синтаксиса объединения. Выбор способа построения запроса при этом часто становится делом вкуса пользователя базы данных. Одни предпочитают использовать объединения (JOIN), в то время как другие — применять вложенные запросы. Впрочем, иногда бывает так, что получить нужный результат с помощью объединений (JOIN) невозможно. Тогда приходится использовать вложенный запрос или разбивать задачу на несколько операторов SQL и выполнять их поочередно.
Что такое рекурсия
Что такое рекурсияЭто довольно старая возможность таких языков программирования, как Logo, LISP и C++. В этих языках можно определить функцию (совокупность одной или множества команд), которая выполняет заданную операцию. Главная программа вызывает функцию, выполняя для этого команду, которая называется вызовом функции. В процессе своей работы функция вызывает сама себя — это самая простая форма рекурсии.
Для иллюстрации достоинств и недостатков рекурсии приведем простую программу, в которой одна из функций использует рекурсивные вызовы. Эта программа, написанная на C++, чертит на экране компьютера спираль, начиная с единичного сегмента, направленного вверх.
В ее состав входят три функции.
{
line(segment);
left_turn(90);
spiral(seement + 1);
}
Если из главной программы вызвать spiral(1), то будут выполняться такие действия:
Что такое рекурсивный запрос
Что такое рекурсивный запросРекурсивным называется запрос, который функционально зависит от себя самого. Самой простой формой такой функциональной зависимости является случай, когда внутри оператора запроса Q1 находится вызов этого же запроса. Более сложный случай будет тогда, когда запрос Q1 зависит от запроса Q2, который, в свою очередь, зависит от Q1. И сколько бы запросов ни находилось между первым и вторым вызовом одного и того же запроса, главное, чтобы имела место функциональная зависимость.
Где еще можно использовать рекурсивный запрос
Где еще можно использовать рекурсивный запросЛюбая задача, которую можно представить в виде древовидной структуры, поддается решению с помощью рекурсивного запроса. Классическим примером того, как такие запросы используются в промышленности, является обработка материалов (процесс превращения сырья в конечный продукт). Предположим, ваша компания выпускает новый гибридный бензи-ново-электрический автомобиль. Такую машину собирают из узлов (двигателя, батарей и т.п.), которые, в свою очередь, состоят из меньших подузлов (коленчатого вала, электродов и пр.), а те — из еще меньших компонентов. Данные обо всех этих компонентах компонентов сохранять в реляционной базе очень трудно — если, конечно, в ней не используется рекурсия. Рекурсия дает возможность, начав с целой машины, добраться любым путем к самой малой детали. Хотите найти данные о крепежном винте, который держит клемму отрицательного электрода вспомогательной батареи? Это можно — и причем без особых затрат времени. Справляться с такими задачами SQL может с помощью структуры WITH RECURSIVE (рекурсивный оператор).
Кроме того, рекурсия вполне естественна при анализе "что, если?". Например, что произойдет, если руководство авиакомпании Vannevar Airlines решит прекратить полеты ю Портленда в Шарлотт? Как это повлияет на полеты в те города, куда сейчас можно добраться кз Портленда? Рекурсивный запрос незамедлительно даст ответ на эти вопросы.
Где можно использовать запрос
Где можно использовать запросВо многих трудных ситуациях рекурсивные запросы помогают сэкономить и время, и нервы. Предположим, например, что у вас есть пропуск, который дает право бесплатно летать любым авиарейсом воображаемой компании Vannevar Airlines. Неплохо, правда? И тут встает вопрос: Куда же можно бесплатно попасть? Все авиарейсы Vannevar Airlines перечислены в таблице FLIGHT (авиарейс), и для каждого из них указан его номер, начальный пункт и место назначения (табл. 12.1).
Экономия времени с помощью рекурсивного запроса
Экономия времени с помощью рекурсивного запросаПолучить нужную информацию будет проще, если создать единственный рекурсивный запрос, который сделает всю работу за одну операцию. Вот его синтаксис:
WITH RECURSIVE
ReachableFrom (Source, Destination)
AS (SELECT Source, Destination
FROM FLIGHT
UNION
SELECT in.Source, out.Destination
FROM ReachableFrom in, FLIGHT out
WHERE in.Destination = out.Source
)
SELECT * FROM ReachableFrom
WHERE Source = "Portland";
В начале первого прохода, выполняемого во время рекурсии, в таблице FLIGHT будет семь строк, а в ReachableFrom (означает "можно попасть из") — ни одной. Оператор UNION берет семь строк из FLIGHT и копирует их в таблицу ReachableFrom. Тогда в ReachableFrom появятся данные, показанные в табл. 12.2.
Маленькие трудности
Маленькие трудностиНу ладно. Здесь ситуация не такая серьезная, как с Аполлоном-13, когда на пути к Луне прорвало его главный кислородный бак. Но и мы тоже испытываем трудности: наша маленькая программа от нас "убегает". Она все продолжает и продолжает вызывать сама себя и чертит все большие и большие отрезки. Программа будет делать это до тех пор, пока компьютер, пытающийся ее выполнить, не исчерпает свои ресурсы и не выведет на экран сообщение об ошибке. А если вам не повезет, то компьютер просто зависнет.
Результат вызова spiral(1)
Рисунок 12.1 Результат вызова spiral(1)
Сбой недопустим
Сбой недопустимТакой сценарий развития событий показывает одну из опасностей, связанных с использованием рекурсии. Программа, написанная для того, чтобы обращаться к себе самой, вызывает на выполнение свой новый экземпляр, а тот, в свою очередь, вызывает еще один, и так до бесконечности. Обычно это не то, что нужно. Чтобы решить проблему, программисты помешают в рекурсивную функцию условие завершения — предел того, насколько глубоко должна зайти рекурсия. В результате программа выполняет нужные действия, а затем красиво завершается. Условие завершения мы можем поместить и в нашу программу черчения спиралей, чтобы сберечь ресурсы компьютера и избежать головокружения у программистов:
void spiral2(int segment)
{
if (segment <= 10)
{
line(segment);
left_turn(90) ,
spiral2(segment+ 1) ;
}
}
При вызове программа spiral2(l) выполняется и затем рекурсивно вызывает сама себя до тех пор, пока значение segment не превысит 10. Как только значение segment станет равным 11, конструкция if (segment <= 10) возвратит значение False, и код, находящийся во внутренних скобках, будет пропущен. Управление снова передается предыдущему вызову spiral2(l), а оттуда постепенно возвращается к самому первому вызову, после которого программа завершается.
Каждый раз, когда функция вызывает саму себя, она еще на один уровень удаляется от главной программы — места начала операции. Чтобы эта главная программа продолжила работу, самая последняя итерация (т.е. повторение выполнения) должна вернуть управление предпоследней — той, которая ее вызвала. Предпоследняя итерация обязана поступить точно так же, и процесс продолжается, пока управление не вернется в главную программу, в которой был сделан первый вызов рекурсивной функции.
Рекурсия — это мощный инструмент для повторного выполнения кода. Она идеально подходит для поиска в древовидных структурах, например, в файловых системах, сложных электронных схемах или многоуровневых распределенных сетях.
Авиарейсы компании Vannevar Airlines
Таблица 12.1. Авиарейсы компании Vannevar Airlines| Flight No. (номер авиарейса) | Source (начальный пункт) | Destination (место назначения) |
| 3141 | Portland (Портленд) | Orange County (округ Ориндж) |
| 2173 | Portland | Charlotte (Шарлотт) |
| 623 | Portland | Daytona Beach (Дейтона-Бич) |
| 5440 | Orange County | Montgomery (Монтгомери) |
| 221 | Charlotte | Memphis (Мемфис) |
| 32 | Memphis | Champaign (Шампейн) |
| 981 | Montgomery | Memphis |
| CREATE TABLE FLIGHT ( | ||
| FlightNo | INTEGER | NOT NULL, |
| Source | CHARACTER (30), | |
| Destination | CHARACTER (30) | |
| ) ; |
Предположим, вы хотите лететь из Портленда к своему другу в Монтгомери. Естественно, что вы зададите себе вопросы: "В какие города я попаду самолетами Vannevar Airlines, если начинать с Портленда? А куда я смогу долететь самолетами этой же авиакомпании, если садиться на самолет в Монтгомери?" В некоторые города долететь без промежуточных посадок можно, а в другие — нельзя. По пути в некоторые города придется делать не менее одной такой посадки. Конечно, можно найти все города, куда самолеты Vannevar Airlines могут вас доставить из любого выбранного вами города просто, что называется, "в лоб". Но если вы будете искать города, выполняя один запрос за другим, то тогда вами выбран...
Таблица ReachableFrom
Таблица 12.2. Таблица ReachableFrom после одного прохода рекурсии| Source | Destination |
| Portland | Orange County |
| Portland | Charlotte |
| Portland | Daytona Beach |
| Orange County | Montgomery |
| Charlotte | Memphis |
| Memphis | Champaign |
| Montgomery | Memphis |
Таблица ReachableFrom
Таблица 12.3. Таблица ReachableFrom после двух проходов рекурсии| Source | Destination |
| Portland | Orange County |
| Portland | Charlotte |
| Portland | Daytona Beach |
| Orange County | Montgomery |
| Charlotte | Memphis |
| Memphis | Champaign |
| Montgomery | Memphis |
| Portland | Montgomery |
| Portland | Memphis |
| Orange County | Memphis |
| Charlotte | Champaign |
После завершения рекурсии третий и последний оператор SELECT (который в рекурсии не участвует) выделяет из ReachableFrom только те города, в которые можно попасть из Портленда. В этом примере можно попасть во все остальные шесть городов, причем с достаточно малым числом промежуточных посадок. Так что вам не придется метаться, как будто вы скачете на ходуле с пружиной.
Если вы внимательно изучите код рекурсивного запроса, то увидите, что он не выглядит проще, чем семь отдельных запросов. Однако у этого запроса есть два преимущества:
Что же делает запрос рекурсивным? То, что мы определяем таблицу ReachableFrom на основе ее самой. Рекурсивной частью определения является второй оператор SELECT, который расположен сразу после UNION. ReachableFrom — это временная таблица, которая наполняется данными по мере выполнения рекурсии. И это наполнение продолжается до тех пор, пока все возможные пункты назначения не окажутся в ReachableFrom. Повторяющихся строк в этой таблице не будет, потому что туда их не пропустит оператор UNION. Когда рекурсия завершится, в таблице ReachableFrom окажутся все города, в которые можно попасть из любого города-начального пункта. Третий и последний оператор SELECT возвращает только те города, в которые вы можете попасть из Портленда. Так что желаем приятного путешествия.
Трудный способ
Трудный способНайти то, что хотите узнать, — при условии, что у вас есть терпение и время, — можно с помощью последовательности запросов, в первом из которых начальным пунктом является Портленд:
SELECT Destination FROM FLIGHT WHERE Source = "Portland";
Этот первый запрос возвращает Orange County, Charlotte и Daytona Beach. Первый из них, если хотите, можно сделать начальным пунктом уже во втором запросе:
SELECT Destination FROM FLIGHT WHERE Source = "Orange County";
В результате второй запрос возвращает Montgomery. В третьем же запросе можете снова использовать результаты первого запроса, взяв на этот раз в качестве начального пункта второй город:
SELECT Destination FROM FLIGHT WHERE Source = "Charlotte";
Этот запрос возвращает Memphis. Результаты первого запроса можно использовать ив четвертом, взяв в качестве начального пункта последний из этих результатов:
SELECT Destination FROM FLIGHT WHERE Source = "Daytona Beach";
Прошу прощения, четвертый запрос возвращает неопределенное значение — у Vannevar Airlines нет авиарейсов из Дейтона-Бич. Но в качестве начального пункта можете также использовать город (Montgomery), который возвращен вторым запросом, что и делается в очередном, пятом, запросе:
SELECT Destination FROM FLIGHT WHERE Source = "Montgomery";
В результате его выполнения возвращается Memphis, но для вас это не имеет значения. Вы еще раньше узнали, что в этот город попасть можно через Шарлотт. Но Мемфис в качестве начального пункта можно использовать в следующем запросе:
SELECT Destination FROM FLIGHT WHERE Source = "Memphis";
Этот запрос возвращает Champaign. Им также можно пополнить список городов, куда вы можете попасть (пусть даже с промежуточной посадкой). А так как вас интересуют авиарейсы и с промежуточными посадками, то в запросе в качестве начального пункта можно использовать и этот город:
SELECT Destination FROM FLIGHT WHERE Source = "Champaign";
Обидно! Запрос возвращает неопределенное значение; оказывается у Vannevar Airlines нет авиарейсов и из Шампейн. (Пока что семь запросов. Они еще не действуют кому-то на нервы?)
Конечно, с помощью этой авиакомпании из Дейтона-Бич улететь нельзя. Так что если вы туда попадете, то там и застрянете. Впрочем, если это случится во время пасхальных каникул — а они, как известно, длятся целую неделю, то особой беды не будет. (Но если вы, чтобы узнать, куда еще можно долететь, будете неделю напролет запускать на выполнение один запрос за другим, то заработаете головную боль похуже, чем от недельного загула.) Или, возможно, застрянете в Шампейн. В этом случае вы можете, кроме всего прочего, поступить в Университет штата Иллинойс и прослушать в нем пару курсов по базам данных.
Конечно, когда-нибудь, со временем, этот метод даст исчерпывающий ответ на вопрос: В какие города можно попасть из Портленда? Но отправлять на выполнение один запрос за другим, при этом составляя каждый из них (кроме самого первого) на основе результатов предыдущего, — это работа сложная, требующая много времени, и, скажу прямо, нудная.
Администратор базы данных
Администратор базы данныхВ большинстве крупных баз данных с большим количеством пользователей высшей властью обладает администратор базы данных (database administrator, DBA). У администратора имеются права и полномочия на любые действия с базой данных. Впрочем, администратор еще должен нести и огромную ответственность. Он может легко испортить базу данных и "пустить на ветер" тысячи часов работы. Все администраторы должны ясно и тщательно продумывать те последствия, которые может иметь каждое их действие.
Администратор не только обладает полным доступом к базе данных, но под его контролем также находятся и все права других пользователей. Некоторые избранные пользователи должны получать доступ к большему количеству данных и, возможно, к большему количеству таблиц, чем большинство пользователей.
Самый лучший способ стать администратором — это самостоятельно установить систему управления базой данных. В руководстве по установке находится учетная запись, или регистрационное имя (login), а также пароль. Это регистрационное имя удостоверяет, что вы являетесь привилегированным пользователем. В системе такой привилегированный пользователь называется администратором базы данных, иногда — системным администратором, или суперпользователем (к сожалению, ему, в отличие от Супермена, плащ и сапоги не положены). В любом случае первым официальным актом, который вы совершите после ввода учетной записи и пароля (иначе говоря, регистрации), должно стать изменение полученного вами пароля на свой собственный, секретный. Если пароль не будет изменен, то любой, кто прочитает руководство пользователя СУБД, сможет также зарегистрироваться с полным набором полномочий. Вряд ли вам это понравится. Но если изменение сделано, то зарегистрироваться в качестве администратора смогут только те, кто знает ваш новый пароль.
Предпочтительно, чтобы ваш новый пароль администратора базы данных был известен малому кругу особо доверенных людей. Вдруг на вас свалится метеорит или выигрыш в лотерею — всякое может случиться. Вашим коллегам надо иметь возможность работать и в ваше отсутствие. Каждый, кто знает регистрационное имя администратора базы данных и пароль, становится еще одним администратором — следующим после того, кто уже использует эти реквизиты для доступа к системе.
Совет 1
Совет 1
Если у вас есть полномочия администратора, регистрироваться в системе в качестве такового следует только тогда, когда нужно выполнять какую-либо специальную задачу, для которой требуются эти полномочия. Как только закончите с задачей, тут же выходите из системы. А для выполнения обычной работы регистрируйтесь с помощью своей личной учетной записи и пароля. Такой подход защитит вас от совершения ошибок, имеющих серьезные последствия для таблиц других пользователей, не говоря уже о ваших собственных таблицах.
Аннулирование полномочий
Аннулирование полномочийНаряду с предоставлением полномочий доступа существует необходимость иметь возможность аннулировать эти полномочия. Обязанности сотрудников со временем изменяются, следовательно, изменяются их потребности в доступе к данным. Нередки случаи перехода на работу к конкуренту. В этом случае все полномочия перешедших сотрудников придется отозвать. В SQL удаление полномочий на доступ выполняется с помощью оператора REVOKE (отозвать). Его синтаксис аналогичен синтаксису оператора GRANT, но только результат получается противоположный.
REVOKE [GRANT OPTION FOR] список-полномочий
ON объект
FROM список-пользователей [RESTRICT | CASCADE] ;
С помощью этой структуры можно отзывать перечисленные в списке полномочия, не затрагивая при этом все остальные. Главное отличие между операторами REVOKE и GRANT состоит в том, что в первом из них применяется одно из двух необязательных ключевых слов — RESTRICT (ограничить) или CASCADE (каскадное удаление). Пусть для предоставления полномочий вы использовали оператор GRANT вместе с WITH GRANT OPTION. Тогда применение ключевого слова CASCADE в операторе REVOKE приводит к отзыву указанных полномочий как у того пользователя, которому вы их предоставили, так и у всех пользователей, кому (благодаря атрибуту WITH GRANT OPTION) эти полномочия он уже успел предоставить. С другой стороны, оператор REVOKE с ключевым словом RESTRICT будет отзывать полномочия пользователя, который никому больше их не предоставлял. Если пользователь уже с кем-то поделился полномочиями, указанными в операторе REVOKE с ключевым словом RESTRICT, то выполнение этого оператора будет прервано и будет выведено сообщение об ошибке.
Оператор REVOKE с необязательным предложением GRANT OPTION FOR (возможность предоставления) можно использовать, чтобы отзывать у пользователя возможность предоставлять указанные полномочия, но оставляя их для самого этого пользователя. Если оператор содержит предложение GRANT OPTION FOR и ключевое слово CASCADE, то отзываются все полномочия, предоставленные пользователем, а также полномочия этого пользователя на предоставление полномочий. А если в операторе есть и GRANT OPTION FOR и RESTRICT, то события развиваются по одному из двух вариантов.
Иерархическая структура полномочий доступа
Рисунок 13.1. Иерархическая структура полномочий доступа
Инициирование выполнения операторов SQL
Инициирование выполнения операторов SQLВ некоторых случаях выполнение одного оператора SQL может вызвать запуск другого оператора или даже целого их блока. Поддержка такой функции (триггерной схемы) и была осуществлена в версии SQL:2OO3. Триггер— это механизм, который задает триггер-событие (событие для запуска), время активизации триггера и одно или несколько запускаемых действий. Триггер-событие инициирует запуск, выражаясь простым языком, дает команду "огонь". Время активизации триггера указывает, в какой момент должно произойти действие: непосредственно перед триггер-событием или после него. Запускаемое действие — это выполнение одного или нескольких операторов SQL. При запуске более одного оператора SQL все операторы должны содержаться в пределах структуры BEGIN ATOMIC... END. Само триггер-событие может использовать оператор INSERT, UPDATE или DELETE.
К примеру, вы можете использовать триггер для выполнения оператора, который контролирует истинность новых значений, перед применением обновления данных. Если новые значения будут неверными, обновление данных будет прервано.
Как показано в следующем примере, пользователь или роль должны иметь привилегию на создание триггера:
CREATE TRIGGER CustomerDelete BEFORE DELETE
ON CUSTOMER FOR EACH ROW
WHEN State = NY
INSERT INTO CUSTLOG VALUES ('deleted a NY customer') :
Теперь при каждом удалении нью-йоркского клиента из таблицы CUSTOMER в регистрационной таблице CUSTLOG будет сделана запись об удалении.
Использование доменов наборов
Использование доменов, наборов символов, сопоставлений и трансляцийНа безопасность также влияют домены, наборы символов, сопоставления и трансляции. В частности, создавая домены, внимательно следите, чтобы из-за них не пострадала ваша система безопасности.
Можно определить домен, который охватывает какой-либо набор столбцов. Таким образом, у всех этих столбцов был один и тот же тип, а также одни и те же ограничения. Теперь столбцы, создаваемые оператором CREATE TABLE, смогут унаследовать тип и ограничения домена. Конечно, если нужно, то для отдельных столбцов эти характеристики можно перезаписать. Однако домены — это удобное средство, которое дает возможность с помощью одного объявления задавать многочисленные характеристики сразу для целого набора столбцов.
Домены удобны тогда, когда есть множество таблиц, имеющих столбцы с похожими характеристиками. Например, база данных вашей фирмы может состоять из нескольких таблиц. Представим, что в каждой из них находится столбец PRICE (цена), у которого должен быть тип данных DECIMAL(10,2), а значения в этом столбце должны быть не отрицательными и не больше 10000. Тогда, прежде чем создавать таблицы с такими столбцами, нужно создать домен, указывающий характеристики этих столбцов. Создание домена PriceTypeDomain (домен типа цены) показано в следующем примере:
CREATE DOMAIN PriceTypeDomain DECIMAL (10,2)
CHECK (Price > = 0 AND Price <= 10000) ;
Возможно, в каком-либо наборе таблиц ваши товары будут определяться с помощью столбца ProductCode (код товара), у которого в каждой таблице тип данных составляет CHAR(5), первый символ должен быть X, С или Н, а последний — или 9, или 0. Для таких столбцов также можно создать домен, например ProductCodeDomain (домен кода товара), что и делается в следующем примере:
CREATE DOMAIN ProductCodeDomain CHAR (5)
CHECK (SUBSTR (VALUE, 1,1) IN ("X", "С", "Н")
AND SUBSTR (VALUE, 5,1) IN ("9", "0") ) ;
Определив домены, можно приняться за создание таблиц, например таблицы PRODUCT (товар):
CREATE TABLE PRODUCT
(ProductCode ProductCodeDomain,
ProductName CHAR (30),
Price PriceTypeDomain) ;
Как только в определении таблицы для поля ProductCode или Price нужно задавать тип данных, указывается соответствующий домен. Таким образом, эти столбцы получают нужные типы данных и, кроме того, для них устанавливаются ограничения, определенные в операторах CREATE DOMAIN.
При использовании доменов возникают вопросы, связанные с безопасностью. Если кто-то другой вдруг захочет использовать созданные вами домены, то может ли такое использование привести к осложнениям? Может. Что если кто-то создаст таблицу со столбцом, в котором используется домен PriceTypeDomain? Пользователь может в этом столбце постепенно увеличивать значения и делать это до тех пор, пока столбец не перестанет их принимать. Таким образом можно будет определить верхнюю границу значений PriceType (тип цены), которую вы указали в предложении CHECK (проверка) оператора CREATE DOMAIN. И если значение этой верхней границы является закрытой информацией, необходимо запретить использовать домен PriceType неуполномоченным пользователям. Чтобы защитить вас в подобных ситуациях, SQL позволяет использовать чужие домены только тем, кому владельцы доменов явно предоставят соответствующее разрешение. Такое разрешение может предоставлять только владелец домена (и, конечно же, администратор). А само предоставление разрешения выглядит так:
GRANT USAGE ON DOMAIN PRICE_TYPE TO SALES_MGR ;
Язык управления данными как часть SQL
Язык управления данными как часть SQLОператоры SQL, используемые для создания баз данных, составляют группу, которая называется языком определения данных (Data Definition Language, DDL). Создав базу данных, для добавления, изменения или удаления из нее данных можно использовать другие инструкции , известные под собирательным названием язык манипулирования данными (Data Manipu-п Language, DML). В SQL есть также операторы, которые не попадают ни в одну из этих категорий. Иногда программисты называют эти операторы языком управления данными (Data Control Language, DCL). Операторы DCL в основном защищают базу данных от несанкционированного доступа, от нежелательных последствий одновременной работы сразу нескольких пользователей, а также от аварий в электрических сетях и неисправностей оборудования. В этой главе рассказывается о защите от несанкционированного доступа.
Экономия времени и сил благодаря
Экономия времени и сил благодаря совместному использованию операторов GRANT и REVOKEПредоставление множеству пользователей множества полномочий на выбранные столбцы таблицы сопряжено с вводом большого количества кодов. Проанализируйте следующий пример. Вице-президент по продажам хочет, чтобы все те, кто занимается продажами, могли просматривать все содержимое таблицы CUSTOMER (клиент). Но обновлять, удалять или вставлять строки должны только менеджеры по продажам. И никто не должен обновлять поле CustID (идентификатор клиента). Соответствующие полномочия можно предоставить с помощью следующих операторов GRANT:
GRANT SELECT, INSERT, DELETE
ON CUSTOMER
TO Tyson, Keith, David ;
GRANT UPDATE
ON CUSTOMER (Company, CustAddress, CustCity,
CustState, CustZip, CustPhone, ModelLevel)
TO Tyson, Keith, David ;
GRANT SELECT
ON CUSTOMER
TO Jenny, Valerie, Melody, Neil, Robert, Sam,
Brandon, MichelleT, Allison, Andrew,
Scott, MishelleB, Jaime, Linleigh, Matt, Amanda;
А теперь попробуем упростить этот код. Все пользователи обладают полномочиями просмотра таблицы CUSTOMER. Менеджеры по продажам имеют на эту таблицу полномочия вставки и удаления, а также могут обновлять любой ее столбец, кроме CustlD. Поэтому тот же результат, что и в предыдущих операторах, можно получить более легким способом:
GRANT SELECT
ON CUSTOMER
TO SalesReps
GRANT INSERT, DELETE, UPDATE
ON CUSTOMER
TO Tyson, Keith, David ;
REVOKE UPDATE
ON CUSTOMER (CustlD)
FROM Tyson, Keith, David ;
Это та же защита, что и в предпоследнем примере, и для нее также надо использовать три оператора. Никто не может изменить данные в столбце CustlD. Полномочия INSERT, DELETE и UPDATE имеют только Тайсон, Кейт и Дэвид. Как видно, три последних оператора значительно короче, так как в них не приходится вводить имя каждого сотрудника отдела продаж и перечислять каждый столбец таблицы.
Модификация табличных данных
Модификация табличных данныхВ любой работающей организации табличные данные со временем меняются. Поэтому некоторым сотрудникам необходимо предоставить возможность обновлять данные базы, а всем остальным, наоборот, запретить этим заниматься. Ниже приведен пример предоставления полномочий на обновление.
GRANT UPDATE (BonusPct)
ON BONUSRATE
TO SalesMgr ;
Исходя из рыночной конъюнктуры, менеджер по продажам может регулировать значения премиальных процентов, на основе которых рассчитываются премии продавцов (столбец ВоnusPct). Однако менеджер не может изменять значения в столбцах Min Amount и Max Amount, определяющие диапазон, который соответствует каждому уровню шкалы премий. Чтобы разрешить модификацию значений всех столбцов таблицы, необходимо указать все имена столбцов или, как в следующем примере, — ни одного:
GRANT UPDATE
ON BONUSRATE
TO VPSales ;
Предоставление полномочий пользователям
Предоставление полномочий пользователямВ силу своего положения администратор базы данных имеет все полномочия на все ее объекты. Но, в конце концов, владелец объекта имеет на него все полномочия, а база данных сама является объектом. Ни у кого из пользователей не будет полномочий, относящихся к какому-либо объекту, если только их ему специально не предоставит тот, у которого эти полномочия уже есть (а также право их передавать). Предоставлять полномочия кому-либо другому вы можете с помощью оператора GRANT (предоставить). У этого оператора следующий синтаксис:
GRANT СПИСОК-ПОЛНОМОЧИЙ
ON объект
ТО список-пользователей
[WITH GRANT OPTION] ;
Предложение WITH GRANT OPTION означает "предоставляющий полномочия"; список полномочий в операторе GRANT определяется следующим образом:
ПОЛНОМОЧИЯ [ , ПОЛНОМОЧИЯ] . . .
или
ALL PRIVILEGES
В свою очередь, вот как здесь определяются полномочия:
SELECT
| DELETE
| INSERT [(имя-столбца[, имя-столбца] . ..) ]
| UPDATE [(имя-столбца[, имя-столбца]...)]
|REFERENCES [(имя-столбца[, имя-столбца] ...)]
| USAGE
| UNDER
| TRIGGER
| EXECUTE
А объект в операторе GRANT определяется таким способом:
[TABLE] <имя таблицы>
|DOMAIN <имя домена>
| COLLATION <имя сопоставления>
| CHARACTER SET <имя символьного набора>
|TRANSLATION <имя трансляции>
| ТУРЕ <схематмчески обозначенный определенный пользователем тип>
|SEQUENCE <имя генератора последовательности спецификатор указателя шаблона>
И наконец, список пользователей в операторе определяется следующим образом:
регистрационное-имя [, регистрационное-имя]...
| PUBLIC
Указанный синтаксис применяется к представлению точно так же, как и к таблице. Полномочия SELECT, DELETE, INSERT, UPDATE и REFERENCES относятся только к таблицам и представлениям. А полномочие USAGE имеет отношение к доменам, наборам символов, сопоставлениям и трансляциям. В последующих разделах приведены различные примеры использования оператора GRANT.
Предоставление полномочий
Предоставление полномочийАдминистратор базы данных может предоставить любому пользователю любые полномочия. Владелец объекта также может предоставить любому пользователю любые полномочия, связанные с этим объектом. Однако те, кто получил таким образом свои полномочия, не могут их, в свою очередь, предоставить третьим лицам. Это ограничение позволяет администратору или владельцу объекта в достаточной степени сохранять контроль над ситуацией. Доступ к объекту могут получить только пользователи, уполномоченные на это администратором или владельцем объекта.
Если смотреть с точки зрения безопасности, то представляется разумным ограничить возможность раздавать полномочия доступа. Тем не менее часто пользователям нужны именно права на предоставление полномочий. Конвейер не может остановиться только из-за того, что кто-то заболел, находится в отпуске или ушел на обед. Вы можете дать некоторым пользователям право предоставлять их права доступа надежным сменщикам. Для передачи пользователю такого права в операторе GRANT используется предложение WITH GRANT OPTION (предоставляющий полномочия). Следующий оператор показывает пример того, как можно использовать это предложение:
GRANT UPDATE (BonusPct)
ON BONUSRATE
TO SalesMgr
WITH GRANT OPTION ;
Теперь менеджер по продажам может предоставить права на обновление данных при помощи следующего оператора:
GRANT UPDATE (BonusPct)
ON BONUSRATE
TO AsstSalesMgr ;
После того как этот оператор выполнится, заместитель менеджера по продажам сможет обновлять данные таблицы BONUSRATE, т.е. получит полномочия, которых у него до этого не было.
Просмотр данных
Просмотр данныхА вот пример предоставления полномочий просмотра:
GRANT SELECT
ON PRODUCT
TO PUBLIC ;
Эти полномочия позволяют пользователям системы просматривать содержимое таблицы PRODUCT (товар).
"Публика"
"Публика"В сетевой терминологии словом "public" обозначают всех пользователей, не имеющих специальных полномочий администратора или владельцев объектов, и кому эти привилегированные пользователи специально не предоставили права доступа. Если привилегированный пользователь предоставляет некоторые права доступа PUBLIC, их получают все пользователи системы.
В большинстве установленных баз данных пользовательские полномочия представляют собой иерархическую структуру. В этой структуре полномочия администратора находятся на самом высоком уровне, а полномочия рядовых пользователей — на самом низком. Пример иерархической структуры полномочий приведен на Рисунок 13.1.
Роли
РолиОдним из типов идентификатора подтверждения полномочий, причем не единственным, является имя пользователя. Это имя удостоверяет пользователя или программу, имеющих полномочия на выполнение с базой данных одной или множества операций. Если в большой организации с большим числом пользователей предоставлять полномочия отдельно каждому сотруднику, то такая операция может занять очень много времени. В стандарте SQL:2OO3 есть понятие роли. Оно-то и помогает решить эту проблему.
Роль, определяемая именем, — это набор полномочий, предоставляемый совокупности пользователей, которым нужен одинаковый уровень доступа к базе данных. Например, одинаковые полномочия должны быть у всех пользователей, имеющих роль SecurityGuard (означает "охранник"). Эти полномочия, скорее всего, должны отличаться от тех, что предоставляются пользователям, имеющим роль SalesClerk ("торговый служащий").
Помни: Так как в основном стандарте SQL:2OO3 ролей нет, то в некоторых реализациях их также может не быть. Перед тем как пытаться использовать роли, проверьте документацию конкретной СУБД.
Для создания роли можно использовать примерно такой синтаксис:
CREATE ROLE SalesClerk ;
После того как роль создана, вы можете назначить ее тому или иному пользователю с помощью оператора GRANT:
GRANT SalesClerk to Becky ;
Полномочия ролям предоставляются точно так же, как и пользователям, за одним, правда, исключением: роль не будет спорить и жаловаться на вас начальству.
Ссылки для связанных друг с другом таблиц
Ссылки для связанных друг с другом таблицЕсли в одной таблице в качестве внешнего ключа находится первичный ключ другой таблицы, то пользователи первой таблицы имеют доступ к данным из второй. Эта ситуация создает потенциально опасную лазейку, через которую неуполномоченные пользователи могут получать секретную информацию. При этом пользователю, чтобы что-то узнать о содержимом таблицы, не нужны никакие права доступа к этой таблице. Если у этого пользователя есть права доступа к первой таблице, которая ссылается на вторую, то этих прав часто бывает достаточно, чтобы иметь доступ и ко второй таблице.
Предположим, например, что в таблице LAYOFF_LIST (список временно уволенных) находятся имена и фамилии сотрудников, которых в следующем месяце временно уволят. Доступ с правом SELECT к этой таблице имеют только уполномоченные сотрудники администрации. Однако один неуполномоченный сотрудник обнаружил, что первичным ключом таблицы LAYOFF_LIST является EmpID (идентификатор сотрудника). Тогда этот сотрудник создает новую таблицу SNOOP ("ищейка"), в которой EmpID является внешним ключом. Этот внешний ключ и дает возможность потихоньку заглядывать в таблицу LAYOFF_LIST. Как создать внешний ключ с помощью предложения REFERENCES (ссылки), см. в главе 5. Все эти приемы должны быть известны каждому системному администратору.
CREATE TABLE SNOOP
(EmpID INTEGER REFERENCES LAYOFF_LIST) ;
Теперь все, что нужно сделать, — это пытаться с помощью оператора INSERT вставить в таблицу SNOOP строки, соответствующие идентификатору каждого сотрудника. Вставки, принимаемые этой таблицей, как раз и относятся к сотрудникам, внесенным в список временно увольняемых, в то время как все отвергаемые вставки — к сотрудникам, отсутствующим в этом списке.
Стандарт SQL:2003 не позволяет таким способом взламывать систему защиты. Он требует, чтобы любые права на использование ссылок предоставлялись уполномоченным пользователем другим пользователям только в явном виде. Каким образом это сделать, показано в следующем примере:
GRANT REFERENCES (EmpID)
ON LAYOFF_LIST
TO PERSONNEL_CLERK ;
Удаление из таблицы устаревших строк
Удаление из таблицы устаревших строкПокупатели переезжают в другие города. Сотрудники увольняются, уходят на пенсию или в мир иной. Товары устаревают. Жизнь продолжается, данные базы теряют актуальность. Устаревшие записи необходимо удалять. С другой стороны, следует тщательно контролировать, кто какие записи может удалять. И с этой задачей справится оператор GRANT:
GRANT DELETE
ON EMPLOYEE
TO PersonnelMgr ;
Менеджер по персоналу может удалять записи из таблицы EMPLOYEE (сотрудник). Этим также может заниматься администратор или владелец этой таблицы. Кроме них, записи о сотрудниках больше никто удалять не может (если только кто-то не получит такую возможность благодаря другому оператору GRANT).
Уровни пользовательского доступа
Уровни пользовательского доступаSQL:2003 обеспечивает контролируемый доступ к девяти функциям управления базами
данных.
Владельцы объектов базы данных
Владельцы объектов базы данныхКроме администраторов, есть еще один класс привилегированных пользователей — это владельцы объектов базы данных. Такими объектами, например, являются таблицы и представления. Любой пользователь, создающий какой-либо объект базы данных, может назначить владельца этого объекта. Владелец таблицы обладает всеми возможными полномочиями, которые только связаны с этой таблицей, включая управление доступом к ней. Представление создается на основе таблиц, причем владелец представления необязательно должен быть владельцем этих таблиц. Однако в этом случае владелец представления получает на него полномочия, аналогичные имеющимся у него на таблицы, на основе которых это представление создано. Отсюда следует вывод, что нельзя обойти защиту таблицы, принадлежащей другому пользователю, создав на ее основе какое-либо представление.
Неплохая работенка, но...
Вас, вероятно, интересует, как можно стать администратором базы данных и купаться в лучах славы, уважения и восхищения, сопутствующих этой должности. Очевидный ответ состоит в том, чтобы подлизаться к своему боссу. Иногда достаточно демонстрировать компетентность, честность и надежность при выполнении своих ежедневных обязанностей. На самом же деле, главное, что требуется для этой должности, — иметь крепкие нервы. Говоря о славе, уважении и восхищении, я всего лишь шутил. Если с базой данных происходит что-то не то, всегда виноват администратор. А ведь рано или поздно это случается. Так что начинайте тренировать выдержку.
может храниться информация, не предназначенная
ВниманиеЭтот оператор может быть по-настоящему опасным. В столбцах таблицы PRODUCT — например, таких как CostOfGoods (стоимость товаров), — может храниться информация, не предназначенная для всеобщего обозрения. И чтобы предоставить доступ к большей части информации, скрывая при этом важные данные, определите на основе таблицы представление, в котором не будет столбцов с конфиденциальной информацией. Затем предоставляйте полномочия SELECT не на саму таблицу, а на ее представление. Синтаксис этой процедуры приведен ниже. CREATE VIEW MERCHANDISE AS
SELECT Model, ProdName, ProdDesc, ListPrice
FROM PRODUCT ;
GRANT SELECT
ON MERCHANDISE
TO PUBLIC ;
Пользуясь лишь представлением MERCHANDISE (товары), рядовой пользователь не сможет увидеть CostOfGoods или любой другой столбец из таблицы PRODUCT, за исключением тех четырех, которые перечислены в операторе CREATE VIEW. Это столбцы Model (модель), ProdName (название товара), ProdDesc (описание товара), ListPrice (цена по прейскуранту).
Вставка данных
Вставка данныхНиже приведен пример предоставления полномочий на вставку данных в таблицу:
GRANT INSERT
ON CUSTOMER
TO SalesClerk ;
Эти полномочия позволяют служащему из отдела продаж добавлять в таблицу CUSTOMER (клиент) новые записи.
Аппаратный сбой
Аппаратный сбойДаже хорошо проверенное, высоконадежное оборудование иногда отказывает, отправляя данные на тот свет. Все материальное со временем изнашивается, даже современные полупроводниковые схемы. Если такой отказ происходит тогда, когда база данных открыта, то данные можно потерять, даже не осознавая этого. Опыт показывает — рано или поздно это произойдет. Уж если закон Мерфи и проявляет себя, так только в самое неподходящее время.
Одним из способов защитить данные от отказов оборудования является резервное копирование. Сохраняйте лишние копии всего подряд. Если ваша организация может себе это позволить, для обеспечения максимальной безопасности можно продублировать аппаратные средства вместе с их настройками с таким расчетом, чтобы, если потребуется, можно было быстро установить и запустить резервные копии базы данных и приложений на резервном оборудовании. Ну а если ограниченность в средствах не позволяет дублировать все подряд, включая расходы? Тогда, по крайней мере, делайте достаточно часто резервные копии вашей базы данных и приложений — причем настолько часто, чтобы вам после неожиданного отказа не пришлось заново вводить слишком много данных.
Другой способ избежать наихудших последствий аппаратных сбоев — использование транзакций. Это основная тема данной главы. Транзакция — это неделимая единица работы. Или транзакция выполняется целиком, или не выполняется вовсе. Если этот подход "все или ничего" кажется вам слишком радикальным, примите к сведению, что самые большие проблемы возникают в результате неполного выполнения операции с базой данных.
Блокирование объектов базы данных
Ограничения в транзакциях Проверка достоверности данных заключается не только в проверке правильности типа данных. Кроме этого, возможно, потребуется следить, чтобы в некоторых столбцах не было неопределенных значений, а в других — значения не выходили за границы определенного диапазона. Об этих ограничениях см. в главе 5.Говорить о связи ограничений с транзакциями имеет смысл потому, что первые влияют на работу последних. Предположим, например, что нужно добавить данные в таблицу, в которой имеется столбец с ограничением NOT NULL. Как правило, в начале создают пустую строку и затем заполняют ее значениями. Однако ограничение NOT NULL не позволит воспользоваться данным способом, поскольку SQL не даст ввести строку с неопределенным значением, находящимся в столбце с ограничением NOT NULL, даже если данные
в этот столбец предполагается ввести еще до конца транзакции. Для решения этой проблемы в SQL:2003 существует возможность определять ограничения как DEFERRABLE (задерживаемые) или NOT DEFERRABLE (незадерживаемые).
Ограничения, определенные как NOT DEFERRABLE, применяются немедленно. А те, что определены как DEFERRABLE, первоначально могут быть заданы как DEFERRED (задержанные) или IMMEDIATE (немедленные). Если ограничение типа DEFERRABLE задано как IMMEDIATE, то оно действует так, как и ограничение типа NOT DEFERRABLE, — немедленно. Если же ограничение типа DEFERRABLE задано как DEFERRED, то его действие может быть отсрочено.
Чтобы добавлять пустые записи или выполнять другие операции, которые могут нарушить ограничения типа DEFERRABLE, можно использовать следующий оператор:
SET CONSTRAINTS ALL DEFERRED ;
Он определяет все ограничения типа DEFERRABLE как DEFERRED. На ограничения типа NOT DEFERRABLE этот оператор не действует. После того как выполнены все операции, которые могут нарушить ваши ограничения, и таблица достигла того состояния, когда их нарушать уже нельзя, тогда эти ограничения можно применить заново. Соответствующий оператор выглядит следующим образом:
SET CONSTRAINTS ALL IMMEDIATE ;
Если вы сделали ошибку и данные не соответствуют каким-либо ограничениям, вы сразу же это увидите после выполнения данного оператора.
Если ограничения, ранее заданные как DEFERRED, явно не задаются вами как IMMEDIATE, то, когда вы попытаетесь завершить свою транзакцию с помощью оператора COMMIT, SQL активизирует все задержанные ограничения. Если к этому моменту не все ограничения выполняются, транзакция будет отменена и SQL выдаст сообщение об ошибке.
Ограничения в SQL защищают от ввода неправильных данных (или, что так же важно, от недопустимого отсутствия данных), причем у вас есть возможность на время действия транзакции временно отменить имеющиеся ограничения.
Чтобы увидеть, насколько важна отсрочка применения ограничений, проанализируйте пример с платежными ведомостями.
Предположим, что в таблице EMPLOYEE (сотрудник) имеются столбцы EmpNo (номер сотрудника), EmpName (фамилия сотрудника), DeptNo (номер отдела) и Salary (оклад). DeptNo является внешним ключом, который ссылается на таблицу DEPT (отдел). Предположим также, что в таблице DEPT имеются столбцы DeptNo и DeptName (название отдела), причем DeptNo — это первичный ключ.
Представим, что, кроме этого, вы хотите иметь такую же таблицу, как и DEPT, но в которой еще есть столбец Payroll (платежная ведомость), и в нем для каждого отдела имеется сумма значений Salary сотрудников этого отдела.
Эквивалент этой таблицы можно создать с помощью следующего представления:
CREATE VIEW DEPT2 AS
SELECT D.*, SUM(E.Salary) AS Payroll
FROM DEPT D, EMPLOYEE E
WHERE D.DeptNo = E.DeptNo
GROUP BY D.DeptNo ;
Точно такое же представление можно определить еще и другим способом:
CREATE VIEW DEPT3 AS
SELECT D.*,
(SELECT SUM(E.Salary)
FROM EMPLOYEE E
WHERE D.DeptNo = E.DeptNo) AS Payroll
FROM DEPT D ;
Но предположим, что для большей эффективности вы не собираетесь вычислять значение SUM каждый раз, когда ссылаетесь на столбец DEPT.Payroll. Вместо этого вы хотите, чтобы в таблице DEPT в действительности находился столбец Payroll. Значения в этом столбце вы будете обновлять каждый раз, когда будете вносить изменения в столбце Salary.
И, чтобы обеспечить правильность значений в столбце Salary, в определение таблицы можно вставить ограничение:
CREATE TABLE DEPT
(DeptNo CHAR(5),
DeptName CHAR(20),
Payroll DECIMAL(15,2),
CHECK (Payroll = (SELECT SUM(Salary)
FROM EMPLOYEE E WHERE E.DeptNo = DEPT.DeptNo)));
Теперь предположим, что вам надо увеличить на 100 значение Salary сотрудника под номером 123. Это можно сделать с помощью следующего обновления:
UPDATE EMPLOYEE
SET Salary = Salary + 100
WHERE EmpNo = '123' ;
Кроме того, следует обновить таблицу DEPT:
UPDATE DEPT D
SET Payroll = Payroll + 100
WHERE D.DeptNo = (SELECT E.DeptNo
FROM EMPLOYEE E
WHERE E.EmpNo 423') ;
(Подзапрос используется для того, чтобы получить ссылку на значение DeptNo сотрудника с идентификационным номером 123.)
Но здесь имеется трудность: после каждого оператора ограничения проверяются. Теоретически должны проверяться все ограничения. Но на практике реализациями проверяются только те из них, которые относятся к значениям, измененным в ходе работы оператора.
После первого из двух последних операторов UPDATE реализация проверяет все ограничения, которые ссылаются на измененные им значения. В число этих ограничений входит то, которое определено в таблице DEPT, потому что оно относится к столбцу Salary таблицы EMPLOYEE, а значения в этом столбце изменяются оператором UPDATE. После выполнения первого оператора UPDATE это ограничение оказалось нарушенным. Допустим, что перед выполнением этого оператора база данных находится в полном порядке и каждое значение Payroll в таблице DEPT равно сумме значений Salary из соответствующих строк таблицы EMPLOYEE. Тогда, как только первый оператор UPDATE увеличит значение Salary, это равенство выполняться не будет. Такая ситуация исправляется вторым оператором UPDATE, после выполнения которого значения базы данных соответствуют имеющимся ограничениям. Но в промежутке между этими обновлениями ограничения не выполняются.
Оператор SET CONSTRAINTS DEFERRED дает возможность временно отключить все или только указанные вами ограничения. Действие этих ограничений будет задержано до тех пор, пока выполнится или оператор SET CONSTRAINTS IMMEDIATE, или один из двух операторов: COMMIT и ROLLBACK. Следовательно, в нашем случае перед оператором UPDATE и после него необходимо поместить операторы SET CONSTRAINTS:
SET CONSTRAINTS DEFERRED ;
UPDATE EMPLOYEE
SET Salary = Salary + 100
WHERE EmpNo = '123' ;
UPDATE DEPT D
SET Payroll = Payroll + 100
WHERE D.DeptNo = (SELECT E.DeptNo
FROM EMPLOYEE E
WHERE E.EmpNo = '123') ;
SET CONSTRAINTS IMMEDIATE ;
Эта процедура откладывает действие всех ограничений. Например, при вставке в DEPT новых строк первичные ключи проверяться не будут; т.е. вы удалили и ту защиту, которая, возможно, вам нужна. Поэтому следует явно указывать ограничения, которые надо задержать. Для этого при создании ограничений им следует давать имена:
CREATE TABLE DEPT
(DeptNo CHAR(5),
DeptName CHAR(20),
Payroll DECIMAL(15,2),
CONSTRAINT PayEqSumsal
CHECK (Payroll = SELECT SUM(Salary)
FROM EMPLOYEE E
WHERE E.DeptNo = DEPT.DeptNo)) ;
На ограничения с именами можно ссылаться индивидуально:
SET CONSTRAINTS PayEqSumsal DEFERRED ;
UPDATE EMPLOYEE
SET Salary = Salary + 100
WHERE EmpNo = '12 3' ;
UPDATE DEPT D
SET Payroll = Payroll + 100
WHERE D.DeptNo = (SELECT E.DeptNo
FROM EMPLOYEE E
WHERE E.EmpNo = 423') ;
SET CONSTRAINTS PayEqSumsal IMMEDIATE ;
Если для ограничения в операторе CREATE не указано имя, то SQL создает это имя неявно. Оно находится в специальных таблицах — таблицах каталога. Но все-таки лучше явно задавать имена ограничений.
Теперь предположим, что во втором операторе UPDATE в качестве значения возрастания вы по ошибке указали 1000. Это значение в операторе UPDATE является разрешенным, потому что имеющееся ограничение было отсрочено. Но при выполнении оператора SET CONSTRAINTS...IMMEDIATE будут проверяться указанные в нем ограничения. Если они не соблюдаются, то будет сгенерировано исключение. Но если вместо оператора SET CONSTRAINTS...IMMEDIATE выполняется оператор COMMIT, а ограничения не соблюдаются, то вместо оператора COMMIT выполняется оператор ROLLBACK.
Подведем итог. Временно отменять действие ограничений можно только внутри транзакции. Как только транзакция завершается, ограничения сразу же вступают в силу. Если использовать эту возможность правильно, то транзакция не создаст никаких данных, нарушающих какие-либо ограничения.
Использование транзакций SQL Одним
ВниманиеЕсли вам нужны точные результаты, уровнем изоляции READ UNCOMMITED лучше не пользоваться. Уровень READ UNCOMMITED можно использовать тогда, когда нужно статистически обрабатывать такие малоизменяющиеся данные:
Во многих подобных случаях приблизительной информации вполне достаточно; дополнительное снижение производительности, нужное для получения более точного результата, не оправдано.
Проблемы неповторяющегося чтения
Следующим, более высоким уровнем изоляции является READ COMMITED: изменение, производимое другой транзакцией, невидимо для вашей транзакции до тех пор, пока другой пользователь не завершит ее с помощью оператора COMMIT. Этот уровень обеспечивает лучший результат, чем READ UNCOMMITED, но он все-таки подвержен неповторяющемуся чтению — новой серьезной неприятности.
Для пояснения рассмотрим классический пример с наличными товарами. Пользователь 1 отправляет запрос в базу данных, чтобы узнать количество определенного товара, имеющееся на складе. Это количество равно десяти. Почти в то же самое время пользователь 2 начинает, а затем с помощью оператора COMMIT завершает транзакцию, которая записывает заказ на десять единиц того же товара, уменьшая, таким образом, его запасы до нуля. И тут пользователь 1, думая, что в наличии имеется десять единиц товара, пытается оформить заказ на пять единиц. Но и такого количества уже нет. Пользователь 2, по существу, опустошил склад. Первоначальная операция чтения, выполненная пользователем 1 по имеющемуся количеству, является неповторяющейся. Это количество изменили прямо под носом пользователя 1, и все предположения, сделанные на основе полученных им данных, являются неправильными.
Риск фиктивного чтения
Уровень изоляции REPEATABLE READ дает гарантию, что такой неприятности, как неповторяющееся чтение, уже не будет. Однако на этом уровне все равно часто происходит фиктивное чтение — неприятность, возникающая тогда, когда данные, читаемые пользователем, меняются в результате другой транзакции, причем меняются как раз во время чтения.
Предположим, например, что пользователь 1 отправляет на выполнение команду, условие поиска которой (предложение WHERE или HAVING) выбирает какое-либо множество строк. И сразу же после этого пользователь 2 выполняет, а затем с помощью оператора COMMIT завершает операцию, в результате которой данные, хранящиеся в одной из этих строк, становятся другими. Вначале эти данные удовлетворяли условию поиска, заданному пользователем 1, а теперь — нет. Или, возможно, некоторые строки, которые вначале этому условию не соответствовали, теперь вполне для него подходят. А пользователь 1, транзакция которого еще не завершена, и понятия не имеет об этих изменениях; само же приложение ведет себя так, как будто ничего не произошло. И вот несчастный пользователь 1 оправляет на выполнение еще один оператор SQL. В нем условия поиска те же самые, что и в первоначальном операторе, поэтому пользователь надеется, что получит те же строки, что и перед этим. Но вторая операция выполняется уже не с теми строками, что первая. В результате фиктивного чтения надежная информация оказалась негодной.
Тише едешь - дальше будешь
Уровню изоляции SERIALIZABLE не свойственны неприятности, характерные для остальных трех уровней. Одновременные транзакции с уровнем SERIALIZABLE будут выполняться не параллельно, а последовательно. Если вы задали этот уровень изоляции, то только отказы оборудования и программ могут привести к невыполнению транзакции, и зная, что ваша система работает корректно, можно не беспокоиться о правильности результатов операций с базой данных.
Конечно, наивысшая надежность имеет свою цену в виде уменьшения производительности, так что во всем нужно знать меру. В табл. 14.1 приведены четыре уровня изоляции и решаемые ими проблемы.
Таблица 14.1. Уровни изоляции и решаемые ими проблемы
| Уровень изоляции | Решаемые проблемы |
| READ UNCOMMITED | Нет |
| READ COMMITED | "Черновое" чтение |
| REPEATABLE READ | "Черновое" чтение |
| Неповторяющееся чтение | |
| SERIALIZABLE | "Черновое" чтение |
| Неповторяющееся чтение | |
| Фиктивное чтение |
Неявный оператор начала транзакции
Некоторые реализации требуют, чтобы о начале транзакции сообщалось явным образом с помощью специального оператора, такого как BEGIN (начало) или BEGIN TRAN (начало транзакции). Стандарт SQL:2003 этого не требует. Если еще никакая транзакция не начата и на выполнение отправляется команда SQL, то в системе, совместимой с SQL:20039, создается транзакция по умолчанию. Например, операторы CREATE TABLE, SELECT и UPDATE выполняются только в транзакции. Достаточно запустить один из них на выполнение, и начнется транзакция по умолчанию.
Оператор SET TRANSACTION
Время от времени для транзакции приходится использовать характеристики, отличные от устанавливаемых по умолчанию. Эти нестандартные характеристики можно задавать с помощью оператора SET TRANSACTION. Этот оператор должен быть первым оператором транзакции. Оператор SET TRANSACTION позволяет задавать режим, уровень изоляции и размер диагностики.
Чтобы изменить, например, все три характеристики, можно отправить на выполнение такую команду:
SET TRANSACTION
READ ONLY,
ISOLATION LEVEL READ UNCOMMITED,
DIAGNOSTICS SIZE 4 ;
Операторы данной транзакции не смогут изменить базу данных (режим READ ONLY). Кроме того, определен самый низкий и, следовательно, наиболее опасный уровень изоляции (READ UNCOMMITED). Для области диагностики выбран размер 4. Как видно, параметры подобраны таким образом, чтобы транзакция использовала поменьше системных ресурсов.
Теперь сравните предыдущую команду с этой:
SET TRANSACTION
READ WRITE,
ISOLATION LEVEL SERIALIZABLE,
DIAGNOSTICS SIZE 8 ;
Эти значения позволяют транзакции изменить базу данных, задают наивысший уровень изоляции и назначают большую область диагностики. Эта транзакция предъявляет гораздо более высокие требования к системным ресурсам. В зависимости от реализации указанные значения могут оказаться значениями по умолчанию. Естественно, в операторе SET TRANSACTION можно использовать и другие значения уровня изоляции и размера диагностики.
Совет 1
Совет 1
Не задавайте слишком высокий уровень изоляции. Может показаться, что для надежности всегда лучше выбирать значение SERILIZABLE. В зависимости от конкретной ситуации это может оказаться неплохой идеей. С другой стороны, не всем транзакциям требуется такой высокий уровень изоляции, очень снижающий общую производительность системы. Если во время транзакции не требуется модифицировать базу данных, смело задавайте режим READ ONLY. В общем, лучше обойтись без фанатизма.
Нестабильность платформы
Нестабильность платформыНестабильность платформы относится к таким неприятностям, которых даже быть-то не должно, но, увы, они существуют. Чаще всего она проявляется при запуске одного или множества новых и относительно непроверенных компонентов системы. Проблемы могуг "притаиться" в новом выпуске СУБД, в новой версии операционной системы или же в новом оборудовании. Проявляются они обычно тогда, когда вы запускаете очень важное задание. В результате система блокируется, а данные — портятся. И вам больше ничего не остается, кроме как ругать последними словами свой компьютер и тех, кто его сделал, — особенно, если не осталось резервной копии.
Одновременный доступ
Одновременный доступПредставим, что программы и оборудование, с которыми вы работаете, проверены, данные введены правильно, приложения свободны от ошибок, а оборудование абсолютно надежно. Получается, что данным ничего не грозит? К сожалению, нет. Если несколько людей одновременно пытаются использовать одну и ту же таблицу из базы данных, создается ситуация одновременного доступа и их компьютеры соревнуются за право первоочередного доступа. Многопользовательские системы баз данных должны иметь возможность эффективно разрешать возникающие коллизии одновременного доступа.
не предусмотрен явный оператор
Оператор COMMIT Хотя в SQL: 2OO3 не предусмотрен явный оператор начала транзакции, но зато есть два опера-юра ее завершения: COMMIT и ROLLBACK. Первый из них используйте тогда, когда вы уже дошли до конца транзакции и собираетесь подтвердить изменения, внесенные в базу данных. В операторе COMMIT может находиться необязательное ключевое слово WORK (СОММТГ WORK означает "завершить работу")- Если при выполнении оператора COMMIT произойдет ошибка или системный сбой, возможно, потребуется выполнить откат транзакции и повторить ее снова.Оператор ROLLBACK
В конце транзакции иногда требуется отменить изменения, внесенные во время этой транзакции, т.е. восстановить базу данных в том состоянии, в каком она была перед самым началом транзакции. Для этого можно воспользоваться оператором ROLLBACK, который является отказоустойчивым. Даже если во время его выполнения в системе произойдет аварийный сбой, то после перезагрузки оператор ROLLBACK можно запустить снова. И он должен восстановить базу данных в состоянии, в каком она была перед началом транзакции.
Последовательное выполнение исключает
Последовательное выполнение исключает нежелательные взаимодействии Конфликт транзакций не происходит, если они выполняются последовательно. Главное — побыстрее занять очередь. Если невыполнимая транзакция пользователя 1 заканчивается перед началом транзакции пользователя 2, то функция ROLLBACK (откат) возвращает все товары, заказанные пользователем 1, делая их доступными во время транзакции пользователя 2. Если бы во втором примере транзакции выполнялись последовательно, то у пользователя 2 не было бы возможности изменить количество единиц любого товара, пока не закончится транзакция пользователя 1. Только после окончания транзакции пользователя 1 пользователь 2 сможет увидеть, сколько есть в наличии единиц требуемого товара.Если транзакции выполняются последовательно, одна за другой, они не смогут друг с другом взаимодействовать и нежелательные последствия таких взаимодействий исключаются, Если результат одновременных транзакций будет таким же, как и при последовательном выполнении, то эти транзакции называются упорядочиваемыми (serializable).
Проблемы взаимодействия транзакций
Проблемы взаимодействия транзакцийПроблемы, связанные с одновременным доступом, возникают даже в относительно простых приложениях. Представьте, например, такой случай. Вы пишете приложение, которое предназначено для обработки заказов и включает в себя четыре таблицы: ORDER_MASTER (главная таблица заказов), CUSTOMER (таблица клиентов), LINE_ITEM (строка заказа) и INVENTORY (таблица с описанием товаров). Выполняются следующие условия.
INVENTORY возвращается в состояние, в котором она была перед тем, как эти пользователи начали работать. Получается, что не оформлен ни один из заказов, хотя заказ пользователя 2 вполне можно было выполнить.
Угрозы целостности данных
Угрозы целостности данныхКиберпространство (в том числе ваша собственная сеть) может быть прекрасным местом для посещения, но для размещенных в нем данных это отнюдь не райский уголок. Данные могут быть повреждены или совсем испорчены самыми разными способами. В главе 5 рассказывалось о проблемах, возникающих в результате ввода неправильных данных, ошибок оператора, злонамеренного повреждения данных и одновременного доступа. "Неаккуратные" команды SQL и неправильно спроектированные приложения также могут повредить данные, и не нужно иметь много фантазии, чтобы представить, как это может произойти. Впрочем, данным угрожают и две очевидные опасности: нестабильность платформы и аппаратные сбои. В данном разделе как раз говорится об этих двух опасностях, а также о неприятностях, связанных с одновременным доступом.
Последовательное выполнение
Уменьшение уязвимости данных Чтобы уменьшить шансы потери данных из-за несчастного случая или непредвиденного взаимодействия, можно принимать меры предосторожности на нескольких уровнях. Можно так настроить СУБД, чтобы она без вас принимала некоторые из этих мер. Вы даже иногда не будете знать о них. Кроме того, администратор базы данных может по своему усмотрению обеспечить и другие меры предосторожности. О них вы также можете быть либо осведомлены, либо нет. И наконец, как разработчик вы можете сами принять определенные меры предосторожности при написании кода. Можно избавить себя от большей части неприятностей, если выработать привычку автоматически придерживаться следующих простых принципов и всегда реализовывать их в своем коде или во время работы с базой данных.Теперь поговорим об этих принципах подробно.
Никогда не выполняйте ответственное задание
ВниманиеНикогда не выполняйте ответственное задание в системе, имеющей хотя бы один непроверенный компонент. Не поддавайтесь искушению немедленно перейти на только что появившуюся бета-версию СУБД или операционной системы, даже если эта версия предоставляет расширенную функциональность. По необходимости экспериментируйте с новым программным обеспечением на машине, полностью изолированной от рабочей сети.
Модульный язык
Модульный языкИспользовать SQL вместе с процедурным программным языком можно и по-другому — с помощью модульного языка. Благодаря ему вы сможете разместить все операторы SQL в
отдельном модуле SQL.
Помни: Модуль SQL— это список команд SQL. Каждая из этих команд вызывается процедурой SQL, и перед ней стоят имя процедуры, имена и типы ее параметров.
В каждой процедуре SQL находится не менее одного оператора SQL. В любом месте программы на базовом языке можно явно вызвать эту процедуру. Вызов процедуры SQL происходит так, как если бы она была подпрограммой, написанной на базовом языке.
Таким образом, создать модуль SQL вместе со связанной с ним программой на базовом языке — это, в сущности, просто написать вручную тот код, который получается после обработки препроцессором встроенного SQL-кода.
Встроенный SQL получил большее распространение, чем модульный. И хотя большинство поставщиков предлагают какой-либо вариант модульного языка, но мало кто из них в своей документации уделяет ему особое внимание. Впрочем, у модульных языков есть несколько преимуществ.
Несовместимость типов данных
Несовместимость типов данныхДругое препятствие к легкой интеграции SQL с любым процедурным языком состоит в том, что типы данных SQL отличаются от типов данных основных языков программирования. Это не должно вас удивлять, ведь даже типы данных одного процедурного языка отличаются от типов данных другого. Общего стандарта типов данных не существует. В ранних версиях SQL, предшествовавших SQL-92, одной из главных проблем была несовместимость данных. Однако в SQL-92 (а также в SQL: 1999 и SQL:2003) с этой проблемой справляется оператор CAST. Как с его помощью преобразовать тип элемента данных, поддерживаемый в процедурном языке, в тип, который используется SQL, см. в главе 8.
Объектноориентированные RADинструменты
Объектно-ориентированные RAD-инструментыИспользуя современные RAD-инструменты, можно создавать сложные приложения и при этом не знать, как написать хотя бы одну строку кода на С, Pascal, COBOL или FORTRAN. Вместо того чтобы писать код, вы выбираете из библиотеки объекты и помещаете каждый из шх в нужное место на экране.
Помни: У объектов разных стандартных типов имеются характеризующие их свойства, а каждому типу объектов свойственны специальные события. С объектом можно также связать какой-либо метод. Метод — это процедура, написанная на процедурном языке. Впрочем, возможно создать очень сложные приложения, не написав при этом ни одного метода.
Хотя и можно написать сложные приложения, не пользуясь при этом процедурным языком, но SQL вам рано или поздно все же понадобится. SQL настолько богат выразительными средствами, что их трудно или даже невозможно выразить в объектной парадигме. Поэтому RAD-инструменты позволяют вставлять операторы SQL в объектно-ориентированные приложения. Примером объектно-ориентированной среды разработки, в которой можно работать с SQL, является Borland C++ Builder. Microsoft Access — это несколько другая среда разработки приложений, которая позволяет использовать язык SQL совместно с процедурным языком VBA.
В главе 4 описывалось создание таблиц базы данных с помощью приложения Access. Эти операции представляют собой лишь малую часть возможностей Access. Основная цель этого инструмента — разработка приложений, которые обрабатывают данные в таблицах базы данных. Разработчик помещает объекты в формы, а затем назначает им свойства, события и возможные методы. Для обработки форм и объектов используется код VBA, содержащий встроенный язык SQL.
Объявление базовых переменных
Объявление базовых переменныхПомни: Между программой, написанной на базовом языке, и кодом SQL должна передаваться информация. Для этого используются базовые переменные. Чтобы SQL признал эти переменные, их перед использованием необходимо объявить. Объявления находятся в сегменте объявлений, который расположен перед программным сегментом. Сегмент объявлений начинается со следующей директивы:
EXEC SQL BEGIN DECLARE SECTION ;
А конец этого сегмента отмечается с помощью такого выражения:
EХЕС SQL END DECLARE SECTION ;
Перед каждым оператором SQL должна стоять директива SQL EXEC. Конец сегмента SQL может быть отмечен директивой завершения. В языке COBOL такой директивой является '"END-EXEC", I в языке FORTRAN — это конец строки, а в языках Ada, С, Pascal и PL/1 — точка с запятой.
Объявления модулей
Объявления модулейСинтаксис объявлений в модулях имеет такой вид:
MODULE [имя-модуля]
[NAMES ARE имя-набора-символов]
LANGUAGE (ADA|С|COBOL|FORTRAN|MUMPS I PASCAL IPLI|SQL}
[SCHEMA имя - схемы]
[AUTHORIZATION идентификатор-подтверждения-полномочий]
[объявления-временных-таблиц.. . ]
[объявления-курсоров.. . ]
[объявления-динамических-курсоров...]
процедуры...
Как видно, имя модуля не является обязательным. Но, вероятно, все-таки неплохо модуль как-то назвать, чтобы избежать слишком большой путаницы. Необязательное предложение NAMES ARE (имена) определяет набор символов, используемый для имен. Если этого предложения в объявлении не будет, то используется набор символов, установленный в вашей реализации по умолчанию. Предложение LANGUAGE (язык) сообщает модулю, на каком языке написаны программы, которые должны его вызывать. Компилятору надо обязательно знать, что это за язык. Ведь он собирается преобразовать команды SQL так, чтобы для вызывающей их программы они выглядели как подпрограммы, написанные на том же языке, что и сама эта программа.
Хотя оба предложения, SCHEMA (схема) и AUTHORIZATION (подтверждение полномочий), являются необязательными, непременно надо указать хотя бы одно из них. Можно также указать и оба сразу. Первое из них определяет схему по умолчанию, а второе — идентификатор подтверждения полномочий. Если зы его не укажете, то для предоставления вашему модулю полномочий СУБД будет использовать полномочия текущего сеанса. Если на выполнение операции, которую вызывает ваша процедура, у вас нет прав, то процедура не будет выполняться.
Совет 3
Совет 3
Если вашей процедуре нужны временные таблицы, то объявляйте их с помощью специального предложения объявлений временных таблиц. Объявляйте курсоры и динамические курсоры еще до того, как они будут использованы какой-либо процедурой. Объявление курсора после процедуры возможно в случае, если его будет использовать не она, а процедуры, объявленные после. Более подробная информация, относящаяся к курсорам, приведена в главе 18.
Преобразование типов данных
Преобразование типов данныхВ зависимости от типов данных, поддерживаемых базовым языком и языком SQL, вам, воз-рсно, для преобразования некоторых из этих типов данных придется использовать оператор CAST. Можно использовать базовые переменные, которые были объявлены с помощью DECLARE SECTION. А когда в операторах SQL применяются имена этих переменных, то не надо забывать следующее: перед этими именами необходимо ставить двоеточие (:). Это как раз и делается в следующем примере с таблицей FOODS (продукты питания) со столбцами FOODNAME (название продукта), CALORIES (калории), PROTEIN (белки), FAT (жиры), CARBOHYDRATE (углеводы):
INSERT INTO FOODS
(FOODNAME, CALORIES, PROTEIN, FAT, CARBOHYDRATE)
VALUES
(: foodname, rcalories, .-protein, :fat, :carbo) ;
Процедуры модуля
Процедуры модуляИ наконец, после всех этих объявлений в модуле находятся его функциональные части — процедуры. У процедуры в модульном SQL есть имя, объявления параметров и выполняющиеся операторы SQL. Программа, написанная на процедурном языке, вызывает процедуру по ее имени и передает ей значения через объявленные параметры. Синтаксис процедуры следующий:
PROCEDURE имя-процедуры
(объявление-параметра [, объявление-параметра]...)
оператор SQL ;
[операторы SQL] ;
Объявление параметра должно иметь такой вид:
имя-параметра тип-данных
или такой:
SQLSTATE
Объявляемые вами параметры могут быть входными, выходными или теми и другими одновременно. SQLSTATE является параметром состояния, посредством которого выводится информация об ошибках. Чтобы подробнее разобраться с параметрами, обратитесь к главе 20.
Противоположные режимы работы
Противоположные режимы работыПри комбинировании SQL с процедурными языками немалая трудность состоит в том, что при работе с таблицами язык SQL сразу может обрабатывать целый их набор, в то время как процедурные языки — только одну строку. Иногда это не так уж и важно. Операции с наборами таблиц можно выполнять отдельно от операций со строками, используя каждый раз нужный инструмент. Однако в некоторых случаях приходится проверять все записи таблицы на соответствие определенным условиям и в зависимости от этого выполнять с ней те или иные действия. Для такого процесса требуются как мощные возможности SQL по извлечению данных, так и возможности условных переходов, имеющиеся у процедурных языков. Эту комбинацию возможностей можно достичь с помощью SQL-кода, встроенного в программу, написанную на обычном процедурном языке.
Сильные и слабые стороны процедурных языков
Сильные и слабые стороны процедурных языковВ противоположность SQL, процедурные языки легко оперируют с отдельными строками, позволяя разработчику приложения точно управлять способом обработки таблицы. Такое управление является очень большим достоинством процедурных языков. Впрочем, имеете» и слабая сторона подобного подхода. Разработчик приложения должен точно знать, каким образом данные хранятся в таблицах базы данных. Имеет значение порядок расположении строк и столбцов, и его приходится учитывать.
Благодаря своей природе процедурные языки являются очень гибкими, и с их помощью можно создавать удобные экраны ввода и просмотра данных. Кроме того, для вывода на печать можно создавать очень сложные отчеты, имеющие любую требуемую структуру.
Сильные и слабые стороны SQL
Сильные и слабые стороны SQLОдной из сильных сторон языка SQL является получение данных. Если важная информация размещена где-то в однотабличной или многотабличной базе данных, то ее можно отыскать с помощью инструментов SQL. Этот язык не работает с отдельными строками или столбцами, поэтому вам не нужно знать порядок расположения в таблице строк или столбцов. Имеющиеся в SQL средства обработки транзакций гарантируют, что на операции, выполняемые с базой данных, не повлияют любые другие пользователи, которые одновременно с вами могут получить доступ к одним и тем же таблицам.
Однако одной из слабых сторон SQL является его недоразвитый пользовательский интерфейс. В SQL нет средств для форматирования вывода и генерации отчетов. Результат выполнения запроса передается на терминал построчно.
Иногда качество, которое в одном случае является достоинством, может в другом оказаться, наоборот, недостатком. Например, одна из сильных сторон языка SQL состоит в том, что он может работать сразу с целой таблицей. Сколько бы в таблице ни было строк: одна, сто или сто тысяч — нужные вам данные можно извлечь из нее с помощью единственного оператора SELECT. Однако SQL не может легко работать с каждой строкой индивидуально. Для этого придется использовать или курсоры, которые описаны в главе 18, или процедурный базовый язык.
SQL код в программе написанной на процедурном языке
SQL - код в программе, написанной на процедурном языкеХотя на пути интеграции SQL с процедурными языками может возникать множество трудностей, эта задача выполнима. Во многих случаях эта интеграция — единственный способ решения поставленной задачи за разумное время. О трех методах подобной интеграции — встроенном SQL, модульном языке и инструментах RAD — кратко рассказывается в следующих разделах.
SQL в приложении
SQL в приложенииВ главе 2 SQL был назван неполным языком программирования. Для использования в приложении язык SQL необходимо комбинировать с процедурным языком, таким как Pascal, FORTRAN, Visual Basic, С, C++, Java или COBOL. У SQL есть как сильные, так и слабые стороны. У процедурных языков, имеющих другую структуру, также есть сильные и слабые стороны, но не такие, как у SQL. К счастью, сильные стороны SQL обычно компенсируют слабость процедурных языков, а сильные стороны процедурных языков проявляются как раз там, где SQL оказывается не на высоте. Если совместно использовать процедурные языки с SQL, можно создать мощные приложения с широким набором возможностей. Недавно появились объектно-ориентированные RAD-инструменты, которые позволяют встраивать код SQL в приложения, создаваемые из объектов, а не с помощью написания процедурного кода. В качестве примера таких инструментов можно привести Delphi и C++ Builder.
Трудности совместного использования SQL с процедурным языком
Трудности совместного использования SQL с процедурным языкомУчитывая то, как достоинства SQL компенсируют недостатки процедурных языков и наоборот, имеет смысл комбинировать их таким образом, чтобы объединить их достоинства, а не недостатки. Чтобы реализовать такое объединение на практике, приходится преодолевать некоторые трудности.
В предыдущих главах мы часто
ВниманиеВ предыдущих главах мы часто использовали звездочку ('*') для краткого обозначения всех столбцов в таблице. Если в таблице много столбцов, то звездочка помогает уменьшить ввод кода. Если же SQL применяется в прикладной программе, лучше раз и навсегда от нее отказаться. После того как приложение будет написано, вы или кто-то другой, возможно, добавите в таблицу новые столбцы или удалите старые. Однако в таком случае меняется значение понятия "все столбцы". Ваше приложение, когда оно указывает с помощью звездочки "все столбцы", может получить не те столбцы, на которые рассчитывает. Такое изменение в таблице не окажет влияния на имеющиеся программы, пока для исправления ошибки или внесения какого-либо изменения эти программы не будут перекомпилированы. Тогда воздействие символа шаблона ('*') расширится на все имеющиеся столбцы. Это изменение может привести к аварийному завершению работы приложения, не связанному с текущей отладкой, или вызвать другие изменения, которые сделают отладку настоящим кошмаром.
Совет 1
Совет 1
Чтобы обезопасить себя, указывайте имена всех столбцов в явном виде, а не полагайтесь на символ звездочки.
Внимание
Хотя RAD-инструменты, подобные Access, могут помочь в создании высококачественных приложений за сравнительно короткое время, но обычно эти инструменты работают только на одной или на нескольких платформах. Например, Access работает только в операционной системе Microsoft Windows. He забывайте об этом, если хотите предусмотреть возможный перенос своих приложений на другую платформу. RAD-инструменты, такие как Access, являются предвестниками окончательного объединения процессов проектирования реляционных и объектно-ориентированных баз данных. При этом основные достоинства реляционного подхода и SQL сохранятся. К ним будет добавлена возможность быстрой — и сравнительно свободной от ошибок — разработки, свойственная объектно-ориентированному программированию.
Встроенный SQL
Встроенный SQLСамый распространенный метод сочетания SQL с процедурными языками называется встроенным SQL. Как понятно из названия, операторы SQL вставляются в нужные места процедурной программы. Естественно, внезапно появившись в программе, написанной, скажем, на языке С, оператор SQL может стать неприятным сюрпризом для компилятора. По этой причине программы, в которых имеется встроенный SQL, перед компиляцией или интерпретацией пропускают через препроцессор. О встроенном SQL-коде препроцессор предупреждается директивой EXEC SQL.
В качестве примера использования встроенного SQL рассмотрим программу, написанную на языке Рго*С фирмы Oracle, являющемся вариантом языка С. Программа получает доступ к таблице с данными сотрудников компании, предлагает пользователю ввести имя сотрудника, а затем выводит зарплату и комиссионные этого сотрудника. Затем она предлагает ввести новые данные по зарплате и комиссионным этого же сотрудника и обновляет этими данными таблицу.
EXEC SQL BEGIN DECLARE SECTION;
VARCHAR uid[20];
VARCHAR pwd[20];
VARCHAR ename[10];
FLOAT salary, comm;
SHORT salary_ind, comm_ind;
EXEC SQL END DECLARE SECTION;
main ()
{
int sret; /* Возвращаемый код scanf */
/* Регистрация */
strcpy(uid.arr,"FRED"); /*Копировать имя пользователя */
uid.len=strlen(uid.arr);
strcpy(pwd.arr,"TOWER") ; */ Копировать пароль /*
pwd.len=strlen(pwd.arr);
EXED SQL WHENEVER SQLERROR STOP;
EXED SQL WHENEVER NOT FOUND STOP;
EXEC SQL CONNECT :uid;
printf("Соединение с пользователем: percents \n",uid.arr);
printf("Введите имя пользователя для обновления: ");
scanf("percents",ename.arr);
ename.len=strlen(ename.arr);
EXEC SQL SELECT SALARY,COMM INTO :salary,:cornm
FROM EMPLOY
WHERE ENAME=:ename;
printf("Сотрудник: percents оклад: percent6.2f комиссионные:
percent6.2f \n",
ename.arr, salary, comm);
printf("Введите новый оклад: ");
sret=scanf("percentf",&salary);
salary_ind =0; /* Инициализировать индикатор */
if (sret == EOF !! sret == 0)
salary_ind =-1; /* Установить индикатор отсутствия ввода */
printf("Введите новые комиссионные: ");
sret=scanf("percentf",&comm);
comm_ind =0; /* Инициализировать индикатор */
if (sret == EOF !! sret == 0)
comm_ind=-1; /* Установить индикатор отсутствия ввода */
EXEC SQL UPDATE EMPLOY
SET SALARY=:salary:salary_ind
SET COMM=:comm:comm_ind
WHERE ENAME=:ename;
printf("Данные сотрудника percents обновлены. \n",ename.arr);
EXEC SQL COMMIT WORK;
exit(0);
}
He надо быть экспертом по языку С, чтобы понять суть того, что и каким образом делает программа. Ниже приведена последовательность выполнения операторов.
Совет 2
Смешивать команды двух языков так, как это делается здесь, можно благодаря препроцессору. Он отделяет операторы SQL от команд базового языка, помещая эти операторы в отдельную внешнюю процедуру. Каждый оператор SQL заменяется вызовом соответствующей внешней процедуры. Теперь за свою работу может приниматься компилятор этого языка. Способ, с помощью которого операторы SQL будут передаваться базе данных, зависит от реализации. Но вы как разработчик ни о чем таком беспокоиться не должны. Этим займется препроцессор. Вам надо лишь позаботиться о базовых переменных и совместимости типов данных.
Аплеты Java
Аплеты JavaJava — язык подобный C++ и разработанный Sun Microsystems специально для создания клиентских Web-расширений. После того как соединение между клиентом и сервером установлено, аплет загружается на клиентский компьютер и запускается на нем. Подходящий аплет, встроенный на HTML-страницу, позволит получить удобный доступ к данным сервера. Схема работы Web-приложения базы данных с аплетом Java на клиентском компьютере приведена на Рисунок 16.3.
Интерфейс ODBC
Интерфейс ODBCИнтерфейс ODBC— это стандартизированный набор определений, которые включают все необходимое для организации взаимодействия приложения и требуемой базы данных. Интерфейс ODBC определяет следующее.
Для того чтобы выполнить какое-либо действие с базой данных, необходимо использовать соответствующую команду SQL в качестве аргумента функции ODBC. При условии использования стандартного для ODBC синтаксиса SQL результат выполнения этой функции не зависит от того, какая база данных установлена на сервере.
JDBC
JDBCJDBC (Java DataBase Connectivity— Java-интерфейс взаимодействия с базами данных) имеет как много общих черт с ODBC, так и несколько существенных отличий. Одно из отличий явствует из названия. Как и ODBC, JDBC — универсальный интерфейс к базе данных, не зависящий от источника данных на сервере. Разница в том, что клиентское приложение для JDBC может быть написано только на Java, а не на любом другом языке типа C++ или Visual Basic. Другое отличие состоит в том, что и Java, и JDBC с начала и до конца разрабатывались для использования в Internet.
Java — это полнофункциональный язык программирования, на котором создаются полноценные приложения для работы с базами данных для различных систем клиент/сервер. При этом приложение Java, работающее с базой данных через ODBC, очень похоже на ODBC-приложение, написанное на C++. Основная разница заключается в их работе в Internet или интранет.
Когда система функционирует в Internet, то условия ее работы отличаются от условий в системе клиент/сервер. Клиентская часть приложения, которая работает с Internet,— это браузер с минимальными вычислительными способностями. Эти способности должны быть увеличены, чтобы переложить на клиента часть работы с базой данных; и аплеты Java предоставляют такую возможность.
Аплет — это небольшое приложение, постоянно находящееся на сервере. Когда клиент соединяется по Internet с сервером, аплет загружается и запускается на клиентском компьютере. Аплеты Java разработаны таким образом, чтобы запускаться в "песочнице" (sandbox). "Песочница" — это определенное место в памяти клиентского компьютера, в котором выполняются аплеты Java. Аплет не может взаимодействовать с чем-нибудь вне "песочницы". Такая архитектура позволяет защитить клиентский компьютер от потенциально опасных аплетов, которые могут получить доступ к секретной информации или нанести данным серьезный вред.
Клиентские расширения
Клиентские расширенияWeb-браузеры разрабатываются с целью создать простой и понятный интерфейс для любых Web-страниц. Программы Netscape Navigator, Microsoft Internet Explorer и Apple's Safari изначально не были предназначены для использования в качестве клиентской части базы данных, Чтобы добиться необходимого уровня взаимодействия с базой данных в Internet, необходимы дополнительные функциональные возможности. Для обеспечения этих возможностей были разработаны различные клиентские расширения. Эти расширения включают вспомогательные приложения, включаемые модули Netscape Navigator, управляющие элементы ActiveX, Java-аплеты и сценарии. Расширения общаются с сервером с помощью протокола HTTP на языке HTML. Любой HTML-код, который оперирует данными из базы, преобразуется серверным расширением в ODBC-совместимый SQL-код перед тем, как быть направленным к источнику данных.
Компоненты ODBC
Компоненты ODBCИнтерфейс ODBC состоит из четырех функциональных компонентов. Благодаря каждому из них достигается гибкость ODBC, позволяющая взаимодействовать любым ODBC-совместимым клиентам и серверам. ODBC интерфейс включает в себя четыре уровня.
ОDBC и Internet
ОDBC и InternetОперации с базами данных в Internet кое в чем серьезно отличаются от операций с базами данных в среде клиент/сервер. Самое заметное отличие с точки зрения пользователя заключается в клиентской части системы, которая включает в себя интерфейс пользователя. В системе клиент/сервер интерфейс пользователя — это часть приложения, которое связывается с источником данных на сервере через ODBC-совместимые операторы SQL. В World Wide Web клиентской частью системы является Web-браузер, который взаимодействует с источником данных на сервере с помощью протокола HTTP посредством языка HTML (HyperText Markup Language).
Так как любой Web-пользователь имеет доступ к данным в Internet, открытие доступа к базе данных в Internet называется опубликованием базы данных. Теоретически база данных в Internet доступна гораздо большему количеству людей, чем база данных на сервере в локальной сети. Обычно даже неизвестно, кто они. Таким образом, размещение данных в Internet больше похоже на публикацию их по всему миру, нежели на распределение информации между несколькими сотрудниками. На Рисунок 16.1 приведены различия между системой клиент/сервер и системой на базе Web.
ODBC и интранет
ODBC и интранетИнтранет — это локальная или глобальная сеть, работающая как упрощенная версия Internet. Так как вся сеть интранет принадлежит одной организации, то, как правило, отсутствует необходимость в применении комплексных мер безопасности, таких как брандмауэры. Все инструменты, разработанные для создания приложений в Internet, также подходят для создания приложений для интранет. ODBC работает в интранет точно так же, как и в Internet. При наличии нескольких различных источников данных клиенты, использующие Web-браузеры и соответствующие клиентские и серверные расширения, могут взаимодействовать с этими источниками посредством SQL-кода, передаваемого с помощью HTTP и ODBC. Драйвер ODBC переводит SQL-код в собственный язык команд базы данных и выполняет его.
ODBC в среде клиент/сервер
ODBC в среде клиент/серверВ среде клиент/сервер интерфейс между клиентской и серверной частью называется программным интерфейсом приложения (application programmer's interface, API). API может быть как специальным, так и стандартным. Специальным (proprietary) API называется интерес, созданный для работы с определенной серверной частью. Программой, которая формирует этот интерфейс, является драйвер, и в специальной системе он называется собственным драйвером (native driver). Собственный драйвер оптимизирован для работы с определенной клиентской частью и связанной с ней серверной частью источника данных. В связи с тем, что собственные драйверы настроены как для работы с приложением, так и с СУБД, они передают команды и информацию достаточно быстро и без задержек.
Совет 1
Совет 1
Если система клиент/сервер рассчитана на использование с определенным источником данных и заведомо не будет использовать другой, лучше воспользоваться собственным драйвером из поставки СУБД. С другой стороны, если система должна уметь работать с различными источниками данных, использование интерфейса ODBC избавит разработчика от выполнения огромного количества ненужной работы.
Каждый драйвер ODBC создан для работы с конкретным источником данных, однако у всех них одинаковый интерфейс с диспетчером драйверов. Любой драйвер, не оптимизированный для работы с конкретным клиентом, скорее всего проиграет в быстродействии собственному драйверу. Основным недостатком первого поколения драйверов ODBC была их плохая производительность по сравнению с собственными драйверами. Последние измерения, однако, показали, что производительность драйверов ODBC 4.0 вполне сравнима с производительностью собственных драйверов. На сегодняшний день технология достигла уровня, когда уже не нужно жертвовать производительностью ради преимуществ стандартизации.
ODBC
ODBCODBC — это стандартный интерфейс между базой данных и приложением, взаимодействующим с ней. Наличие подобного стандарта позволяет приложению на клиентском компьютере получать доступ к любой базе данных на сервере, используя SQL. Единственное требование заключается в том, чтобы и клиентская, и серверная части поддерживали стандарт ODBC. ODBC 4.0 — текущая версия данного стандарта.
Приложение получает доступ к конкретной базе данных, используя специально разработанный под эту базу драйвер. Клиентская часть драйвера, работающая напрямую с приложением, должна соответствовать стандарту ODBC. Для приложения безразлично, какая СУБД установлена на сервере. Серверная часть драйвера адаптирована к конкретной базе данных. С использованием такой архитектуры приложения не только не нужно настраивать на определенную СУБД, а даже и знать не обязательно, какая именно СУБД используется. Драйвер скрывает различия между различными типами серверных частей СУБД.
Сценарии
СценарииСценарии (scripts) — наиболее гибкий инструмент для создания клиентских расширений. Использование языка сценариев, такого как Netscape JavaScript или Microsoft VBScript, позволяет максимально контролировать происходящее на клиентском компьютере. С их помощью можно организовать проверку достоверности ввода в поля формы, что позволит отбраковывать неправильно заполненные формы еще на клиентском компьютере. Это сэкономит время пользователей и уменьшит загрузку сети. Как и аплеты Java, сценарии встроены в страницу HTML и выполняются при открытии пользователем страницы.
Серверные расширения
Серверные расширенияВ системе на базе Web общение между клиентским компьютером и Web-сервером происходит с помощью HTTP. Серверное расширение — это компонент системы, который переводит HTML-текст в ODBC-совместимый SQL-код, после чего сервер базы данных связывается с источником данных и выполняет этот код. В обратном направлении источник данных пересылает результат запроса серверу базы данных и далее серверному расширению, которое преобразует результат запроса в форму, понятную Web-серверу. Затем данные отсылают к клиентскому компьютеру по Internet, и Web-браузер пользователя их отображает. На Рисунок 16.2 приведена схема подобной системы.
Система базы данных на основе Web с серверным расширением
Рисунок 16.2. Система базы данных на основе Web с серверным расширением
Система клиент/сервер в сравнении с системой на базе Web
Рисунок 16.1. Система клиент/сервер в сравнении с системой на базе Web
Управляющие элементы ActiveX
Управляющие элементы ActiveXУправляющие элементы Microsoft ActiveX подобны модулям Netscape, но работают по другому принципу. ActiveX базируется на ранней технологии OLE, разработанной Microsoft. Netscape поддерживает ActiveX, а также некоторые другие популярные технологии Microsoft. Естественно, Microsoft Internet Explorer совместим с ActiveX. Под контролем Netscape и Microsoft сейчас находится большая часть рынка браузеров.
Включаемые модули Netscape Navigator
Включаемые модули Netscape NavigatorМодули Netscape Navigator (Netscape Navigator plug-in) так же, как и вспомогательные приложения, помогают обрабатывать информацию, которую не понимает браузер. От последних они отличаются тем, что совместимы только с браузерами Netscape и в большей степени интегрированы с данными браузерами. Подобная интеграция позволяет браузеру отображать файл еще до полной его загрузки. Это качество является серьезным преимуществом такого рода программ. Пользователь не обязан ждать конца загрузки, чтобы увидеть изображение. Большое и всевозрастающее количество модулей Netscape делает доступным воспроизведение звука, общение с другими пользователями, анимацию, видео и интерактивную трехмерную виртуальную реальность. Для нас главное, что некоторые из модулей обеспечивают доступ к удаленным базам данных в Internet.
Загружая исполняемый код из Internet,
ВниманиеЗагружая исполняемый код из Internet, вы подвергаетесь определенной опасности. Даже Java-аплеты могут оказаться не такими уж и безобидными. Поэтому будьте осторожны при загрузке исполняемого кода с подозрительных серверов. Как и ODBC, JDBC передает операторы SQL от клиентской части приложения (аплета), запускаемого на компьютере клиента, к источнику данных на сервере. Также JDBC служит для передачи результатов выполнения запросов или сообщений об ошибках от источника данных обратно приложению. Польза от JDBC заключается в том, что разработчик аплетов может использовать стандартный интерфейс JDBC, не заботясь о том, какая база данных находится на сервере. JDBC выполняет все преобразования, необходимые для правильного двустороннего взаимодействия.
Вспомогательные приложения
Вспомогательные приложенияПервые клиентские расширения назывались вспомогательными приложениями (helper application). Вспомогательное приложение — это самостоятельная программа, выполняющаяся на компьютере пользователя. Она не интегрирована в Web-страницу и не отображается в окне Web-браузера. Ее можно использовать как программу просмотра для графических файлов, формат которых не поддерживается браузером. Чтобы воспользоваться такой программой, ее необходимо установить. После загрузки рисунка подходящего формата браузер вызывает программу просмотра. Серьезным недостатком этого метода является то, что перед запуском вспомогательного приложения весь файл данных должен быть записан во временный файл. Таким образом, для больших файлов время ожидания заметно увеличивается.
Webприложение базы данных использующее аплет Java
Рисунок 16.3. Web-приложение базы данных, использующее аплет Java
Преимущество использования аплетов Java заключается в том, что они не устаревают. Так как аплеты каждый раз при использовании загружаются с сервера, клиент всегда имеет дело с последней версией аплета. Разработчикам можно не беспокоиться о возможной потере совместимости при переходе сервера на новое программное обеспечение. Надо лишь убедиться в том, что загружаемые аплеты Java совместимы с новой конфигурацией сервера. Положительный ответ будет означать, что все клиенты также совместимы.
Домены
ДоменыДля того чтобы преобразовать SQL-домен в XML, вначале этот домен необходимо создать. Для создания домена воспользуемся оператором CREATE DOMAIN:
CREATE DOMAIN WestCoast AS CHAR (2)
CHECK (State IN ('CA\ 'OR1, ' WA' , ' AK ' ) ) ;
Теперь создадим таблицу, которая использует этот домен:
| CREATE TABLE WestRegion ( | ||
| ClientName | Character (20) | NOT NULL, |
| State | WestCoast | NOT NULL |
| ) ; |
Name='DOMAIN.SalesWestCoast'>
typeName='WestCoast'
mappedType='CHAR_2'
finals'true'/>
После применения этого преобразования вы получите XML-документ примерно следующего содержания:
.
.
.
State>AK
.
.
.
.
.
.
Как XML связываемся с SQL
Как XML связываемся, с SQLXML, как и HTML, является языком разметки, а значит, не полнофункциональным языком, как, например, C++ или Java. Это даже не подъязык манипулирования данными, как SQL. Однако он достаточно осведомлен в отношении перемещаемых им данных. Там, где HTML имеет дело только с форматированием текста и графики в документе, XML позволяет работать со структурой содержимого документа. Однако сам XML не предназначен для прямого форматирования. Для осуществления задачи форматирования необходимо дополнить XML таблицей стилей, которая, как и в HTML, позволяет форматировать XML-документы.
SQL и XML структурируют данные двумя различными способами. Таким образом, вы можете сохранить данные и выбрать из них необходимую информацию одним из следующих способов.
Когда ХМLданные не используются
Когда ХМL-данные не используютсяИногда использование XML-данных не имеет никакого смысла, поскольку сегодня большинство данных в реляционных базах данных гораздо лучше работает в их текущем формате, нежели в формате XML. Для подтверждения вышесказанного приведем два примера. Итак, XML тип не используется в следующих случаях.
Когда используются XMLданные
Когда используются XML-данныеНеобходимость хранения данных в формате XML зависит от того, что вы планируете делать с этим данными. Принятие такого решения целесообразно в следующих случаях.
Массивы
МассивыЕсли вы хотите поместить в одно поле больше одного элемента, вместо типа ROW воспользуйтесь типом Array. Для этого примера в таблице CONTACTINFO объявим столбец Phone как массив, а затем создадим XML-схему, которая будет преобразовывать массив в XML:
Например:
| CREATE TABLE CONTACTINFO ( | |
| Name | CHARACTER (30) |
| Phone | CHARACTER (13) ARRAY [4] |
| ) ; |
nillable='true' type='CHAR_13'/>
Результат будет примерно следующим:
Элемент в массиве, содержащий xsi:nil='true', говорит о том, что второй телефонный номер в исходной таблице содержит неопределенное значение.
Мультимножества
МультимножестваНомера телефонов из предыдущего примера могут также хорошо храниться и в мультимножествах. Для преобразования мультимножества воспользуемся следующим программным кодом:
| CREATE TABLE CONTACTINFO ( | |
| Name | CHARACTER (30) |
| Phone | CHARACTER (13) MULTISET |
| ) ; | |
nillable='true' type='CHAR_13'/>
Результат будет примерно следующим:
Обработка неопределенных значений
Обработка неопределенных значенийПоскольку SQL-данные могут быть неопределенными значениями, необходимо решить, как представлять их в XML-документе. Неопределенные значения могут быть представлены либо как нуль, либо как отсутствие всякого значения. Если элемент столбца необходимо представить как неопределенное (нулевое) значение, то он будет иметь атрибут xsi:nil="true". Это может быть сделано следующим способом:
Если элемент столбца отсутствует, то выполните следующее:
При выборе этой опции строка содержит неопределенное значение, которое
"отсутствует". С ним нет связей.
Оператор XMLAGG
Оператор XMLAGGXMLAGG — это агрегирующая функция, которая создает единственный XML-документ из других XML-документов или их отдельных фрагментов. Агрегирование содержит дерево элементов. Рассмотрим это на примере:
SELECT XMLELEMENT
( NAME "City",
XMLATTRIBUTES ( с.City AS "name" ) ,
XMLLAGG (XMLELEMENT ( NAME "last" c.LastNa,e )
)
) AS "CityList"
FROM CUSTOMER С
GROUP BY City ;
При обработке таблицы CUSTOMER этот запрос выведет следующее:
Оператор XMLCONCAT
Оператор XMLCONCATОператор XMLCONCAT обеспечивает альтернативный способ создания дерева элементов. Это осуществляется путем связывания его XML-аргументов. Например:
SELECT с.LastName
XMLCONCAT (
XMLELEMENT ( NAME "first", c.FirstName,
XMLELEMENT ( NAME "last", c.LastName)
) AS "Result" FROM CUSTOMER с ;
В результате получится следующее:
| LastName | Result |
| Abelson |
|
| Bailey |
Оператор XMLELEMENT
Оператор XMLELEMENTОператор XMLELEMENT создает XML-элемент. Этот оператор может использоваться в операторе SELECT для помещения данных формата XML в базу данных SQL. Рассмотрим следующий пример:
SELECT с.LastName
XMLELEMENT ( NAME "City", c.City ) AS "Result"
FROM CUSTOMER С
WHERE LastName="Abelson" ;
Возвращаемый результат:
| LastName | Result |
| Abelson |
Оператор XMLFOREST
Оператор XMLFORESTОператор XMLFOREST создает дерево (forest) элементов из списка аргументов. Каждый аргумент оператора создает новый элемент. Ниже приведен пример использования такого оператора.
SELECT с . LastNartie
XMLFOREST (с.City,
с.AreaCode,
с.Telephone ) AS "Result"
FROM CUSTOMER с
WHERE LastName="Abelson" OR LastName="Bailey" ;
В результате вы получите следующую информацию:
| LastName | Result |
| Abelson | |
| Bailey |
Оператор XMLGEN
Оператор XMLGENПервый аргумент оператора XMLGEN— это шаблон, который содержит символы-заполнители для значений, которые будут добавлены позже. Символы-заполнители представлены в форме "{$name}". Последовательность аргументов задает значения и связанные с ними имена, которые характеризуют шаблон. Ниже приведен пример использования этого оператора.
SELECT с.LastName
XMLGEN ( '
c.LastName AS Name,
с City ) AS "Result"
FROM CUSTOMER с
WHERE LastName="Abelson" OR LastName="Bailey" ;
Результат будет следующим:
| LastName | Result |
| Abelson | |
| Bailey |
Отдельные UDTтипы
Отдельные UDT-типыС отдельными UDT-типами вы можете делать то же самое, что и с доменами, однако здесь необходим более строгий подход к определению типа. Например: CREATE TYPE WestCoast AS Character (2) FINAL ;
Для преобразования этого типа в XML-тип используется следующая XML-схема:
Name='UDT.SalesWestCoast'>
typeName='WestCoast'
mappedType='CHAR_2'
final='true'/>
В результате создается элемент, похожий на созданный для домена, описанного нами ранее.
Получение XML результатов при использовании операторов SQL
Получение XML результатов при использовании операторов SQLSQL: 2003 имеет пять операторов, XMLELEMENT, XMLFOREST, XMLGEN, XMLCONCAT и XMLAGG, которые при применении к содержимому базы данных SQL дают XML-результат.
Преобразование данных из формата SQL в формат XML и наоборот
Преобразование данных из формата SQL в формат XML и наоборотДля обмена данными между базами данных SQL и XML-документами различные элементы базы данных SQL должны быть преобразованы в эквивалентные элементы XML-документа и наоборот. Как вы узнаете в следующих разделах, порой преобразование просто необходимо для удачного выполнения некоторых операций.
Преобразование идентификаторов
Преобразование идентификаторовВ отличие от SQL, XML более точно подходит к определению идентификаторов. Прежде чем стать частью XML-документа, символы, допустимые в SQL и недопустимые в XML, должны быть соответствующим образом преобразованы. SQL поддерживает неограниченные идентификаторы. Это означает, что все виды добавочных символов, такие как %, $ и &, будут допустимыми до тех пор, пока они заключены в двойные кавычки. Но такие символы не допустимы для XML. Кроме того, имена в XML, начинающиеся с символов XML, в любых комбинациях уже зарезервированы и, таким образом, не могут использоваться без каких-либо негативных последствий. Именно поэтому идентификаторы SQL, начинающиеся с этих символов, должны быть изменены.
При преобразовании из SQL в XML все идентификаторы конвертируются в Unicode. Любые же идентификаторы языка SQL, которые являются также допустимыми именами XML, остаются неизменными. Символы идентификатора языка SQL, не допустимые для имен XML, заменяются шестнадцатеричным кодом. Полученный результат имеет форму записи типа "_хНННН_" или "_xНННННННН_", где Н— шестнадцатеричный разряд верхнего регистра. Например, символ подчеркивания "_" будет представлен как "_x005F_". Двоеточие— как "_х003А_". Эти представления являются кодами для описания в системе Unicode таких символов, как подчеркивание и двоеточие. В случае, если идентификатор SQL начинается с символов х, т или /, перед такими символами необходимо поставить префикс с кодом в форме "_xFFFF_".
Преобразовать символы из XML-формата в SQL-формат гораздо проще. Все, что для этого необходимо сделать, — это развернуть символы XML-имени в последовательность "_XFFFF_" или "_XFFFFFFFF_". Всякий раз, когда вы находите такую последовательность, заменяйте ее символами, соответствующими символам Unicode. Если же имя XML начинается с символов "_XFFFF_", игнорируйте их.
Следуя этим простым правилам, вы можете преобразовывать идентификатор SQL в XML-имя, а затем вернуться к идентификатору SQL. Однако это хорошее правило не действует для преобразования XML-имени в идентификатор SQL и наоборот.
Преобразование наборов символов
Преобразование наборов символовВ языке SQL поддержка наборов символов зависит от его реализации. Это означает, что приложение DB2 производства компании IBM может поддерживать наборы символов, которые не поддерживаются приложением SQL Server компании Microsoft. SQL Server, в свою очередь, поддерживает наборы символов, которые не поддерживаются приложением Oracle. Несмотря на то что большинство общих наборов символов практически всегда универсально поддерживается тем или иным приложением, использование мало распространенных символов может усложнить перемещение вашей базы данных и приложения с одной платформы реляционной СУБД (РСУБД) на другую.
У XML нет никаких проблем совместимости с наборами символов, поскольку он поддерживает только один набор — Unicode. Этот универсальный набор символов, публикуемый консорциумом Unicode Consortium, представляет собой стандартный набор символов для компьютеров, в котором каждому письменному знаку на любом языке присвоен определенный номер. С точки зрения обмена данными между любыми реализациями языка SQL и XML это очень хорошо. Поставщики РСУБД должны описать преобразование строк из каждого набора символов в символы Unicode и обратное преобразование символов Unicode в строки, состоящие из наборов символов. К счастью, XML не поддерживает множество наборов символов, что освобождает поставщиков от большого количества проблем, связанных с бесчисленными преобразованиями символов.
Преобразование не предопределенных типов данных в XML
Преобразование не предопределенных типов данных в XMLВ SQL:2003 к не предопределенным типам данных относятся домены, отдельные UDT-типы, строки, массивы и мультимножества. Для преобразования каждого из этих XML-типов данных используется соответствующий XML-код. Примеры преобразования таких типов данных рассматриваются в следующих разделах.
Преобразование таблиц
Преобразование таблицВы можете преобразовать таблицу в XML-документ, а также все таблицы в схему или все таблицы в каталог. Преобразование также определяет привилегии. Пользователь, имеющий привилегию SELECT только для нескольких столбцов таблицы, может преобразовать в XML-документ только эти столбцы. В действительности преобразование порождает два документа: один, который содержит данные в таблице, а другой — XML-схему, описывающую первый документ. Ниже приведен пример преобразования SQL-таблицы в документ, содержащий XML-данные.
.
.
.
Основной элемент документа дал имя таблице (CUSTOMER). Каждая строка таблицы содержится в пределах элемента
Преобразование типов данных
Преобразование типов данныхВ SQL:2003 определено, что данные типа SQL преобразуются в наиболее близкую схему XML-данных. Формулировка "наиболее близкая" означает, что все значения, допустимые для SQL-типа, будут допустимы и для типа XML-схемы, а наименее возможные значения, не допустимые для SQL-типа, будут допустимы для типа XML-схемы. Элементы XML, такие как maxlnclusive и minlnclusive, могут ограничивать значения, допускаемые типом XML-схемы, до значений, допускаемых соответствующим SQL-типом. Например, если тип SQL-данных Ограничивает значения типа INTEGER в диапазоне значений от -2157483648 до 2157483647, в XML значение minlnclusive может быть задано числом -2157483648. Ниже приведен пример такого преобразования.
Раздел примечаний содержит информацию из описания SQL-типа, который в настоящий момент не используется XML, но позднее, при преобразовании этого документа в SQL-формат, может быть очень кстати.
Создание XMLсхемы
Создание XML-схемыПри преобразовании данных из SQL в XML первый созданный документ содержит данные, а второй — информацию о схеме. В качестве примера рассмотрим схему для документа CUSTOMER (см. "Преобразование таблиц").
Эта схема подходит в том случае, если при обработке неопределенных значений используется опция "нуль". Опция "отсутствие" требует несколько иного определения элемента. Например:
Строки
СтрокиТип ROW позволяет поместить целую строку с ценной информацией в одно лишь отдельное поле строки таблицы. Тип ROW создается как часть описания таблицы следующим образом:
| CREATE TABLE CONTACTINFO ( | |
| Name | CHARACTER (30) |
| Phone | ROW (Home CHAR (13), Work CHAR (13)) |
Такое преобразование создает для столбца следующий XML-тип:
Тип данных XML
Тип данных XMLВ SQL:2OO3 впервые был представлен новый для SQL тип данных — тип XML. Это означает, что согласованные между собой реализации могут хранить и работать непосредственно с данными формата XML, без их предварительного преобразования из какого-либо типа данных SQL в тип XML.
Несмотря на то что XML-данные встроены в любую поддерживающую их реализацию, они работают как тип данных, определяемый пользователем (UDT-тип, user-defined type). XML-данные обеспечивают непосредственное взаимодействие языков SQL и XML, поскольку позволяют приложениям выполнять SQL-операции над содержимым XML и, наоборот, XML-операции над содержимым SQL. Вы можете использовать столбцы с XML-данными совместно со столбцами, содержащими любые предопределенные типы данных (см. главу 2), объединяя их в запросах с предложением WHERE. А ваша СУБД, в свою очередь, следуя истинным традициям реляционных баз данных, будет определять оптимальный путь для выполнения запроса, что в конце концов и произойдет.
Базы данных: Разработка - Управление - Excel
- Базы данных
- Разработка баз данных
- СУБД и базы данных
- Управление базами данных
- Классика баз данных
- Софт для создания базы данных
- SQL
- Access
- FoxProо
- Расширенная оптимизация подзапросов в Oracle
- Informix
- Линтер
- Postgres
- СУБД DB2
- InterBase
- Excel
- Таблицы Excel
- Справка Excel
- Программирование в Excel
- Деньги в Excel
- Задачи Excel