23. ГЛОБАЛЬНЫЕ АСПЕКТЫ SQL ЭТА ГЛАВА БУДЕТ ОБСУЖДАТЬ АСПЕКТЫ ЯЗЫКА SQL которые имеют отношение к базе данных как к единому целому, включая использование многочисле- ных имен для объектов данных, размещение запоминаемых данных, восста- новлние и сохранение изменений в базе данных а также координирование одновременных действий многочисленных пользователей. Этот материал даст вам возможность конфигурации вашей базы данных, отмены действия ошибок, и определения как действия одного пользователя в базе данных будут влиять на действия других пользователей. ======= ПЕРЕИМЕНОВАНИЕ ТАБЛИЦ ======== Каждый раз, когда вы ссылаетесь в команде к базовой таблице или представлению не являющимися вашей собственностью, вы должны устано- вить в ней префикс имени владельца, так что бы SQL знала где ее ис- кать. Так как это со временем становится неудобным, большинство реали- заций SQL позволяют вам создавать синонимы для таблиц ( что не являет- ся стандартом ANSI ) Синоним - это альтернативное имя, наподобии проз- вища, для таблицы. Когда вы создаете синоним, вы становитесь его собс- твенником, так что нет никакой необходимости, чтобы он предшествовал другому пользовательскому идентификатору доступа( имени пользователя ) Если вы имеете по крайней мере одну привилегию в одном или более столбцах таблицы; вы можете создать для них синоним. ( Некоторое отно- шение к этому может иметь специальная привилегия для создания синони- мов.) Adrian может создать синоним с именем Clients, для таблицы с именем Diane.Customers, с помощью команды CREATE SYNONYM следующим образом: CREATE SYNONYM Clients FOR Diane.Customers; Теперь, Adrian может использовать таблицу с именем Clients в команде точно так же как ее использует Diane.Customers. Синоним Clients - это собственность, используемая исключительно для Adrian. ПЕРЕИМЕНОВАНИЕ С ТЕМ ЖЕ САМЫМ ИМЕНЕМ Префикс (прозвище) пользователя - это фактически часть имени любой таблицы. Всякий раз, когда вы не указываете ваше собственное имя поль- зователя вместе с именем вашей собственной таблицы, SQL сам заполняет для вас это место. Следовательно, два одинаковых имени таблицы но свя- занные с различными владельцами, становятся не идентичными и следова- тельно не приводят к какому-нибудь беспорядку (по крайней мере в SQL). Это означает что два пользователя могут создать две полностью несвя- занные таблицы с одинаковыми именами, но это также будет означать, что один пользователь может создать представление основанное на имени дру- гого пользователя стоящем после имени таблицы. Это иногда делается когда представление, рассматривается как сама таблица - например, если представление просто использует CHECK OPTION как заменитель ограниче- ния CHECK в базовой таблице( смотрите Главу 22 для подробностей ). Вы можете также создавать ваши собственные синонимы, имена которых будут такими же что и первоначальные имена таблиц. Например, Adrian может определить Customers, как свой синоним для таблицы Diane.Customers : CREATE SYNONYM Customers FOR Diane.Customers; С точки зрения SQL, теперь имеются два разных имени одной таблицы: Diane.Customers и Adrian.Customers. Однако, каждый из этих пользовате- лей может ссылаться к этой таблице просто как к Customers, SQL как го- ворилось выше сам добавит к ней недостающие имена пользователей. ОДНО ИМЯ ДЛЯ КАЖДОГО Если вы планируете иметь таблицу Заказчиков используемую большим числом пользователей, лучше всего что бы они ссылались к ней с помощью одного и того же имени. Это даст вам возможность, например, использо- вать это имя в вашем внутреннем общении без ограничений. Чтобы создать единое имя для всех пользователей, вы создаете общий синоним. Напри- мер, если все пользователи будут вызывать таблицу Заказчиков с именем Customers, вы можете ввести CREATE PUBLIC SYNONYM Customers FOR Customers; Мы пронимаем, что таблица Заказчиков это ваша собственность, поэтому никакого префикса имени пользователя в этой команды не указывается. В основном, общие синонимы создаются владельцами объектов или привилеги- рованными пользователями, типа DBA. Пользователям кроме того, должны еще быть предоставлены привилегии в таблице Заказчиков чтобы они могли иметь к ней доступ. Даже если имя является общим, сама таблица общей не является. Общие синонимы становятся собственными с помощью команды PUBLIC, а не с помощью их создателей. УДАЛЕНИЕ СИНОНИМОВ Общие и другие синонимы могут удаляться командой DROP SYNONYM. Сино- нимы удаляются их владельцами, кроме общих синонимов, которые удаляют- ся соответствующими привилегированными личностями, обычно DBA. Чтобы удалить например синоним Clients, когда вместо него уже появился общий синоним Customers, Adrian может ввести DROP SYNONYM Clients; Сама таблица Заказчиков, естественно, становится не эффективной. КАК БАЗА ДАННЫХ РАСПРЕДЕЛЕНА ДЛЯ ПОЛЬЗОВАТЕЛЕЙ ? Таблицы и другие объекты данных сохраняются в базе данных и находят- ся там связанными с определенными пользователями которые ими владеют. В некотором смысле, вы могли бы сказать, что они сохраняются в " имен- ной области пользователя ", хотя это никак не отражает их физического расположения, но зато, как и большинство вещей в SQL, находятся в строгой логической конструкции. Однако, на самом деле, объекты данных сохраняться, в физическом смысле, и количество памяти которое может использоваться определенным объектом или пользователем, в данное вре- мя, имеют свой предел. В конце концов, никакой компьютер не имеет прямого доступа к беско- нечному числу аппаратных средств ( диску, ленте, или внутренней памя- ти) для хранения данных. Кроме того, эффективность SQL расширится если логическая структура данных будет отображаться неким физическим спосо- бом при котором эти команды получать преимущество. В больших SQL системах, база данных будет разделена на области, так называемые Области Базы Данных или Разделы. Это области сохраняемой информации, которые размещены так, чтобы ин- формация внутри них находилась близко друг к другу для выполнения ко- манд; то-есть программа не должна искать где-то далеко информацию, сгруппированную в одиночной области базы данных. Хотя ее физические возможности зависят от аппаратного оборудования, целесообразно чтобы команда работала в этих областях внутри самой SQL. Системы которые ис- пользуют области базы данных ( в дальнейшем называемых - DBS (Data Ba- se Spaces)), позволяют вам с помощью команд SQL обрабатывать эти об- ласти как объекты. DBS создаются командами CREATE DBSPACE (СОЗДАТЬ DBS), ACQUIRE DBSPA- CE(ПОЛУЧИТЬ DBS) или CREATE TABLESPACE (СОЗДАТЬ ТАБЛИЧНУЮ ОБЛАСТЬ), в зависимости от используемой реализации. Одна DBS может вмещать любое число пользователей, и отдельный пользователь может иметь доступ к многим DBS. Привилегия создавать таблицы, хотя и может быть передана по всей базу данных, часто передается в конкретной DBS. Мы можем соз- дать DBS с именем Sampletables, следующей командой: CREATE DBSPACE Sampletables ( pctindex 10, pctfree 25); Параметр pctindex определяет какой процент DBS должен быть оставлен, чтобы сохранять в нем индексы таблиц. Pctfree - это процент DBS кото- рый оставлен чтобы позволить таблицам расширять размеры их строк ( AL- TER TABLE может добавлять столбцы или увеличивать размер столбцов, де- лая каждую строку длиннее. Это - расширение памяти отводимой для это- го). Имеются также другие параметры которые вы также можете опреде- лять, и которые меняются от программы к программе. Большинство прог- рамм автоматически будут обеспечивать значения по умолчанию, поэтому вы можете создавать DBS не определяя эти параметры. DBS может иметь или определенное ограничение размера, или ей может быть позволено рас- ти неограниченно вместе с таблицами. Если DBS создалась, пользователям предоставляются права создавать в ней объекты. Вы можете например пре- доставить Diane право создать таблицу Sampletables с помощью следующей команды: GRANT RESOURCE ON Sampletables TO Diane; Это даст вам возможность более конкретно определять место хранения данных. Первый DBS назначаемый данному пользователю - обычно тот, где все объекты этого пользователя создаются по умолчанию. Пользователи имеющие доступ к многочисленым DBS могут определить, где они хотят разместить определенный объект. При разделении вашей базы данных на DBSы, вы должны иметь в виду типы операций, которые вы будете часто выполнять. Таблицы которые, как вам уже известно, будут часто объеди- няться, или которые имеют одну таблицу ссылающуюся на другую во внеш- нем ключе, должны находиться вместе в одной DBS. Например, вы могли бы сообщить при назначении типовых таблиц, что таблица Порядков будет часто объединяться с одной или обеими из двух других таблиц,, так как таблица Порядков использует значения из обеих этих таблиц. При прочих равных условиях, эти три таблицы должны вхо- дить в ту же самую область DBS, независимо от того, кто их владелец. Возможное присутствие ограничения внешнего ключа в таблице Порядков, просто приведет к более строгому совместному использованию области DBS. КОГДА СДЕЛАННЫЕ ИЗМЕНЕНИЯ ======== СТАНОВЯТСЯ ПОСТОЯННЫМИ ? ======= Визуально, среда базы данных, это картина которая постоянно отобра- жает для существующих пользователей, постоянно вводимые и изменяемые данные, допуская, что если система правильно разработана, она будет функционировать без сбоев. Однако реально, благодаря человеческим или компьютерным сбоям, ошибки время от времени случаются, и поэтому хоро- шие компьютерные программы стали применять способы отмены действий вызвавших такие ошибки. Команда 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 которую система выполнит автоматически при регистрации. Если сеанс пользователя завершается аварийно - например, произошел сбой системы или выполнена перезагрузка пользователя, - то текущая транзакция выполнит автоматический откат изменений. Это - одна из причин, по которой вы можете управлять выпо- нением вашей диалоговой обработки запросов, разделив ваши команды на большое количество различных транзакций. Одиночная транзакция не долж- на содержать много несвязанных команд; фактически, она может состоять из единственной команды. Транзакции которые включают всю группу несвя- занных изменений не оставляют вам фактически никакого выбора сохранить или отклонить целую группу, если вы хотите отменить только одно опре- деленное изменение. Хорошим правилом которому надо следовать, это де- лать ваши транзакции состоящими из одной команды или нескольких близко связанных команд. Например, предположим вы хотите удалить продавца Mo- tika из базы данных. Прежде, чем вы удалите его из таблицы Продавцов, вы сначала должны сделать что-нибудь с его порядками и его заказчика- ми. ( Если используются ограничения внешнего ключа, и ваша система, следуя 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. КАК 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 получит ряд усредненых значений основанных на тех изменениях ко- торые были позже отменены, не зная что ее информации неточна. Обработ- ка одновременных транзакций называется - параллелизмом или совпадени- ем, и имеет номера возможных проблем которые могут при этом возникать. Имеются следующие примеры: * Модификация может быть сделана без учета другой модификации. Напри- мер, продавец должен сделать запрос к таблице инвентаризации, чтобы найти десять фрагментов пунктов торговцев акциями, и упорядочить шесть из их для заказчика. Прежде, чем это изменение было сделано, другой продавец делает запрос к таблице и упорядочивает семь из тех же фразментов для своего заказчика. ПРИМЕЧАНИЕ: Термин "упорядо- чить", аналогичен общепринятому - "заказать", что в принципе более соответствует логике запроса, потому что с точки зрения пользовате- ля, он именно "заказывает" информацию в базе данных, которая упоря- дочивает эту информацию в соответствии с "заказом". * Изменения в базе данных могут быть прокручены обратно уже после то- го, как их действия уже были закончены. Например если Вы отменили вашу ошибку уже после того, как Diane получила свой вывод. * Одно действие может воздействовать частично на результат другого действия. Например когда Diane получает среднее от значений в то время как вы выполняете модификацию этих значений. Хотя это не всегда проблематично, в большинстве случаев действие такое же как если бы агрегаты должны были отразить состояние базы данных в пунк- те относительной стабильности. Например в ревизионных книгах, долж- на быть возможность вернуться назад и найти это существующее усред- ненное значение для Diane в некоторой временной точке, и оставить его без изменений которые можно было бы сделаны начиная уже с этого места. Это будет невозможно сделать, если модификация была выполне- на во время вычисления функции. * Тупик. Два пользователя могут попытаться выполнить действия которые конфликтуют друг с другом. Например, если два пользователя попробу- ют изменить и значение внешнего ключа и значение родительского клю- ча одновременно. Имеется много сложнейших сценариев которые нужно было бы последова- тельно просматривать, если бы одновременные транзакции были неуправля- емыми. К счастью, SQL обеспечивает вас средством управления паралле- лизмом для точного указания места получения результата. Что ANSI ука- зывает для управления параллелизмом -это что все одновременные команды будут выполняться по принципу - ни одна команда не должна быть выдана, пока предыдущая не будет завершена (включая команды COMMIT или ROLL- BACK ). Более точно, нужно просто не позволить таблице быть доступной более чем для одной транзакции в данный момент времени. Однако в большинстве ситуаций, необходимость иметь базу данных доступную сразу многим поль- зователям, приводит к некоторому компромису в управлении параллелиз- мом. Некоторые реализации SQL предлагают пользователям выбор, позволяя им самим находить золотую середину между согласованностью данных и доступностью к базе данных. Этот выбор доступен пользователю, DBA, или тому и другому. На самом деле они осуществляют это управление вне SQL, даже если и воздействуют на процесс работы самой SQL. Механизм используемый SQL для управления параллелизмом операций, на- зывается - блокировкой. Блокировки задерживают определенные операции в базе данных, пока другие операции или транзакции не завершены. Задер- жанные операции выстраиваюится в очередь и выполняются только когда блокировка снята ( некоторые инструменты блокировок дают вам возмож- ность указывать NOWAIT, которая будет отклонять команду вместо того чтобы поставить ее в очередь, позволяя вам делать что-нибудь другое). Блокировки в многопользовательских системах необходимы. Следователь- но, должен быть некий тип схемы блокировки по умолчанию, который мог бы применяться ко всем командам в базе данных. Такая схема по умолча- нию, может быть определена для всей базы данных, или в качестве пара- метра в команде CREATE DBSPACE или команде ALTER DBSPACE, и таким об- разом использовать их по разному в различных DBS. Кроме того, системы обычно обеспечиваются неким типом обнаружителя зависания, который мо- жет обнаруживать ситуации, где две операции имеют блокировки, блокиру- ющие друг друга. В этом случае, одна из команд будет прокручена обрат- но и получит сброс блокировки. Так как терминология и специфика схем блокировок меняются от программы к программе, мы можем смоделировать наши рассуждения на примере программы базы данных DB2 фирмы IBM. IBM - лидер в этой области (как впрочим и во многих других ), и поэтому та- кой подход наиболее удобен. С другой стороны, некоторые реализации мо- гут иметь значительные различия в синтаксисе и в функциях, но в основ- ном их действия должно быть очень похожими. ТИПЫ БЛОКИРОВОК Имеется два базовых типа блокировок: - распределяемые блокировки и - специальльные блокировки. Распределяемые ( или S-блокировки ) могут быть установлены более чем однимо пользователя в данный момент времени. Это дает возможность лю- бому числу пользователей обращаться к данным, но не изменять их. Специальные блокировки ( или X-блокировки ) не позволяют никому во- обще, кроме владельца этой блокировки обращаться к данным. Специальные блокировки используются для команд которые изменяют содержание или структуру таблицы. Они действуют до конца транзакции. Общие блокировки используются для запросов. Насколько они продолжи- тельны зависит фактически от уровня изоляции. Что такое уровень изоляции блокировки? Это - то, что определяет, сколько таблиц будет блокировано. В DB2, имеется три уровня изоляции, два из которых можно применить и к распределеным и к специальным бло- кировкам, а третий, ограниченный, чтобы использовать эти блокировки совместно. Они управляются командами поданными извне SQL, так что мы можем обсуждать не указывая их точного синтаксиса. Точный синтаксис команд связанных с блокировками различен для различных реализаций. Следующее обсуждение полезно прежде всего на концептуальном уровне. Уровень изоляции - повторное чтение - гарантирует, что внутри данной транзакции, все записи извлеченные с помощью запросов, не могут быть изменены. Поскольку записи модифицируемые в транзакции являются субъ- ектами специальной блокировки, пока транзакция не завершена, они не могут быть изменены в любом случае. С другой стороны для запросов, повторное чтение означает, что вы можете решить заранее, какие строки вы хотите заблокировать и выполнить запрос который их выберет. Выпол- няя запроса, вы гарантированы, что никакие изменения не будут сделаны в этих строках, до тех пор пока вы не завершите текущую транзакцию. В то время как повторное чтение защищает пользователя, который по- местил блокировку, она может в то же время значительно снизить произ- водительность. Уровень указатель стабильности - предохраняет каждую запись от изме- нений, на время когда она читается или от чтения на время ее измене- ния. Последний случай это специальная блокировка, и применяется пока изменение не завершено или пока оно не отменено( т.е. на время отката изменения ). Следовательно, когда вы модифицируете группу записей ис- пользующих указатель стабильности, эти записи будут заблокированы пока транзакция не закончится, что аналогично действию производимому уров- нем повторное чтение. Различиие между этими двумя уровнями в их воз- действии на запросы. В случае уровня указатель стабильности, строки таблицы которые в данное время не используются запросом, могут быть изменены. Третий уровень изоляции DB2 - это уровень только чтение. Только чтение фиксирует фрагмент данных; хотя на самом деле он блоки- рует всю таблицу. Следовательно, он не может использоваться с команда- ми модификации. Любое содержание таблицы как единое целое, в момент выполнения команды, будет отражено в выводе запроса. Это не обязательно так как в случае с уровнем указатель стабильнос- ти. Блокировка только чтение, гарантирует что ваш вывод будет внутрен- не согласован, если конечно нет необходимости во второй блокировке, не связывающей большую часть таблицы с уровнем повторное чтение. Блоки- ровка только чтение удобна тогда, когда вы делаете отчеты, которые должны быть внутренне согласованны, и позволять доступ к большинству или ко всем строкам таблицы, не связывая базу данных. ДРУГИЕ СПОСОБЫ БЛОКИРОВКИ ДАННЫХ Некоторые реализации выполняют блокировку страницы вместо блокировки строки. Это может быть либо возможностю для вашего управления либо нечто заложенным уже в конструкцию системы. Страница - это блок накопления памяти, обычно равный 1024 байт. Страница может состоять из одной или более строк таблицы, возможно сопровождаемых индексами и другой периферийной информацией, а может состоять даже из нескольких строк другой таблицы. Если вы блокируете страницы вместо строк, все данные в этих страницах будут блокированы точно также как и в индивидуальных строках, согласно уровням изоляции описаным выше. Основным преимуществом такого подхода является эффективность. Когда SQL не следит за блокированность и разблокированностью строк индивиду- ально, он работает быстрее. С другой стороны, язык SQL был разработан так чтобы максимизировать свои возможности, и произвольно блокирует строки которые необязательно было блокировать. Похожая возможность, доступная в некоторых системах - это блокировка областей DBS. Области базы данных имеют тенденцию быть больше чем страница, так что этот подход удовлетворяет и достоинству увиличения производительности и недостатку блокирования страниц. Вообще то лучше отключать блокировку низкого уровня если вам кажется что появились значительные проблемы с эффективностью. ============== РЕЗЮМЕ =================== Ключевые определения, с которыми вы познакомились в этой главе: * Синонимы, или как создавать новые имена для объектов данных. * Области базы даных (DBS), или как распределяется доступная память в базе данных. * Транзакция, или как сохранять или восстанавливать изменения в базе данных. * Управление Параллелизмом, или как SQL предохраняет от конфликта од- ной команды с другой. Синонимы - это объекты, в том смысле, что они имеют имена и (иногда) владельцев, но естественно они не могут существовать без таблицы, чье имя они замещают. Они могут быть общими и следовательно доступными каждому кто имеет доступ к объекту, или они могут принадлежать опреде- ленному пользователю. Области DBS или просто DBS - это подразделы базы данных, которые распределены для пользователей. Связанные таблицы, ( например таблицы, которые будут часто объединяться,) лучше хранить в общей для них DBS. СOMMIT и ROLLBACK - это команды, используемые для выполнения измене- ний в базе данных, в то время когда предыдущая команда COMMIT или ко- манда ROLLBACK, начинают сеанс и оставляют изменения , или игнорируют их как группу. Средство Управление Параллелизмом - определяет в какой степени од- новременно поданные команды будут мешать друг другу. Оно является адаптируемым средством, находящим компромис между производительностью базы данных и изоляцией действующих команд. ************** РАБОТА С SQL ************** 1. Создайте область базы данных с именем Myspace которая выделяет 15 процентов своей области для индексов, и 40 процентов на расширение строк. 2. Вы получили право SELECT в таблице Порядков продавца Diane. Введите команду так чтобы вы могли ссылаться к этой таблице как к "Orders" не используя имя "Diane" в качестве префикса. 3. Если произойдет сбой питания, что случится с всеми изменениями сде- ланными во время текущей транзакции ? 4. Если вы не можете видеть строку из-за ее блокировки, какой это тип блокировки ? 5. Если вы хотите получить общее, максимальное, и усредненое значения сумм приобретений для всех порядков, и не хотите при этом запрещать другим пользоваться таблицей, какой уровень изоляции будет этому соответствовать ? ( См. Приложение A для ответов. ) . 24. КАК ДАННЫЕ SQL СОДЕРЖАТСЯ В УПОРЯДОЧЕННОМ ВИДЕ В ЭТОЙ ГЛАВЕ, ВЫ УЗНАЕТЕ КАК ТИПОВАЯ SQL БАЗА данных сохраняет само- организованность. Не удивительно, что самоорганизованность обеспечива- ется реляционной базой данных, создаваемой и поддерживаемой с помощью программы. Вы можете обращаться к этим таблицам самостоятельно для по- лучения информации о привилегиях, таблицах, индексах, и так далее. Эта глава покажет вам некоторые типы содержащиеся в такой базе данных. КАТАЛОГ СИСТЕМЫ Чтобы функционировать как SQL база данных, ваша компьютерная система должна следить за многими различными вещями: таблицами, представления- ми, индексами, синонимами, привилегиями, пользователями, и так далее. Имеются различные способы делать это, но ясно, что наиболее логичный, эффективный, и согласованный способ делать это в реляционной среде состоит в том, чтобы сохранять эту информацию в таблицах. Это дает возможность компьютеру размещать и управлять информацией в которой он нуждается, используя те же самые процедуры которые он использует чтобы размещать и управлять данными которые он хранит для вас. Хотя это - вопрос конкретной программы, а не часть стандарта ANSI, большинство SQL баз данных, используют набор SQL таблиц хранящих служебную инфор- мацию для своих внутренних потребностей. Этот набор называется в раз- личных публикациях как - системный каталог, словарь данных, или просто системные таблицы ( Термин "словарь данных" может также относится к общему архиву данных, включая информацию о физических параметрах базы данных которые хранятся вне SQL. Следовательно, имеются программы баз данных, которые имеют и системный каталог и словарь данных. ) Таблицы системного каталога - напоминают обычные SQL таблицы: те же строки и столбцы данных. Например, одна таблица каталога обычно содер- жит информацию о таблицах существующих в базе данных, по одной строке на каждую таблицу базы данных; другая содержит информацию о различных столбцах таблиц, по одной строке на столбец, и так далее. Таблицы ка- талога создаются и присваиваются с помощью самой базы данных, и иден- тифицируются с помощью специальных имен, таких например как SYSTEM. База данных создает эти таблицы и модифицирует их автоматически; таб- лицы каталога не могут быть непосредственно подвергнуты действию ко- манды модификации. Если это случится, это значительно запутает всю систему и сделает ее неработоспособной. Однако, в большинстве систем, каталог может быть запрошен пользователем. Это очень полезно, потому что это дает вам возможность узнать кое-что о базе данных, которую вы используете. Ко- нечно, вся информация не всегда доступна всем пользователям. Подобно другим таблицам, доступ к каталогу ограничен для пользователей без со- ответствующих привилегий. Так как каталог принадлежит самой системе, имеется некоторая неясность относительно того, кто имеет привилегии и кто может предоставить привилегии в этом каталоге. Обычно, привилегии каталога предоставляет суперпользователь, например, администратор сис- темы, зарегистрированый как SYSTEM или DBA. Кроме того, некоторые при- вилегии могут предоставляться пользователям автоматически. ТИПИЧНЫЙ СИСТЕМНЫЙ КАТАЛОГ Давайте рассмотрим некоторые таблицы которые мы могли бы найти в ти- повом каталоге системы: ────────────────────────────────────────────────────────────────────── Таблицы Содержание ────────────────────────────────────────────────────────────────────── SYSTEMCATALOG Таблицы (базовые и представления) SYSTEMCOLUMNS Столбцы таблицы SYSTEMTABLES Каталог Представления в SYSTEMCATALOG SYSTEMINDEXES Индексы в таблице SYSTEMUSERAUTH Пользователи базы данных SYSTEMTABAUTH Объектные привилегии пользователей SYSTEMCOLAUTH Столбцовые привилегии пользователей SYSTEMSYNONS Синонимы для таблиц Теперь, если наш DBA предоставит пользователю Stephen право просмат- ривать SYSTEMCATALOG такой командой, GRANT SELECT ON SYSTEMCATALOG TO Stephen; то Stephen сможет увидеть некоторую информацию обо всех таблицах в ба- зе данных ( мы имеем здесь пользователя DBA, пользователя Chris, вла- дельца трех наших типовых таблиц, а также Adrian владельца представле- ния Londoncust ). SELECT tname, owner, numcolumns, type, CO FROM SYSTEMCATALOG; =============== SQL Execution Log =============== | | | SELECT tname, owner, numcolumns, type, CO | | FROM SYSTEMCATALOG; | | | | ================================================ | | tname owner numcolumns type CO | | ------------- ------- ---------- ---- --- | | SYSTEMCATALOG SYSTEM 4 B | | Salespeople Chris 4 B | | Customers Chris 5 B | | Londoncust Adrian 5 V Y | | Orders Chris 5 B | | | ================================================== Рисунок 24.1: Содержание таблицы SYSTEMCATALOG Как вы можете видеть, каждая строка описывает свою таблицу. Первый столбец - имя; второй - имя пользователя который владеет ею; третий - число столбцов которые содержит таблица; и четвертый - код из одного символа, это или B ( для базовой таблицы ) или V ( для представления ). Последний столбец имеет пустые(NULL) значения, если его тип не ра- вен V; и этот столбец указывает, определена или нет возможность про- верки. Обратите внимание что SYSTEMCATALOG(СЧИСТЕМНЫЙ КАТАЛОГ) предс- тавлен как одна из таблиц в вышеуказаном списке. Для простоты, мы иск- лючили остальные каталоги системы из вывода. Таблицы системного ката- лога обычно показываются в SYSTEMCATALOG. ИСПОЛЬЗОВАНИЕ ПРЕДСТАВЛЕНИЙ В ТАБЛИЦАХ КАТАЛОГА Поскольку SYSTEMCATALOG - это таблица, вы можете использовать ее в представлении. Фактически можно считать, что имеется такое представле- ние с именем SYSTEMTABLES. Это представление SYSTEMCATALOG содержит только те таблицы, которые входят в системный каталог; это обычно таб- лицы базы данных, типа таблицы Продавцов, которые показаны в SYSTEMCA- TALOG, но не в SYSTEMTABLES. Давайте предположим, что только таблицы каталога являются собственностью пользователя SYSTEM. Если вы захоти- те, вы можете определить другое представление, которое бы специально исключало таблицы каталога из вывода: CREATE VIEW Datatables AS SELECT * FROM SYSTEMCATALOG WHERE owner < > 'SYSTEM'; РАЗРЕШИТЬ ПОЛЬЗОВАТЕЛЯМ ВИДЕТЬ (ТОЛЬКО) ИХ СОБСТВЕННЫЕ ОБЪЕКТЫ Кроме того, имеются другое использование представлений каталога. Предположим вам нужно чтобы каждый пользователь был способен сделать запрос каталога, для получения информации только из таблиц которыми он владеет. Пока значение USER, в команде SQL постоянно для ID доступа пользователя выдающего команду, оно может всегда быть использоваться, чтобы давать доступ пользователям только к их собственным таблицам. Вы можете, для начала создать следующее представление: CREATE VIEW Owntables AS SELECT * FROM SYSTEMCATALOG WHERE Owner = USER; Теперь вы можете предоставить всем пользователям доступ к этому предс- тавлению: GRANT SELECT ON Owntables TO PUBLIC; Каждый пользователь теперь, способен выбирать (SELECT) только те стро- ки из SYSTEMCATALOG, владельцем которых он сам является. ПРЕДСТАВЛЕНИЕ SYSTEMCOLUMNS Одно небольшое добавление к этому, позволит каждому пользователю прос- матривать таблицу SYSTEMCOLUMNS для столбцов из его собственных таб- лиц. Сначала, давайте рассмотрим ту часть таблицы SYSTEMCOLUMNS, кото- рая описывает наши типовые таблицы( другими словами, исключим сам ка- талог): tname cname datatype cnumber tabowner ──────────────────────────────────────────────────── Salespeople snum integer 1 Diane Salespeople sname char 2 Diane Salespeople city char 3 Diane Salespeople comm decimal 4 Diane Customers cnum integer 1 Claire Customers cname char 2 Claire Customers city char 3 Claire Customers rating integer 4 Claire Customers snum integer 5 Claire Orders onum integer 1 Diane Orders odate date 2 Diane Orders amt decimal 3 Diane Orders cnum integer 4 Diane Orders snum integer 5 Diane Как вы можете видеть, каждая строка этой таблицы показывает столбец таблицы в базе данных. Все столбцы данной таблицы должны иметь разные имена, также как каждая таблица должна иметь данного пользователя, и наконец все комбинации пользователей, таблиц, и имен столбцов должны различаться между собой. Следовательно табличные столбцы: tname ( имя таблицы ), tabowner (владелец таблицы ), и cname ( имя столбца ), вместе составляют пер- вичный ключ этой таблицы. Столбец datatype( тип данных ) говорит сам за себя. Столбец cnumber ( номер столбца ) указывает на местоположении этого столбца внутри таблицы. Для упрощения, мы опустили параметры длины столбца, точности, и масштаба. Для справки, показана строка из SYSTFMCATALOG которая ссылается к этой таблице: tname owner numcolumns type CO ─────────────────────────────────────────────── SYSTEMCOLUMNS System 8 B Некоторые SQL реализации, будут обеспечивать вас большим количеством данных чем показано в этих столбцах, но показанное являются основой для любый реализаций. Для иллюстрации процедуры предложенной в начале этого раздела, име- ется способ, позволяющий каждому пользователю видеть информацию SYS- TEMCOLUMNS только для принадлежащих ему таблиц: CREATE VIEW Owncolumns AS SELECT * FROM SYSTEMCOLUMNS WHERE tabowner = USER; GRANT SELECT ON Owncolumns TO PUBLIC; КОММЕНТАРИЙ В ========== СОДЕРЖАНИИ КАТАЛОГА ============ Большинство версий SQL, позволяют вам помещать комментарии(ремарки) в специальные столбцы пояснений таблиц каталогов SYSTEMCATALOG и SYS- TEMCOLUMNS, что удобно, так как эти таблицы не всегда могут объяснить свое содержание. Для простоты, мы пока исключали этот столбец из наших иллюстраций. Можно использовать команду COMMENT ON со строкой текста, чтобы пояс- нить любую строку в одной из этих таблиц. Состояние - TABLE, для ком- ментирования в SYSTEMCATALOG, и текст - COLUMN, для SYSTEMCOLUMNS. Например: COMMENT ON TABLE Chris.Orders IS 'Current Customer Orders'; Текст будет помещен в столбец пояснений SYSTEMCATALOG. Обычно, макси- мальная длина таких пояснений - 254 символов. Сам комментарий, указывается для конкретной строки, одна с tname=Or- ders, а другая 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 =========== ОСТАЛЬНОЕ ИЗ КАТАЛОГА=========== Здесь показаны оставшиеся из ващих системных таблиц определения, с типовым запросом для каждого: SYSTEMINDEXES - ИНДЕКСАЦИЯ В БАЗЕ ДАННЫХ Имена столбцов в таблице SYSTEMINDEXES и их описания - следующие: СТОЛБЦЫ ОПИСАНИЕ ──────────────────────────────────────────────────────────────── iname Имя индекса ( используемого для его исключения ) iowner Имя пользователя который создал индекс tname Имя таблицы которая содержит индекс cnumber Номер столбца в таблице tabowner Пользователь который владеет таблицей содержащей индекс numcolumns Число столбцов в индексе cposition Позиция текущего столбца среди набора индексов isunique Уникален ли индекс ( Y или N ) ТИПОВОЙ ЗАПРОС Индекс считается неуникальным, если он вызывает продавца, в snum столбце таблицы Заказчиков: SELECT iname, iowner, tname, cnumber, isunique FROM SYSTEMINDEXES WHERE iname = 'salesperson'; Вывод для этого запроса показывается в Рисунке 24.4. =============== SQL Execution Log ================ | | | SELECT iname, iowner, tname, cnumber, isunique | | FROM SYSTEMINDEXES | | WHERE iname = 'salespeople' | | ; | | ================================================= | | iname iowner tname cnumber isunique | | ----------- ------ ---------- ------- -------- | | salesperson Stephan Customers 5 N | | | =================================================== Рисунок 24.4: Строка из таблицы SYSTEMINDEXES SYSTEMUSERAUTH - ПОЛЬЗОВАТЕЛЬСКИЕ И СИСТЕМНЫЕ ПРИВИЛЕГИИ В БАЗЕ ДАННЫХ Имена столбцов для SYSTEMUSERAUTH и их описание, следующее: СТОЛБЦЫ ОПИСАНИЕ ──────────────────────────────────────────────────────────────── username Идентификатор ( ID ) доступа пользователя password Пароль пользователя вводимый при регистрации resource Где пользователь имеет права RESOURCE dba Где пользователь имеет права DBA Мы будем использовать простую схему системных привилегий, которая представлена в Главе 22, где были представлены три системных привиле- гии - CONNECT( ПОДКЛЮЧИТЬ ), RESOURCE( РЕСУРСЫ ) и DBA. Все пользова- тели получают CONNECT по умолчанию при регистрации, поэтому он не опи- сан в таблице выше. Возможные состояния столбцов resource и dba могут быть - Y ( Да, пользователь имеет привилегии) или - No (Нет, пользова- тель не имеет привилегий). Пароли (password) доступны только высоко привилегированным пользова- телям, если они существуют. Следовательно запрос этой таблицы можно вообще делать только для информации относительно привилегий системы и пользователей. ТИПОВОЙ ЗАПРОС Чтобы найти всех пользователей которые имеют привиле- гию RESOURCE, и увидеть какие из них - DBA, вы можете ввести следующее условие: SELECT username, dba FROM SYSTEMUSERAUTH WHERE resource = 'Y'; Вывод для этого запроса показывается в Рисунке 24.5. SYSTEMTABAUTH - ПРИВИЛЕГИИ ОБЪЕКТА КОТОРЫЕ НЕ ОПРЕДЕЛЯЮТ СТОЛБЦЫ Здесь показаны имена столбцов в таблице SYSTEMTABAUTH и их описание: COLUMN ОПИСАНИЕ ────────────────────────────────────────────────────────────── username Пользователь который имеет привилегии grantor Пользователь который передает привилегии по имени пользователя tname Имя таблицы в которой существуют привилегии owner Владелец tname selauth Имеет ли пользователь привилегию SELECT insauth Имеет ли пользователь привилегию INSERT delauth Имеет ли пользователь привилегию DELETE Возможные значения для каждой из перечисленных привилегий объекта ( имена столбцов которых окончиваются на auth ) - Y, N, и G. G указывает что пользователь имеет привилегию с возможностью передачи привилегий. В каждой строке, по крайней мере один из этих столбцов должен иметь состояние отличное от N (другими словами, иметь хоть какую-то привиле- гию ). =============== SQL Execution Log ================ | | | SELECT username, dba | | FROM SYSTEMUSERAUTH | | WHERE resource = 'Y' | | ; | | ================================================= | | username dba | | ----------- ------ | | Diane N | | Adrian Y | | | =================================================== Рисунок 24 .5: Пользователи которые имеют привилегию RESOURCE Первые четыре столбца этой таблицы составляют первичный ключ. Это означает что каждая комбинация из tname, владелец-пользователь ( не забудьте, что две различные таблицы с различными владельцами могут иметь одно и тоже имя ), пользователь и пользователь передающий права( гарантор ), должна быть уникальной. Каждая строка этой таблицы содер- жит все привилегии ( которые не являются определенным столбцом ), пре- доставляются одним определенным пользователем другому определенному пользователю в конкретном объекте. UPDATE и REFERENCES, являются при- вилегиями, которые могут быть определенными столбцами, и находиться в различных таблицах каталога. Если пользователь получает привилегии в таблице от более чем одного пользователя, такие привилегии могут быть отдельными строками созданными в этой таблице. Это необходимо для кас- кадного отслеживания при вызове привилегий. ТИПОВОЙ ЗАПРОС. Чтобы найти все привелегии SELECT, INSERT, и DELETE, которые Adrian предоставляет пользователям в таблице Заказчиков, вы можете ввести следующее ( вывод показан в Рисунке 24.6 ): SELECT username, selauth, insauth, delauth FROM SYSTEMTABAUTH WHERE grantor = 'Adrian' ANDtname = 'Customers'; =============== SQL Execution Log ================ | | | SELECT username, selauth, insauth, delauth | | FROM SYSTEMTABAUTH | | WHERE grantor = 'Adrian' | | AND tname = 'Customers' | | ; | | ================================================= | | username selauth insauth delauth | | ----------- ------- -------- -------- | | Claire G Y N | | Norman Y Y Y | | | =================================================== Рисунок 24. 6: Пользователи получившие привилегии от Adrian Выше показано, что Adrian предоставил Claire привилегии INSERT и SE- LECT в таблице Заказчиков, позднее предоставив ей права на передачу привилегий. Пользователю Norman, он предоставил привелегии SELECT, IN- SERT, и DELETE, но не дал возможность передачи привилегий ни в одной из них. Если Claire имела привилегию DELETE в таблице Заказчиков от какого-то другого источника, в этом запросе это показано не будет. SYSTEMCOLAUTH СТОЛБЦЫ ОПИСАНИЕ ─────────────────────────────────────────────────────────── username Пользователь который имеет привилегии grantor Пользователь который предоставляет привилегии другому пользователю tname Имя таблицы в которой существуют привилегии cname Имя столбца в котором существуют привилегии owner Владелец tname updauth Имеет ли пользователь привилегию UPDATE в этом столбце refauth Имеет ли пользователь привилегию REFERENCES в этом столбце Столбцы updauth и refauth могут быть в состоянии Y, N, или G; но не могут быть одновременно в состоянии N для одной и той же строки. Это - первые пять столбцов таблицы, которы не составляют первичный ключ. Он отличается от первичного ключа SYSTEMTABAUTH в котором содержится поле cname, указывающее на определенный столбец обсуждаемой таблицы для ко- торой применяются одна или обе привилегии. Отдельная строка в этой таблице может существовать для каждого столбца в любой данной таблицы в которой одному пользователю передаются превилегии определенного столбца с помощью другого пользователя. Как и в случае с SYSTEMTABAUTH та же привилегия может быть описана в более чем одной строке этой таб- лицы если она была передана более чем одним пользователем. ТИПОВОЙ ЗАПРОС. Чтобы выяснить, в каких столбцах какой таблицы вы имеете привилегию REFERENCES, вы можете ввести следующий запрос ( вы- вод показывается в Рисунке 24.7 ) SELECT owner, tname, cname FROM SYSTEMCOLAUTH WHERE refauth IN ('Y', 'G') AND username = USER ORDER BY 1, 2; который показывает, что эти две таблицы, которые имеют различных вла- дельцев, но одинаковые имя, в действительности, совершенно разные таб- лицы ( т.е. это не как два синонима для одной таблицы ). =============== SQL Execution Log ================ | | | SELECT OWNER, TNAME, CNAME | | FROM SYSTEMCOLAUTH | | WHERE refaulth IN ('Y' , 'G' ) | | AND username = USER | | ORDER BY 1, 2 | | ; | | ================================================= | | owner tname cname | | ----------- ----------- ------- | | Diane Customers cnum | | Diane Salespeople sname | | Diane Salespeople sname | | Gillan Customers cnum | =================================================== Рисунок 24. 7: Столбцы в пользователь имеет привилегию INSERT SYSTEMSYNONS - СИНОНИМЫ ДЛЯ ТАБЛИЦ В БАЗЕ ДАННЫХ Это - имена столбцов в таблице SYSTEMSYNONS и их описание: СТОЛБЕЦ ОПИСАНИЕ ───────────────────────────────────────────────────────────── synonym Имя синонима synowner Пользователь, который является владельцем синонима ( может быть PUBLIC (ОБЩИЙ)) tname Имя таблицы используемой владельцем tabowner Имя пользователя который является владельцем таблицы ТИПОВОЙ ЗАПРОС. Предположим, что Adrian имеет синоним Clients для таблицы Заказчиков принадлежащей Diane, и что имеется общий синоним Customers для этой же таблицы. Вы делаете запрос таблицы для всех си- нонимов в таблице Заказчиков ( вывод показывается в Рисунке 24.8 ): SELECT * FROM SYSTEMSYNONS WHERE tname = 'Customers' =============== SQL Execution Log ================ | | | SELECT * | | FROM SYSTEMSYNONS | | WHERE tname = 'Customers' | | ; | | ================================================= | | synonym synowner tname tabowner | | ----------- ----------- ---------- ---------- | | Clients Adrian Customers Diane | | Customers PUBLIC Customers Diane | | | =================================================== Рисунок 24.8: Синонимы для таблицы Заказчиков ====== ДРУГОЕ ИСПОЛЬЗОВАНИЕ КАТАЛОГА ======= Конечно, вы можете выполнять более сложные запросы в системном ката- логе. Обьединения, например, могут быть очень удобны. Эта команда поз- волит вам увидеть столбцы таблиц и базовые индексы установленые для каждого, ( вывод показывается в Рисунке 24.9 ): SELECT a.tname, a.cname, iname, cposition FROM SYSTEMCOLUMNS a, SYSTEMINDEXES b WHERE a.tabowner = b. tabowner AND a.tname = b.tname AND a.cnumber = b.cnumber ORDER BY 3 DESC, 2; Она показывает два индекса, один для таблицы Заказчиков и один для таблицы Продавцов. Последний из них - это одностолбцовый индекс с име- нем salesno в поле snum; он был помещен первым из-за сортировки по убыванию ( в обратном алфавитном порядке ) в столбце iname. Другой ин- декс, custsale, используется продавцами чтобы отыскивать своих заказ- чиков. Он основывается на комбинации полей snum и cnum внутри таблицы Заказчиков, с полем snum приходящим в индексе первым, как это и пока- зано с помощью поля cposition. =============== SQL Execution Log ================ | | | SELECT a.tname, a.cname, iname, cposition | | FROM SYSTEMCOLUMNS a, SYSTEMINDEXES b | | WHERE a.tabowner = b.tabowner | | AND a.tname = b.tname | | AND a.cnumber = b.cnumber | | ORDER BY 3 DESC, 2; | | | | ================================================= | | tname cname iname cposition | | ----------- ------- -------- ------------ | | Salespeople sname salesno 1 | | Customers cnum custsale 2 | | Customers snum custsale 1 | | | =================================================== Рисунок 24.9 Столбцы и их индексы Подзапросы также могут быть использованы. Имеется способ увидеть данные столбца только для столбцов из таблиц каталога: SELECT * FROM SYSTEMCOLUMNS WHERE tname IN ( SELECT tname FROM SYSTEMCATALOG); Для простоты, мы не будем показывать вывод этой команды, которая состоит из одного входа для каждого столбца каждой таблицы каталога. Вы могли бы поместить этот запрос в представление, назвав его, напри- мер, SYSTEMTABCOLS, для представления SYSTEMTABLES. ================ РЕЗЮМЕ ================= Итак, система SQL использует набор таблиц, называемый ситемным ката- логом в структуре базы данных. Эти таблицы могут запрашиваться но мо- дифицироваться. Кроме того, вы можете добавлять комментарии столбцов в ( и удалять их из) таблицы SYSTEMCATALOG и SYSTEMCOLUMNS. Создание представлений в этих таблицах - превосходный способ точно определить, какая пользовательская информация может быть доступной. Теперь, когда вы узнали о каталоге, вы завершили ваше обучение SQL в диалоговом режиме. Следующая глава этой книги расскажет вам как SQL используется в программах которые написаны прежде всего на других язы- ках но которые способны извлечь пользу из возможностей SQL, взаимо- действуя с его таблицами базы данных. ************** РАБОТА С SQL ************** 1. Сделайте запрос каталога чтобы вывести, для каждой таблицы имеющей более чем четыре столбца, имя таблицы, имя владелеца, а также имя столбцов и тип данных этих столбцов. 2. Сделайте запрос каталога чтобы выяснить, сколько синонимов сущест- вует для каждой таблицы в базе данных. Не забудьте, что один и тот же синоним принадлежащий двум различным пользователям - это факти- чески два разных синонима. 3. Выясните сколько таблиц имеют индексы в более чем пятьдесяти про- центов их столбцов. ( См. Приложение A для ответов. ) . 25. ИСПОЛЬЗОВАНИЕ SQL С ДРУГИМ ЯЗЫКОМ ( ВЛОЖЕННЫЙ SQL ) В ЭТОЙ ГЛАВЕ ВЫ УЗНАЕТЕ КАК SQL ИСПОЛЬЗУЕТСЯ для расширения программ написанных на других языках. Хотя непроцедурность языка SQL делает его очень мощным, в то же время это накладывает на него большое число ог- раничений. Чтобы преодолеть эти ограничения, вы можете включать SQL в программы написанные на том или другом процедурном языке( имеющем оп- ределенный алгоритм). Для наших примеров, мы выбрали Паскаль, считая что этот язык наиболее прост в понимании для начинающих, и еще потому, что Паскаль - один из языков для которых ANSI имеет полуофициальный стандарт. ЧТО ТАКОЕ - ВЛОЖЕНИЕ SQL Чтобы вложить SQL в другой язык, вы должны использовать пакет прог- рамм который бы обеспечивал поддержку вложения SQL в этот язык и ко- нечно же, поддержку самого языка. Естественно, вы должны быть знакомы с языком который вы используете. Главным образом, вы будете использо- вать команды SQL для работы в таблицах базы данных, передачи результа- тов вывода в программу и получение ввода из программы в которую они вкладываются, обобщенно ссылаясь к главной программе ( которая может или не может ппринимать их из диалога или посылать обратно в диалог пользователя и программы ). ЗАЧЕМ ВКЛАДЫВАТЬ SQL? Хотя и мы потратили некоторое время на то чтобы показать что умеет делать SQL , но если вы - опытный программист, вы вероятно отметили, что сам по себе, он не очень полезен при написании программ. Самое очевидное ограничение - это то, что в то время как SQL может сразу вы- полнить пакет команды, интерактивный SQL в основном выполняет по одной команде в каждый момент времени. Типы логических конструкций типа if ... then ( "если ... то" ), for ... do ( "для ... выполнить") и while ... repeat( "пока ... повторять" ) - используемых для структур большинства компьютерных программ, здесь от- сутствуют, так что вы не сможете принять решение - выполнять ли, как выполнять, или как долго выполнять одно действие в результате другого действия. Кроме того, интерактивный SQL не может делать многого со значениями, кроме ввода их в таблицу, размещения или распределения их с помощью запросов, и конечно вывода их на какое-то устройство. Более традиционные языки, однако, сильны именно в этих областях. Они разработаны так чтобы программист мог начинать обработку данных, и ос- новываясь на ее результатах, решать, делать ли это действие или дру- гое, или же повторять действие до тех пока не встретится некоторое ус- ловие, создавая логические маршруты и циклы. Значения сохраняются в переменных, которые могут использоваться и изменяться с помощью любого числа команд. Это дает вам возможность указывать пользователям на ввод или вывод этих команд из файла, и возможность форматировать вывод сложными способами ( например, преобразовывать числовых данных в диаг- раммы ). Цель вложенного SQL состоит в том, чтобы объединить эти воз- можности, позволяющие вам создавать сложные процедурные программы ко- торые адресуют базу данных посредством SQL - позволяя вам устранить сложные действия в таблицах на процедурном языке который не ориентиро- ван на такую структуру данных, в тоже время поддерживая структурную строгость процедурного языка. КАК ДЕЛАЮТСЯ ВЛОЖЕНИЯ 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 или ей подобную. ИСПОЛЬЗОВАНИЕ ПЕРЕМЕННЫХ ======= ОСНОВНОГО ЯЗЫКА В SQL ======= Основной способ которым SQL и части базового языка ваших программ будут связываться друг с другом - это с помощью значений переменных. Естественно, что разные языки распознают различные типы данных для пе- ременных. ANSI определяет эквиваленты SQL для четыре базовых языков - ПЛ/1, Паскаль, КОБОЛ, и ФОРТРАН; все это подробности описаны в Прило- жении B. Эквиваленты для других языков - определяет проектировщик. Имейте в виду, что типы, такие как DATE, не распознаются ANSI; и следовательно никаких эквивалентных типов данных для базовых языков, не существуют в стандарте ANSI. Более сложные типы данных базового языка, такие как матрицы, не имеют эквивалентов в SQL. Вы можете ис- пользовать переменные из главной программы во вложенных операторах SQL везде, где вы будете использовать выражения значений. ( SQL, использу- емый в этой главе, будет пониматься как к вложенный SQL, до тех пор пока это не будет оговорено особо. ) Текущим значением переменной, может быть значение, используемое в команде. Главные переменные должны - * быть обьявленными в SQL DECLARE SESSION ( РАЗДЕЛ ОБЪЯВЛЕНИЙ ) кото- рый будет описан далее. * иметь совместимый тип данных с их функциями в команде SQL ( напри- мер, числовой тип если они вставляется в числовое поле ) * быть назначеными значению во время их использования в команде SQL, если команда SQL самостоятельно не может сделать назначение. * предшествовать двоеточию (:) когда они упоминаются в команде SQL. Так как главные переменные отличаются от имен столбцов SQL наличием у них двоеточия, вы можете использовать переменные с теми же самыми именами что и ваши столбцы, если это конечно нужно. Предположим что вы имеете четыре переменных в вашей программе, с именами: id_num, sales- person, 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, должны сначала быть обьявлены в SQL DECLARE SECTION ( РАЗДЕЛе ОБЪЯВЛЕНИЙ ), использующем обычный синтаксис главного языка. Вы можете иметь любое число таких разделов в программе, и они могут размещаться где-нибудь в коде перед используемой переменной, подчиненной ограничениям опреде- ленным в соответствии с главным языком. Раздел объявлений должен начи- нать и кончаться вложенными командами SQL - BEGIN DECLARE SECTION ( Начало Раздела Объявлений ) и END DECLARE SECTION ( Конец Раздела Объ- явлений ), которым предшествует, как обычно EXEC SQL (Выполнить). Чтобы обьявить переменные используемые в предыдущем примере, вы мо- жете ввести следующее: EXEC SQL BEGIN DECLARE SECTION; Var id-num: integer; Salesperson: packed array (1 . .10) ot char; loc: packed array (1. .10) ot char; comm: real; EXEC SQL END DECLARE SECTION; Для незнакомых с ПАСКАЛем, Var - это заголовок который предшествует ряду обьявляемых переменных, и упакованным ( или распакованным ) мас- сивам являющимися серией фиксированных переменных значений различаемых с помощью номеров( например, третий символ loc будет loc (3) ). Ис- пользование точки с запятой после каждой переменной указывает на то что это - Паскаль, а не SQL. ИЗВЛЕЧЕНИЕ ЗНАЧЕНИЙ ПЕРЕМЕННЫХ Кроме помещения значений переменных в таблицы используя команды SQL, вы можете использовать SQL чтобы получать значения для этих перемен- ных. Один из способов делать это - с помощью разновидности команды SE- LECT которая содержит предложение 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 - это соот- ветственно, уникальный и первичный ключи этой таблицы, а Custo- mers.snum - это внешний ключ ссылающийся на Salespeople.snum, и вы предполагали что этот запрос произведет единственную строку. Имеются другие случаи, когда вы можете знаете, что запрос должен произвести единственную строку вывода, но они мало известны и, в большинстве слу- чаев, вы основываетесь на том что ваши данные имеют целостность, кото- рая не может быть предписана с помощью ограничений. Не полагайтесь на это! Вы создаете программу которая, вероятно, будет использоваться в течение некоторого времени, и лучше всего проиграть ее чтобы быть га- рантированным в будущем от возможных отказов. Во всяком случае, нет необходимости группировать запросы которые производут одиночные стро- ки, поскольку SELECT INTO - используется только для удобства. Как вы увидите, вы можете использовать запросы выводящие многочис- ленные строки, используя курсор. КУРСОР Одна из сильных качеств 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_mo- re должна быть установлена в состояние верно, открыт курсор, и введен цикл. Внутри цикла, строка выбирается из курсора и выводится на экран. У пользователя спрашивают, хочет ли он видеть следующую строку. Пока он не ответил N ( Нет ), цикл повторяется, и следующая строка значений будет выбрана. Хотя переменные Look_at_more и ответ должны быть обьявлены как Буле- ва переменная и символьная(char) переменная, соответственно, в разделе обьявлений переменных в Паскаля, они не должны быть включены в раздел обьявлений SQL, потому что они не используются в командах SQL. Как вы можете видеть, двоеточия перед именами переменных не исполь- зуются для не-SQL операторов. Далее обратите внимание, что имеется оператор CLOSE CURSOR соответствующий оператору OPEN CURSOR. Он, как вы поняли, освобождает курсор значений, поэтому запрос будет нужно вы- полнить повторно с оператором OPEN CURSOR, прежде чем перейти в выбору следующих значений. Это необязательно для тех строк которые были выб- раны запросом после закрытия курсора, хотя это и обычная процедура. Пока курсор закрыт, SQL не следит за тем, какие строки были выбраны. Если вы открываете курсор снова, запрос повторно выполняется с этой точки, и вы начинаете все сначала. Этот пример не обеспечивает автома- тический выхода из цикла, когда все строки уже будут выбраны. Когда у FETCH нет больше строк которые надо извлекать, он просто не меняет значений в переменных предложения INTO. Следовательно, если данные ис- черпались, эти переменные будут неоднократно выводиться с идентичными значениями, до тех пор пока пользователь не завершит цикл, введя ответ - N. ============== SQL КОДЫ =================== Хорошо было бы знать, когда данные будут исчерпаны, так чтобы можно было сообщить об этом пользователю и цикл завершился бы автоматически. Это - даже более важно чем например знать что команда SQL выполнена с ошибкой. Переменная SQLCODE ( называемая еще SQLCOD в ФОРТРАНе ) пред- назначена чтобы обеспечить эту функцию. Она должна быть определена как переменная главного языка и должна иметь тип данных который в главном языке соответствует одному из точных числовых типов SQL, как это пока- зано в Приложении B. Значение SQLCODE устанавливается каждый раз, ког- да выполняется команда SQL. В основном существуют три возможности: 1. Команда выполнилась без ошибки, но не произвела никакого действия. Для различных команд это выглядит по разному: а) Для SELECT, ни одна строка не выбрана запросом. б) Для FETCH, последняя строка уже была выбрана, или ни одной строки не выбрано запросом в курсоре. в) Для INSERT, ни одной строки не было вставлено ( подразумевается что запрос использовался чтобы сгенерировать значения для вставки, и был отвергнут при попытке извлечения любой строки. г) Для UPDATE и DELETE, ни одна строка не ответила условию преди- ката, и следовательно никаких изменений сделано в таблице не будет. В любом случае, будет установлен код SQLCODE = 100. 2. Команда выполнилась нормально, не удовлетворив ни одному из выше указанных условий. В этом случае, будет установлен код SQLCOD = 0. 3. Команда сгенерировала ошибку. Если это случилось, изменения сделан- ные к базе данных текущей транзакцией, будут восстановлены( см. Главу 23 ). В этом случае будет установлен код SQLCODE = некоторому отрицательному числу, определяемому проектировщиком. Задача этого числа, идентифицировать проблему, так точно насколько это возможно. В принципе, ваша система должна быть снабжена подпрограммой, кото- рая в этом случае, должна выполниться чтобы выдать для вас информа- цию расшифровывающее значение негативного числа определенного вашим проектировщиком. В этом случае некоторое сообщение об ошибке будет выведено на экран или записано в файл протокола, а программа в это время выполнит восстановление изменений для текущей транзакции, отключится от базы данных и выйдет из нее. Теперь мы можем усовер- шенствовать ИСПОЛЬЗОВАНИЕ SQLCODE ДЛЯ УПРАВЛЕНИЯ ЦИКЛАМИ Наш предыдущий пример для выхода из цикла автоматически, при условии что курсор пуст, все строки выбраны, или произошла ошибка: Look_at_more: = lhe; EXEC SQL OPEN CURSOR Londonsales; while Look_at_more and SQLCODE = O do begin EXEC SQL FETCH London$ales INTO :id_num, :Salesperson, :loc, :comm; writeln (id_num, Salesperson, loc, comm); writeln ('Do you want to see more data? (Y/N)'); readln (response); If response = 'N' then Look_at_more: = Fabe; end; EXEC SQL CLOSE CURSOR Londonsales; ПРЕДЛОЖЕНИЕ WHENEVER Это удобно для выхода при выполненом условии - все строки выбраны. Но если вы получили ошибку, вы должны предпринять нечто такое, что описано для третьего случая, выше. Для этой цели, SQL предоставляет предложение GOTO. Фактически, SQL позволяет вам применять его доста- точно широко, так что программа может выполнить команду GOTO автомати- чески, если будет произведено определенное значение SQLCODE. Вы можете сделать это соввместно с предложением WHENEVER. Имеется кусгок из при- мера для этого случая: EXEC SQL WHENEVER SQLERROR GOTO Error_handler; EXEC SQL WHENEVER NOT FOUND CONTINUE; SQLERROR- это другой способ сообщить что SQLCODE < 0; а NOT FOUND - это другой способ сообщить что SQLCODE = 100. ( Некоторые реализации называют последний случай еще как - SQLWARNING.) Error_handler - это имя того места в программе в которое будет перенесено выполнение прог- раммы если произошла ошибка ( GOTO может состоять из одного или двух слов ). Такое место определяется любым способом соответствующим для главного языка, например, с помощью метки в Паскале, или имени раздела или имени параграфа в КОБОЛЕ ( в дальнейшем мы будем использовать тер- мин - метка ). Метка более удачно идентифицирует стандартную процедуру распространяемую проектировщиком для включения во все программы. CONTINUE не делает чего-то специального для значения SQLCODE. Оно также является значением по умолчанию. если вы не используете команду WHENEVER, определяющую значение SQLCODE. Однако, эти неактивные опре- деления дают вам возможность переключаться вперед и назад, выполняя и не выполняя действия, в различных точках(метках) вашей программы. Нап- ример, если ваша программа включает в себя несколько команд INSERT, использующих запросы, которые реально должны производить значения, вы могли бы напечатать специальное сообщение или сделать что-то такое, что поясняло бы, что запросы возвращаются пустыми и никакие значения не были вставлены. В этом случае, вы можете ввести следующее: EXEC SQL WHENEVER NOT FOUND GOTO No_rows; No_rows - это метка в некотором коде, содержащем определенное дейс- твие. С другой стороны, если вам нужно сделать выборку в программе позже, вы можете ввести следующее в этой точке, EXEC SQL WHENEVER NOT FOUND CONTINUE; что бы выполнение выборки повторялось до тех пор пока все строки не будут извлечены, что является нормальной процедурой не требующей спе- циальной обработки. ========= МОДИФИЦИРОВАНИЕ КУРСОРОВ ======== Курсоры могут также быть использованы, чтобы выбирать группу строк из таблицы, которые могут быть затем модифицированы или удалены одна за другой. Это дает вам возможность, обходить некоторые ограничения предикатов используемых в командах 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; Это обеспечит вас определенной защитой от случайных модификаций, кото- рые могут разрушить весь порядок в базе данных. ========= ПЕРЕМЕННАЯ INDICATOR ========== Пустые (NULLS) значения - это специальные маркеры определяемые самой SQL. Они не могут помещаться в главные переменные. Попытка вставить NULL значения в главнуюпеременную будет некорректна, так как главные языки не поддерживают NULL значений в SQL, по определению. Хотя ре- зультат при попытке вставить NULL значение в главную переменную опре- деляет проектировщик, этот результат не должен ротиворечить теории ба- зы данных, и поэтому обязан произвести ошибку: код SQLCODE ввиде отри- цательного числа, и вызвать подпрограмму управления ошибкой. Естеcт- венно вам нужно этого избежать. Поэтому, вы можете выбрать NULL значе- ния с допустимыми значениями, не приводящими к разрушению вашей прог- раммы. Даже если программа и не разрушится, значения в главных пере- менных станут неправильными, потому что они не могут иметь NULL значе- ний. Альтернативным методом предоставляемым для этой ситуацией являет- ся - функция переменной indicator(указатель). Переменная indicator - обьявленная в разделе объявлений SQL напоминает другие переменные. Она может иметь тип главного языка который соответствует числовому типу в SQL. Всякий раз, когда вы выполняете операцию, которая должна помес- тить NULL значение в переменную главного языка, вы должны использовать переменную indicator, для надежности. Вы помещаете переменную indica- tor в команду SQL непосредственно после переменной главного языка ко- торую вы хотите защитить, без каких-либо пробелов или запятых, хотя вы и можете, при желании, вставить слово - INDICATOR. Переменной indica- tor в команде, изначально присваивается значение 0. Однако, если про- изводится значение NULL , переменная indicator становится равной отри- цательному числу. Вы можете проверить значение переменной indicator, чтобы узнать, было ли найдено значение NULL. Давайте предположим, что поля city и comm, таблицы Продавцов, не имеют ограничения NOT NULL, и что мы объявили вразделе обьявлений SQL, две ПАСКАЛЬевские переменные целого типа, i_a и i_b. ( Нет ничего такого в разделеобьявлений, что могло бы представить их как переменные indicator. Они станут перемен- ными indicator, когда будут использоваться как переменные indicator. ) Имеется одна возможность: EXEC SQL OPEN CURSOR High_cust; while SQLCODE = O do begin EXEC SQL FETCH High_cust INTO :id_num, :salesperson, :loc:i_a, :commINDlCATOR:i_b; If i_a > = O and i_b > = O then {no NULLs produced} EXEC SQL UPDATE Salespeople SET comm = comm + .01 WHERE CURRENT OF Hlgh_cust; else {one or both NULL} begin If i_a < O then writeln ('salesperson ', id_num, ' has no city'); If i_b < O then writeln ('salesperson ', id_num, ' has no commission'); end; {else} end; {while} EXEC SQL CLOSE CURSOR High_cust; Как вы видите, мы включили, ключевое слово INDICATOR в одном случае, и исключили его в другом случае, чтобы показать, что эффект будет оди- наковым в любом случае. Каждая строка будет выбрана, но команда UPDATE выполнится только если NULL значения не будут обнаружены. Если будут обнаружены NULL значения, выполнится еще одна часть программы, которая распечатает предупреждающее сообщение, где было найдено каждое NULL значение. Обратите внимание: переменные indicator должны проверяться в главном языке, как указывалось выше, а не в предложении WHERE команды SQL. Последнее в принципе не запрещено, но результат часто бывает непред- виденным. ИСПОЛЬЗОВАНИЕ ПЕРЕМЕННОЙ INDICATOR ДЛЯ ЭМУЛЯЦИИ NULL ЗНАЧЕНИЙ SQL Другая возможность состоит в том, чтобы обрабатывать переменную in- dicator, связывая ее с каждой переменной главного языка, специальным способом, эмулирующим поведение NULL значений SQL. Всякий раз, когда вы используете одно из этих значений в вашей прог- рамме, например в предложении if ... then, вы можете сначала проверить связанную переменную indicator, является ли ее значение=NULL. Если это так, то вы обрабатываете переменную по-другому. Например, если NULL значение было извлечено из поля city для главной переменной city, ко- торая связана с переменной indicator - i_city, вы должны установить значение city равное последовательности пробелов. Это будет необходи- мо, только если вы будете распечатывать его на принтере; его значение не должно отличаться от логики вашей программы. Естественно, i_city автоматически установливается в отрицательное значение. Предположим, что вы имели следующую конструкцию в вашей программе: If sity = 'London' then comm: = comm + .01 else comm: = comm - .01 Любое значение, вводимое в переменную city, или будет равно "London" или не будет равно. Следовательно, в каждом случае значение комиссион- ных будет либо увеличино либо уменьшено. Однако, эквивалентные команды в SQL выполняются по разному: EXEC SQL UPDATE Salespeople SET comm = comm + .01 WHERE sity = 'London'; и EXEC SQL UPDATE Salespeople SET comm = comm .01; WHERE sity < > 'London'; ( Вариант на ПАСКАЛе работает только с единственным значением, в то время как вариант на SQL работает со всеми таблицами. ) Если значение city в варианте на SQL будет равно значению NULL , оба предиката будут неизвестны, и значение comm, следовательно, не будет изменено в любом случае. Вы можете использовать переменную indicator чтобы сделать поведение вашего главного языка непротиворечащим этому, с помощью создания усло- вия, которое исключает NULL значения: If i_city > = O then begin If city = 'London' then comm: = comm + .01 else comm: = comm - .01; end; {begin and end нужны здесь только для понимания} ────────────────────────────────────────────────────────────── ПРИМЕЧАНИЕ: Последняя строка этого примера сожержит ремарку - { begin и end необходимы только для понимания } ────────────────────────────────────────────────────────────── В более сложной программ, вы можете захотеть установить Булеву пере- менную в "верно" , чтобы указать что значение city =NULL. Затем вы мо- жете просто проверять эту переменную всякий раз, когда вам это необхо- димо. ДРУГОЕ ИСПОЛЬЗОВАНИЕ ПЕРЕМЕННОЙ INDICATOR Переменная indicator также может использоваться для назначения зна- чения NULL. Просто добавьте ее к имени главной переменной в команде UPDATE или INSERT тем же способом что и в команде SELECT. Если пере- менная indicator имеет отрицательное значение, значение NULL будет по- мещено в поле. Например, следующая команда помещает значения NULL в поля city и comm, таблицы Продавцов, всякий раз, когда переменные in- dicator - i_a или i_b будут отрицательными; в противном случае она по- мещает туда значения главных переменных: EXEC SQL INSERT INTO Salespeople VALUES (:Id_num, :salesperson, :loc:i_a, :comm:i_b); Переменная indicator используется также, чтобы показывать отбрасыва- емую строку. Это произойдет если вы вставляете значения символов SQL в главную переменную которая не достаточно длинна чтобы вместить все символы. Это особая проблема с нестандартным типами данных - VARCHAR и LONG ( смотри Приложению C ). В этом случае, переменная будет заполне- на первыми символами строки, а последние символы будут потеряны. Если используется переменная indicator, она будет установлена в положитель- ное значение, указывающее на длину отбрасываемой части строки, позво- ляя таким образом вам узнать, сколько символов было потеряно. В этом случае, Вы можете проверить с помощью просмотра -значение пе- ременной indicator > 0, или < 0. ================ РЕЗЮМЕ ================= Команды SQL вкладываются в процедурные языках, чтобы объединить силы двух подходов. Некоторые дополнительные средства SQL необходимы, чтобы выполнить эту работу. Вложенные команды SQL транслируемые программой, называемой прекомпилятором, в форму пригодную для использования транс- лятором главного языка, и используемые в этом главном языке, как вызо- вы процедуры к подпрограммам которые создает прекомпилятор, называются - модулями доступа. ANSI поддерживает вложение SQL в языки: ПАСКАЛЬ, ФОРТРАН, КОБОЛ, и PL/I. Другие языки также используются, особенно Си. В попытке кратко описать вложенный SQL, имеются наиболее важные места в этой главе: * Все вложенные команды SQL начинаются словами EXEC SQL и заканчивают- ся способом который зависит от используемого главного языка. * Все главные переменные доступные в командах SQL, должны быть обьяв- лены в разделе объявлений SQL прежде, чем они будут использованы. * Всем главным переменным должно предшествовать двоеточие когда они используются в команде SQL. * Запросы могут сохранять свой вывод непосредственно в главных пере- менных, используя предложение INTO, если и только если, они выбирают единственную строку. * Курсоры могут использоваться для сохранения вывода запроса, и досту- па к одной строке в каждый момент времени. Курсоры бывают обьявлены- ми ( если определяют запрос в котором будут содержаться), открытыми( если выполняют запрос ), и закрытыми ( если удаляют вывод запроса из курсора ). Если курсор открыт, команда FETCH, используется чтобы пе- ремещать его по очереди к каждой строке вывода запроса. * Курсоры являются модифицируемыми или только-чтение. Чтобы стать мо- дифицируемым, курсор должен удовлетворять всем критериям которым удовлетворяет просмотр; кроме того, он не должен использовать пред- ложений ORDER BY или UNION, которые в любом случае не могут исполь- зоваться просмотрами. Не модифицируемый курсор является курсором только-чтение. * Если курсор модифицируемый, он может использоваться для определения, какие строки задействованы вложенными командами UPDATE и DELETE че- рез предложение WHERE CURRENT OF. DELETE или UPDATE должны быть вне той таблицы к которой курсор обращается в запросе. * SQLCODE должен быть обьявлен как переменная числового типа для каж- дой программы которая будет использовать вложенный SQL. Его значение установливается автоматически после выполнения каждой команды SQL. * Если команда SQL выполнена как обычно, но не произвела вывода или ожидаемого изменения в базе данных, SQLCODE = 100. Если команда про- извела ошибку, SQLCODE будет равняться некоторому аппаратноопреде- ленному отрицательному числу которое описывает ошибку. В противном случае, SQLCODE = 0. * Предложение WHENEVER может использоваться для определения действия которое нужно предпринять когда SQLCODE = 100 (не найдено) или когда SQLCODE равен отрицательному числу ( SQLERROR ). Действием может быть или переход к некоторой определенной метке в программе ( GOTO