    SQL для простых смертных 

    ГЛАВА 5
 
    Назначение этой главы — приподнять покров таинственности, которым окружено 
программирование на языке SQL (Structured Query Language — структурированный 
язык запросов). Хотя SQL считается достаточно простым языком, изучение его 
может оказаться довольно трудным делом, особенно для неподготовленных 
пользователей. На мой взгляд, такое несоответствие между простотой языка и 
ассоциациями, которые он вызывает у большинства пользователей, в какой-то 
степени вызвано манерой изложения программирования на SQL в книгах и 
руководствах. Часто можно увидеть, как при описании какой-либо темы 
объясняется полный синтаксис команды, вплоть до самых незначительных и редко 
используемых ее элементов. При этом все эти подробности круто замешиваются на 
изрядной дозе теории реляционных баз данных. Только самые отважные и 
целеустремленные читатели могут прорваться через расставленные авторами 
заслоны и самостоятельно отделить главное от второстепенного. 
    В этой главе я постарался, насколько возможно, избежать такого подхода. Мы 
рассмотрим все этапы создания объектов баз данных, изучая методы разработки 
запросов на простых примерах. В этой главе преследуются чисто практические 
цели: в максимально сжатые сроки дать вам набор самых важных знаний по SQL. 
Идея сама по себе хороша, но реализовать ее было не так-то просто — довольно 
легко сбиться с намеченного пути и увязнуть в ненужных подробностях. 
    Глава состоит из двух разделов. Прочитав первый из них, — "Приступим к 
работе", — вы сможете самостоятельно разрабатывать несложные запросы SQL. Во 
втором разделе собраны некоторые подробности, не вошедшие в первый раздел. 
Более сложные вопросы использования SQL, предназначенные для опытных 
разработчиков, излагаются в главе 24, "Дополнительные сведения о SQL". 

    На заметку 
    Данная глава написана, исходя из предположения, что вы не обладаете 
какими-либо знаниями по SQL. Если вы уже знакомы с основами использования 
этого языка, — переходите сразу к главе 6, "Проектирование базы данных 
архитектуры клиент/сервер" 

    На заметку 
    Второе предположение, на основании которого построен материал этой главы, 
состоит в том, что в вашем распоряжении имеется какой-либо сервер базы данных, 
поддерживающий SQL, и средство для направления запросов SQL этому серверу. В 
материалах этой главы использовался сервер Local InterBase, поставляемый 
вместе с Delphi Client/Server, однако вы можете работать с любым другим 
сервером, поддерживающим спецификации SQL ANSI'92. 

    Глава 5. SQL для простых смертных 75 

    Приступим к работе 

    Самый быстрый способ обучения плаванию в океане SQL — погружение в его 
бурные волны. Поэтому отбросим в сторону ненужные колебания и вперед! 

    На заметку 
    В этой главе я буду постоянно сравнивать два семейства языков SQL с 
различным синтаксисом — семейство Sybase и семейство ANSI. СУБД Sybase SQL 
Server и ее лицензированная кузина, Microsoft SQL Server, поддерживают 
синтаксис SQL, который значительно отличается от стандарта ANSI. InterBase, 
Oracle и большинство других СУБД не стали конфликтовать с ANSI, а лишь 
дополнили в той или иной степени стандартные операторы. Во всех примерах 
данной главы использовался синтаксис InterBase, поэтому для применения их в 
системе SQL Server может потребоваться та или иная модификация. Кроме того, 
имейте в виду, что когда я говорю о синтаксисе Sybase, то это распространяется 
и на Microsoft SQL Server. И наоборот, под синтаксисом InterBase я буду 
подразумевать, что данные операторы будут работать на всех ANSIсовместимых 
СУБД, включая и Oracle. 

    Выбор редактора SQL 

    Прежде всего нужно выбрать редактор запросов SQL — средство, с помощью 
которого можно вводить и выполнять команды SQL. Во всех примерах этой главы 
используется утилита Windows Interactive SQL (WISQL), поставляемая вместе с 
Local InterBase Server. Ее возможностей вполне достаточно для начинающего 
пользователя. Пользователи Sybase SQL Server могут предпочесть входящую в 
комплект поставки этой СУБД утилиту ISQL, а пользователи Oracle — SQL* Plus. 
Можно применять и утилиты других разработчиков, такие, например, как Desktop 
DBA компании Datura или DB Artisan компании Embarcadero. Короче говоря, можете 
использовать любое средство, которое умеет отправлять команды SQL серверу и 
отображать полученные от него результаты. 

    Разделители 

    В языке SQL используются два типа разделителей: разделители операторов 
(statement terminator) и разделители пакетов (batch terminator). В некоторых 
диалектах SQL наличие в конце каждого оператора символа разделителя является 
обязательным. Например, в InterBase и Oracle требуется, чтобы каждый оператор 
заканчивался символом точки с запятой (;). Использование или неиспользование 
разделителей операторов обычно зависит от диалекта SQL, а не от средства, с 
помощью которого команды SQL пересылаются серверу. 
    Разделители пакетов, как видно из их названия, предназначены для 
разграничения пакетов операторов SQL. Наличие такого разделителя часто 
является необходимым условием отправки пакета на обработку серверу. В 
большинстве случаев использование разделителей пакетов обусловлено требованием 
редактора SQL, а не диалекта. Например, утилита ISQL Sybase требует, чтобы 
каждый пакет команд SQL завершался словом GO (при необходимости ключевое 
слово, используемое в качестве разделителя, можно изменить). Между тем, слово 
GO не является ключевым словом диалекта Transact-SQL Sybase и отправка его 
серверу приводит к появлению ошибки. 
    Во всех примерах данной главы, созданных с помощью утилиты WISQL 
InterBase, можно опустить символ точки с запятой в качестве разделителя 
операторов. Все примеры будут выполнятся независимо от введения точки с запятой. 

    На заметку 
    В СУБД InterBase при компиляции хранимых процедур и других конструкций, 
содержащих внедренные операторы SQL, использование разделителя операторов 
становится обязательным, какой бы редактор SQL вы не использовали. Например, 
некоторые команды SQL, такие как CREATE PROCEDURE, сами состоят из операторов 
SQL, выполнение которых не входит в ваши планы при разработке запросов с 
помощью редактора SQL. В данном случае нужно, чтобы эти операторы выполнялись 
при выполнении разрабатываемой процедуры, а не при ее компиляции. Во избежание 
подобных проблем, нужно воспользоваться командной SET TERMINATOR, чтобы на 
время разработки процедур изменить используемый по умолчанию разделитель 
команд. Подробнее эта тема освещена в главе 24. 

    76 Часть I. Основы 

    Создание базы данных 

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

    CREATE DATABASE "С:\DATA\IB\ORDENT.GDB" 
    USER "SYSDBA" PASSWORD "masterkey"; 

    Если вы используете утилиту ISQL InterBase, то можете попробовать создать 
собственную базу данных, введя этот или подобный оператор. Пользователи 
утилиты WISQL могут создать базу данных с помощью команды Create Database 
подменю File этой утилиты. Для этого нужно выполнить следующее. 
    1. Найдите в папке InterBase пиктограмму Windows Interactice SQL и 
запустите утилиту на выполнение. 
    2. Выберите из меню File утилиты команду Create Database. 
    3. В строке Database диалогового окна Create Database введите путь с: 
\DATA\IB\ORDENT, при необходимости заменив с: \DATA\IB на каталог, который вы 
выделили для хранения создаваемой базы данных. 
    4. В строках Usemame и Password введите, соответственно, имя пользователя 
и пароль. По умолчанию в качестве имени пользователя применяется идентификатор 
SYSDBA, а в качестве пароля — masterkey. В паролях InterBase допускаются как 
прописные, так и строчные буквы, поэтому будьте внимательны при вводе пароля. 
    5. Щелкните на кнопке ОК. InterBase создаст базу данных и подключит вас к 
ней. При следующих сеансах работы для подключения к ранее созданной базе 
данных нужно воспользоваться командой File=>Connect to Database. 

    Создание таблиц 

    Создав базу данных, можно приступать к наполнению ее объектами. Начнем с 
создания трех таблиц с помощью оператора SQL CREATE TABLE. В окне редактора 
SQL введите и выполните с помощью щелчка на кнопке Run следующую команду. 

    CREATE TABLE CUSTOMER 
    ( 
    
    CustomerNumber     int          NOT NULL 
    LastName            char (30)     NOT NULL 
    FirstName           char (30)     NOT NULL 
    StreetAddress       char(30)     NOT NULL 
    City               char(20)     NOT NULL 
    State               char (2)      NOT NULL 
    Zip                char(10)     NOT NULL 
    ) 

    Этот оператор создаст таблицу CUSTOMER, предназначенную для хранения 
информации о клиентах. Теперь создадим таблицу ORDERS, в которой будут 
хранится сведения о заказах. 

    CREATE TABLE ORDERS 
    ( 
    
    OrderNumber        int                NOT NULL, 
    OrderDate          date               NOT NULL, 
    CustomerNumber     int                NOT NULL, 
    ItemNumber         int                NOT NULL, 
    Amount             numeric(9,2)        NOT NULL 
    ) 
    
    Осталась одна таблица ITEMS, предназначенная для хранения информации о товарах. 

    CREATE TABLE ITEMS 
    ( 
    ItemNumber   int                 NOT NULL, 
    Description char(30)            NOT NULL, 
    Price        numeric(9,2)        NOT NULL 
    ) 


    Глава 5. SQL для простых смертных 77 


    На заметку 
    Большинство серверов не требует явного указания инициализатора NULL (NOT 
NULL). Однако при этом используемое по умолчанию значение может быть разным. 
Например, сервер InterBase, в соответствии со стандартом ANSI SQL по умолчанию 
инициализирует столбцы значением NULL. Сервер же SQL Server, наоборот, по 
умолчанию использует значение NOT NULL. Поэтому лучше всего явно указывать 
инициализатор. Правда, существует одна оговорка. Сервер InterBase не позволит 
явно указать инициализатор NULL, так как он используется по умолчанию. Если вы 
все же предпримете такую попытку, то оператор SQL не будет,выполнен.


    Добавление данных 

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

    INSERT INTO CUSTOMER 
    VALUES(1,'Doe','John','123 Sunnylane','Anywhere','OK','73115') 
    
    INSERT INTO CUSTOMER 
    VALUES(2,'Doe','Jane',423 Sunnylane','Anywhere','OK','73115') 
    
    INSERT INTO CUSTOMER 
    VALUES(3,'Citizen','John','57 Riverside','Reo','МО','65803') 
    Теперь добавим четыре строки в таблицу ORDERS. 
    
    INSERT INTO ORDERS 
    VALUES(101,'07/07/97',1,1001,123.45) 
    
    INSERT INTO ORDERS 
    VALUES(102,'07/08/97',2, 1002,678.90) 
    
    INSERT INTO ORDERS 
    VALUES(103,'07/09/97',3,1003,86753.О 9) 
    
    INSERT INTO ORDERS 
    VALUES(104,'07/10/97',1,1002,678.90) 


    На заметку 
    При добавлении строк в таблицы Oracle нужно использовать правильный формат 
даты. Используемый по умолчанию в СУБД Oracle формат даты зависит от 
региональных настроек и значения параметра инициализации NLS_DATE__FORMAT. На 
моем сервере используется формат 'DD-MMM-YY' , поэтому в моем случае 
приведенные выше операторы INSERT для таблицы ORDERS будут иметь следующий вид. 
    
    INSERT INTO ORDERS 
    VALUES(101,'07-JUL-97',1,1001,123.45) 
    
    INSERT INTO ORDERS 
    VALUES(102,'08-JUL-97',2,1002,678.90)      
    
    INSERT INTO ORDERS    
    VALUES(103,'09-JUL-97',3,1003,86753.09) 
    
    INSERT INTO ORDERS 
    VALUES(104,'10-JUL-97',1,1002,678.90) 


    78 Часть I. Основы 

    Однако можно не изменять операторы, а преобразовать даты к нужному 
формату, используя функцию Oracle TO_DATE(). 
    
    INSERT INTO ORDERS 
    VALUES(101,TO_DATE('07/07/97','MM/DD/YY'),1,1001,123.45) 
    
    INSERT INTO ORDERS    
    VALUES(102,TO_DATE('07/08/97','MM/DD/YY'),2,1002,678.90) 
    
    INSERT INTO ORDERS 
    VALUES(103,TO_DATE('07/09/97','MM/DD/YY'),3,1003,86753.09) 
    
    INSERT INTO ORDERS 
    VALUES(104,TO_DATE('07/10/97','MM/DD/YY;) ,J-,10Д2,678.90) 
    
    
    Наконец, с помощью следующих операторов добавим три строки в таблицу ITEMS. 
    
    INSERT INTO ITEMS 
    VALUES(1001,'WIDGET A',123.45) 
    
    INSERT INTO ITEMS 
    VALUES(1002,'WIDGET B',678.90) 
    
    INSERT INTO ITEMS 
    VALUES(1003,'WIDGET C',87653.09) 
    
    Обратите внимание: во всех операторах INSERT указываются только списки 
значений, без указания полей. Дело в том, что оператор INSERT по умолчанию 
помещает значения в том порядке, в котором они перечислены в операторе, в 
соответствующие поля таблицы. В случае необходимости, например, если нужно 
вставить значения только некоторых полей, можно использовать список таких полей: 

    INSERT INTO ITEMS (ItemNumber, Price) VALUES(1001,123.45) 

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

    INSERT INTO ITEMS (Price, ItemNumber) VALUES(123.45, 1001) 


    Команда SELECT 

    Самый быстрый способ просмотра содержимого таблицы — с помощью команды 
SELECT. Для этого необходимо ввести ее в виде SELECT * FROM имя, где параметр 
имя заменяется именем нужной таблицы (в нашем примере — CUSTOMER, ORDERS или 
ITEMS). Напомню, что в данный момент таблицы CUSTOMER и ITEMS имеют по три 
строки, а таблица ORDERS — четыре. 
    На рис. 5.1 показано содержимое таблицы CUSTOMER после выполнения команды 
SELECT * FROM CUSTOMER УТИЛИТОЙ WISQL. 
    На рис. 5.2 представлено содержимое таблицы ORDERS после выполнения 
команды SELECT * FROM ORDERS. 
    Наконец, на рис. 5.3 представлено содержимое таблицы ITEMS после 
выполнения команды SELECT * FROM ITEMS. 
    Если оператор SELECT задан в виде SELECT *, он возвращает содержимое всех 
столбцов таблицы. Однако в нем можно использовать и список конкретных полей, 
разделив их запятыми, например: 

    SELECT CustomerNumber, LastName, State FROM CUSTOMER 

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

    Глава 5. SQL для простых смертных 79 


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

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

    В разных СУБД ключевое слово FROM трактуется по-разному. В некоторых 
системах оно является обязательным, в других — нет. Например, в операторах 
SELECT систем Oracle и InterBase ключевое слово FROM является обязательным, 
даже если информация из таблицы не выбирается. С другой стороны, в Sybase SQL 
Server требуется включать ключевое слово FROM только в том случае, когда 
выбираются значения столбцов таблицы. В тех же операторах SELECT, которые не 
выбирают информацию из таблицы (в которых используются только, например, 
выражения или системные функции), в системе Sybase SQL Server ключевое слово 
FROM можно опускать. Например, если вы в операторе SELECT СУБД SQL Server 
используете системную функцию, такую как getdate(), то можете не указывать 
слова FROM, тогда как в системе Oracle при применении функции SYSDATE вам 
придется указать его. 

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

    Рис. 5.4. Результат выбора полей, указанных в операторе SELECT 


    80 Часть I. Основы 

    Выражения в столбцах 

    В качестве столбцов в операторах SELECT можно указывать не только имена 
столбцов таблицы. Вполне допустимыми являются также выражения в столбцах 
(expression column), содержащие, кроме имени столбца, математические операции, 
абсолютные значения и вызовы функций. В некоторых диалектах SQL имеются 
различные полезные функции, которые возвращают значения даже без обращения к 
собственно столбцам таблицы. Например, в СУБД Sybase и Microsoft SQL Server 
можно использовать следующий оператор, возвращающий текущую дату и время: 

    SELECT getdate() 

    В системе InterBase для получения фамилии каждого клиента, представленной 
прописными буквами, можно воспользоваться следующим оператором SQL (рис. 5.5): 

    SELECT UPPER(LastName), FirstName FROM CUSTOMER 


    Puc. 5.5. Использование функции UPPER в операторе SELECT 


    Вычисляемые столбцы 

    Вычисляемые столбцы (aggregate column) — это функции, выполняющие те или 
иные действия над наборами данных, представленными столбцами таблицы. В 
качестве примера таких функций можно привести функции COUNT (количество 
элементов), SUM (сумма всех элементов), AVG (среднее арифметическое), MIN 
(минимальное значение), STDDEV (среднеквадратичное отклонение), VARIANCE 
(дисперсия) и МАХ (максимальное значение). Ниже приведены примеры 
использования таких функций. 

    SELECT COUNT(*) FROM CUSTOMER 

    Данный оператор выводит количество записей в таблице клиентов. 

    SELECT Max(Amount) FROM ORDERS 

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

    SELECT SUM(Amount) FROM ORDERS 


    Ключевое слово WHERE 

    Ключевое слово WHERE применяется в SQL для уточнения значений, отбираемых 
с помощью оператора SELECT. Рассмотрим следующий пример. 

    SELECT * FROM CUSTOMER WHERE State='OK' 

    Этот оператор выводит список всех клиентов (рис. 5.6), проживающих в шт. 
Оклахома (т.е. отбирает записи, в поле State которых указано значение, 
соответствующее почтовому коду данного штата — ОК). 
    Как показано на рис. 5.7, следующий оператор возвращает данные только о 
тех клиентах, в записях о которых в поле StreetAddress имеется слово Sunny. 

    SELECT LastName, StreetAddress FROM CUSTOMER WHERE StreetAddress LIKE '%Sunny%' 

    Следующий оператор возвращает сведения только о тех заказах, сумма которых 
превышает $500 (рис.5.8). 

    SELECT OrderNumber, OrderDate, Amount FROM ORDERS WHERE Amount > 500 


    Глава 5. SQL для простых смертных 81 


    Рис. 5.6. Использование ключевого слова WHERE в операторе SELECT 

    Рис. 5.7. Использование ключевых слов WHERE и LIKE в операторе SELECT 

    И наконец, последний пример, в котором выводятся сведения о заказах, 
сделанных за период между 8 и 9 июля 1997 г. (рис. 5.9). 

    SELECT OrderNumber, OrderDate, Amount FROM ORDERS WHERE OrderDate BETWEEN 
'07/08/97' AND '07/09/97' 

    Рис. 5.8. Информация о заказах на сумму свыше $500 

    Рис. 5.9. Использование ключевого слова BETWEEN 


    Объединения 

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

    82                                                            Часть I. Основы 


    SELECT CUSTOMER.CustomerNumber, ORDERS.Amount 
    FROM CUSTOMER, ORDERS 
    WHERE 
    CUSTOMER.CustomerNumber=ORDERS.CustomerNumber 


    Рис. 5.10. Информация, выбранная из объединенных таблиц CUSTOMER и ORDERS 

    Обратите внимание, что с помощью ключевого слова FROM к таблице CUSTOMER 
добавляется таблица ORDERS. Другой важный момент в приведенном выше операторе 
— наличие знака равенства между ссылками на поля CustomerNumber таблиц 
CUSTOMER и ORDERS. Таблица, указанная слева от знака равенства, называется 
внешней (outer) таблицей, а таблица, указанная справа от этого знака, — 
внутренней (inner). Часто такие таблицы называют просто по их расположению, 
т.е. левой и правой. Объединения такого вида, называемые левыми объединениями 
(left join) из-за того, что таблицы в операторе расположены слева направо, 
являются самыми популярными в реляционных СУБД. 

    Внутренние и внешние объединения 

    Только что упоминавшиеся левые объединения более формально называются 
внутренними объединениями (inner join). Такие объединения возвращают только те 
строки, для которых выполняется условие объединения. Однако объединения могут 
быть и внешними (outer join), т.е. такими, которые возвращают строки вне 
зависимости от выполнения условия объединения. При использовании внешних 
объединений все поля внутрённей таблицы, не соответствующие условию 
объединения, возвращаются со значением NULL. 
    Синтаксис создания внешних объединений зависит от сервера и от того, какой 
уровень стандарта ANSI SQL он поддерживает. Существуют два основных варианта 
такого синтаксиса. Серверы Sybase и Microsoft SQL Server используют следующий синтаксис. 

    SELECT CUSTOMER.CustomerNumber, ORDERS.Amount 
    FROM CUSTOMER, ORDERS 
    WHERE CUSTOMER.CustomerNumber *= ORDERS.CustomerNumber 

    Как видно из этого примера, для создания внешнего объединения слева от 
знака равенства указывается символ звездочки (*). В СУБД SQL Server такое 
объединение называется левым внешним объединением (left outer join). Внешние 
объединения могут быть и правыми, но левые внешние объединения применяются 
гораздо чаще. Синтаксис системы SQL Server для создания правого внешнего 
объединения (right outer join)  ничем не отличается от синтаксиса левого 
внешнего объединения, за исключением того, что символ звездочки располагается 
после знака равенства. 

    SELECT CUSTOMER.CustomerNumber, ORDERS.Amount 
    FROM CUSTOMER, ORDERS 
    WHERE CUSTOMER.CustomerNumber =* ORDERS.CustomerNumber 

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

    Синтаксис объединений ANSI 

    Синтаксис создания объединений, определяемый стандартами ANSI, имеет 
следующий вид (рис. 5.11). 

    SELECT CUSTOMER.CustomerNumber, ORDERS.Amount 
    FROM CUSTOMER LEFT OUTER JOIN ORDERS 
    ON CUSTOMER.CustomerNumber=ORDERS.CustomerNumber 

    Согласно соглашениям ANSI, если нужно создать правое внешнее объединение 
или левое внутреннее объединение, необходимо заменить, соответственно, слово 
LEFT на RIGHT или слово OUTER на INNER. 

    Глава 5. SQL для простых смертных 83 

    СУБД InterBase не поддерживает соглашений Sybase о синтаксисе оператора 
SELECT для создания внешних объединений с помощью ключевого слова WHERE, а 
использует соглашения ANSI. Однако при создании внутренних объединений, 
синтаксис InterBase совпадает с синтаксисом Sybase.

    Вложенные запросы 

    Вложенные запросы (subquery) — это операторы SELECT, используемые в части 
запроса, определяемой ключевым словом WHERE. Такие запросы применяются для 
предварительного выбора информации, которая затем выбирается с помощью 
основного оператора SELECT. Рассмотрим следующий пример, результаты работы 
которого представлены на рис. 5.12. 

    SELECT * FROM CUSTOMER WHERE CustomerNumber IN (SELECT CustomerNumber FROM ORDERS) 


    Puc. 5.11. Объединение таблиц CUSTOMER и запрос, определяемый с помощью ключевого слова 
WHERE основного оператора SELECT

    Рис. 5.12. Вложенные ORDERS согласно стандарту ANSI SQL                  
 

    GROUP BY 

    Так как язык SQL является языком запросов, ориентированным на 
использование наборов данных, а не записей, в него встроены операторы, 
предназначенные для группирования данных. Операторы, часто применяемы вместе с 
функциями, являются одним из основных средств для создания запросов. 
Программистам систем dBASE такой подход может показаться несколько непонятным, 
так как они привыкли работать с данными, организованными в виде записей. В 
базах данных, разработанных для платформы PC, при поиске информации 
выполняется просмотр таблиц с последовательным перебором всех записей. Однако 
при использовании SQL такой метод работы становится ненужным. Один оператор 
SQL может выполнить задачи, для программирования которых в системах семейства 
Xbase может понадобиться 10 или даже 50 строк исходного текста. Такое 
могущество операторов SQL не в последнюю очередь определяется наличием 
ключевого слова GROUP BY, а также его применением вместе с функциями SQL. 
Рассмотрим следующий пример. 

    SELECT CUSTOMER.CustomerNumber, sum(ORDERS.Amount) TotalOrders 
    FROM CUSTOMER, ORDERS 
    WHERE CUSTOMER.CustomerNumber=ORDERS.CustomerNumber 
    GROUP BY CUSTOMER.CustomerNumber 

    Запрос возвращает перечень всех клиентов с указанием общей стоимости 
заказов каждого клиента (рис. 5.13). 

    Часть I. Основы     84                                                                         

    Как узнать какие поля нужно включить в список, определяемый с помощью 
GROUP BY? Основное правило состоит в том, чтобы включать в этот список все 
поля, указанные в списке полей оператора SELECT, которые не используются в 
качестве вычисляемых столбцов или абсолютных значений. Рассмотрим следующий 
пример оператора SELECT.            

    SELECT CUSTOMER.CustomerNumber, 
    CUSTOMER.LastName, CUSTOMER.State, 
    sum(ORDERS.Amount) TotalOrders 
    FROM CUSTOMER, ORDERS 
    WHERE 
    CUSTOMER.CustomerNumber=ORDERS.CustomerNumber 

    Для такого оператора список полей, определяемый ключевым словом GROUP BY, 
будет иметь следующий вид. 

    GROUP BY CUSTOMER.CustomerNumber, CUSTOMER.LastName, CUSTOMER.State 


    Puc. 5.13. Использование в запросе ключевого слова GROUP BY 

    Некоторые серверы, включая Oracle и InterBase, требуют соблюдения этого 
правила и не выполняют запросы, отступающие от него. Другие же, среди которых 
и SQL Server, пытаются выполнить запрос, хотя его результаты, скорее всего, 
будут ошибочными. Рассмотрим, например, следующий запрос. 

    SELECT CUSTOMER.LastName, COUNT(*) NumberWithName FROM CUSTOMER 

    На первый взгляд может показаться, что такой запрос возвращает список всех 
фамилий (last name) клиентов из таблицы CUSTOMER с информацией о том, сколько 
раз данная фамилия встречается в таблице. Однако при выполнении запроса 
окажется, что это не так. Как я уже сказал, некоторые серверы, такие как 
InterBase, просто не станут выполнять этот, казалось бы, вполне безобидный 
запрос, так как в нем нет ключевого слова GROUP BY. Другие же серверы, 
например SQL Server, попытаются выполнить запрос, но вернут в каждой строке 
результата общее количество записей в таблице. Это объясняется тем, что 
подсчет количества записей ведется по всем записям таблицы, а не по совпадению 
с указанным полем. Косвенным признаком, по которому можно судить о том, что 
запрос выполнился неправильно, является одно и то же значение, возвращаемое 
функцией вычисляемого столбца во всех строках результата. Другой признак: если 
набор данных очень большой, то такой "порочный" запрос может работать слишком 
долго. Например, в рассматриваемом запросе в качестве значения NumberWithName 
в каждой строке результата выводится общее количество строк таблицы. Для того 
чтобы избавится от ошибки, нужно внести в запрос следующие изменения. 

    SELECT CUSTOMER.LastName, COUNT(*) NumberWithName 
    FROM CUSTOMER 
    GROUP BY CUSTOMER.LastName 

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

    HAVING 

    Ключевое слово HAVING используется для отбора строк, возвращаемых в 
результате выполнения запроса, в котором применяется ключевое слово GROUP BY. 
Соотношение ключевых слов HAVING и GROUP BY примерно такое же, как соотношение 
ключевого слова WHERE с оператором SELECT. Отличие состоит лишь в том, что 
HAVING работает со строками, возвращаемыми запросом, a WHERE — со строками 
исходной таблицы, над которой выполняется запрос (рис. 5.15). 
    Как правило, запрос, в котором используется ключевое слово HAVING, можно 
переписать таким образом, чтобы обойтись без него. Дело в том, что запросы с 
HAVING в большинстве случаев менее эффективны по сравнению с запросами, в 
которых используется только ключевое слово WHERE. При использовании WHERE 
отбор идет до организации наборов данных в группы, а при использовании HAVING 
— после. Рассмотрим пример оператора SELECT, в котором имеется ключевое слово HAVING.

    Глава 5. SQL для простых смертных 85 


    SELECT CUSTOMER.LastName, COUNT(*) NumberWithName 
    FROM CUSTOMER 
    GROUP BY CUSTOMER.LastName 
    HAVING CUSTOMER.LastName<>'Citizen' 


    Рис. 5.14. Результаты работы исправленного запроса   

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


    Этот же запрос можно записать в другом виде, исключив применение HAVING. 

    SELECT CUSTOMER.LastName, COUNT(*) NumberWithName 
    FROM CUSTOMER 
    WHERE CUSTOMER.LastName<>'Citizen' 
    GROUP BY CUSTOMER.LastName 

    Некоторые серверы (например, Sybase SQL Server) сами исправляют запросы, в 
которых используется ключевое слово HAVING, тем самым оптимизируя запрос перед 
выполнением. Однако многие серверы не делают этого, поэтому к применению 
HAVING нужно подходить достаточно взвешенно, особенно при обработке очень 
больших таблиц. В тех СУБД, которые не оптимизируют запросы с HAVING, 
использование этого ключевого слова приводит к отключению ранее созданных 
индексов и, как следствие, к резкому снижению производительности. 

    ORDER RY 

    Ключевое слово ORDER BY предназначено для сортировки возвращаемого набора 
данных. Рассмотрим пример, результаты которого представлены на рис. 5.16. 

    SELECT LastName, State FROM CUSTOMER ORDER BY State 

    Вот еще один пример применения ORDER BY. 

    SELECT FirstName, LastName FROM CUSTOMER ORDER BY LastName 


    Puc. 5.16. Использование ключевого слова ORDER BY 


    86 Часть I. Основы 


    Псевдонимы столбцов 

    Вы уже, наверное, заметили, что для представления результата функций 
вычисляемых столбцов COUNT () и SUM (), я использовал имена логических 
псевдостолбцов. Такие имена называются псевдонимами столбцов (column aliase) и 
предназначены для того, чтобы сделать текст запроса более понятным. В ANSI SQL 
псевдонимы столбцов размещаются справа от соответствующих вычисляемых столбцов 
в списке полей оператора SELECT. Например, в операторах, которые мы 
рассматривали при обсуждении ключевого слова GROUP BY, в качестве псевдонима 
вычисляемого столбца с функцией COUNT () использовался идентификатор 
NumberWithName. Псевдонимы можно создавать не только для вычисляемых столбцов, 
но и для любых элементов выходного набора данных. Например, в приведенном ниже 
операторе создается псевдоним LName столбца LastName. 

    SELECT CUSTOMER.LastName LName, COUNT(*) NumberWithName 
    FROM CUSTOMER 
    GROUP BY CUSTOMER.LastName 

    Однако в других частях оператора запроса, например в списках, определяемых 
ключевыми словами WHERE или GROUP BY, псевдонимы не допускаются. Здесь всегда 
нужно указывать реальные имена столбцов. 
    В СУБД SQL Server применяется вариант такого синтаксиса, заключающийся в 
указании псевдонима слева от имени столбца, с применением между ними знака равенства. 

    SELECT CUSTOMER.LastName, NumberWithName=COUNT(*) 
    FROM CUSTOMER 
    GROUP BY CUSTOMER.LastName 

    Данный синтаксис не поддерживается системами Oracle и InterBase. 

    Псевдонимы таблиц 

    Для того чтобы не указывать каждый раз полное имя таблицы в разных частях 
команды SELECT, можно создать его сокращенный вариант. Такое укороченное имя 
называется псевдонимом таблицы (table alias) и создается с помощью ключевого 
слова FROM оператора SELECT. Рассмотрим пример создания такого псевдонима. 

    SELECT С.LastName, COUNT (*) NumberWithName FROM CUSTOMER С GROUP BY C.LastName 

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

    Подведем итог 

    Итак, раздел "Приступим к работе" завершен. Теперь вы знаете, как с 
помощью операторов SQL создавать базы данных и таблицы, как заполнять эти 
таблицы данными, а также как обращаться к таблицам баз данных с запросами SQL. 

    Еще несколько полезных команд 

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

    Глава 5. SQL для простых смертных 87 


    Команда CONNECT 

    В большинстве диалектов SQL команда CONNECT используется для изменения 
"контекста базы данных" или, проще говоря, — для подключения к той или иной 
базе данных. Эта команда поддерживается системами InterBase и Oracle. Обратная 
операция выполняется с помощью команды DISCONNECT. 
    В СУБД SQL Server для изменения контекста базы данных предназначена 
команда USE. В отличие от CONNECT, команда USE позволяет в определенный момент 
времени работать только с одной базой данных. В диалекте SQL Server нет 
функционального эквивалента команды DISCONNECT — для отключения от базы данных 
нужно повторить команду USE с именем другой базы данных. 

    Команда UPDATE 

    Время от времени вы будете изменять информацию, хранящуюся в таблицах 
вашей базы данных. Для этого нужно воспользоваться командой SQL UPDATE. По 
логике работы эта команда напоминает команду REPLACE ALL системы dBASE. 
Рассмотрим пример использования команды UPDATE. 

    UPDATE CUSTOMER SET Zip='90210' WHERE City='Beverly Hills' 

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

    UPDATE CUSTOMER SET State='CA' 

    Кроме того, при изменении значения в определенном поле, можно использовать 
исходное значение этого поля, например: 

    UPDATE ORDERS SET Amount=Amount+(Amount*.07) 

    В диалекте SQL Server, Transact-SQL, можно изменить значения поля одной 
таблицы, присвоив ему значения поля другой таблицы, например: 

    UPDATE ORDERS 
    SET Amount=Price 
    FROM ORDERS, ITEMS 
    WHERE ORDERS.ItemNumber=ITEMS.ItemNumber 


    Команда DELETE 

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

    DELETE FROM CUSTOMER 

    В некоторых системах для удаления всех записей таблицы имеются более 
быстрые команды, подобные команде ZAP системы dBASE, ориентированные на работу 
с таблицей в целом, а не с отдельными записями. В системе SQL Server, 
например, для этих целей применяется следующий оператор: 

    TRUNCATE TABLE CUSTOMER 

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

    DELETE FROM CUSTOMER WHERE LastNameo'Doe' 

    Команды COMMIT и ROLLBACK 

    Группа изменений, вносимых в базу данных, называется транзакцией 
(transaction). С помощью команды SQL COMMIT все изменения, сделанные в ходе 
одной транзакции, можно внести в базу данных. Другими словами, эта команда 
работает как команда сохранения базы данных — пока вы ее не выполнили, из-

    88 Часть I. Основы 

менения не будут записаны. Команда ROLLBACK, наоборот, удаляет все 
внесенные в ходе выполнения транзакции изменения, действуя подобно команде 
отмены. Действие обеих команд распространяется только на те транзакции, 
которые имели место после последней команды COMMIT — если вы уже приняли 
изменения, их нельзя отменить. 
    В некоторых системах, включая SQL Server, для того, чтобы можно было 
принимать и отменять транзакции, надо явно включить режим выполнения 
транзакций. Утилита WISQL системы InterBase запускает такой режим 
автоматически с помощью эквивалента команды InterBase SET TRANSACTION. При 
выходе из утилиты, пользователю предлагается принять все изменения, сделанные 
во время сеанса работы. Кроме того, принять или отменить внесенные изменения 
можно во время использования утилиты с помощью команд Commit Work и Rollback 
Work, соответственно, находящихся в подменю File утилиты. 

    Резюме 

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

    Что дальше... 

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

    Глава 5. SQL для простых смертных 89 
