Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Линтер методичка

.pdf
Скачиваний:
78
Добавлен:
21.05.2015
Размер:
1.38 Mб
Скачать

 

 

./inl –u SYSTEM/MANAGER

 

Пр

SQL>select * from T1;

акт

 

 

ич

Задача 3. Создать таблицу T1 и загрузить ее данные из файла формата XML.

 

еск

1. Предполагаем, в файле /home/linter/prac3/dbstore_xml/SYSTEM.lod/T1.xml

 

ое

находится описание таблицы T1 и ее данных;

 

зан

2. Перейдем в каталог bin дистрибутива и подадим команду на удаление данных из

 

ят

таблицы при помощи утилиты inl:

 

./inl –u SYSTEM/MANAGER

 

ие

 

Заг3.

SQL>drop table T1;

 

руз

3. Запустим процесс импорта данных командой:

 

каи

./loarel –u SYSTEM/MANAGER –f /home/linter/prac3/

 

вы

dbstore_xml/SYSTEM.lod/T1.xml –x

 

гру

(указанная команда привела к падению loarel на Linux x86_64, на Windows падение

не воспроизводится)

 

зка

При этом будет создана таблица и будут импортированы данные.

дан

 

ны

Задача 4. (студенты выполняют самостоятельно) Создать три таблицы,

 

х

описывающие взаимосвязь между автомобилями и их владельцами (T_AUTO, T_PERSON,

T_LINK), с учетом хранения промежутков времени владения человеком данной машиной.

!21

Заполнить их данными. Провести экспорт в текстовые файлы, поля которых разделены ‘#’.

Импортировать данные в другие таблицы (T1_AUTO, T1_PERSON, T1_LINK).

3.4. Импорт данных из формата DBF

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

Полный формат вызова утилиты таков:

E- mail

:

mar ket @re lex.r u

dbf2lin -f

имя_файла_dbf

[-u

имя_пользователя/пароль]

[-n

сервер] [-t имя_таблицы]

[-i

имя_файла_входных_параметров]

[-o

имя_файла_выходных_параметров]

[-b

тип_данных_атрибута_blob]

[-s номер_уровня_загрузки]

[-l] [-p] [-e] [-h] [-version]

[-k тип кодировки]

[-2

имя_файла_перенаправления_вывода]

[-c

количество записей в одной транзакции]

[-d

имя_blob_файла]

22!

Пр

Задача 5. Создать таблицу AUTO и импортировать в нее данные из файла /home/

linter/samples/db/dbf/autoакт

.dbf

ич

1. Создадим таблицу AUTO (предполагаем, что СУБД ЛИНТЕР установлена в

еск

каталог /home/linter):

 

ое

inl –u SYSTEM/MANAGER –f /home/linter/samples/db/sql/auto/

 

занят

cauto.sql;

 

ие

2.

Запустим процесс импорта:

3.

dbf2lin –u SYSTEM/MANAGER –t AUTO –f /home/linter/samples/

 

Загруз

db/dbf/auto.dbf

 

ка

Задача 6. (студенты выполняют самостоятельно) Импортировать данные из

 

и

файла /home/linter/samples/db/dbf/person.dbf без предварительного создания таблицы.

 

вы

 

 

 

гру

3.5. Импорт и экспорт данных при помощи ldba

 

зка

Как было показано в таблице (см. «Обзор средств для экспорта-импорта данных»),

многиедан утилиты администрирования поддерживают импорт и экспорт данных.

 

ны

В утилите ldba существует специальное подменю Файл, в котором представлены

пункты для импорта и экспорта данных.

 

х

Задача 7. Импортировать данные из файла:

 

 

 

 

/home/linter/prac3/dbstore_xml/SYSTEM.lod/T1.xml.

 

 

1.

Запустим ldba:

 

 

 

 

ldba –u SYSTEM/MANAGER

 

 

 

 

2.

Через меню вызовем диалог Файл|Импорт из XML;

 

 

3.

Выберем файл /home/linter/prac3/dbstore_xml/SYSTEM.lod/T1.xml;

 

 

4.

Согласимся с конвертированием в новую таблицу;

 

 

5.

Введем имя новой таблицы T1_1;

 

 

6.

В появившемся диалоге выберем кнопку Выполнить.

Задача 8. (студенты выполняют самостоятельно) Создать таблицу

AUTO_PLANTS со столбцами ID типа integer, автоинкремент, первичный ключ; и NAME типа char(50), уникальное. Импортировать данные из файла /home/linter/samples/db/dbf/auto.dbf,

занося в столбец NAME данные из столбца MAKE.

E- mail

:

mar ket @re lex.r u

Практическое занятие 4 Создание, исполнение и отладка хранимых процедур и триггеров

Практика 2 часа (Лекция 4)

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

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

При проектировании баз данных часть в качестве первичного ключа некоторой таблицы используется суррогатный ключ – уникальное числовое значение. Для генерации значений такого ключа удобно использовать механизм последовательностей (sequence). Однако, в СУБД ЛИНТЕР версий младше 5.9 последовательности не поддерживаются. Другим удобным и универсальным способом для генерации уникальных значений может быть использование хранимой процедуры. Хранимая процедура будет обращаться к специальной таблице (назовем ее SEQUENCES), чтобы определить очередное значение.

Таблицу SEQUENCES можно создать следующим запросом:

create table SEQUENCES(

TAB_NAME VARCHAR(64) primary key not null,

CUR_VALUE INT

not null

);

Здесь колонка TAB_NAME хранит имена таблиц, для которых генерируются уникальные значения, а колонка CUR_VALUE – текущее значение для генерации.

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

SQL-операторы на создание таблицы SEQUENCES и хранимой процедуры

GeneratePK содержатся в файле sp_seq.sql. Процедура создается таким запросом:

create procedure GeneratePK(in tabname char(64)) result int

for debug

declare

var v cursor(value int); //

code

execute direct "lock table SEQUENCES wait;"; //

execute direct "update SEQUENCES set CUR_VALUE = CUR_VALUE + 1 where TAB_NAME = '"+

tabname+"';"; // if rowcount() > 0 then

24!

актПр

open v for direct "select CUR_VALUE from SEQUENCES

where TAB_NAME = '"+

ич

tabname+"';"; //

еск

else

ое

зан

execute direct "insert into SEQUENCES values

ятие

('"+tabname+"',1);"; //

4

v.value := 1; //

Со

endif

зда

ни

execute direct "unlock table SEQUENCES;"; //

е,

ис

commit; //

по

return v.value; //

лне

ни

end;

е и

Перед созданием процедуры должна быть создана таблица процедур с

отл

использованием dict\systab.sql.

адк

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

а

 

одновременно работающих пользователей от возможности модификации одних и тех же

данныххра ; надо иметь в виду, что работать это будет только в режиме обработки транзакций,

отличном от autocommit).

ни

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

мы

запятой, содержат

пустой комментарий в конце строки (символы //). Эта простая техника

х

 

 

позволяет выполнять такой запрос из утилиты inl , которая считает точку с запятой в конце

пр

 

 

строки признаком окончания запроса.

оце

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

соответствующегодур

запроса; например, из утилиты inl.

иВыполняем команды:

тр

перейти в директорию, содержащую файл sp_seq.sql;

игг

запускаем inl и соединяемся с сервером БД;

еро

исполняем файл: _sp_seq.

в

Теперь процедура создана. Запустить ее можем тут же, из inl, например:

SQL>

Return value = 1

SQL> execute generatepk(‘TEST’);

Return value = 2

Мы создали процедуру с опцией FOR DEBUG , что позволяет нам отлаживать ее исполнение.

Для отладки необходимо запустить утилиту spman

(ЛИНТЕР-ВС: в дистрибутиве ЛИНТЕР-ВС утилита для отладки хранимых процедур

итриггеров называется spdebug; отличается она тем, что в ней нет возможности создавать

иEмодифицировать- процедуры).

mail По команде «Открыть процедуру…» в меню «Файл» выбираем нужную процедуру.

Ее: код показывается в отдельном окне. Теперь можно запустить ее из-под отладчика mar

ket @re lex.r u

4.2. Модификация процедуры, синтаксические ошибки

(команда «Отладчик/Пуск» или клавиша F9). На запрос входных параметров укажем значение ‘TEST’ (апострофы обязательны для символьных констант). В результате

процедура запускается и останавливается для отладки на первой строке (она

Пр

подсвечивается). Можно наблюдать значения локальных переменных.

акт В меню «Отладчик» перечислены команды для отладки процедур и

соответствующиеич

горячие клавиши. Так, можно пройти процедуру по шагам, исполнять

процедуру до точек останова, до конца, или принудительно завершить процедуру, если наc

еск

 

 

 

не устраивает текущих ход ее работы. Кроме локальных переменных, можно

ое

 

 

 

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

зан

 

 

 

другой процедуры).

ят

Выполняем команды:

ие

проходим процедуру по шагам до конца (в окне «Сообщения» отображаются

4

 

результаты работы);

Со

заново запускаем процедуру, но в качестве параметра передаем ‘TEST1’;

зда

 

проходим процедуру по шагам (видно, что исполнение теперь идет по другой

ни

 

ветке: вставляется новая запись в таблицу SEQUENCES).

 

 

 

е,

Отлаживать можно также процедуру, запущенную не только из-под отладчика, но и

ис

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

по

 

 

 

процедуру в spman, открыть отладочную сессию (если она еще не открыта), и

активизироватьлне

ожидание (команда «Ждать процедуру/триггер»). Теперь можно в другой

сессиини запустить inl и подать запрос типа execute. Процедура попадет в режим отладки, а

inl будет ждать ее окончательного завершения.

е и отл

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

генерируемых нашей процедурой значений. Для этого надо добавить еще один входной

а

 

параметр (со значением по умолчанию 1), и использовать его в теле процедуры.

хра

Чтобы модифицировать процедуру , надо выполнить запрос alter procedure с полным

ни

новым телом процедуры. Можно сделать соответствующий sql-файл и использовать inl, но

мы

еще удобнее воспользоваться spman.

х

пр

оце

дур

и

тр

игг

еро

в

25!

Просто отредактируем заголовок процедуры в ее окне: после

tabname char(64)

всписке параметров добавим

;in change int default 1

Теперь надо модифицировать запрос в строке 7

execute direct "update SEQUENCES set CUR_VALUE = CUR_VALUE + 1 where TAB_NAME = '"

пытаемся заменить на

execute direct "update SEQUENCES set CUR_VALUE = CUR_VALUE + “+change+” where TAB_NAME = '"

Теперь для сохранения процедуры просто можно выбрать команду обработки в меню «Файл» или нажать клавишу <F2>. spman автоматически генерирует запрос alter procedure. Однако трансляция процедуры на этот раз проходит неудачно, и мы получаем окно со списком ошибок. Нажав <Enter> на тексте сообщения об ошибке, можно попасть в то место, где была ошибка.

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

mail

:

mar ket @re lex.r u

26!

Пр

execute direct "update SEQUENCES set CUR_VALUE = CUR_VALUE

акт

+ “+itoa(change)+” where TAB_NAME = '"

ич

Теперь процедура транслируется удачно, о чем свидетельствует исчезнувшее окно с

ошибками. Заметим, что при подаче запроса из inl мы не получим полной расшифровки

еск

 

 

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

ое

Попробуйте запустить процедуру с разными значениями параметра change. Можно

зан

запустить процедуру из inl , по-прежнему указывая всего один параметр. Второй параметр

ят

 

 

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

 

ие

4.3. Обработка ошибок, исключения

 

4

 

Наша процедура будет некорректно работать, если в качестве значения параметра

Со

change передать значение, меньшее единицы, или NULL

. Мы можем вставить

зда

 

 

соответствующую проверку в начало тела процедуры:

 

ни

if change = NULL or change < 1 then

 

е,

 

ис

signal INVPARAM;

 

по

 

лне

endif

 

ни

Оператор signal вызывает исключение с именем

INVPARAM. Это исключение

необходимои описать в секции деклараций процедуры, например перед ключевым словом code:отл

адка

exception INVPARAM for BADPARAM;

хра

Здесь мы назначили исключению имя стандартного и критичного исключения

BADPARAM, т.е. оно тут же завершает исполнение хранимой процедуры, и передает

ни

 

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

мы

 

всегда будет NULL.

х

Попробуем запустить процедуру и указать неправильное значение (например, 0) в

качествепр параметра change . Отладчик всегда останавливается в строке, в которой произошлооце исключение.

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

код возврата 7201.

тр

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

игг

EXCEPTIONS, как было рассмотрено в лекции.

еро

Например, если присоединиться к БД от имени пользователя, отличного от

в

создателя процедуры, то при попытке выполнить процедуру (ее имя в запросе execute, кстати, должно будет уже включать имя владельца, отделенной точкой от собственно имени) может возникнуть ошибка доступа к таблице SEQUENCES 2202 (у пользователя скорее всего не будет таблицы с именем SEQUENCES).

(Замечание: конечно, мы могли бы в запросах явно написать SYSTEM.SEQUENCES; это более верно, но тем не менее, может возникнуть ошибка нарушения прав доступа; чтобы процедура реально работала для нескольких пользователей, обычно удобно создать общие (PUBLIC) синонимы на используемые таблицы, или же использовать полное имя таблицы, и в любом случае надо назначить необходимые права

grant execute on GENERATEPK to PUBLIC;).

Если мы подадим запрос на выполнение SYSTEM.GENERATEPK из-под другого пользователя (не SYSTEM), то увидим, что процедура всегда возвращает 1. Чтобы разобраться, в чем дело, можно запустить ожидание процедуры и прийти в режим ее отладки. В отладчике мы видим, что при попытке исполнения запросов происходит исключение 2202 (нет такой таблицы), но процедура продолжает исполняться. Это происходитE- из-за того, что исключение некритичное, и при отсутствии обработки оно просто игнорируетсяmail . Значит, нам необходимо обработать это исключение.

: В секции определений объявим исключение: mar

ket @re lex.r u

 

 

exception NOTAB for 2202;

актПр

А в конец процедуры (перед end) добавим блок обработки исключения:

exceptions

 

ич

when NOTAB then

 

еск

resignal;

 

ое

 

зан

Оператор resignal завершает процедуру и передает состояние исключения на

уровень выше.

 

ят

Теперь наша процедура корректно реагирует на ошибку 2202 исполнения запросов.

 

ие

 

4.4. Упражнение: создание еще одной процедуры

4

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

 

Со

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

создана другая таблица с таким же именем. По-хорошему, надо при удалении таблицы

удалять и соответствующую запись из таблицы SEQUENCE.

 

ни

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

 

е,

DropTabWithSequence. Процедура должна сначала удалить таблицу, а затем, в случае

ис

 

успеха, удалить соответствующую запись из таблицы SEQUENCES.

по

spman предоставляет специальный интерфейс для создания процедур: по сути он

лне

 

просто подготовит запрос create procedure для Вас. Используйте пункт «Новая процедура»

менюни «Файл». Предупреждение: в диалоге создания процедуры регистр символов имени

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

е и

 

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

отл

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

адк

параметр (имя таблицы) и тело процедуры (указание: для проверки любого ненормального

кода завершения запроса drop table удобно использовать стандартную функцию errcode).

Примечаниех а : Студенты пытаются написать процедуру сами…

ни (процедура должна иметь примерно такой вид:

мы

create procedure "DROPTABWITHSEQUENCE"(in tabname char(64))

х

for debug

оцепр

code

дур

execute direct "drop table "+tabname+";";

и

if errcode() = 0 then

тр

игг

execute direct "delete from SEQUENCES where

еро

tab_name='"+tabname+"';";

вcommit;

27! endif end;

)

4.5. Создание триггера

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

create table object(id int primary key, value varchar(64));

E-

Триггер позволяет принудительно установить значение поля ID новой записи, какое

бы значение не задал пользователь в исходном запросе: mail

: create trigger INS_OBJECT before insert on object

mar ket @re lex.r u

28!

Пр

for each row execute for debug

акт

code

ич

call GeneratePk("OBJECT") into new.id; //

еск

ое

end;

зан

Соответствующие SQL-операторы содержатся в файле trig_obj.sql. Его можно

ят

 

исполнить обычным способом, через inl. Теперь попробуйте добавить несколько записей в

таблицуие

object. Потом подать SELECT из этой таблицы – мы видим сгенерированные

значения колонки ID.

4

 

Со Это иллюстрация только одной области применения триггеров. Допустим теперь, что

мы хотим хранить информацию обо всех изменениях, сделанных в таблице OBJECT в

зда

 

разные моменты времени. Для этого создадим еще одну таблицу:

ни

create table object_history(id int, dt date, value

е,

ис

varchar(64), status char(1));

лнепо

alter table object_history add primary key(id, dt);

ни

Дополнительными атрибутами здесь являются dt – дата изменения, и status – статус

изменения – одна из букв ‘I’, ‘U’ или ‘D’.

е и

Чтобы обеспечить занесение в эту таблицу записей после успешного изменения

отл

таблицы OBJECT, можно создать триггеры на различные виды DML -запросов аналогично

адк

 

следующему:

а

create trigger INS_OBJECT_HIST after insert on object

хра

мыни

for each row execute for debug

х

code

пр

execute direct

оце

дур "insert into OBJECT_HISTORY(id, dt, value, status)

и

values(“ +

тр

itoa(new.id) + “, sysdate, '” + new.value + “',

игг

еро

'I');"; //

вend;

Вфайле obj_hist.sql содержатся запросы на создание таблицы OBJECT_HISTORY и двух триггеров на добавление и на изменение данных.

Прогоните этот файл в inl и попробуйте добавить и обновить несколько записей в таблице OBJECT. Затем сделайте SELECT из таблицы OBJECT_HISTORY . Вы должны увидеть историю сделанных изменений.

Теперь не хватает только триггера на DELETE. Создайте его самостоятельно. В качестве упражнения используем для этого утилиту spman. Утилита spman включает диалоговое окно для упрощения создания триггера, также как и для хранимых процедур. Выберите пункт «Новый триггер» в меню «Файл». Введите все характеристики триггера в диалоговом окне. У вас должно открыться окно с шаблоном нового триггера, примерно такого содержания:

E- mail

:

mar ket @re lex.r u

create trigger "DEL_OBJECT_HIST" after delete on OBJECT for each row execute for debug

 

 

 

code

 

Пр

end;

акт

Вставьте соответствующий оператор между code и end и сохраните триггер (как

обычно, командой сохранения или клавишей <F2>).

 

ич

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

 

еск

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

 

ое

4.6. Модификация триггера

 

зан

 

Для модификации триггера необходимо удалить старый триггер (запрос drop trigger),

 

ят

и затем пересоздать новый. spman автоматически делает эти операции при необходимости.

 

ие

Модифицируем наш самый первый триггер (INS_OBJECT) так, чтобы он запрещал

4

 

добавление в таблицу OBJECTS строк, у которых значение value равно NULL (простейшая

 

Со

 

проверка, которая может быть выполнена и декларативным способом, но потенциально

триггер может реализовать любой алгоритм проверки).

 

зда

 

 

ни

Выбираем в spman команду «Открыть триггер» меню «Файл». В результате

откроется окно существующего триггера.

 

е,

В начало тела триггера добавим проверку:

 

ис

 

по

if new.value = NULL then

лне

return false;

 

ни

 

е и

endif

 

отл

Возврат false из триггера означает запрет текущей операции.

адк

Сохраняем триггер, и пробуем теперь выполнить в inl такой запрос:

 

храа

 

insert into object(id) values(0);

 

ни

Получаем сообщение о том, что добавлено 0 строк – это результат работы триггера.

 

мы

4.7. Отладка триггера

 

х

Триггер можно отлаживать точно так же, как и хранимую процедуру. Отличие в том,

 

пр

 

 

что можно отладить триггер, только ожидая его инициирования внешним запросом.

 

оце

Откроем в spman триггер INS_OBJECT. Включим отладочную сессию (клавиша

<F4>)дур

и режим ожидания (клавиши <Alt>-<F9>). Теперь перейдем в inl и подадим запрос:

 

три

insert into object(id) values(0);

игг

В результате попадаем в отладку триггера. Можно отследить по шагам, что

происходит внутри него.

еро

Повторим то же самое для другого, допустимого запроса. Видно, что триггер

в

 

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

процедуры.

29!

E- mail

:

mar ket @re lex.r u

30!

Практическое занятие 5 Разграничение доступа в СУБД ЛИНТЕР. Дискреционный доступ

Практика 2 часа (Лекция 5)

Целью занятия является освоение способов разграничения доступа к базе данных на основе дискреционного доступа. При выполнении задания будет использовано приложение «Интерактивный SQL» (INL).

5.1. Создание нового пользователя. Полномочия пользователей

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

Задача: Создать двух пользователей A и B, создать от имени пользователя A таблицу для хранения телефонной книги, предоставить пользователю B доступ для чтения и добавления данных.

Решение:

Запускаем утилиту INL, вводим имя и пароль администратора базы данных или пользователя обладающего привилегией администратора базы данных (DBA).

1.Создаем пользователей A и B с паролями ‘1’ и ’2’: create user A identified by ‘1’;

create user B identified by ‘2’;

2.Даем пользователю A привилегию RESOURCE: grant resource to A;

3.Выходим из утилиты INL командой exit и запускаем утилиту повторно с именем пользователя ‘A’ и паролем ‘1':

4.Создаем таблицу для хранения телефонов:

create table PHONES(ID integer autoinc,NAME char(20), PHONE char(12));

5.Передаем привилегии на чтение и вставку пользователю “B’: grant select,insert on PHONES to B;

6.Выходим из утилиты INL командой exit и запускаем утилиту повторно с именем пользователя ‘B’ и паролем ‘2':

7.Осуществляем вставку данных:

insert into A.PHONES(NAME,PHONE) values(‘Иванов’,’8-095-167-34-80’); 8. Осуществляем выборку данных:

select * from A.PHONES;

Задачи для самостоятельной работы:

1.Создать пользователя с привилегией DBA, создать от имени этого пользователя таблицы для хранения почтовых адресов предприятий (название, почтовый индекс, город, улица, дом, а/я). Созданным ранее пользователям A,B предоставить возможность заполнения, модификации, удаления и просмотра данных из этой таблицы. Всем остальным пользователям предоставить возможность только чтения данных из созданной таблицы.

5.2.Использование представлений для разграничения доступа

Для «вертикального» разграничения доступа к таблице используются представления. Рассмотрим это на следующей задаче.

E-mail: market@relex.ru

ЗАО НПП «РЕЛЭКС»

http://www.relex.ru