ИСПОЛЬЗОВАНИЕ SQL С ДРУГИМ ЯЗЫКОМ ( ВЛОЖЕННЫЙ SQL )
В ЭТОЙ ГЛАВЕ ВЫ УЗНАЕТЕ КАК SQL ИСПОЛЬЗУЕТСЯ
для расширения программ написанных на других языках. Хотя непроцедурность языка SQL делает его очень мощным, в то же время это накладывает на него большое число ограничений.
Чтобы преодолеть эти ограничения, вы можете включать SQL в программы написанные на том или другом процедурном языке( имеющем определенный алгоритм). Для наших примеров, мы выбрали Паскаль, считая что
этот язык наиболее прост в понимании для начинающих, и еще потому,
что Паскаль - один из языков для которых ANSI имеет полуофициальный
стандарт.
Основной способ которым SQL и части базового языка ваших программ будут связываться друг с другом - это с помощью значений переменных.
Естественно, что разные языки распознают различные типы данных для переменных. ANSI определяет эквиваленты SQL для четыре базовых языков -
ПЛ/1, Паскаль, КОБОЛ, и ФОРТРАН; все это подробности описаны в
Приложении B.
Эквиваленты для других языков - определяет проектировщик.
Имейте в виду, что типы, такие как DATE, не распознаются ANSI; и
следовательно никаких эквивалентных типов данных для базовых языков,
не существуют в стандарте ANSI. Более сложные типы данных базового
языка, такие как матрицы, не имеют эквивалентов в SQL.
Вы можете использовать переменные из главной программы во вложенных операторах SQL везде, где вы будете использовать выражения значений. ( SQL, используемый в этой главе, будет пониматься как вложенный SQL, до тех пор пока это не будет оговорено особо. )
Текущим значением переменной, может быть значение, используемое в
команде. Главные переменные должны -
* быть объявлеными в SQL DECLARE SESSION ( РАЗДЕЛ
ОБЪЯВЛЕНИЙ ) который будет описан далее.
* иметь совместимый тип данных с их функциями в команде SQL
( например, числовой тип если он вставляется в числовое поле )
* быть назначенными значению во врем их использования в команде
SQL, если команда SQL самостоятельно не может сделать назначение.
* предшествовать двоеточию (:) когда они упоминаются в команде SQL
Так как главные переменные отличаются от имен столбцов SQL наличием
у них двоеточия, вы можете использовать переменные с теми же самыми
именами что и ваши столбцы, если это конечно нужно.
Предположим что вы имеете четыре переменных в вашей программе, с
именами: id_num, salesperson, loc, и comm. Они содержат значения
которые вы хотите вставить в таблицу Продавцов. Вы могли бы вложить
следующую команду SQL в вашу программу:
EXEC SQL INSERT INTO Salespeople
VALUES ( :id_num, :salesperson, :loc, :comm)
Текущие значения этих переменных будут помещены в таблицу. Как вы
можете видеть, переменна comm имеет то же самое им что и столбец в
который это значение вкладывается.
Обратите внимание, что точка с запятой в конце команды отсутствует. Это
потому, что соответствующее завершение для вложенной команды SQL за-
висит от языка для которого делается вложение.
Для Паскаля и PL/1, это будет точка с запятой; для КОБОЛА, слово
END-EXEC ; и для ФОРТРАНА не будет никакого завершения.
В других языках это зависит от реализации, и поэтому мы договоримся
что будем использовать точку с запятой (в этой книге) всегда, чтобы не
противоречить интерактивному SQL и Паскалю. Паскаль завершает вложенный SQL и собственные команды одинаково - точкой с запятой.
Способ сделать команду полностью такой как описана выше, состоит в
том, чтобы включать ее в цикл и повторять ее, с различными значения-
ми переменных, как например показано в следующем примере:
while not end-ot-file (input) do
begin
readln (id_num, salesperson, loc, comm);
EXEC SOL INSERT INTO Salespeople
VALUES (:id_num, :salesperson, :loc, :comm);
end;
Фрагмент программы на ПАСКАЛЕ, определяет цикл, который будет
считывать значения из файла, сохранять их в четырех проименованных
переменных, сохранять значения этих переменных в таблице Продавцов,
и затем считывать следующие четыре значения, повтор этот процесс
до тех пор пока весь входной файл не прочитается. Считается, что каждый набор значений завершается возвратом каретки ( для незнакомых
с Паскалем, функция readln считывает вводимую информацию и пере-
ходит на следующую строку источника этой информации). Это дает вам
простой способ передать данные из текстового файла в реляционную структуру.
Конечно, вы можете сначала обработать данные любыми возможными
способами на вашем главном языке, например для исключения всех комиссионных ниже значения .12
while not end-ot-file (input) do
begin
readln (id_num, salesperson, loc, comm);
if comm > = .12 then
EXEC SQL INSERT INTO Salespeople
VALUES (:id_num, :salesperson, :loc, :comm);
end;
Только строки которые встретят условие comm >= .12 будут вставлены в
вывод. Это показывает что можно использовать и циклы и условия как
нормальные для главного языка.
Хорошо было бы знать, когда данные будут исчерпаны, так чтобы можно
было сообщить об этом пользователю и цикл завершился бы автоматически.
Это - даже более важно чем например знать что команда SQL выполнена
с ошибкой. Переменна SQLCODE ( называемая еще SQLCOD в
ФОРТРАНе ) предназначена чтобы обеспечить эту функцию. Она должна быть определена как переменна главного языка и должна иметь
тип данных который в главном языке соответствует одному из точных
числовых типов SQL, как это показано в Приложении B.
Значение SQLCODE устанавливается каждый раз, когда выполняется
команда SQL. В основном существуют три возможности:
1. Команда выполнилась без ошибки, но не произвела никакого
действия. Для различных команд это выглядит по разному:
а) Для SELECT, ни одна строка не выбрана запросом.
б) Для FETCH, последняя строка уже была выбрана, или ни
одной строки не выбрано запросом в курсоре.
в) Для INSERT, ни одной строки не было вставлено ( подразумевается что запрос использовался чтобы сгенерировать значения для вставки, и был отвергнут при попытке извлечения любой строки.
г) Для UPDATE и DELETE, ни одна строка не ответила условию предиката, и следовательно никаких изменений сделано
в таблице не будет.
В любом случае, будет установлен код SQLCODE = 100.
2. Команда выполнилась нормально, не удовлетворив ни одному из
выше указанных условий. В этом случае, будет установлен код
SQLCOD = 0.
3. Команда сгенерировала ошибку. Если это случилось, изменения
сделанные к базе данных текущей транзакцией, будут восстановлены( см. Главу 23 ).
В этом случае будет установлен код SQLCODE = некоторому отрицательному числу, определяемому проектировщиком. Задача этого числа, идентифицировать проблему, так точно насколько это возможно. В принципе, ваша система должна быть снабжена подпрограммой, которая в этом случае, должна выполниться чтобы вы-
дать для вас информацию расшифровывающее значение негатив-
ного числа определенного вашим проектировщиком. В этом случае
некоторое сообщение об ошибке будет выведено на экран или за-
писано в файл протокола, а программа в это врем выполнит восстановление изменений для текущей транзакции, отключится от базы данных и выйдет из нее. Теперь мы можем усовершенствовать
Курсоры могут также быть использованы, чтобы выбирать группу строк
из таблицы, которые могут быть затем модифицированы или удалены од-
на за другой. Это дает вам возможность, обходить некоторые ограничения
предикатов используемых в командах 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;
Это обеспечит вас определенной защитой от случайных модификаций,
которые могут разрушить весь порядок в базе данных.