SQL1

         

ИСПОЛЬЗОВАНИЕ ВЫРАЖЕНИЙ В ПОДЗАПРОСАХ


Вы можете использовать выражение, основанное на столбце, а не просто сам столбец, в предложении SELECT подзапроса. Это может быть выполнено или с помощью реляционных операторов, или с IN. Например, следующий запрос использует реляционный оператор = (вывод показан на Рисунке 10.6):

SELECT * FROM Customers WHERE cnum = (SELECT snum + 1000 FROM Salespeople WHERE sname = Serres);

Он находит всех заказчиков, чьё значение поля cnum, равное 1000, выше поля snum Serres. Мы предполагаем, что столбец sname не имеет никаких двойных значений (это может быть предписано или UNIQUE INDEX, обсуждаемым в Главе 17, или ограничением UNIQUE, обсуждаемым в Главе 18); иначе

=============== SQL Execution Log ============ | | | SELECT * | | FROM Customers | | WHERE cnum = | | (SELECT snum + 1000 | | WHERE Salespeople | | WHERE sname = 'Serres' | | ============================================= | | cnum cname city rating snum | | ----- -------- ---- ------ ----- | | 2002 Giovanni Rome 200 1003 | =============================================

Рисунок 10.6 Использование подзапроса с выражением

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



ИЗМЕНЕНИЕ ТАБЛИЦЫ, ПОСЛЕ ТОГО КАК ОНА БЫЛА СОЗДАНА


Команда ALTER TABLE не является частью стандарта ANSI, но это широко распространённая и довольно содержательная форма, хотя её возможности несколько ограничены. Она используется для того, чтобы изменить определение существующей таблицы. Обычно она добавляет столбцы к таблице. Иногда она может удалять столбцы или изменять их размеры, а также, в некоторых программах, добавлять или удалять ограничения (рассмотренные в Главе 18). Типичный синтаксис добавления столбца к таблице:

ALTER TABLE

ADD <column name> <data type> <size>;

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

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

ALTER TABLE не действует, когда таблица должна быть переопределена, но вы должны разрабатывать вашу БД по возможности так, чтобы не слишком ей в этом доверяться. Изменять структуры таблицы, когда она уже в использовании, опасно! Просмотрите внимательно таблицы, которые, являясь вторичными таблицами с извлеченными данными из другой таблицы (см. Главу 20), не долго будут работать правильно, а программы, использующие вложенный SQL (Глава 25), начнут работать неправильно или не всегда правильно. Кроме того, изменение может стереть всех пользователей, имеющих разрешение на обращение к таблице.

По этим причинам вы должны разрабатывать ваши таблицы так, чтобы использовать ALTER TABLE только в крайнем случае. Если ваша система не поддерживает ALTER TABLE, или если вы хотите избежать её использования, вы можете просто создать новую таблицу, с необходимыми изменениями при создании, и использовать команду INSERT с SELECT * запросом чтобы переписать в нее данные из старой таблицы. Пользователям, которым был предоставлен доступ к старой таблице (см. Главу 22), должен быть предоставлен доступ к новой таблице.



ИЗМЕНЕНИЕ ЗНАЧЕНИЙ ПОЛЯ


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



UPDATE Customers SET rating = 200;



ИЗВЛЕЧЕНИЕ ЗНАЧЕНИЙ ПЕРЕМЕННЫХ


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

Один из способов сделать это - с помощью разновидности команды SELECT, которая содержит предложение INTO. Давайте вернемся к нашему предыдущему примеру и переместим строку Peel из таблицы Продавцов в наши переменные главного языка.

EXEC SQL SELECT snum, sname, city, comm INTO :id_num, :salesperson, :loc, :comm FROM Salespeople WHERE snum = 1001;

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

По этой причине, SELECT INTO должна использоваться только при следующих условиях:

когда вы используете предикат, проверяющий значения, которые, как вы знаете, могут быть уникальным, как в этом примере. Значения, которые, как вы знаете, могут быть уникальными, это те значения, которые имеют принудительное ограничение уникальности или уникальный индекс, как это говорилось в Главах 17 и 18;

когда вы используете одну или более агрегатных функций и не используете GROUP BY;

когда вы используете SELECT DISTINCT во внешнем ключе с предикатом, ссылающимся на единственное значение родительского ключа (обеспечивая вашей системе предписание справочной целостности), как в следующем примере:

EXEC SQL SELECT DISTINCT snum INTO :salesnum FROM Customers WHERE snum = (SELECT snum FROM Salespeople WHERE sname = 'Motika');

Предполагалось что Salespeople.sname и Salespeople.snum это, соответственно, уникальный и первичный ключи этой таблицы, а Customers.snum - внешний ключ, ссылающийся на Salespeople.snum, и вы предполагали, что этот запрос произведёт единственную строку.

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

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



ЭКВИВАЛЕНТНЫЕ ТИПЫ ДАННЫХ В ДРУГИХ ЯЗЫКАХ


Когда используется вложение SQL в другие языки, значения, используемые и произведённые командами SQL, обычно сохраняются в переменных главного языка (см. Главу 25). Эти переменные должны иметь тип данных, совместимый со значениями SQL, которые они будут получать.

В дополнениях, которые не являются частью официального SQL-стандарта, ANSI обеспечивает поддержку при использовании вложения SQL в четыре языка: Паскаль, PL/I, КОБОЛ, и ФОРТРАН. Между прочим, он включает определение эквивалентов SQL для данных типов переменных, используемых в этих языках.

Эквиваленты типов данных четырёх языков, определенные ANSI:



ЭЛЕМЕНТЫ SQL


Этот раздел определяет элементы команд SQL.

Они разделены на две категории: Основные элементы языка и Функциональные элементы языка.

Основные элементы - это создаваемые блоки языка; когда SQL исследует команду, то он сначала оценивает каждый символ в тексте команды в терминах этих элементов. Разделитель <separator> отделяет одну часть команды от другой; всё, что находится между разделителями <separator>, обрабатывается как модуль. Основываясь на этом разделении, SQL интерпретирует команду.

Функциональные элементы - это разнообразные элементы, отличающиеся от ключевых слов, которые могут интерпретироваться как модули. Это части команды, отделяемые с помощью разделителей <separator>, имеющие специальное значение в SQL. Некоторые из них являются специальными для определенных команд и будут описаны вместе с этими командами позже в этом приложении.

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

Функциональные элементы могут определяться в терминах друг друга или даже в собственных терминах. Например, предикат <predicate>, наш последний и наиболее сложный случай, содержит предикат <predicate> внутри собственного определения. Это потому, что предикат <predicate>, использующий AND или OR, может содержать любое число предикатов <predicate>, которые могут работать автономно. Мы представляли вам предикат <predicate> в отдельной секции в этом приложении из-за разнообразия и сложности этого функционального элемента языка. Он будет постоянно присутствовать при обсуждении других функциональных частей команд.

БАЗОВЫЕ ЭЛЕМЕНТЫ ЯЗЫКА

ЭЛЕМЕНТ ОПРЕДЕЛЕНИЕ

<separator> <comment> | <space> | <newline>

<comment> --<string> <newline>

<space> пробел

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

<identifier> <letter>[{<letter or digit> | <underscore}... ] ИМЕЙТЕ В ВИДУ: Следуя строгому стандарту ANSI, символы должны быть набраны в верхнем регистре, а идентификатор <identifier> не должен быть длиннее 18-ти символов.

ЭЛЕМЕНТ ОПРЕДЕЛЕНИЕ <underscore> -

<percent sign> %

<delimiter> любое из следующих: , ( ) <> . : = + " - | <> >= <= или <string>

<string> [любой печатаемый текст в одинарных кавычках] Примечание: В <string>, две последовательных одинарных кавычки ( ' ' ) интерпретируются как одна ( ' ).

<SQL term> окончание/терминатор, зависящее от главного языка. (*только вложенный SQL*)



КАК ANY, ALL И EXIST ПОСТУПАЮТ С ОТСУТСТВУЮЩИМИ И НЕИЗВЕСТНЫМИ ДАННЫМИ?


Как было сказано, имеются некоторые различия между EXISTS и операторами, представленными в этой главе, в том, как они обрабатывают оператор NULL. ANY и ALL также отличаются друг от друга тем, как они реагируют, если подзапрос не произвел никаких значений, чтобы использовать их в сравнении. Эти различия могут привести к непредвиденным результатам в ваших запросах, если вы не будете их учитывать.



КАК ANY МОЖЕТ СТАТЬ НЕОДНОЗНАЧНЫМ?


Как говорилось выше, ANY не полностью однозначен. Если мы создаём запрос, чтобы выбрать заказчиков, которые имеют больший рейтинг, чем любой заказчик в Риме, мы можем получить вывод, который несколько отличается от того, что мы ожидали (как показано в Рисунке 13.5):

SELECT * FROM Customers WHERE rating > ANY (SELECT rating FROM Customers WHERE city = Rome);

В английском языке способ, которым мы обычно склонны интерпретировать оценку "больше, чем любой (где city = Rome) ", должен вам сообщить, что это значение оценки должно быть выше, чем значение оценки в каждом случае, где значение city = Rome. Однако это не так в случае с ANY, используемом в SQL. ANY оценивает как true, если подзапрос находит любое значение, которое делает условие верным.

=============== SQL Execution Log ============ | | | SELECT * | | FROM Customers | | WHERE rating > ANY | | (SELECT rating | | FROM Customers | | WHERE city = 'Rome'); | | ============================================= | | cnum cname city rating snum | | ----- -------- ---- ------ ------ | | 2002 Giovanni Rome 200 1003 | | 2003 Liu San Jose 200 1002 | | 2004 Grass Berlin 300 1002 | | 2008 Cisneros San Jose 300 1007 | =============================================

Рисунок 13.5 Как операция "больше"(>) интерпретируется ANY

Если мы оценим ANY способом, использующим грамматику английского языка, то только заказчики с оценкой 300 будут превышать Giovanni, который находится в Риме и имеет оценку 200. Однако подзапрос ANY также находит Pereira в Риме с оценкой 100. Так как все заказчики с оценкой 200 были выше этой, они будут выбраны, даже если имелся другой заказчик из Рима (Giovanni), чья оценка не была выше (фактически то, что один из выбранных заказчиков также находится в Риме, несущественно). Так как подзапрос произвел по крайней мере одно значение, которое сделает предикат верным в отношении этих строк, строки были выбраны. Чтобы дать другой пример, предположим, что мы должны были выбирать все заказы на сумму, которая была больше, чем по крайней мере один из заказов на 6-е октября:


SELECT * FROM Orders WHERE amt > ANY ( SELECT amt FROM Orders WHERE odate = 10/06/1990);

Вывод для этого запроса показан на Рисунке 13.6.

Даже если самая высокая сумма приобретений в таблице (9891.88) имелась на 6-е октября, предыдущая строка имеет более высокое значение суммы, чем другая строка на 6-е октября, которая имела значение суммы = 1309.95. Имея реляционную операцию ">=" вместо просто " > ", эта строка будет также выбрана, потому что она равна самой себе.

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

Этот запрос будет находить все заказы со значением суммы меньше, чем значение любой суммы для заказчика в San Jose (вывод показан на Рисунке 13.7):

SELECT * FROM Orders WHERE amt < ANY (SELECT amt FROM Orders A, Customers b WHERE a.cnum = b.cnum AND b.city = " San Jose');

Даже если наименьший заказ в таблице был для заказчика из San Jose, то был второй наибольший; следовательно, почти все строки будут выбраны. Простой запомните, что < ANY это значение, меньшее, чем наибольшее выбранное значение, а > ANY - значение, большее, чем наименьшее выбранное значение.

=============== SQL Execution Log ============== | | | SELECT * | | FROM Orders | | WHERE amt > ANY | | (SELECT amt | | FROM Orders | | WHERE odate = 10/06/1990); | | =============================================== | | onum amt odate cnum snum | | ----- -------- ---------- ----- ------ | | 3002 1900.10 10/03/1990 2007 1004 | | 3005 5160.45 10/03/1990 2003 1002 | | 3009 1713.23 10/04/1990 2002 1003 | | 3008 4723.00 10/05/1990 2006 1001 | | 3011 9891.88 10/06/1990 2006 1001 | ================================================

Рисунок 13.6 Выбрано значение, большее чем любое (ANY) на 6-е октября

=============== SQL Execution Log ============== | | | WHERE amt > ANY | | (SELECT amt | | FROM Orders a, Customers b | | WHERE a.cnum = b.cnum | | AND b.city = 'San Jose'); | | =============================================== | | onum amt odate cnum snum | | ----- -------- ---------- ----- ------ | | 3001 18.69 10/03/1990 2008 1007 | | 3003 767.10 10/03/1990 2001 1001 | | 3002 1900.10 10/03/1990 2007 1004 | | 3006 1098.10 10/03/1990 2008 1007 | | 3009 1713.23 10/04/1990 2002 1003 | | 3007 75.10 10/04/1990 2004 1002 | | 3008 4723.00 10/05/1990 2006 1001 | | 3010 1309.88 10/06/1990 2004 1002 | ================================================



Рисунок 13.7 Использование ANY с объединением

Фактически вышеуказанные команды весьма похожи на следующие (вывод показан на Рисунке 13.8):

SELECT * FROM Orders WHERE amt < (SELECT MAX amt FROM Orders A, Customers b WHERE a.cnum = b.cnum AND b.city = " San Jose');

=============== SQL Execution Log ============== | | | WHERE amt < | | (SELECT MAX (amt) | | FROM Orders a, Customers b | | WHERE a.cnum = b.cnum | | AND b.city = 'San Jose'); | | =============================================== | | onum amt odate cnum snum | | ----- -------- ---------- ----- ------ | | 3002 1900.10 10/03/1990 2007 1004 | | 3005 5160.45 10/03/1990 2003 1002 | | 3009 1713.23 10/04/1990 2002 1003 | | 3008 4723.00 10/05/1990 2006 1001 | | 3011 9891.88 10/06/1990 2006 1001 | ================================================

Рисунок 13.8 Использование агрегатной функции вместо ANY


КАК БАЗА ДАННЫХ РАСПРЕДЕЛЕНА МЕЖДУ ПОЛЬЗОВАТЕЛЯМИ?


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

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

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

В больших SQL-системах, БД будет разделена на области, так называемые Области Базы Данных или Разделы. Это области сохраняемой информации размещены так, чтобы информация внутри них находилась близко друг к другу для выполнения команд; то есть программа не должна искать где-то далеко информацию, сгруппированную в отдельной области базы данных. Хотя её физические возможности зависят от аппаратного оборудования, целесообразно чтобы команда работала в этих областях внутри самой SQL.

Системы, которые используют области БД (в дальнейшем называемых - DBS (Data Base Spaces)), которые дают возможность, с помощью команд SQL, обрабатывать эти области как объекты.

DBS создаются командами CREATE DBSPACE (СОЗДАТЬ DBS), ACQUIRE DBSPACE (ПОЛУЧИТЬ DBS) или CREATE TABLE SPACE (СОЗДАТЬ ТАБЛИЧНУЮ ОБЛАСТЬ), в зависимости от используемой реализации.

Одна DBS может вмещать любое число пользователей, и отдельный пользователь может иметь доступ к многим DBS.

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

Мы можем создать DBS с именем Sampletables следующей командой:


CREATE DBSPACE Sampletables ( pctindex 10, pctfree 25);

Параметр pctindex определяет, какой процент DBS должен быть оставлен, чтобы сохранять в нём индексы таблиц. Pctfree это процент DBS, который оставлен, чтобы позволить таблицам расширять размеры их строк (ALTER TABLE может добавлять столбцы или увеличивать размер столбцов, делая каждую строку длиннее. Это расширяет памяти, отводимой для этого).

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

DBS может иметь определенное ограничение размера, или ей может быть позволено расти неограниченно вместе с таблицами.

Если DBS создана, пользователям предоставляются права создавать в неё объекты.

Вы можете, например, предоставить Diane право создать таблицу Sampletables с помощью следующей команды:

GRANT RESOURCE ON Sampletables TO Diane;

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

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

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

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


КАК ДЕЛАЕТСЯ ВЛОЖЕНИЕ SQL?


Команды SQL помещаются в исходный текст главной программы; им предшествует фраза EXEC SQL (EXECute SQL). Далее устанавливаются некоторые команды, которые являются специальными для вложенной формы SQL и которые будут рассмотрены в этой главе.

Строго говоря, стандарт ANSI не поддерживает вложенный SQL как таковой. Он поддерживает понятие, называемое "модуль", которое, более точно, является вызываемым набором процедур SQL, а не вложением в другой язык. Официальное определение синтаксиса вложения SQL будет включать расширение официального синтаксиса каждого языка, в который может вкладываться SQL, что весьма долгая и неблагодарна работа, которой ANSI избегает. Однако ANSI обеспечивает четыре приложения (не являющиеся частью стандарта), которые определяют синтаксис вложения SQL для четырех языков: КОБОЛ, ПАСКАЛЬ, ФОРТРАН и ПЛ/1.

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

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

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

Согласно подходу к модульному языку, определённому ANSI, основная программа вызывает процедуры SQL. Процедуры выбирают параметры из главной программы и возвращают уже обработанные значения обратно в основную программу. Модуль может содержать любое число процедур, каждая из которых состоит из одиночной команды SQL. Идея в том, чтобы процедуры могли работать тем же самым способом, что и процедуры на языке, в который они были вложены (хотя модуль ещё должен идентифицировать базовый язык из-за различий в типах данных различных языков).

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

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



КАК ИСПОЛЬЗОВАТЬ АГРЕГАТНЫЕ ФУНКЦИИ?


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

Только числовые поля могут использоваться с SUM и AVG.

С функциями COUNT, MAX и MIN могут использоваться и числовые, и символьные поля.

При использовании с символьными полями, MAX и MIN будут транслировать их в эквивалент ASCII, который должен сообщать, что MIN будет означать первое, а MAX последнее значение в алфавитном порядке (алфавитное упорядочивание обсуждается более подробно в Главе 4).

Чтобы найти SUM всех наших покупок в таблице Заказов, мы можем ввести следующий запрос, с выводом на Рисунке 6.1:

SELECT SUM ((amt)) FROM Orders;

=============== SQL Execution Log ============ | | | SELECT SUM (amt) | | FROM Orders; | | ==============================================| | | | ------- | | 26658.4 | | | | | ===============================================

Рисунок 6.1 Определение суммы

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

Нахождение усреднённой суммы - похожая операция (вывод следующего запроса показан на Рисунке 6.2):

SELECT AVG (amt) FROM Orders;

=============== SQL Execution Log ============ | | | SELECT AVG (amt) | | FROM Orders; | | ==============================================| | | | ------- | | 2665.84 | | | | | ===============================================

Рисунок 6.2 Выбор средней суммы



КАК МОЖНО ПРЕДСТАВИТЬ ПОЛЯ В КАЧЕСТВЕ ВНЕШНИХ КЛЮЧЕЙ


Вы используете ограничение FOREIGN KEY в команде CREATE TABLE (или ALTER TABLE), содержащей поле, которое вы хотите объявить внешним ключом. Вы даёте имя родительскому, ключу на которое вы будете ссылаться внутри ограничения FOREIGN KEY. Помещение этого ограничения в команду - такое же, что и для других ограничений, обсуждённых в предыдущей главе.

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



КАК РАБОТАЕТ EXISTS?


EXISTS это оператор, который производит верное или неверное значение, другими словами, булево выражение (см. в Главе 4 обзор этого термина).

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

Например, мы можем решить, извлекать ли нам некоторые данные из таблицы Заказчиков, если, и только если, один или более заказчиков в этой таблице находятся в San Jose (вывод для этого запроса показан на Рисунке 12.1):

SELECT cnum, cname, city FROM Customers WHERE EXISTS (SELECT * FROM Customers WHERE city = " San Jose');

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

=============== SQL Execution Log ============ | | | SELECT snum, sname, city | | FROM Customers | | WHERE EXISTS | | (SELECT * | | FROM Customers | | WHERE city = 'San Jose'); | | ============================================= | | cnum cname city | | ----- -------- ---- | | 2001 Hoffman London | | 2002 Giovanni Rome | | 2003 Liu San Jose | | 2004 Grass Berlin | | 2006 Clemens London | | 2008 Cisneros San Jose | | 2007 Pereira Rome | =============================================

Рисунок 12.1 Использование оператора EXISTS



КАК РАБОТАЕТ ПОДЗАПРОС?


С помощью SQL вы можете вкладывать запросы друга в друга. Обычно внутренний запрос генерирует значение, которое проверяется в предикате внешнего запроса, определяющего, верно оно или нет. Например, предположим, что мы знаем имя продавца: Motika, но не знаем значение его поля snum и хотим извлечь все заказы из таблицы Заказов. Вот способ сделать это (вывод показан на Рис. 10.1 ):

SELECT * FROM Orders WHERE snum = (SELECT snum FROM Salespeople WHERE sname = 'Motika');

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

Единственной найденной строкой, естественно, будет snum = 1004. Однако SQL не просто выдает это значение, а помещает его в предикат основного запроса вместо самого подзапроса, так чтобы предикат прочитал, что

WHERE snum = 1004

=============== SQL Execution Log ============== | | | SELECT * | | FROM Orders | | WHERE snum = | | (SELECT snum | | FROM Salespeople | | WHERE sname = 'Motika'); | |=================================================| | onum amt odate cnum snum | | ----- ------- ---------- ----- ----- | | 3002 1900.10 10/03/1990 2007 1004 | | | =================================================

Рисунок 10.1 Использование подзапроса

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

Часто, как показано выше, выбранное поле и его значение будут иметь одинаковые имена (в данном случае snum), но это не обязательно. Конечно, если бы мы уже знали номер продавца Motika, мы могли бы просто напечатать WHERE snum = 1004 и работать далее с подзапросом в целом, но это было бы не так универсально. Этот же запрос будет продолжать работать, даже если номер Motika изменился, а с помощью простого изменения имени в подзапросе вы можете использовать его для чего угодно.



КАК РАБОТАЕТ СООТНЕСЕННЫЙ ПОДЗАПРОС?


В вышеупомянутом примере, "внутренний" (inner) и "внешний" (outer) это псевдонимы, подробно обсуждённые в Главе 9. Мы выбрали эти имена для большей ясности; они отсылают к значениям внутренних и внешних запросов, соответственно. Так как значение в поле cnum внешнего запроса меняется, внутренний запрос должен выполняться отдельно для каждой строки внешнего запроса. Строка внешнего запроса, для которого внутренний запрос каждый раз будет выполнен, называется текущей строкой-кандидатом.

=============== SQL Execution Log ============ | | | SELECT * | | FROM Customers outer | | WHERE 10/03/1990 IN | | (SELECT odate | | FROM Orders inner | | WHERE outer.cnum = inner.cnum); | | ============================================= | | cnum cname city rating snum | | ----- -------- ---- ------ ----- | | 2001 Hoffman London 100 1001 | | 2003 Liu San Jose 200 1002 | | 2008 Cisneros San Jose 300 1007 | | 2007 Pereira Rome 100 1004 | =============================================

Рисунок 11.1 Использование соотнесённого подзапроса

Следовательно, процедура оценки, выполняемой соотнесённым подзапросом:

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

Сохранить значения из этой строки-кандидата в псевдониме с именем в предложении FROM внешнего запроса.

Выполнить подзапрос. Везде, где псевдоним, заданный для внешнего запроса, найден (в данном случае "внешний"), использовать значение текущей строки-кандидата. Использование значения из строки-кандидата внешнего запроса в подзапросе называется внешней ссылкой.

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

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

В вышеупомянутом примере SQL осуществляет следующую процедуру:

Выбирает строку Hoffman из таблицы Заказчиков.

Сохраняет эту строку как текущую строку-кандидат под псевдонимом "внешний".


Затем он выполняет подзапрос. Подзапрос просматривает всю таблицу Заказов, чтобы найти строки, где значение поля cnum - такое же, как значение outer.cnum, которое в настоящее время равно 2001 - поле cnum строки Hoffman.


Затем он извлекает поле odate из каждой строки таблицы Заказов, для которой это верно, и формирует набор значений поля odate.

Получив набор всех значений поля odate для поля cnum = 2001, он проверяет предикат основного запроса, чтобы увидеть, имеется ли значение на 3 октября в этом наборе.


Если это так (а это так), то он выбирает строку Hoffman для вывода её из основного запроса.

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

Как видите, вычисления, которые SQL выполняет с помощью этих простых инструкций, довольно сложны. Конечно, вы могли бы решить ту же самую проблему, используя объединение следующего вида (вывод для этого запроса показан на Рисунке 11.2):

SELECT * FROM Customers first, Orders second WHERE first.cnum = second.cnum AND second.odate = 10/03/1990;

Обратите внимание, что Cisneros был выбран дважды, по одному разу для каждого заказа, который он имел для данной даты. Мы могли бы устранить это, используя SELECT DISTINCT вместо просто SELECT. Но это не обязательно в варианте подзапроса. Оператор IN, используемый в варианте подзапроса, не делает никакого различия между значениями, которые выбираются подзапросом один раз, и значениями, которые выбираются неоднократно. Следовательно, DISTINCT не обязателен.

=============== SQL Execution Log ============ | | | SELECT * | | FROM Customers first, Orders second | | WHERE first.cnum = second.cnum | | (SELECT COUNT (*) | | FROM Customers | | WHERE snum = main.snum; | | ============================================= | | cnum cname | | ----- -------- | | 1001 Peel | | 1002 Serres | =============================================

Рисунок 11.2 Использование объединения вместо соотнесенного подзапроса



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

SELECT snum, sname FROM Salespeople main WHERE 1 < ( SELECT COUNT (*) FROM Customers WHERE snum = main.snum );

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

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

=============== SQL Execution Log ============ | | | SELECT snum sname | | FROM Salespeople main | | WHERE 1 < | | AND second.odate = 10/03/1990; | | ============================================= | | cnum cname city rating snum | | ----- -------- ---- ------ ----- | | 2001 Hoffman London 100 1001 | | 2003 Liu San Jose 200 1002 | | 2008 Cisneros San Jose 300 1007 | | 2007 Pereira Rome 100 1004 | =============================================

Рисунок 11.3 Нахождение продавцов с несколькими заказчиками


КАК РАБОТАЕТ SQL?


SQL это язык, ориентированный специально на реляционные базы данных (РБД). Он выполняет большую работу, которую вы должны были бы делать, если бы использовали универсальный язык программирования, например C. Чтобы сформировать РБД на C, вам необходимо было бы начать с нуля. Вы должны были бы определить объект, называемый таблицей, которая могла бы увеличиваться, чтобы иметь любое число строк, а затем создавать постепенно процедуры для вставки и извлечения значений.

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

Рассмотреть строку таблицы.Выполнить проверку: является ли эта строка одной из строк, которая вам нужна.

Если это так, сохранить её где-нибудь, пока вся таблица не будет проверена.Проверить, имеются ли другие строки в таблице.

Если имеются, возвратиться на шаг 1.Если строк больше нет, вывести все значения, сохранённые в шаге 3.

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



КАК СФОРМИРОВАТЬ СООТНЕСЁННЫЙ ПОДЗАПРОС?


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

Например, вот способ найти всех заказчиков в Заказах на 3-е октября (вывод показан на Рисунке 11.1):

SELECT * FROM Customers outer WHERE 10/03/1990 IN (SELECT odate FROM Orders inner WHERE outer.cnum = inner.cnum);



КАК СПРАВОЧНАЯ ЦЕЛОСТНОСТЬ ОГРАНИЧИВАЕТ ЗНАЧЕНИЯ РОДИТЕЛЬСКОГО КЛЮЧА?


Поддержание справочной целостности требует некоторых ограничений на значения, которые могут быть представлены в полях, объявленных как внешний ключ и родительский ключ. Родительский ключ должен быть структурирован, чтобы гарантировать, что каждое значение внешнего ключа будет соответствовать одной указанной строке. Это означает, что он (ключ) должен быть уникальным и не должен содержать никаких пустых значений (NULL). Этого недостаточно для родительского ключа в случае выполнения такого требования, как при объявлении внешнего ключа. SQL должен быть уверен, что двойные значения или пустые значения (NULL) не были введены в родительский ключ. Следовательно, вы должны убедиться, что все поля, которые используются как родительские ключи, имеют или ограничение PRIMARY KEY, или ограничение UNIQUE, наподобие ограничения NOT NULL.



КАК SQL ОБЩАЕТСЯ СРАЗУ С НЕСКОЛЬКИМИ ПОЛЬЗОВАТЕЛЯМИ?


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

UPDATE Salespeople SET comm = comm * 2 WHERE sname LIKE 'R%';

и в это же время Diane вводит такой запрос:

SELECT city, AVG (comm) FROM Salespeople GROUP BY city;

Может ли усреднённое значение (AVG) Diane отразить изменения, которые вы делаете в таблице? Не важно, будет это сделано или нет, а важно, чтобы были отражены все или ни одно из значений комиссионных (comm), для которых выполнялись изменения. Любой промежуточный результат является случайным или непредсказуемым для заказа, в котором значения были изменены физически. Вывод запроса не должен быть случайным и непредсказуемым.

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

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

Вот примеры:

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



КАК ВЫПОЛНИТЬ ОБЪЕДИНЕНИЕ ТАБЛИЦЫ С СОБОЙ?


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

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



КАКОЕ ПРЕДСТАВЛЕНИЕ ЯВЛЯЕТСЯ МОДИФИЦИРУЕМЫМИ?


Вот некоторые примеры модифицируемых представлений и представлений только_чтение.

CREATE VIEW Dateorders (odate, ocount) AS SELECT odate, COUNT (*) FROM Orders GROUP BY odate;

Это представление только_чтение из-за присутствия в нём агрегатной функции и GROUP BY.

CREATE VIEW Londoncust AS SELECT * FROM Customers WHERE city = 'London';

А это - модифицируемое представление.

CREATE VIEW SJsales (name, number, percentage) AS SELECT sname, snum, comm * 100 FROM Salespeople WHERE city = 'SanJose';

Это - представление только_чтение из-за выражения "comm * 100". При этом, однако, возможны переупорядочивание и переименование полей. Некоторые программы будут позволять удаление в этом представлении или в заказах столбцов snum и sname.

CREATE VIEW Salesonthird AS SELECT * FROM Salespeople WHERE snum IN (SELECT snum FROM Orders WHERE odate = 10/03/1990);

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

CREATE VIEW Someorders AS SELECT snum, onum, cnum FROM Orders WHERE odate IN (10/03/1990,10/05/1990);

Это - модифицируемое представление.



КАТАЛОГ СИСТЕМЫ


Чтобы функционировать как БД SQL, ваша компьютерная система должна следить за многими вещами: таблицами, представлениями, индексами, синонимами, привилегиями, пользователями и т.д. Имеются различные способы делать это, но ясно, что наиболее логичный, эффективный и согласованный способ делать это в реляционной среде состоит в том, чтобы сохранять эту информацию в таблицах. Это даёт возможность компьютеру размещать информацию и управлять ею, используя те же самые процедуры, которые он использует чтобы размещать и управлять данными, которые он хранит для вас.

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

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

Таблицы каталога создаются и заполняются с помощью самой БД и идентифицируются с помощью специальных имён, таких, например, как SYSTEM.

База данных создает эти таблицы и модифицирует их автоматически; таблицы каталога не могут быть непосредственно подвергнуты действию команды модификации. Если это случится, это значительно запутает всю систему и сделает её неработоспособной. Однако в большинстве систем каталог может быть запрошен пользователем. Это очень полезно, потому что это даёт возможность узнать кое-что о БД, которую вы используете. Конечно, не вся информация всегда доступна всем пользователям. Подобно другим таблицам, доступ к каталогу ограничен для пользователей без соответствующих привилегий. Так как каталог принадлежит самой системе, имеется некоторая неясность относительно того, кто имеет привилегии и кто может предоставить привилегии в этом каталоге. Обычно привилегии каталога предоставляет суперпользователь, например, администратор системы, зарегистрированный как SYSTEM или DBA. Кроме того, некоторые привилегии могут предоставляться пользователям автоматически.



КОБОЛ


SQL ТИПЭКВИВАЛЕНТ КОБОЛА
CHAR ()PIC X ()

INTEGERPIC S () USAGE COMPUTTATIONAL NUMERICPIC S (< nines with embedded V >) DISPLAY SING LEADING SEPERATE

КОГДА ПОДЗАПРОС ВОЗВРАЩАЕТСЯ ПУСТЫМ?


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

SELECT * FROM Customers WHERE rating > ANY (SELECT rating FROM Customers WHERE city = Boston);

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

SELECT FROM Customers WHERE rating > ALL (SELECT rating FROM Customers WHERE city = 'Boston');

выведет всю таблицу Заказчиков. Когда нет никаких заказчиков в Boston, естественно, ни одно из этих сравнений не имеет значения.



КОГДА СДЕЛАННЫЕ ИЗМЕНЕНИЯ СТАНОВЯТСЯ ПОСТОЯННЫМИ ?


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

Команда SQL, которая воздействует на содержание или структуру БД - например, команда модификации DML или команда DROP TABLE, - не обязательно будет необратимой. Вы можете определить после окончания её действия, останутся ли изменения, сделанные данной командой или группой команд, постоянными в базе данных, или они будут полностью проигнорированы. С этой целью команды обрабатываются группами, называемыми транзакциями. Транзакция начинается всякий раз, когда вы начинаете сеанс с SQL. Все команды, которые вы введёте, будут частью этой транзакции, пока вы не завершите их вводом команды COMMIT WORK или команды ROLLBACK WORK. COMMIT может сделать все изменения постоянными с помощью транзакции, а ROLLBACK может откатить их обратно или отменить. Новая транзакция начинается после каждой команды COMMIT или ROLLBACK. Этот процесс известен как диалоговая обработка запросов или транзакция.

Вот синтаксис, чтобы оставить все ваши изменения постоянными во время регистрации или во время последнего COMMIT или ROLLBACK:

COMMIT WORK;

Синтаксис отмены изменения:

ROLLBACK WORK;

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

SET AUTOCOMMIT ON;

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


SET AUTOCOMMIT OFF;

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

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

Хорошее правило, которому надо следовать: делать ваши транзакции состоящими из одной команды или нескольких тесно связанных команд.

Например, предположим, вы хотите удалить продавца Motika из базы данных. Прежде чем вы удалите его из таблицы Продавцов, вы сначала должны сделать что-нибудь с его заказами и его заказчиками. (Если используются ограничения внешнего ключа, и ваша система, следуя ANSI, ограничивает изменение родительского ключа, у вас не будет выбора: делать или не делать этого. Это будет сделано обязательно.) Одно из логических решений будет состоять в том, чтобы установить поле snum в его заказах в NULL, вследствие чего ни один продавец не получит комиссионные в этих заказах, пока комиссионные не будут предоставлены заказчикам для Peel. Затем вы можете удалить их из таблицы Продавцов:

UPDATE Orders SET snum = NULL WHERE snum = 1004;

UPDATE Cudomers SET snum = 1001 WHERE snum = 1004;

DELETE FROM Salespeople WHERE snum = 1004;

Если у вас проблема с удалением Motika (возможно, имеется другой внешний ключ, ссылающийся на него, о котором вы не знали и который не учитывали), вы могли бы отменить все изменения, которые вы сделали, до тех пор пока проблема не будет определена и решена. Более того, это должна быть группа команд, чтобы обрабатывать её как одиночную транзакцию. Вы можете предусмотреть это с помощью команды COMMIT и завершить её с помощью команды COMMIT или ROLLBACK.


КОГДА ВЫ МОЖЕТЕ ДЕЛАТЬ ОБЪЕДИНЕНИЕ ЗАПРОСОВ?


Когда два (или более) запроса подвергаются объединению, их столбцы вывода должны быть совместимы для объединения. Это означает, что каждый запрос должен указывать одинаковое количество столбцов и в том же порядке и каждый должен иметь тип, совместимый с каждым. Значение совместимости типов меняется. ANSI следит за этим очень строго, и поэтому числовые поля должны иметь одинаковый числовой тип и размер, хотя некоторые имена, используемые ANSI для этих типов, являются синонимами. (Смотрите в Приложении B подробности о числовых типах ANSI.) Кроме того, символьные поля должны иметь одинаковое количество символов (значение предназначенного числа - не обязательно такое же, как используемое число).

Хорошо, что некоторые SQL-программы обладают большей гибкостью, чем это определяется ANSI. Типы, не определённые ANSI, такие как DATA и BINARY, обычно должны совпадать с другими столбцами такого же нестандартного типа.

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

Другое ограничение на совместимость: пустые значения (NULL) запрещены в любом столбце объединения, причем эти значения необходимо запретить и для всех соответствующих столбцов в других запросах объединения. Пустые значения (NULL) запрещены ограничением NOT NULL, которое будет обсуждаться в

Главе 18. Кроме того, вы не можете использовать UNION в подзапросах, а также не можете использовать агрегатные функции в предложении SELECT-запроса в объединении. (Большинство программ пренебрегают этими ограничениями.)



КОМАНДА CREATE VIEW


Вы создаёте представление командой CREATE VIEW. Она состоит из слов CREATE VIEW (СОЗДАТЬ ПРЕДСТАВЛЕНИЕ), имени представления, которое нужно создать, сл&#x0301;ова AS (КАК) и запроса, как в следующем примере:

CREATE VIEW Londonstaff AS SELECT * FROM Salespeople WHERE city = 'London';

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

Давайте сделаем запрос представления (вывод показан на Рисунке 20.1):

SELECT * FROM Londonstaff;

=============== SQL Execution Log ============ | | | SELECT * | | FROM Londonstaff; | | | | ==============================================| | snum sname city comm | | ------ ---------- ----------- ------- | | 1001 Peel London 0.1200 | | 1004 Motika London 0.1100 | | | ===============================================

Рисунок 20.1 Представление Londonstaff

Когда вы приказываете SQL выбрать (SELECT) все строки (*) из представления, он выполняет запрос, содержащий в определении Loncfonstaff, и возвращает всё из его вывода. Имея предикат в запросе представления, можно вывести только те строки представления, которые будут удовлетворять этому предикату. Вы можете вспомнить, что в Главе 15 вы имели таблицу Londonstaff, в которую вы вставляли это же самое содержимое (конечно, мы понимаем, что таблица не слишком велика. Если это так, вы должны будете выбрать другое имя для вашего представления).

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

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

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


CREATE VIEW Salesown AS SELECT snum, sname, city FROM Salespeople:

=============== SQL Execution Log ============ | | | SELECT * | | FROM Salesown; | | | | ==============================================| | snum sname city | | ------ ---------- ----------- | | 1001 Peel London | | 1002 Serres San Jose | | 1004 Motika London | | 1007 Rifkin Barcelona | | 1003 Axelrod New York | ===============================================

Рисунок 20.2 Представление Salesown

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


КОМАНДА FORMAT


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

Среди возможных функций команды FORMAT существуют такие:

определение ширины столбцов (при печати)

определение представления NULL-значений

обеспечение (новых) заголовков для столбцов

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

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

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

(Альтернативным подходом к этой проблеме в некоторых программах является предложение COMPUTE.)

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

Вот некоторые примеры команды FORMAT:

FORMAT NULL '_ _ _ _ _ _ _'; FORMAT BTITLE 'Orders Grouped by Salesperson'; FORMAT EXCLUDE (2, 3);

Первая команда NULL представляется в виде ' _ _ _ _ _ _ _ ' при выводе на печать; вторая вставляет заголовок 'Orders Grouped by Salesperson' в нижнюю часть каждой страницы; третья исключает второй и третий столбцы из вывода предыдущего запроса. Вы могли бы использовать последнюю, если выбираете конкретные столбцы, чтобы использовать их в предложении ORDER BY в вашем выводе. Так как указанные функции команды FORMAT могут выполняться по разному, все варианты их использования не могут быть здесь показаны.

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

COLUMN odate FORMAT dd-mon-yy;

что форсирует формат типа 10-Oct-90 в поле даты, использующемся в выводе запроса на печать. Предложение COMPUTE, упомянутое ранее, вставляется в запрос, следующим образом:

SELECT odate, amt FROM Orders WHERE snum = 1001 COMPUTE SUM (amt);

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

BREAK ON odate;

вывода вышеупомянутого запроса на страницы, сгруппировав по датам, поэтому все значения odate в каждой группе - одинаковые. Затем вы можете ввести следующее предложение:

COMPUTE SUM OF amt ON odate;

Столбец в предложении ON предварительно должен быть использован в команде BREAK.



КОМАНДА GRANT


Предположим, что пользователь Diane имеет таблицу Заказчиков и хочет разрешить пользователю Adrian выполнить запрос к ней. Diane должна в этом случае ввести следующую команду:

GRANT INSERT ON Salespeople TO Diane;

Теперь Adrian может выполнить запросы к таблице Заказчиков. Без этой привилегии он может только выбирать значения, но не может выполнить любое действие, которые влияло бы на значения в таблице Заказчиков (включая использование таблицы Заказчиков в качестве родительской таблицы внешнего ключа, что ограничивает изменения, которые можно выполнять со значением в таблице Заказчиков).

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

Adrian самостоятельно не может выдать эту команду. Он также не может предоставить право SELECT другому пользователю: таблица еще принадлежит Diane (далее мы покажем, как Diane может дать право Adrian предоставлять SELECT другим пользователям).

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

GRANT INSERT ON Salespeople TO Diane;

Теперь Diane имеет право помещать нового продавца в таблицу.



КОМАНДА SELECT


В самой простой форме команда SELECT просто инструктирует БД, чтобы извлечь информацию из таблицы. Например, вы могли бы вывести таблицу Продавцов, напечатав следующее:

SELECT snum, sname, city, comm FROM Salespeople;

Вывод для этого запроса показан на Рисунке 3.1.

=============== SQL Execution Log ============ | | | SELECT snum, sname, city, comm | | FROM Salespeople; | | | | ==============================================| | snum sname city comm | | ------ ---------- ----------- ------- | | 1001 Peel London 0.12 | | 1002 Serres San Jose 0.13 | | 1004 Motika London 0.11 | | 1007 Rifkin Barcelona 0.15 | | 1003 Axelrod New York 0.10 | ===============================================

Рисунок 3.1 Команда SELECT

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

Вот объяснение каждой части этой команды:

SELECT

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

snum, sname

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

FROM Salespeople

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

;

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

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

Использование возврата каретки (клавиша ENTER) является произвольным. Мы должны точно установить, как удобнее составить запрос - в несколько строк или в одну строку - следующим образом:

SELECT snum, sname, city, comm FROM Salespeople;

С тех пор как SQL использует точку с запятой, чтобы указывать конец команды, большинство программ SQL обрабатывают возврат каретки (через нажатие Возврат или клавиши ENTER ) как пробел. Хорошая идея - использовать возвраты каретки и выравнивание, как мы делали ранее, чтобы сделать ваши команды более лёгкими для чтения и более понятными.



КОМАНДА СОЗДАНИЯ ТАБЛИЦЫ


Таблицы создаются командой CREATE TABLE. Эта команда создает пустую таблицу - таблицу без строк. Значения вводятся с помощью DML-команды INSERT (См. Главу 15). Команда CREATE TABLE определяет имя таблицы и описание набора имён столбцов, указанных в определенном порядке. Она также определяет типы данных и размеры столбцов. Каждая таблица должна иметь по крайней мере один столбец.

Синтаксис команды CREATE TABLE:

CREATE TABLE table-name (<column name> <data type>[(size)], <column name> <data type> [(size)] ...);

Как сказано в Главе 2, типы данных значительно меняются от программы к программе. Для совместимости со стандартом, все они должны по крайней мере поддерживать стандарт типа ANSI. Он описан в Приложении B.

Так как пробелы используются для разделения частей команды SQL, они не могут быть частью имени таблицы (или любого другого объекта, такого как индекс).

Знак подчеркивания ( _ ) обычно используется для разделения слов в именах таблиц.

Значение аргумента размера зависит от типа данных. Если вы его не указываете, ваша система сама будет назначать значение автоматически. Для числовых значений, это - лучший выход, потому что в этом случае все ваши поля такого типа получат один и тот же размер, что освобождает вас от проблем их совместимости (см. Главу 14). Кроме того, использование аргумента размера с некоторыми числовым наборами - не совсем простой вопрос. Если вам нужно хранить большие числа, вам, несомненно, понадобятся гарантии, что поля достаточно велики, чтобы вместить их.

Тип данных, для которого вы, в основном, должны назначать размер, это CHAR.

Аргумент размера это целое число, определяющее максимальное число символов, которые может вместить поле. Фактически число символов поля может быть от нуля (если поле - NULL) до этого числа. По умолчанию аргумент размера = 1, что означает, что поле может содержать только одну букву. Это, конечно, не совсем то, что вам нужно.

Таблицы принадлежат пользователю, который их создал, и имена всех таблиц, принадлежащих данному пользователю, должны отличаться друга от друга, как и имена всех столбцов внутри данной таблицы. Отдельные таблицы могут использовать одинаковые имена столбцов, даже если они принадлежат одному и тому же пользователю. Пример этого - столбец city в таблице Заказчиков и в таблице Продавцов. Пользователи, не являющиеся владельцами таблиц, могут обращаться к этим таблицам с помощью имени владельца этих таблиц, сопровождаемого точкой; например, таблица Employees, созданная Smith, будет называться Smith.Employees, когда она упоминается каким-то другим пользователем (мы понимаем, что Smith это идентификатор (ID). ID, сообщаемый пользователем (ваш ID - это ваше имя в SQL. Этот вывод обсуждался в Главе 2 и будет продолжен в Главе 22).

Эта команда создаст таблицу Продавцов:

CREATE TABLE Saleepeople (snum integer, sname char (10), city char (10), comm declmal);

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



КОМАНДА UPDATE ДЛЯ НЕСКОЛЬКИХ СТОЛБЦОВ


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

UPDATE Salespeople SET sname = 'Gibson', city = 'Boston', comm = .10 WHERE snum = 1004;

Эта команда передаст новому продавцу Gibson, всех текущих заказчиков бывшего продавца Motika и заказы в том виде, в котором они были скомпонованы для Motika, с помощью поля snum. Вы не можете, однако, модифицировать сразу несколько таблиц в одной команде, отчасти потому, что вы не можете использовать префиксы таблиц со столбцами, изменёнными предложением SET. Другими словами, вы не можете сказать "SET Salespeople.sname = Gibson" в команде UPDATE, вы можете сказать только так: "SET sname = Gibson".



КОМАНДЫ МОДИФИКАЦИИ ЯЗЫКА DML


Значения могут быть помещены и удалены из полей тремя командами языка DML (Язык Манипулирования Данными):

INSERT (ВСТАВИТЬ), UPDATE (МОДИФИЦИРОВАТЬ), DELETE (УДАЛИТЬ).

Не удивляйтесь, все они упоминались ранее в SQL как команды модификации.



КОМБИНАЦИЯ ИЗ EXISTS И ОБЪЕДИНЕНИЯ


Однако для нас может быть полезнее вывести больше информации об этих продавцах, а не только их номера. Мы можем сделать это, объединив таблицу Заказчиков с таблицей Продавцов (вывод для запроса показан на Рисунке 12.3):

SELECT DISTINCT first.snum, sname, first.city FROM Salespeople first, Customers second WHERE EXISTS (SELECT * FROM Customers third WHERE second.snum = third.snum AND second.cnum < > third.cnum) AND first.snum = second.snum;

=============== SQL Execution Log ============ | | | SELECT DISTINCT first.snum, sname, first.city | | FROM Salespeople first, Customers second | | WHERE EXISTS | | (SELECT * | | FROM Customers third | | WHERE second.snum = third.snum | | AND second.cnum < > third.cnum) | | AND first.snum = second.snum; | | ============================================= | | cnum cname city | | ----- -------- ---- | | 1001 Peel London | | 1002 Serres San Jose | =============================================

Рисунок 12.3 Комбинация EXISTS с объединением

Внутренний запрос здесь, как и в предыдущем варианте, фактически сообщает, что псевдоним был изменён. Внешний запрос это объединение таблицы Продавцов с таблицей Заказчиков, наподобие того, что мы видели прежде. Новое предложение основного предиката (AND first.snum = second.snum), естественно, оценивается на том же самом уровне, что и предложение EXISTS. Это функциональный предикат самого объединения, сравнивающий две таблицы из внешнего запроса в терминах поля snum, которое является для них общим. Из-за булева оператора AND, оба условия основного предиката должны быть верны в заказе для верного предиката. Следовательно, результаты подзапроса имеют смысл только в тех случаях, когда вторая часть запроса верна, а объединение - выполнимо. Таким образом, комбинация объединения и подзапроса может стать очень мощным способом обработки данных.



КОМБИНИРОВАНИЕ ПРЕДИКАТОВ ПРЕДСТАВЛЕНИЙ И ОСНОВНЫХ ЗАПРОСОВ В ПРЕДСТАВЛЕНИЯХ


Когда вы делаете запрос представления, вы, собственно, выполняете запрос. Основной способ для SQL обойти это - объединить предикаты двух запросов в один. Давайте посмотрим ещё раз на наше представление Londonstaff:

CREATE VIEW Londonstaff AS SELECT * FROM Salespeople WHERE city = 'London';

Если мы выполняем следующий запрос в этом представлении

SELECT * FROM Londonstaff WHERE comm > .12;

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

SELECT * FROM Salespeople WHERE city = 'London' AND comm > .12;

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

CREATE VIEW Ratingcount (rating, number) AS SELECT rating, COUNT (*) FROM Customers GROUP BY rating;

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

SELECT * FROM Ratingcount WHERE number = 3;

Посмотрим, что случится если мы скомбинируем два предиката:

SELECT rating, COUNT (*) FROM Customers WHERE COUNT (*) = 3 GROUP BY rating;

Это недопустимый запрос. Агрегатные функции, такие как COUNT (СЧЕТ), не могут использоваться в предикате. Правильным способом при формировании вышеупомянутого запроса, конечно же, будет следующий:

SELECT rating, COUNT (*) FROM Customers GROUP BY rating; HAVING COUNT (*) = 3;

Но SQL может не выполнить превращения. Может ли равноценный запрос вместо запроса Ratingcount потерпеть неудачу? Да может! Это неоднозначная область SQL, где методика использования представлений может дать хорошие результаты. Самое лучшее, что можно сделать в случае, когда об этом ничего не сказано в вашей системной документации, это попытаться разобраться.

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



КОММЕНТАРИЙ В СОДЕРЖАНИИ КАТАЛОГА


Большинство версий SQL, позволяют помещать комментарии (ремарки) в специальные столбцы пояснений таблиц каталогов SYSTEMCATALOG и SYSTEMCOLUMNS, что удобно, так как эти таблицы не всегда могут объяснить свое содержание. Для простоты мы пока исключали этот столбец из наших иллюстраций. Можно использовать команду COMMENT ON со строкой текста, чтобы пояснить любую строку в одной из этих таблиц. Состояние TABLE - для комментирования в SYSTEMCATALOG, а текст COLUMN - для SYSTEMCOLUMNS. Например:

COMMENT ON TABLE Chris.Orders

IS 'Current Customer Orders';

Текст будет помещен в столбец пояснений SYSTEMCATALOG. Обычно максимальная длина таких пояснений - 254 символа.

Сам комментарий указывается для конкретной строки, один с tname=Orders, а другой owner=Chris. Мы увидим этот комментарий в строке таблицы Заказов в SYSTEMCATALOG:

SELECT tname, remarks FROM SYSTEMCATALOG WHERE tname = 'Orders' AND owner = 'Chris';

Вывод для этого запроса показан на Рисунке 24.2.

SYSTEMCOLUMNS работает точно так же. Сначала мы создаём комментарий

COMMENT ON COLUMN Orders.onum IS 'Order #';

затем выбираем эту строку из SYSTEMCOLUMNS:

SELECT cnumber, datatype, cname, remarks FROM SYSTEMCOLUMNS WHERE tname = 'Orders' AND tabowner = 'Chris' AND cname = onum;

Вывод для этого запроса показан на Рисунке 24.3.

Чтобы изменить комментарий, вы можете просто ввести новую команду COMMENT ON для той же строки. Новый комментарий будет записан поверх старого. Если вы хотите удалить комментарий, напишите поверх него пустой комментарий:

COMMENT ON COLUMN Orders.onum IS '';

и этот пустой комментарий затрёт предыдущий.

=============== SQL Execution Log =============== | | | SELECT tname, remarks | | FROM SYSTEMCATALOG | | WHERE tname = 'Orders' | | AND owner = 'Chris' | | ; | | ================================================ | | tname remarks | | ------------- ----------------------- | | Orders Current Customers Orders | | | ==================================================

Рисунок 24.2 Комментарий в SYSTEMCATALOG

=============== SQL Execution Log =============== | | | SELECT cnumber, datatype, cname, remarks | | FROM SYSTEMCOLUMNS | | WHERE tname = 'Orders' | | AND tabowner = 'Chris' | | AND cname = 'onum' | | ; | | ================================================ | | cnumber datatype cname remarks | | ---------- --------- ------ ------------ | | 1 integer onum Orders # | | | ==================================================

Рисунок 24.3 Комментарий в SYSTEMCOLUMNS



КОМПЛЕКСНЫЕ ОБЪЕДИНЕНИЯ


Вы можете использовать любое количество псевдонимов для одной таблицы в запросе, хотя использование более двух в данном предложении SELECT * будет излишним. Предположим, что вы ещё не назначили ваших заказчиков вашему продавцу. Компания должна назначить каждому продавцу первоначально трёх заказчиков, по одному для каждого рейтингового значения. Вы лично можете решить, какого заказчика какому продавцу назначить, но следующий запрос вы используете, чтобы увидеть все возможные комбинации заказчиков, которые вы можете назначать (вывод показан на Рисунке 9.3):

SELECT a.cnum, b.cnum, c.cnum FROM Customers a, Customers b, Customers c WHERE a.rating = 100 AND b.rating = 200 AND c.rating = 300;

=============== SQL Execution Log ============== | | | AND c.rating = 300; | | =============================================== | | cnum cnum cnum | | ----- ------ ------ | | 2001 2002 2004 | | 2001 2002 2008 | | 2001 2003 2004 | | 2001 2003 2008 | | 2006 2002 2004 | | 2006 2002 2008 | | 2006 2003 2004 | | 2006 2003 2008 | | 2007 2002 2004 | | 2007 2002 2008 | | 2007 2003 2004 | | 2007 2003 2008 | =================================================

Рисунок 9.3 Комбинация пользователей с различными значениями рейтинга

Как видите, этот запрос находит все комбинации заказчиков с тремя значениями оценки, поэтому первый столбец состоит из заказчиков с оценкой 100, второй - с 200, и последний - с оценкой 300. Они повторяются во всех возможных комбинациях. Это сортировка с группировкой, которая не может быть выполнена с GROUP BY или ORDER BY, поскольку они сравнивают значения только в одном столбце вывода.

Вы должны также понимать, что не всегда обязательно использовать в предложении SELECT каждый псевдоним или таблицу, которые упомянуты в предложении FROM запроса. Иногда предложение или таблица становятся запрашиваемыми исключительно потому, что они могут вызываться в предикате запроса. Например, следующий запрос находит всех заказчиков, размещённых в городах, где продавец Serres (snum 1002) имеет заказчиков (вывод показан на Рисунке 9.4):


SELECT b.cnum, b. cname FROM Customers a, Customers b WHERE a.snum = 1002 AND b.city = a.city;

=============== SQL Execution Log ============ | | | SELECT b.cnum, b.cname | | FROM Customers a, Customers b | | WHERE a.snum = 1002 | | AND b.city = a.city; | | ==============================================| | cnum cname | | ------ --------- | | 2003 Liu | | 2008 Cisneros | | 2004 Grass | =============================================

Рисунок 9.4 Нахождение заказчиков в городах относящихся к Serres

Псевдоним a будет делать предикат неверным, за исключением случая, когда его значение столбца snum = 1002. Таким образом, псевдоним опускает всё, кроме заказчиков продавца Serres. Псевдоним b будет верным для всех строк с тем же самым значением города, что и текущее значение города для a; в ходе запроса строка псевдонима b будет верна один раз, когда значение города представлено в a. Нахождение этих строк псевдонима b - единственная цель псевдонима a, поэтому мы не выбираем все столбцы подряд. Как вы можете видеть, собственные заказчики Serres выбираются при нахождении их в том же самом городе, что и он сам, поэтому выбор их из псевдонима a не обязателен. Короче говоря, псевдоним находит строки заказчиков Serres, Liu и Grass. Псевдоним b находит всех заказчиков, размещенных в любом из их городов (San Jose и Berlin, соответственно), включая, конечно, самих Liu и Grass.

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

SELECT sname, Salespeople.snum, first.cname second.cname FROM Customers first, Customers second, Salespeople WHERE first.snum = second.snum AND Salespeople.snum = first.snum AND first.cnum < second.cnum;

=============== SQL Execution Log ================== | | | SELECT cname, Salespeople.snum, first.cname | | second.cname | | FROM Customers first, Customers second, Salespeople | | WHERE first.snum = second.snum | | AND Salespeople.snum = first.snum | | AND first.cnum < second.cnum; | | ====================================================| | cname snum cname cname | | ------ ------ -------- -------- | | Serres 1002 Liu Grass | | Peel 1001 Hoffman Clemens | =====================================================

Рисунок 9.5 Объединение таблицы с собой и с другой таблицей


КТО МОЖЕТ СОЗДАВАТЬ ПРЕДСТАВЛЕНИЯ?


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

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

Так как внешние ключи не используются в представлениях, привилегия REFERENCES никогда не используется при создании представлений. Все эти ограничения определяются ANSI.

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

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

ОГРАНИЧЕНИЕ ПРИВИЛЕГИИ SELECT ДЛЯ ОПРЕДЕЛЁННЫХ СТОЛБЦОВ

Предположим, вы хотите дать пользователю Claire способность видеть только столбцы snum и sname таблицы Продавцов. Вы можете сделать это, поместив имена этих столбцов в представление

CREATE VIEW Clairesview AS SELECT snum, sname FROM Salespeople;

и предоставить Claire привилегию SELECT в представлении, а не в самой таблице Продавцов:

GRANT SELECT On Clairesview to Claire;

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

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

ОГРАНИЧЕНИЕ ПРИВИЛЕГИЙ ДЛЯ ОПРЕДЕЛЁННЫХ СТРОК

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


Чтобы предоставить пользователю Adrian привилегию UPDATE в таблице Заказчиков для всех заказчиков, размещенных в Лондоне, вы можете создать такое представление:

CREATE VIEW Londoncust AS SELECT * FROM Customers WHERE city = 'London' WITH CHECK OPTION;

Затем вы должны передать привилегию UPDATE в этой таблице для Adrian:

GRANT UPDATE ON Londoncust TO Adrian;

В этом отличие привилегии для определённых строк от привилегии UPDATE для определённых столбцов, которая распространена на все столбцы таблицы Заказчиков, но не на строки, среди которых строки со значением поля city, иным, чем London, не будут учитываться. Предложение WITH CHECK OPTION предохраняет Adrian от замены значения поля city на любое значение кроме London.

ПРЕДОСТАВЛЕНИЕ ДОСТУПА ТОЛЬКО К ИЗВЛЕЧЁННЫМ ДАННЫМ

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

CREATE VIEW Datetotals AS SELECT odate, COUNT (*), SUM (amt), AVG (amt) FROM Orders GROUP BY odate;

Теперь вы передаёте пользователю Diane привилегию SELECT в представлении Datetotals:

GRANT SELECT ON Datetotals TO Diane;

ИСПОЛЬЗОВАНИЕ ПРЕДСТАВЛЕНИЙ В КАЧЕСТВЕ АЛЬТЕРНАТИВЫ ОГРАНИЧЕНИЯМ

Одной из последних прикладных программ из серии, описанной в Главе 18, является использование представлений с WITH CHECK OPTION как альтернативы ограничениям.

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



CREATE VIEW Curcities AS SELECT * FROM Salespeople WHERE city IN ('London', 'Rome', 'San Jose', 'Berlin') WITH CHECK OPTION;

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

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

CREATE VIEW Othercities AS SELECT * FROM Salespeople WHERE city NOT IN ('London', 'Rome', 'San Jose', 'Berlin') WITH CHECK OPTION;

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


КУРСОР


Одно из сильных качеств SQL - способность функционировать на всех строках таблицы, чтобы найти определенное условие как блок "запись", не зная сколько таких строк там может быть. Если десять строк удовлетворяют предикату, то запрос может вывести все десять строк. Если десять миллионов строк определены, все десять миллионов строк будут выведены. Это несколько затруднительно, когда вы попробуете связать это с другими языками. Как вы сможете назначать вывод запроса для переменных, когда не знаете, насколько велик будет вывод? Решение состоит в том, чтобы использовать то, что называется курсором. Вы, вероятно, знакомы с курсором - мигающей черточкой, которая отмечает вашу позицию на экране компьютера. Вы можете рассматривать SQL-курсор как устройство, которое, аналогично этому, отмечает ваше место в выводе запроса, хотя аналогия не полная.

Курсор это вид переменной, которая связана с запросом. Значением этой переменной может быть каждая строка, которая выводится при запросе. Подобно главным переменным, курсоры должны быть объявлены, прежде чем они будут использованы. Это делается командой DECLARE CURSOR следующим образом:

EXEC SQL DECLARE CURSOR Londonsales FOR SELECT * FROM Salespeople WHERE city = 'London';

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

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

EXEC SQL OPEN CURSOR Londonsales;

Значения в курсоре могут быть получены, когда вы выполняете именно эту команду, но не предыдущую команду DECLARE и не последующую команду FETСH. Затем вы используете команду FETCH, чтобы извлечь вывод из этого запроса, по одной строке в каждый момент времени.


EXEC SQL FETCH Londonsales INTO :id_num, :salesperson, :loc, :comm;

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

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

Look_at_more:= True; EXEC SQL OPEN CURSOR Londonsales; while Look_at_more do begin EXEC SQL FETCH Londonsales INTO :id_num, :Salesperson, :loc, :comm; writeln (id_num, Salesperson, loc, comm); writeln ('Do you want to see more data? (Y/N)'); readln (response); it response = 'N' then Look_at_more: = False end; EXEC SQL CLOSE CURSOR Londonsales;

В Паскале знак : = означает "является назначенным значением из", в то время как = ещё имеет обычное значение "равно". Функция writeln записывает её вывод, а затем переходит к новой строке. Одиночные кавычки вокруг символьных значений во втором writeln и в предложении if ... then обычны для Паскаля, что случается при дубликатах в SQL.

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

Хотя переменные Look_at_more и ответ должны быть объявлены как булева переменная и символьная (char) переменная, соответственно, в разделе объявлений переменных в Паскаля, они не должны быть включены в раздел объявлений SQL, потому что они не используются в командах SQL.

Как видите, двоеточия перед именами переменных не используются для не-SQL операторов. Также обратите внимание, что имеется оператор CLOSE CURSOR, соответствующий оператору OPEN CURSOR. Он, как вы поняли, освобождает курсор значений, поэтому запрос нужно будет выполнить повторно с оператором OPEN CURSOR, прежде чем перейти в выбору следующих значений. Это не обязательно для тех строк, которые были выбраны запросом после закрытия курсора, хотя это и обычная процедура. Пока курсор закрыт, SQL не следит за тем, какие строки выбраны. Если вы открываете курсор снова, запрос повторно выполняется с этой точки, и вы начинаете всё сначала.

Этот пример не обеспечивает автоматический выхода из цикла, когда все строки уже будут выбраны. Когда у FETCH нет больше строк которые надо извлекать, он просто не меняет значений в переменных предложения INTO. Следовательно, если данные исчерпались, эти переменные будут неоднократно выводиться с идентичными значениями до тех пор, пока пользователь не завершит цикл, введя ответ N.


КВАЛИФИЦИРОВАННЫЙ ВЫБОР ПРИ ИСПОЛЬЗОВАНИИ ПРЕДЛОЖЕНИЙ


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

WHERE - предложение команды SELECT, которое позволяет устанавливать предикаты, условие которых может быть или верным (true), или неверным (false) для любой строки таблицы. Команда извлекает только те строки из таблицы, для которых такое утверждение верно. Например, предположим, вы хотите видеть имена и комиссионные всех продавцов в Лондоне. Вы можете ввести такую команду:

SELECT sname, city FROM Salespeople; WHERE city = "LONDON";

Когда предложение WHERE предоставлено, программа базы данных просматривает всю таблицу построчно и исследует каждую строку, чтобы определить, верно ли утверждение. Следовательно, для записи Peel программа рассмотрит текущее значение столбца city, определит, что оно равно "London", и включит эту строку в вывод. Запись для Serres не будет включена, и так далее. Вывод для вышеупомянутого запроса показан на Рисунке 3.6.

=============== SQL Execution Log ============ | | | SELECT sname, city | | FROM Salespeople | | WHERE city = 'London' | | ============================================= | | sname city | | ------- ---------- | | Peel London | | Motika London | =============================================

Рисунок 3.6 SELECT с предложением WHERE

Давайте попробуем пример с числовым полем в предложении WHERE. Поле rating таблицы Заказчиков предназначено для того, чтобы разделять заказчиков на группы, основанные на некоторых критериях, которые могут быть получены в итоге через этот номер. Возможно это - форма оценки кредита или оценки, основанной на объёме предыдущих приобретений. Такие числовые коды могут быть полезны в реляционных базах данных как способ подведения итогов сложной информации. Мы можем выбрать всех заказчиков с рейтингом 100 следующим образом:


SELECT * FROM Customers WHERE rating = 100;

Одиночные кавычки не используются здесь, потому что оценка это числовое поле. Результаты запроса показаны на Рисунке 3. 7.

Предложение WHERE совместимо с предыдущим материалом в этой главе. Другими словами, вы можете использовать номера столбцов, устранять дубликаты или переупорядочивать столбцы в команде SELECT, которая использует WHERE. Однако вы можете изменять порядок столбцов для имён только в предложении SELECT, но не в предложении WHERE.

============ SQL Execution Log ============== | | | SELECT * | | FROM Customers | | WHERE rating = 100; | | ============================================= | | сnum cname city rating snum | | ------ -------- ------ ---- ------ | | 2001 Hoffman London 100 1001 | | 2006 Clemens London 100 1001 | | 2007 Pereira Rome 100 1001 | =============================================

Рисунок 3.7 SELECT с числовым полем в предикате


МАТЕМАТИЧЕСКИЕ ФУНКЦИИ


Эти функции применяются для работы с числами.

ФУНКЦИЯЗНАЧЕНИЕ

МНОГОСТОЛБЦОВЫЕ ВНЕШНИЕ КЛЮЧИ


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

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



МОДИФИЦИРОВАНИЕ КУРСОРОВ


Курсоры могут также быть использованы, чтобы выбирать группу строк из таблицы, которые могут быть затем модифицированы или удалены одна за другой. Это дает вам возможность, обходить некоторые ограничения предикатов, используемых в командах UPDATE и DELETE. Вы можете ссылаться на таблицу, задействованную в предикате запроса курсора или любом из его подзапросов, которые вы не можете выполнить в предикатах самих этих команд. Как подчёркнуто в Главе 16, стандарт SQL отклоняет попытку удалить всех пользователей с рейтингом ниже среднего, в следующей форме:

EXEC SQL DELETE FROM Customers WHERE rating < (SELECT AVG (rating) FROM Customers);

Однако вы можете получить тот же эффект, используя запрос для выбора соответствующих строк, запомнив их в курсоре и выполнив DELETE с использованием курсора. Сначала вы должны объявить курсор:

EXEC SQL DECLARE Belowavg CURSOR FOR SELECT * FROM Customers WHERE rating < (SELECT AVG (rating) FROM Customers);

Затем вы должны создать цикл, чтобы удалить всех заказчиков, выбранных курсором:

EXEC SQL WHENEVER SQLERROR GOTO Error_handler; EXEC SQL OPEN CURSOR Belowavg; while not SOLCODE = 100 do begin EXEC SOL FETCH Belowavg INTO :a, :b, :c, :d, :e; EXEC SOL DELETE FROM Customers WHERE CURRENT OF Belowavg; end; EXEC SOL CLOSE CURSOR Belowavg;

Предложение WHERE CURRENT OF означает, что DELETE применяется к строке, которая в настоящее время выбрана курсором. Здесь подразумевается, что и курсор, и команда DELETE ссылаются на одну и ту же таблицу и, следовательно, что запрос в курсоре - это не объединение. Курсор должен также быть модифицируемым. Являясь модифицируемым, курсор должен удовлетворять тем же условиям, что и представления (см. Главу 21).

Кроме того, ORDER BY и UNION, которые не разрешены в представлениях, в курсорах разрешаются, но предохраняют курсор от модифицируемости. Обратите внимание в вышеупомянутом примере, что мы должны выбирать строки из курсора в набор переменных, даже если мы не собирались использовать эти переменные. Этого требует синтаксис команды FETCH.


UPDATE работает так же.

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

EXEC SOL DECLARE CURSOR High_Cust AS SELECT * FROM Salespeople WHERE snum IN (SELECT snum FROM Customers WHERE rating = 300);

Затем вы выполняете модификации в цикле:

EXEC SQL OPEN CURSOR High_cust; while SQLCODE = 0 do begin EXEC SOL FETCH High_cust INTO :id_num, :salesperson, :loc, :comm; EXEC SQL UPDATE Salespeople SET comm = comm + .01 WHERE CURRENT OF High_cust; end; EXEC SQL CLOSE CURSOR High_cust;

Обратите внимание, что некоторые реализации требуют, чтобы вы указывали в определении курсора, что курсор будет использоваться для выполнения команды UPDATE на определенных столбцах. Это делается с помощью заключительной фразы определения курсора - FOR UPDATE . Для объявления курсора High_cust таким способом, чтобы вы могли модифицировать командой UPDATE столбец comm, вы должны ввести следующее предложение:

EXEC SQL DECLARE CURSOR High_Cust AS SELECT * FROM Salespeople WHERE snum IN (SELECT snum FROM Customers WHERE rating = 300) FOR UPDATE OF comm;

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


МОДИФИЦИРОВАНИЕ NULL-ЗНАЧЕНИЙ


Предложение SET это не предикат. Оно может вводить пустые NULL-значения так же, как оно вводило значения, не используя какого-то специального синтаксиса (такого, например, как IS NULL). Так что, если вы хотите установить все оценки заказчиков в Лондоне в NULL, вы можете ввести следующее предложение:

UPDATE customers SET rating = NULL WHERE city = 'London';

что обнулит все оценки заказчиков в Лондоне.



МОДИФИЦИРОВАНИЕ ПРЕДСТАВЛЕНИЙ


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

UPDATE Salesown SET city = 'Palo Alto' WHERE snum = 1004;

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

UPDATE Salesown SET comm = .20 WHERE snum = 1004;

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



МОДИФИЦИРОВАНИЕ ПРЕДСТАВЛЕНИЯ


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

Представление состоит из результатов запроса, и, когда вы модифицируете представление, вы модифицируете набор результатов запроса. Но модификация не должна воздействовать на запрос; она должна воздействовать на значения в таблице, к которой был сделан запрос, и таким образом изменять вывод запроса. Это не простой вопрос. Следующий оператор будет создавать представление, показанное на Рисунке 21.1:

CREATE VIEW Citymatch (custcity, salescity) AS SELECT DISTINCT a.city, b.city FROM Customers a, Salespeople b WHERE a.snum = b.snum;

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

Например, одна строка этой таблицы - London London - показывает, что имеется по крайней мере один заказчик в Лондоне, обслуживаемый продавцом в Лондоне. Эта строка может быть произведена при совпадении Hoffmanа с его продавцом Peel, причем если оба они из Лондона.

=============== SQL Execution Log ============== | | | SELECT * | | FROM Citymatch; | | =============================================== | | custcity salescity | | --------- --------- | | Berlin San Jose | | London London | | Rome London | | Rome New York | | San Jose Barselona | | San Jose San Jose | | | ================================================

Рисунок 21.1 Представление совпадения по городам

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

Даже если вы не получите выбора, используя отличия, вы всё ещё будете в том же самом положении, потому что вы будете тогда иметь две строки в представлении с идентичными значениями, то есть с обоими столбцами, равными " Lоndon London ". Эти две строки представления будут отличаться друг от друга, так что вы пока не сможете сообщить, какая строка представления исходила из каких значений базовых таблиц (имейте в виду, что запросы, не использующие предложение ORDER BY, производят вывод в произвольном порядке).

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

Что, если вы пробуете удалить строку " London London " из представления? Означало бы это удаление Hoffman из таблицы Заказчиков, удаление Clemens из той же таблицы, или удаление их обоих? Должен ли SQL также удалить Peel из таблицы Продавцов? На эти вопросы невозможно ответить точно, поэтому удаления не разрешены в представлениях такого типа. Представление Citymatch это пример представления только_для_чтения: оно может быть только запрошено, но не изменено.



МОДИФИЦИРОВАНИЕ ТОЛЬКО ОПРЕДЕЛЕННЫХ СТРОК


Конечно, вы не всегда захотите указывать все строки таблицы для изменения единственного значения, так что UPDATE, наподобие DELETE, может использовать предикаты. Вот как, например, можно выполнить изменение, одинаковое для всех заказчиков продавца Peel (имеющего snum=1001):

UPDATE Customers SET rating = 200 WHERE snum = 1001;



МОДИФИЦИРУЕМЫЕ ПРЕДСТАВЛЕНИЯ И ПРЕДСТАВЛЕНИЯ ТОЛЬКО_ДЛЯ_ЧТЕНИЯ.


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

Они также не сравнивают строки таблиц друг с другом (как в объединениях и подзапросах или как с DISTINCT).

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

Цели для которых вы их используете, часто различны. Модифицируемые представления в основном используются точно так же, как и базовые таблицы. Фактически пользователи не могут даже осознать, является ли объект, который они запрашивают, базовой таблицей или представлением. Это превосходный механизм защиты для скрытия частей таблицы, которые являются конфиденциальными или не относятся к потребностям данного пользователя. (В Главе 22 мы покажем вам, как разрешить пользователям обращаться к представлению, а не к базовой таблице).

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

Кроме того, результаты этих запросов в таблицах, которые могут затем использоваться в запросах самостоятельно (например, в объединениях), имеют преимущество над просто выполнением запросов.

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



НЕ ДЕЛАЙТЕ ВЛОЖЕННЫХ АГРЕГАТОВ


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

SELECT odate, MAX (SUM (amt)) FROM Orders GROUP BY odate;

то ваша команда будет, вероятно, отклонена. (Некоторые реализации не предписывают этого ограничения, что выгодно, потому что вложенные агрегаты могут быть очень полезны, даже если они и несколько проблематичны.) В вышеупомянутой команде, например, SUM должен применяться к каждой группе поля odate, а MAX - ко всем группам, производящим одиночное значение для всех групп. Однако предложение GROUP BY подразумевает, что должна иметься одна строка вывода для каждой группы поля odate.









ABX(X)Абсолютное значение X (преобразование отрицательного или положительного значения в положительное).
CEIL (X)X является десятичным значением, которое будет округляться сверху.
FLOOR (X)X является десятичным значением, которое будет округляться снизу.
GREATEST(X,Y)Возвращает большее из двух значений.
LEAST(X,Y)Возвращает меньшее из двух значений.
MOD(X,Y)Возвращает остаток от деления X на Y.
POWER(X,Y)Возвращает значение X в степени Y.
ROUND(X,Y)Цикл от X до десятичного Y. Если Y отсутствует, цикл до целого числа.
SING(X)Возвращает минус если X < 0, или плюс если X > 0.
SQRT (X)Возвращает квадратный корень из X.