Помимо сравнительно простых встроенных типов данных — как перешедших из стандартов SQL, так и собственных, — в Oracle имеется возможность использовать составные. Это конструируемые типы объектов, рассчитанные на хранение в БД данных, имеющих внутреннюю структуру. Эта структура известна СУБД, и СУБД позволяет с ней работать. Объектные типы позволяют хранить и обрабатывать средствами СУБД "сложно устроенные данные" более продвинутым образом, нежели это позволяет техника "больших неструктурированных объектов" типов LOB. Ввиду наличия вполне определенного типа (даже если это
Хранение в столбцах таблицы значений в виде объектов, в смысле объектного подхода (ОП в программировании и моделировании), фирма Oracle впервые обеспечила в рамках так называемой "объектно-реляционной модели" начиная с версии Oracle 8. Некоторые существенные пробелы первой реализации (например, отсутствие
Ниже приводится простой пример использования программируемых (объектных) типов.
Вначале требуется создать "тип", как разновидности хранимых элементов БД. Пример создания типа объекта (в SQL*Plus):
CREATE TYPE address_type AS OBJECT ( zip CHAR ( 6 ) , location VARCHAR2 ( 200 ) ) /
Здесь типу ADDRESS_TYPE приписаны два "свойства" (по объектной терминологии): ZIP и LOCATION. В реальной жизни для представления адреса в типе наверняка будет указано большее количество свойств, однако в ознакомительном примере их более пространный перечень излишен и не добавит понимания техники.
Определение типа напоминает определение таблицы, однако в отличие от таблицы (а также стандарта SQL и от реляционного подхода) тип объекта в Oracle не имеет права содержать ограничений целостности (которые в таком случае можно было бы назвать "ограничениями целостности типа"). Если необходимо их указать, сделать это придется только по месту употребления типа, то есть в описании таблицы.
В соответствии с традициями объектного подхода (уместно вспомнить, что "объектной теории", в отличие от реляционной, не создано) Oracle разрешает использовать тип для создания "буквальных значений" и собственно объектов. Далее приводится сначала несколько примеров первого, а затем второго.
"Буквальные значения" фактически позволяют работать со значениями, обладающими известной СУБД структурой и однозначно определяются набором значений элементов своей структуры.
Примеры использования типа ADDRESS_TYPE для определения столбца в обычной таблице:
CREATE TABLE odept1 ( dname VARCHAR2 ( 20 ) , deptno NUMBER ( 2 ) CONSTRAINT pk_odept1 PRIMARY KEY , addr address_type ); CREATE TABLE oemp1 ( ename VARCHAR2 ( 20 ) , empno NUMBER ( 4 ) CONSTRAINT pk_oemp1 PRIMARY KEY , deptno NUMBER ( 2 ) CONSTRAINT fk_oemp1 REFERENCES dept , home address_type );
Столбцы ADDR и HOME можно с некоторой вольностью назвать "объектными атрибутами". Они не позволяют хранить объектные значения в виде самостоятельной сущности и ссылаться на них ссылками. Локализовать такие значения можно только по обычным правилам поиска данных в таблице.
В выражениях явно указанные объектные значения формулируются с помощью конструктора. В отличие от других объектных систем, например, от Java, в Oracle конструктор умолчательно имеет список параметров, соответствующих свойствам типа. Примеры применения в операциях добавления данных:
INSERT INTO odept1 ( deptno, dname, addr ) VALUES ( 10, 'RESEARCH', address_type ( '123456', 'Archangelsk' ) ) ; INSERT INTO oemp1 ( empno, ename, deptno, home ) VALUES ( 1111, 'SMITH', 10, address_type ( '789012', 'Samara' ) ) ;
Oracle допускает определенные синтаксические вольности в записи выражения над объектными данными. Здесь и далее используются частные случаи возможных формулировок.
Пример применения в запросе о сотрудниках, "работающих по месту жительства", за исключением конкретно указанного адреса:
SELECT e.ename, d.dname
FROM oemp1 e INNER JOIN odept1 d
ON e.home = d.addr
WHERE e.home <> address_type ( '345678', 'Leningrad' )
;
Пример показывает легкость формулирования сравнения составных величин, каковыми являются адреса. Сравнение осуществляется поэлементно, путем сравнением всех свойств по очереди. Увы, но простота формулировки не дает права программисту расслабляться и забывать об особых случаях сравнения с данными типа CHAR и с NULL. Так, присутствие NULL в буквальных объектных значениях запутывает проблему сравнения еще больше, чем для случая простых типов. Сравните:
SQL> SELECT 'OK' ok FROM dual 2 WHERE address_type ( NULL, 'x' ) IS NOT NULL; OK -- OK SQL> SELECT 'OK' ok FROM dual 2 WHERE address_type ( NULL, 'x' ) = address_type ( NULL, 'x' ); no rows selected
То есть получается, что x = x не дает TRUE, но притом x IS NOT NULL дает TRUE (x имеет значение).
В выражениях можно обращаться к буквальному объектному значению как к целому, а можно и к его отдельным свойствам. Во втором случае, как правило, требуется прибегать к псевдониму:
COLUMN home FORMAT A35 SELECT e.ename, e.home, e.home.zip FROM oemp1 e ;
Созданный в БД тип можно употребить и для создания "таблиц объектов":
CREATE TABLE addresses1 OF address_type; CREATE TABLE addresses2 OF address_type;
Хотя для этой категории хранимых элементов используется термин "таблица", такая таблица всегда содержит ровно один столбец, и именно объектного типа.
Запись занесения "строк" в такую таблицу может быть, в частности, такой:
INSERT INTO addresses1 VALUES ( '123456', 'Archangelsk' );
Пример запроса:
SELECT a.*, UPPER ( location ) FROM addresses1 a;
Объекты в таких таблицах хранятся как самостоятельные сущности, у которых имеется автоматически порождаемый СУБД внутренний уникальный идентификатор object ID, в соответствии с классическим объектным подходом позволяющий ссылаться на конкретные объекты из других таблиц или из программы. Сравнение элементов-"строк" в таблице объектов друг с другом происходит уже не по значениям свойств, как в случае объектного столбца в обычной таблице, а по значению object ID. Перейти на сравнение значений свойств позволяет функция VALUE, например:
SELECT dname FROM odept1 d, addresses1 a WHERE d.addr = VALUE ( a );
Сделан запрос об отделах, расположенных по адресам из таблицы ADDRESS1.
Не исключено, что создатели функции VALUE обсуждали другое ее название — LITERAL_VALUE. По крайней мере, оно точнее описывает совершаемое действие: создание значения со структурой из объекта. Буквальные значения сравниваются друг с другом по значениям их свойств, а объекты — по значениям object ID.
Благодаря наличию внутренних идентификаторов object ID у объектов из таблиц объектов и возможности на них ссылаться локализовать такие объекты становится возможным с применением не только обычной техники SQL, но и навигации с помощью ссылок.
Пример создания обычной таблицы со столбцом для ссылки на хранимый в таблице объектов (типа ADDRESS_TYPE) элемент-объект:
CREATE TABLE odept2 ( dname VARCHAR2 ( 50 ) , deptno NUMBER CONSTRAINT pk_odept PRIMARY KEY , addr REF address_type SCOPE IS addresses1 );
Здесь описание ссылки сужено возможностью адресоваться только к объектам из таблицы ADDRESSES1. Допускаются варианты описания ссылки: ее нацеленность на содержимое конкретной таблицы можно не применять или же, напротив, усилить до аналогии со ссылочной целостностью (в этом примере аналогия с правилом внешнего ключа неполная).
Пример заполнения поля ADDR значением-ссылкой:
INSERT INTO odept2
( dname, deptno, addr )
VALUES
( 'RESEARCH', 10, ( SELECT REF ( a )
FROM addresses1 a
WHERE a.location = 'Archangelsk'
) )
;
Пример допустимых оформлений обращения к свойствам объекта через ссылку:
COLUMN deref(d.addr) FORMAT A40 SELECT d.dname, DEREF ( d.addr ), d.addr.zip FROM odept2 d ;
Навигация по объектам в БД с помощью ссылок и в том числе извлечение объекта из БД возможны не только в запросах SQL, но и в программе на PL/SQL.
Более сложные конструкции в описании типа позволяют задавать методы объектов и типов. Пример указания в типе метода:
CREATE TYPE employee_type AS OBJECT ( name VARCHAR2 ( 50 ) , hiredate DATE , home REF address_type , MEMBER FUNCTION days_at_company RETURN NUMBER ) /
Когда методов много, их заголовки перечисляются по очереди через запятую, общим списком со свойствами.
В описании типа приводится только заголовок методов. Для описания тела метода необходимо создать тело типа (полная аналогия пары пакет — тело пакета, имеющейся в PL/SQL):
CREATE TYPE BODY employee_type AS MEMBER FUNCTION days_at_company RETURN NUMBER IS BEGIN RETURN TRUNC ( SYSDATE - hiredate ); END; END; /
Пример использования типа в создании таблицы:
CREATE TABLE sailors ( ship VARCHAR2 ( 30 ), emp employee_type ); -- в этом контексте EMPLOYEE_TYPE — это имя типа
Использование конструктора типа для заполнения таблицы:
INSERT INTO sailors VALUES ( 'Ninna', employee_type ( 'Frank Naude', SYSDATE, NULL ) ) ; -- в этом контексте EMPLOYEE_TYPE — это конструктор
Использование свойств и метода типа для запроса к таблице:
COLUMN emp FORMAT a60 SELECT * FROM sailors; SELECT x.ship, x.emp.name, x.emp.days_at_company ( ) FROM sailors x; -- указание скобок после DAYS_AT_COMPANY сообщает, что это метод, а не свойство
Коллекции позволяют хранить в поле строки таблицы сразу множество значений: скалярных, объектов или ссылок на объект. Таким образом, в БД они представляют собой еще один способ группировки элементов, дополнительно к объектным таблицам. Oracle относит коллекции к объектным возможностям своей СУБД, и поэтому допускаются многоуровневые коллекции.
Формально коллекции определяются через тип, и поэтому явного нарушения скалярности данных в таблицах Oracle коллекции не создают: в столбце таблицы по-прежнему хранятся значения определенного типа. Однако содержательно они все-таки моделируют хранение набора величин как целого, и ввиду этого вызывают определенный скепсис со стороны знатоков реляционной модели.
Начиная с версии 8 в диалекте SQL Oracle имеется два вида коллекций: вложенные таблицы (неупорядоченное множество) и массивы VARRAY (упорядоченный список). Они соответствуют двум видам коллекций в стандарте SQL, но реализованы с некоторыми вольностями.
Вложеные таблицы (nested tables) в Oracle есть термин для обозначения возможности хранить в поле строки сразу множество значений ("таблицу" значений). Обычную таблицу в Oracle, некоторые столбцы которой описаны как "вложеные таблицы", всегда можно перепроектировать в информационно равносильный набор обычных таблиц с "единичными" столбцами.
Пример употребления вложеных таблиц:
CREATE TYPE colourset_typ AS TABLE OF VARCHAR2 ( 32 )
/
CREATE TABLE colour_models (
model_type VARCHAR2 ( 12 )
, colours colourset_typ
)
NESTED TABLE colours STORE AS colour_model_colours_tab
-- фраза NESTED TABLE вынужденная, но может содержать указания оптимизации хранения
;
INSERT INTO colour_models VALUES (
'RGB'
, colourset_typ ( 'RED', 'GREEN', 'BLUE' )
);
-- в этом контексте COLOURSET_TYP — конструктор коллекции
COLUMN colours FORMAT A60
SELECT * FROM colour_models
;
Пример приоткрывает то обстоятельство, что технически перечень самих значений из хранимых в столбце наборов располагается не непосредственно в основной таблице, а в отдельной вспомогательной (служебной), и ей мы обязаны дать по меньшей мере имя. Однако помимо этого во фразе NESTED TABLE при создании основной таблицы мы имеем право указать некоторые подробности организации доступа к этой служебной таблице и особенности хранения (например,
По внутренней технической организации хранят списки в полях типов LOB по правилам, допускаемым для этих типов: как вместе со строкой таблицы (короткие списки), так и в отдельном сегменте LOB (длинные списки). В любом случае специальной вспомогательной таблицы для хранения значений массива здесь не требуется. Простой пример:
CREATE TYPE addresslist_typ IS VARRAY ( 5 ) OF address_type; / CREATE TABLE participants ( name VARCHAR2 ( 20 ) , locations addresslist_typ );
При создании таблицы со столбцом-массивом VARRAY не требуется приводить дополнительных указаний (как для столбца вложеной таблицы), однако имеется возможность их применить при необходимости в том.
Добавление и выборка данных внешне не отличается от осуществляемых для вложеной таблицы, например:
INSERT INTO participants VALUES
( 'Einstein'
, addresslist_typ ( address_type ( '123456', 'Archangelsk' )
, address_type ( '789012', 'Samara' )
-- конструкторы простого объекта
)
-- конструктор массива VARRAY
);
Хотя столбец-коллекция в таблице может показаться удобным для моделирования данных предметной области, работать с такими данными в SQL не обязательно просто. На помощь в этом приходит особая функция TABLE. Она придумана для "разворачивания" элементов коллекции в список строк, к которому можно уже привычным образом применять охватывающие запросы.
Примеры:
SELECT *
FROM TABLE ( SELECT colours
FROM colour_models
WHERE model_type = 'RGB'
);
SELECT *
FROM TABLE ( SELECT locations
FROM participants
WHERE name = 'Einstein'
);
Упражнение. Проверьте работу последних приведенных запросов.
Для разворачивания многоуровневых коллекций предусмотрен особый случай употребления функции TABLE в сочетании с соединением (join).
Кроме того, ряд возможностей по программной обработке данных-коллекций предусмотрен в PL/SQL.
Вложенные таблицы и массивы VARRAY различаются по содержательному употреблению и технике исполнения. Формальные свойства употребления вложенных таблиц и массивов VARRAY, хотя большей частью совпадают, имеют и естественные различия. Например, с версии 10 для определенного класса вложенных таблиц (элементы которых допускают сравнение друг с другом, в частности, встроенных скалярных типов) возможны множественные операции. Еще пример — только для вложенных таблиц возможна проверка на пустоту:
SQL> SELECT 'ok' FROM dual WHERE colourset_typ ( ) IS EMPTY; 'O -- ok
Этот встроенный объектный тип для работы в БД с документами XML появился в версии 9.0. До этого наиболее подходящим для хранения документов XML был тип , либо иметь в БД структуру объекта (начиная с версии 9.2 и при использовании XML DB). Помимо пользовательской направленности тип XMLTYPE активно применяется в последних версиях Oracle для внутренней организации БД.
СУБД и БД Oracle предлагают широкий спектр возможностей по использованию типа XMLTYPE в связи с документами XML. Ниже приводятся только простые ознакомительные примеры.
Создание и пополнение таблицы, в которой решено хранить описания книг в формате XML — ради сохранения формата источника или же в силу отсутствия фиксированной структуры у описания книги:
CREATE TABLE books (
id NUMBER
, description XMLTYPE
);
INSERT INTO books VALUES (
100
, xmltype (
'<?xml version="1.0"?>
<cover>
<title>Java Programming with Oracle JDBC</title>
<author>Donald Bales</author>
<publisher>OReilly and Associates</publisher>
<pubdate>December 2001</pubdate>
<isbn>0-596-00088-x</isbn>
<pages>496</pages>
</cover>'
)
);
-- ... здесь Oracle разрешает вместо конструктора XMLTYPE ( '...' ) написать просто '...'
SET LONG 1000
SELECT id, description FROM books;
SELECT id, b.description.XMLDATA FROM books b;
XMLDATA — специально созданный для XMLTYPE "псевдостолбец". В данном примере его может заменить метод GETCLOBVAL() типа XMLTYPE, один из многих существующих.
Упражнение. Попробуйте занести в поле DESCRIPTION неправильно оформленный документ XML и проследите реакцию СУБД.
Пример выборки с использованием условия отбора на языке XPath:
SELECT id, b.description.GETCLOBVAL ( )
FROM books b
WHERE b.description.EXISTSNODE('/cover[author="Donald Bales"]')=1;
По аналогии с таблицами объектов, проектируемых самостоятельно, можно создавать таблицы документов XMLTYPE:
CREATE TABLE xbooks OF XMLTYPE;
Работать с ними можно как и с прочими таблицами объектов:
INSERT INTO xbooks VALUES (
xmltype (
'<?xml version="1.0"?>
<cover>
<title>Java Programming with Oracle JDBC</title>
<author>Donald Bales</author>
<publisher>OReilly and Associates</publisher>
<pubdate>December 2001</pubdate>
<isbn>0-596-00088-x</isbn>
<pages>496</pages>
</cover>'
)
);
-- ... здесь Oracle разрешает вместо XMLTYPE ( '...' ) указать просто '...'
SELECT x.GETCLOBVAL ( ) FROM xbooks x;
В этом примере данные XML будут храниться как . Более сложный пример — создание таблиц объектов типа XMLTYPE, где документы XML хранятся в виде таблицы объектов, а не как . Вот как это могло бы выглядеть в какой-нибудь БД типа лицами объективно, и в силу отсутствия фиксированной структуры описания книгой области:
CREATE TABLE oxbooks OF XMLTYPE XMLSCHEMA "http://www.oracle.com/xbooks.xsd" ELEMENT "Book" ;
Чтобы таблица была предварительно определена ("зарегистрирована") в "репозитарии" XML DB (сама XML DB со своим репозитарием автоматически включена в состав типовым образом созданной БД). Достоинство такого описания столбца XMLTYPE в том, что он позволяет хранить не произвольные документы XML, а только типизированные схемой XML. Тем самым зарегистрированная схема XML используется как средство ограничения целостности хранимых данных XML, налагаемое в таблице Oracle по правилам технологии XML.
Ряд функций, объединенных названием SQL/XML в
XMLELEMENTXMLATTRIBUTESXMLAGGXMLCONCATXMLFORESTXMLPI[10.2-]XMLCOMMENT[10.2-]XMLROOT[10.2-]XMLSERIALIZE[10.2-]XMLPARSE[10.2-][10.2-] начиная с версии 10.2
Вдобавок к этому Oracle SQL содержит ряд расширений SQL/XML и собственных функций для выполнения подобных преобразований:
XMLCOLATTVALSYS_XMLGEN (распространяется на строки)SYS_XMLAGG (распространяется на группы GROUP BY)XMLSEQUENCE (только курсорный вариант)XMLCDATA[10.2-][10.2-] начиная с версии 10.2
Следующие примеры поясняют действие некоторых из перечисленных функций:
SET LONG 2000
SELECT XMLELEMENT ( "employee", ename ) AS employee
FROM emp;
SELECT
XMLELEMENT (
"employee"
, XMLATTRIBUTES ( ename AS "name", comm AS "commission" )
) AS employee
FROM emp;
SELECT
XMLELEMENT (
"employee"
, XMLFOREST ( ename AS "name", comm AS "commission" )
) AS employee
FROM emp;
SELECT
XMLELEMENT (
"employee"
, XMLCOLATTVAL ( ename AS "name", comm AS "commission" )
) AS employee
FROM emp;
SELECT
XMLELEMENT (
"department"
, XMLATTRIBUTES ( deptno AS no )
) AS department
, XMLAGG ( XMLELEMENT ( "employee", ename ) ) AS employees
FROM emp
GROUP BY deptno
;
Обратите внимание, что в результатах выдаются поля типа XMLTYPE:
CREATE TABLE xtable ( n )
AS
SELECT XMLELEMENT ( "name", ename ) FROM emp
;
DESCRIBE xtable
Имеется начиная с версии 9. Позволяет хранить в столбце таблицы данные одновременно разных типов. Используется с объектным типом ANYDATA, имеющим свои конструкторы и методы.
Пример:
CREATE TABLE t ( x ANYDATA ); INSERT INTO t VALUES ( ANYDATA.CONVERTNUMBER ( 5 ) ); INSERT INTO t VALUES ( ANYDATA.CONVERTDATE ( SYSDATE ) ); INSERT INTO t VALUES ( ANYDATA.CONVERTVARCHAR2 ( 'hello world' ) ); SELECT t.x.GETTYPENAME ( ) typename FROM t t; INSERT INTO t VALUES ( ANYDATA.CONVERTOBJECT ( address_type ( '789012', 'Murmansk' ) ) );
Метода извлечения значения нефиксированного типа в SQL не предусмотрено, и извлечение приходится программировать в два захода: сначала узнать реальный тип значения, а потом уже применить соответствующий типу метод извлечения. Пример ниже подсказывает, как можно при желании составить функцию выборки данного, доступную в SQL, а не только в программе:
SET SERVEROUTPUT ON DECLARE val VARCHAR2 ( 30 ); anumber ANYDATA; BEGIN SELECT t.x INTO anumber FROM t t WHERE t.x.GETTYPENAME ( ) = 'SYS.NUMBER' -- в нашей таблице такая строка единственная ; DBMS_OUTPUT.PUT_LINE ( 'Ret code: ' || anumber.GETNUMBER ( val ) ); DBMS_OUTPUT.PUT_LINE ( 'A number: ' || val ); END; /
Методы типа ANYDATA описаны в документации по Oracle.
Тип ANYDATA, как и XMLTYPE, находит употребление во внутренней организации БД. В частности он используется в построении внутренних "автоматических очередей", лежащих в основе "потоков данных", используемых, в свою очередь, для автоматического переноса данных в Oracle.
Помимо сравнительно простых встроенных типов данных — как перешедших из стандартов SQL, так и собственных, — в Oracle имеется возможность использовать составные. Это конструируемые типы объектов, рассчитанные на хранение в БД данных, имеющих внутреннюю структуру. Эта структура известна СУБД, и СУБД позволяет с ней работать. Объектные типы позволяют хранить и обрабатывать средствами СУБД "сложно устроенные данные" более продвинутым образом, нежели это позволяет техника "больших неструктурированных объектов" типов LOB. Ввиду наличия вполне определенного типа (даже если это
Хранение в столбцах таблицы значений в виде объектов, в смысле объектного подхода (ОП в программировании и моделировании), фирма Oracle впервые обеспечила в рамках так называемой "объектно-реляционной модели" начиная с версии Oracle 8. Некоторые существенные пробелы первой реализации (например, отсутствие
Ниже приводится простой пример использования программируемых (объектных) типов.
Вначале требуется создать "тип", как разновидности хранимых элементов БД. Пример создания типа объекта (в SQL*Plus):
CREATE TYPE address_type AS OBJECT ( zip CHAR ( 6 ) , location VARCHAR2 ( 200 ) ) /
Здесь типу ADDRESS_TYPE приписаны два "свойства" (по объектной терминологии): ZIP и LOCATION. В реальной жизни для представления адреса в типе наверняка будет указано большее количество свойств, однако в ознакомительном примере их более пространный перечень излишен и не добавит понимания техники.
Определение типа напоминает определение таблицы, однако в отличие от таблицы (а также стандарта SQL и от реляционного подхода) тип объекта в Oracle не имеет права содержать ограничений целостности (которые в таком случае можно было бы назвать "ограничениями целостности типа"). Если необходимо их указать, сделать это придется только по месту употребления типа, то есть в описании таблицы.
В соответствии с традициями объектного подхода (уместно вспомнить, что "объектной теории", в отличие от реляционной, не создано) Oracle разрешает использовать тип для создания "буквальных значений" и собственно объектов. Далее приводится сначала несколько примеров первого, а затем второго.
"Буквальные значения" фактически позволяют работать со значениями, обладающими известной СУБД структурой и однозначно определяются набором значений элементов своей структуры.
Примеры использования типа ADDRESS_TYPE для определения столбца в обычной таблице:
CREATE TABLE odept1 ( dname VARCHAR2 ( 20 ) , deptno NUMBER ( 2 ) CONSTRAINT pk_odept1 PRIMARY KEY , addr address_type ); CREATE TABLE oemp1 ( ename VARCHAR2 ( 20 ) , empno NUMBER ( 4 ) CONSTRAINT pk_oemp1 PRIMARY KEY , deptno NUMBER ( 2 ) CONSTRAINT fk_oemp1 REFERENCES dept , home address_type );
Столбцы ADDR и HOME можно с некоторой вольностью назвать "объектными атрибутами". Они не позволяют хранить объектные значения в виде самостоятельной сущности и ссылаться на них ссылками. Локализовать такие значения можно только по обычным правилам поиска данных в таблице.
В выражениях явно указанные объектные значения формулируются с помощью конструктора. В отличие от других объектных систем, например, от Java, в Oracle конструктор умолчательно имеет список параметров, соответствующих свойствам типа. Примеры применения в операциях добавления данных:
INSERT INTO odept1 ( deptno, dname, addr ) VALUES ( 10, 'RESEARCH', address_type ( '123456', 'Archangelsk' ) ) ; INSERT INTO oemp1 ( empno, ename, deptno, home ) VALUES ( 1111, 'SMITH', 10, address_type ( '789012', 'Samara' ) ) ;
Oracle допускает определенные синтаксические вольности в записи выражения над объектными данными. Здесь и далее используются частные случаи возможных формулировок.
Пример применения в запросе о сотрудниках, "работающих по месту жительства", за исключением конкретно указанного адреса:
SELECT e.ename, d.dname
FROM oemp1 e INNER JOIN odept1 d
ON e.home = d.addr
WHERE e.home <> address_type ( '345678', 'Leningrad' )
;
Пример показывает легкость формулирования сравнения составных величин, каковыми являются адреса. Сравнение осуществляется поэлементно, путем сравнением всех свойств по очереди. Увы, но простота формулировки не дает права программисту расслабляться и забывать об особых случаях сравнения с данными типа CHAR и с NULL. Так, присутствие NULL в буквальных объектных значениях запутывает проблему сравнения еще больше, чем для случая простых типов. Сравните:
SQL> SELECT 'OK' ok FROM dual 2 WHERE address_type ( NULL, 'x' ) IS NOT NULL; OK -- OK SQL> SELECT 'OK' ok FROM dual 2 WHERE address_type ( NULL, 'x' ) = address_type ( NULL, 'x' ); no rows selected
То есть получается, что x = x не дает TRUE, но притом x IS NOT NULL дает TRUE (x имеет значение).
В выражениях можно обращаться к буквальному объектному значению как к целому, а можно и к его отдельным свойствам. Во втором случае, как правило, требуется прибегать к псевдониму:
COLUMN home FORMAT A35 SELECT e.ename, e.home, e.home.zip FROM oemp1 e ;
Созданный в БД тип можно употребить и для создания "таблиц объектов":
CREATE TABLE addresses1 OF address_type; CREATE TABLE addresses2 OF address_type;
Хотя для этой категории хранимых элементов используется термин "таблица", такая таблица всегда содержит ровно один столбец, и именно объектного типа.
Запись занесения "строк" в такую таблицу может быть, в частности, такой:
INSERT INTO addresses1 VALUES ( '123456', 'Archangelsk' );
Пример запроса:
SELECT a.*, UPPER ( location ) FROM addresses1 a;
Объекты в таких таблицах хранятся как самостоятельные сущности, у которых имеется автоматически порождаемый СУБД внутренний уникальный идентификатор object ID, в соответствии с классическим объектным подходом позволяющий ссылаться на конкретные объекты из других таблиц или из программы. Сравнение элементов-"строк" в таблице объектов друг с другом происходит уже не по значениям свойств, как в случае объектного столбца в обычной таблице, а по значению object ID. Перейти на сравнение значений свойств позволяет функция VALUE, например:
SELECT dname FROM odept1 d, addresses1 a WHERE d.addr = VALUE ( a );
Сделан запрос об отделах, расположенных по адресам из таблицы ADDRESS1.
Не исключено, что создатели функции VALUE обсуждали другое ее название — LITERAL_VALUE. По крайней мере, оно точнее описывает совершаемое действие: создание значения со структурой из объекта. Буквальные значения сравниваются друг с другом по значениям их свойств, а объекты — по значениям object ID.
Благодаря наличию внутренних идентификаторов object ID у объектов из таблиц объектов и возможности на них ссылаться локализовать такие объекты становится возможным с применением не только обычной техники SQL, но и навигации с помощью ссылок.
Пример создания обычной таблицы со столбцом для ссылки на хранимый в таблице объектов (типа ADDRESS_TYPE) элемент-объект:
CREATE TABLE odept2 ( dname VARCHAR2 ( 50 ) , deptno NUMBER CONSTRAINT pk_odept PRIMARY KEY , addr REF address_type SCOPE IS addresses1 );
Здесь описание ссылки сужено возможностью адресоваться только к объектам из таблицы ADDRESSES1. Допускаются варианты описания ссылки: ее нацеленность на содержимое конкретной таблицы можно не применять или же, напротив, усилить до аналогии со ссылочной целостностью (в этом примере аналогия с правилом внешнего ключа неполная).
Пример заполнения поля ADDR значением-ссылкой:
INSERT INTO odept2
( dname, deptno, addr )
VALUES
( 'RESEARCH', 10, ( SELECT REF ( a )
FROM addresses1 a
WHERE a.location = 'Archangelsk'
) )
;
Пример допустимых оформлений обращения к свойствам объекта через ссылку:
COLUMN deref(d.addr) FORMAT A40 SELECT d.dname, DEREF ( d.addr ), d.addr.zip FROM odept2 d ;
Навигация по объектам в БД с помощью ссылок и в том числе извлечение объекта из БД возможны не только в запросах SQL, но и в программе на PL/SQL.
Более сложные конструкции в описании типа позволяют задавать методы объектов и типов. Пример указания в типе метода:
CREATE TYPE employee_type AS OBJECT ( name VARCHAR2 ( 50 ) , hiredate DATE , home REF address_type , MEMBER FUNCTION days_at_company RETURN NUMBER ) /
Когда методов много, их заголовки перечисляются по очереди через запятую, общим списком со свойствами.
В описании типа приводится только заголовок методов. Для описания тела метода необходимо создать тело типа (полная аналогия пары пакет — тело пакета, имеющейся в PL/SQL):
CREATE TYPE BODY employee_type AS MEMBER FUNCTION days_at_company RETURN NUMBER IS BEGIN RETURN TRUNC ( SYSDATE - hiredate ); END; END; /
Пример использования типа в создании таблицы:
CREATE TABLE sailors ( ship VARCHAR2 ( 30 ), emp employee_type ); -- в этом контексте EMPLOYEE_TYPE — это имя типа
Использование конструктора типа для заполнения таблицы:
INSERT INTO sailors VALUES ( 'Ninna', employee_type ( 'Frank Naude', SYSDATE, NULL ) ) ; -- в этом контексте EMPLOYEE_TYPE — это конструктор
Использование свойств и метода типа для запроса к таблице:
COLUMN emp FORMAT a60 SELECT * FROM sailors; SELECT x.ship, x.emp.name, x.emp.days_at_company ( ) FROM sailors x; -- указание скобок после DAYS_AT_COMPANY сообщает, что это метод, а не свойство
Коллекции позволяют хранить в поле строки таблицы сразу множество значений: скалярных, объектов или ссылок на объект. Таким образом, в БД они представляют собой еще один способ группировки элементов, дополнительно к объектным таблицам. Oracle относит коллекции к объектным возможностям своей СУБД, и поэтому допускаются многоуровневые коллекции.
Формально коллекции определяются через тип, и поэтому явного нарушения скалярности данных в таблицах Oracle коллекции не создают: в столбце таблицы по-прежнему хранятся значения определенного типа. Однако содержательно они все-таки моделируют хранение набора величин как целого, и ввиду этого вызывают определенный скепсис со стороны знатоков реляционной модели.
Начиная с версии 8 в диалекте SQL Oracle имеется два вида коллекций: вложенные таблицы (неупорядоченное множество) и массивы VARRAY (упорядоченный список). Они соответствуют двум видам коллекций в стандарте SQL, но реализованы с некоторыми вольностями.
Вложеные таблицы (nested tables) в Oracle есть термин для обозначения возможности хранить в поле строки сразу множество значений ("таблицу" значений). Обычную таблицу в Oracle, некоторые столбцы которой описаны как "вложеные таблицы", всегда можно перепроектировать в информационно равносильный набор обычных таблиц с "единичными" столбцами.
Пример употребления вложеных таблиц:
CREATE TYPE colourset_typ AS TABLE OF VARCHAR2 ( 32 )
/
CREATE TABLE colour_models (
model_type VARCHAR2 ( 12 )
, colours colourset_typ
)
NESTED TABLE colours STORE AS colour_model_colours_tab
-- фраза NESTED TABLE вынужденная, но может содержать указания оптимизации хранения
;
INSERT INTO colour_models VALUES (
'RGB'
, colourset_typ ( 'RED', 'GREEN', 'BLUE' )
);
-- в этом контексте COLOURSET_TYP — конструктор коллекции
COLUMN colours FORMAT A60
SELECT * FROM colour_models
;
Пример приоткрывает то обстоятельство, что технически перечень самих значений из хранимых в столбце наборов располагается не непосредственно в основной таблице, а в отдельной вспомогательной (служебной), и ей мы обязаны дать по меньшей мере имя. Однако помимо этого во фразе NESTED TABLE при создании основной таблицы мы имеем право указать некоторые подробности организации доступа к этой служебной таблице и особенности хранения (например,
По внутренней технической организации хранят списки в полях типов LOB по правилам, допускаемым для этих типов: как вместе со строкой таблицы (короткие списки), так и в отдельном сегменте LOB (длинные списки). В любом случае специальной вспомогательной таблицы для хранения значений массива здесь не требуется. Простой пример:
CREATE TYPE addresslist_typ IS VARRAY ( 5 ) OF address_type; / CREATE TABLE participants ( name VARCHAR2 ( 20 ) , locations addresslist_typ );
При создании таблицы со столбцом-массивом VARRAY не требуется приводить дополнительных указаний (как для столбца вложеной таблицы), однако имеется возможность их применить при необходимости в том.
Добавление и выборка данных внешне не отличается от осуществляемых для вложеной таблицы, например:
INSERT INTO participants VALUES
( 'Einstein'
, addresslist_typ ( address_type ( '123456', 'Archangelsk' )
, address_type ( '789012', 'Samara' )
-- конструкторы простого объекта
)
-- конструктор массива VARRAY
);
Хотя столбец-коллекция в таблице может показаться удобным для моделирования данных предметной области, работать с такими данными в SQL не обязательно просто. На помощь в этом приходит особая функция TABLE. Она придумана для "разворачивания" элементов коллекции в список строк, к которому можно уже привычным образом применять охватывающие запросы.
Примеры:
SELECT *
FROM TABLE ( SELECT colours
FROM colour_models
WHERE model_type = 'RGB'
);
SELECT *
FROM TABLE ( SELECT locations
FROM participants
WHERE name = 'Einstein'
);
Упражнение. Проверьте работу последних приведенных запросов.
Для разворачивания многоуровневых коллекций предусмотрен особый случай употребления функции TABLE в сочетании с соединением (join).
Кроме того, ряд возможностей по программной обработке данных-коллекций предусмотрен в PL/SQL.
Вложенные таблицы и массивы VARRAY различаются по содержательному употреблению и технике исполнения. Формальные свойства употребления вложенных таблиц и массивов VARRAY, хотя большей частью совпадают, имеют и естественные различия. Например, с версии 10 для определенного класса вложенных таблиц (элементы которых допускают сравнение друг с другом, в частности, встроенных скалярных типов) возможны множественные операции. Еще пример — только для вложенных таблиц возможна проверка на пустоту:
SQL> SELECT 'ok' FROM dual WHERE colourset_typ ( ) IS EMPTY; 'O -- ok
Этот встроенный объектный тип для работы в БД с документами XML появился в версии 9.0. До этого наиболее подходящим для хранения документов XML был тип , либо иметь в БД структуру объекта (начиная с версии 9.2 и при использовании XML DB). Помимо пользовательской направленности тип XMLTYPE активно применяется в последних версиях Oracle для внутренней организации БД.
СУБД и БД Oracle предлагают широкий спектр возможностей по использованию типа XMLTYPE в связи с документами XML. Ниже приводятся только простые ознакомительные примеры.
Создание и пополнение таблицы, в которой решено хранить описания книг в формате XML — ради сохранения формата источника или же в силу отсутствия фиксированной структуры у описания книги:
CREATE TABLE books (
id NUMBER
, description XMLTYPE
);
INSERT INTO books VALUES (
100
, xmltype (
'<?xml version="1.0"?>
<cover>
<title>Java Programming with Oracle JDBC</title>
<author>Donald Bales</author>
<publisher>OReilly and Associates</publisher>
<pubdate>December 2001</pubdate>
<isbn>0-596-00088-x</isbn>
<pages>496</pages>
</cover>'
)
);
-- ... здесь Oracle разрешает вместо конструктора XMLTYPE ( '...' ) написать просто '...'
SET LONG 1000
SELECT id, description FROM books;
SELECT id, b.description.XMLDATA FROM books b;
XMLDATA — специально созданный для XMLTYPE "псевдостолбец". В данном примере его может заменить метод GETCLOBVAL() типа XMLTYPE, один из многих существующих.
Упражнение. Попробуйте занести в поле DESCRIPTION неправильно оформленный документ XML и проследите реакцию СУБД.
Пример выборки с использованием условия отбора на языке XPath:
SELECT id, b.description.GETCLOBVAL ( )
FROM books b
WHERE b.description.EXISTSNODE('/cover[author="Donald Bales"]')=1;
По аналогии с таблицами объектов, проектируемых самостоятельно, можно создавать таблицы документов XMLTYPE:
CREATE TABLE xbooks OF XMLTYPE;
Работать с ними можно как и с прочими таблицами объектов:
INSERT INTO xbooks VALUES (
xmltype (
'<?xml version="1.0"?>
<cover>
<title>Java Programming with Oracle JDBC</title>
<author>Donald Bales</author>
<publisher>OReilly and Associates</publisher>
<pubdate>December 2001</pubdate>
<isbn>0-596-00088-x</isbn>
<pages>496</pages>
</cover>'
)
);
-- ... здесь Oracle разрешает вместо XMLTYPE ( '...' ) указать просто '...'
SELECT x.GETCLOBVAL ( ) FROM xbooks x;
В этом примере данные XML будут храниться как . Более сложный пример — создание таблиц объектов типа XMLTYPE, где документы XML хранятся в виде таблицы объектов, а не как . Вот как это могло бы выглядеть в какой-нибудь БД типа лицами объективно, и в силу отсутствия фиксированной структуры описания книгой области:
CREATE TABLE oxbooks OF XMLTYPE XMLSCHEMA "http://www.oracle.com/xbooks.xsd" ELEMENT "Book" ;
Чтобы таблица была предварительно определена ("зарегистрирована") в "репозитарии" XML DB (сама XML DB со своим репозитарием автоматически включена в состав типовым образом созданной БД). Достоинство такого описания столбца XMLTYPE в том, что он позволяет хранить не произвольные документы XML, а только типизированные схемой XML. Тем самым зарегистрированная схема XML используется как средство ограничения целостности хранимых данных XML, налагаемое в таблице Oracle по правилам технологии XML.
Ряд функций, объединенных названием SQL/XML в
XMLELEMENTXMLATTRIBUTESXMLAGGXMLCONCATXMLFORESTXMLPI[10.2-]XMLCOMMENT[10.2-]XMLROOT[10.2-]XMLSERIALIZE[10.2-]XMLPARSE[10.2-][10.2-] начиная с версии 10.2
Вдобавок к этому Oracle SQL содержит ряд расширений SQL/XML и собственных функций для выполнения подобных преобразований:
XMLCOLATTVALSYS_XMLGEN (распространяется на строки)SYS_XMLAGG (распространяется на группы GROUP BY)XMLSEQUENCE (только курсорный вариант)XMLCDATA[10.2-][10.2-] начиная с версии 10.2
Следующие примеры поясняют действие некоторых из перечисленных функций:
SET LONG 2000
SELECT XMLELEMENT ( "employee", ename ) AS employee
FROM emp;
SELECT
XMLELEMENT (
"employee"
, XMLATTRIBUTES ( ename AS "name", comm AS "commission" )
) AS employee
FROM emp;
SELECT
XMLELEMENT (
"employee"
, XMLFOREST ( ename AS "name", comm AS "commission" )
) AS employee
FROM emp;
SELECT
XMLELEMENT (
"employee"
, XMLCOLATTVAL ( ename AS "name", comm AS "commission" )
) AS employee
FROM emp;
SELECT
XMLELEMENT (
"department"
, XMLATTRIBUTES ( deptno AS no )
) AS department
, XMLAGG ( XMLELEMENT ( "employee", ename ) ) AS employees
FROM emp
GROUP BY deptno
;
Обратите внимание, что в результатах выдаются поля типа XMLTYPE:
CREATE TABLE xtable ( n )
AS
SELECT XMLELEMENT ( "name", ename ) FROM emp
;
DESCRIBE xtable
Имеется начиная с версии 9. Позволяет хранить в столбце таблицы данные одновременно разных типов. Используется с объектным типом ANYDATA, имеющим свои конструкторы и методы.
Пример:
CREATE TABLE t ( x ANYDATA ); INSERT INTO t VALUES ( ANYDATA.CONVERTNUMBER ( 5 ) ); INSERT INTO t VALUES ( ANYDATA.CONVERTDATE ( SYSDATE ) ); INSERT INTO t VALUES ( ANYDATA.CONVERTVARCHAR2 ( 'hello world' ) ); SELECT t.x.GETTYPENAME ( ) typename FROM t t; INSERT INTO t VALUES ( ANYDATA.CONVERTOBJECT ( address_type ( '789012', 'Murmansk' ) ) );
Метода извлечения значения нефиксированного типа в SQL не предусмотрено, и извлечение приходится программировать в два захода: сначала узнать реальный тип значения, а потом уже применить соответствующий типу метод извлечения. Пример ниже подсказывает, как можно при желании составить функцию выборки данного, доступную в SQL, а не только в программе:
SET SERVEROUTPUT ON DECLARE val VARCHAR2 ( 30 ); anumber ANYDATA; BEGIN SELECT t.x INTO anumber FROM t t WHERE t.x.GETTYPENAME ( ) = 'SYS.NUMBER' -- в нашей таблице такая строка единственная ; DBMS_OUTPUT.PUT_LINE ( 'Ret code: ' || anumber.GETNUMBER ( val ) ); DBMS_OUTPUT.PUT_LINE ( 'A number: ' || val ); END; /
Методы типа ANYDATA описаны в документации по Oracle.
Тип ANYDATA, как и XMLTYPE, находит употребление во внутренней организации БД. В частности он используется в построении внутренних "автоматических очередей", лежащих в основе "потоков данных", используемых, в свою очередь, для автоматического переноса данных в Oracle.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.