Перечень статей   Терминология   Choose language


Гладкость между рациональным и xml


Внешний ключ между рациональным и xml, между xml и xml

Внешние ключи такого рода удобно описывать используя XPath, дополнив его выражениями вида '@field/element'. Например, рациональное поле может ссылаться на xml-атрибут, содержащийся в текстовом поле другой таблицы.

create table a(
  a1 num,
  a2 string
);
create table b(
  b1 num,
  b2 num references a(@a2/k/m/n/@n1)
);
Наоборот, xml-атрибут, содержащийся в текстовом поле, может ссылаться на рациональное поле другой таблицы. В этом случае мы не можем указать внешний ключ непосредственно в выражении 'create table', а вынуждены использовать отдельное выражение 'alter table'
create table a (
  a1 num,
  a2 num
);
create table b (
  b1 num,
  b2 string
);
alter table b add foreign key (@b2/k/m/n/@n1) references a(a2);
Комбинацией двух вышеперечисленных случаев является внешний ключ из xml-атрибута текствовой строки одной таблицы в xml-атрибут текствовой строки другой таблицы.
create table a (
  a1 num,
  a2 string
);
create table b (
  b1 num,
  b2 string
);
alter table b add foreign key (@b2/k/m/n/@n1) references a(@a2/p/q/r/@r1);

Вставка рационального в xml

В текстовое поле может быть вставлен не только xml-текст

insert into tab (fld) values ('<tag>...</tag>');
но и дерево, построенное на основе внешних ключей (foreign keys) между таблицами. Не имеет значения, является ли следующее звено дерева родительской таблицей или дочерней - нужна только связь двух таблиц.
Предлагаю в дальнейшем конструкции вида 'a.b.c' называть XTree. Ссылочные поля, использованные при построении рационального дерева, не отображаются в xml-представления (за ненадобностью).
insert into tab (fld) select * from a.b.c;

Сначала родительская таблица, затем дочерняя. Пусть предшествующим звеном дерева является родительская таблица (таблица "a"), а последующим - дочерняя (таблица "b"), ссылающаяся на первичный ключ родительской. Пусть в дочерней таблице существует несколько записей, ссылающихся на одну и ту же родительскую запись - это означает, что следующий уровень дерева содержит несколько элементов. Звено дерева, состоящее из нескольких записей дочерней таблицы, будем называть множеством (set). Для извлечения дерева перечисляем имена таблиц - сначала родительской, затем дочерней.

при условии следующего DDL запрос на DML запрос выполнен так
create table a (
  id   num      primary key,
  data float
);
create table b (
  id   num      primary key,
  ref  num      references a(id),
  data float
);
create table c (
  id   num      primary key,
  link num      references b(id),
  data float
);
insert into a values (1,     12.3);
insert into b values (10, 1, 23.4);
insert into b values (20, 1, 34.5);
insert into b values (30, 1, 45.6);
insert into c values (100,10,56.7);
insert into c values (101,10,67.8);
insert into c values (200,20,78.9);
insert into c values (201,20,89.1);
insert into c values (300,30,91.2);
insert into tab (fld)
  select * from a.b.c;
insert into tab (fld) values ('
  <a     id=1   data=12.3>
    <b   id=10  data=23.4>
      <c id=100 data=56.7/>
      <c id=101 data=67.8/>
    </b>
    <b   id=20  data=34.5>
      <c id=200 data=78.9/>
      <c id=201 data=89.1/>
    </b>
    <b   id=30  data=45.6>
      <c id=200 data=91.2/>
    </b>
  </a>
');
Если дочерняя (вложенная) таблица двумя своими полями ссылается на две разные записи родительской (обрамляющей) таблицы, то в запросе после названия дочерней, вложенной таблицы через знак "#" нужно указать название соответствующего поля родительской таблицы. Такое указание поля в звене будем называть детерминацией звена (determination). Если детерминацию не указать, то извлечены будут все вложенные записи.
при условии следующего DDL запрос на DML запрос выполнен так
create table a (
  id   num      primary key,
  data float
);
create table b (
  id   num      primary key,
  ref1 num      references a(id),
  ref2 num      references a(id),
  data float
);
create table c (
  id   num      primary key,
  lnk1 num      references b(id),
  lnk2 num      references b(id),
  data float
);
insert into a values (1,        12.3);
insert into a values (2,        23.4);
insert into b values (10, 1, 2, 34.5);
insert into b values (20, 1, 2, 45.6);
insert into b values (30, 1, 2, 56.7);
insert into b values (40, 1, 2, 67.8);
insert into c values (100,10,20,78.9);
insert into c values (101,10,20,89.1);
insert into c values (200,30,40,91.2);
insert into c values (201,30,40,88.8);
insert into tab (fld)
  select * from a.b#ref1.c#lnk1;
insert into tab (fld) values ('
  <a     id=1           data=12.3>
    <b   id=10  ref2=2  data=34.5>
      <c id=100 lnk2=20 data=78.9/>
      <c id=101 lnk2=20 data=89.1/>
    </b>
    <b   id=30  ref2=2  data=56.7>
      <c id=200 lnk2=40 data=91.2/>
      <c id=201 lnk2=40 data=88.8/>
    </b>
  </a>
');

Сначала дочерняя таблица, затем родительская. Пусть предшествующим звеном дерева является дочерняя таблица (таблица "a"), а последующим - родительская (таблица "b"). Звено дерева, состоящее из записи родительской таблицы, будем называть эстафета (relay-race). Для извлечения дерева перечисляем имена таблиц - сначала дочерней, затем родительской.

при условии следующего DDL запрос на DML запрос выполнен так
create table a (
  id   num      primary key,
  ref  num      references b(id),
  data float
);
create table b (
  id   num      primary key,
  link num      references c(id),
  data float
);
create table c (
  id   num      primary key,
  data float
);
insert into с values (100,    34.5);
insert into b values (10, 100,23.4);
insert into a values (1,  10, 12.3);
insert into tab (fld)
  select * from a.b.c;
insert into tab (fld) values ('
  <a     id=1   data=12.3>
    <b   id=10  data=23.4>
      <c id=100 data=34.5/>
    </b>
  </a>
');
Если дочерняя (обрамляющая) таблица двумя своими полями ссылается на две разные записи родительской (вложенной) таблицы, то в запросе после названия дочерней, обрамляющей таблицы через знак "#" нужно указать название соответствующего поля родительской таблицы. Таким образом таблица и ее ссылающееся поле указываются в одном звене, а не в разных. Такое указание поля в звене также будем называть детерминацией звена (determination). Если детерминацию не указать, то извлечены будут все обрамляющие записи.
при условии следующего DDL запрос на DML запрос выполнен так
create table a (
  id   num      primary key,
  ref1 num      references b(id),
  ref2 num      references b(id),
  data float
);
create table b (
  id   num      primary key,
  lnk1 num      references c(id),
  lnk2 num      references c(id),
  data float
);
create table c (
  id   num      primary key,
  data float
);
insert into с values (201,        78.9);
insert into с values (200,        67.8);
insert into с values (101,        56.7);
insert into с values (100,        45.6);
insert into b values (20, 200,201,34.5);
insert into b values (10, 100,101,23.4);
insert into a values (1,  10, 20, 12.3);
insert into tab (fld)
  select * from a#ref1.b#lnk1.c;
insert into tab (fld) values ('
  <a     id=1   data=12.3>
    <b   id=10  data=23.4>
      <c id=100 data=45.6/>
    </b>
  </a>
');

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

Множество или список отличаются только в рациональном представлении, в xml-виде они выглядят одинаково.

при условии следующего DDL запрос на DML запрос выполнен так
create table a (
  id   num      primary key,
  ref  num      references a(id),
  lnk  num      references b(id),
  data float
);
create table b (
  id   num      primary key,
  ref  num      references b(id),
  data float
);
insert into b values (300,null,3.3);
insert into b values (30, 300, 3.2);
insert into b values (3,  30,  3.1);
insert into b values (400,null,4.3);
insert into b values (40, 400, 4.2);
insert into b values (4,  40,  4.1);
insert into b values (500,null,5.3);
insert into b values (50, 500, 5.2);
insert into b values (5,  50,  5.1);

insert into a values (100,null,500,34.5);
insert into a values (10, 100, 400,23.4);
insert into a values (1,  10,  300,12.3);
insert into tab (fld)
  select * from a.b;
insert into tab (fld) values ('
  <a     id=1   data=12.3>
    <b   id=3   data=3.1>
    <b   id=30  data=3.2>
    <b   id=300 data=3.3>
  </a>
  <a     id=1   data=23.4>
    <b   id=4   data=4.1>
    <b   id=40  data=4.2>
    <b   id=400 data=4.3>
  </a>
  <a     id=1   data=34.5>
    <b   id=5   data=5.1>
    <b   id=50  data=5.2>
    <b   id=500 data=5.3>
  </a>
');

Вставка xml в рациональное

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

insert into tab values ('<tag>...</tag>');
Более того, под таблицу может быть вставлен не только xml-текст, но и другое рациональное дерево
insert into tab select * from a.b.c;

Успешность записи xml-текста в таблицы зависит от того, соответствует ли схема базы данных вводимому xml (при несоответствии некоторые xml-под-деревья игнорируются).

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

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

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

при условии следующего DDL запрос на DML запрос выполнен так
create table a (
  id   num      primary key,
  lnk  num      references tab,
  data float
);
create table b (
  id   num      primary key,
  ref  num      references a(id),
  data float
);
insert into tab values ('
  <a        data=12.3>
    <b      data=23.4>
    <b      data=34.5>
    <b      data=45.6>
  </a>
');
insert into tab values (1);
insert into a   values (10, 1, 12.3);
insert into b   values (101,10,23.4);
insert into b   values (102,10,34.5);
insert into b   values (103,10,45.6);
-- секция "b" является множеством
create table a (
  id   num      primary key,
  lnk  num      references tab,
  data float
);
create table b (
  id   num      primary key,
  ref1 num      references a(id),
  ref2 num      references a(id),
  data float
);
insert into tab values ('
  <a        data=12.3>
    <b      data=23.4>
    <b      data=34.5>
    <b      data=45.6>
  </a>
');
insert into tab values (1);
insert into a   values (10, 1, 12.3);
-- "b" не внесено, т.к. неопределенность
--  не разрешена с помощью детерминации
                    
insert into tab values ('
  <a        data=12.3>
    <b#ref1 data=23.4>
    <b#ref1 data=34.5>
    <b#ref1 data=45.6>
  </a>
');
insert into tab values (1);
insert into a   values (10, 1,      12.3);
insert into b   values (101,10,null,23.4);
insert into b   values (102,10,null,34.5);
insert into b   values (103,10,null,45.6);


create table a (
  id   num      primary key,
  lnk  num      references tab,
  ref  num      references b(id),
  data float
);
create table b (
  id   num      primary key,
  data float
);
insert into tab values ('
  <a        data=12.3>
    <b      data=23.4>
  </a>
');
insert into tab values (1);
insert into a   values (10, 1,100,12.3);
insert into b   values (100,      23.4);
-- секция "b" является эстафетой
insert into tab values ('
  <a        data=12.3>
    <b      data=23.4>
    <b      data=34.5>
    <b      data=45.6>
  </a>
');
insert into tab values (1);
insert into a   values (10, 1,null,12.3);
-- ни один "b" не введен, т.к. схема базы данных
-- не содержит ни множества, ни списка для них
-- (внесение одного "b" могло бы ввести в заблуждение
--  программиста, отлаживающего программу)

create table a (
  id   num      primary key,
  lnk  num      references tab,
  ref1 num      references b(id),
  ref2 num      references b(id),
  data float
);
create table b (
  id   num      primary key,
  data float
);
insert into tab values ('
  <a        data=12.3>
    <b      data=23.4>
  </a>
');
insert into tab values (1);
insert into a   values (10, 1,null,null,12.3);
-- "b" не внесено, т.к. неопределенность
--  не разрешена с помощью детерминации
                    
insert into tab values ('
  <a#ref1  data=12.3>
    <b     data=23.4>
  </a>
');
insert into tab values (1);
insert into a   values (10, 1,100,null,12.3);
insert into b   values (100,           23.4);


create table a (
  id   num      primary key,
  lnk  num      references tab,
  ref  num      references b(id),
  data float
);
create table b (
  id   num      primary key,
  ref  num      references b(id),
  data float
);
insert into tab values ('
  <a        data=12.3>
    <b      data=23.4>
    <b      data=34.5>
    <b      data=45.6>
  </a>
');
insert into tab values (1);
insert into a   values (10, 1,101, 12.3);
insert into b   values (101,  102, 23.4);
insert into b   values (102,  103, 34.5);
insert into b   values (103,  null,45.6);
-- секция "b" является списком

Вставка под xml-элемент, извлечение из xml-элемента

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

insert into tab/@fld/k/m/n values ('<tag>...</tag>');
insert into tab/@fld/k/m/n select * from a.b.c;
Например
было (в поле 'fld' таблицы 'tab') запрос на DML стало (в поле 'fld' таблицы 'tab')
<k>
  <m>
    <n>
      <p/>
    </n>
  </m>
</k>
insert into tab/@fld/k/m/n
  values ('<tag>...</tag>');
<k>
  <m>
    <n>
      <p/>
      <tag>...</tag>
    </n>
  </m>
</k>
<k>
  <m>
    <n>
      <p/>
    </n>
  </m>
</k>
insert into tab/@fld/k/m/n
  select * from a.b.c;
<k>
  <m>
    <n>
      <p/>

      <a>
        <b> <c/> <c/> </b>
        <b> <c/> <c/> </b>
      </a>
      <a>
        <b> <c/> <c/> </b>
        <b> <c/> <c/> </b>
      </a>

    </n>
  </m>
</k>

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

insert into tabname (field) select * from tab/@fld/k/m/n/a.b.c;
Например
было (в поле 'field' таблицы 'tabname') запрос на DML стало (в поле 'field' таблицы 'tabname')

insert into tabname (field)
  select * from tab/@fld/k/m/n/a.b.c;
<a>
  <b> <c/> <c/> </b>
  <b> <c/> <c/> </b>
</a>
<a>
  <b> <c/> <c/> </b>
  <b> <c/> <c/> </b>
</a>

Вставка не под

Чтобы была возможность вставить xml-данные не под таблицу, а сами по себе, над-таблицу можно не указывать.

при условии следующего DDL запрос на DML запрос выполнен так
create table a (
  id   num      primary key,
  data float
);
create table b (
  id   num      primary key,
  ref  num      references a(id),
  data float
);
insert values ('
  <a        data=12.3>
    <b      data=23.4>
    <b      data=34.5>
    <b      data=45.6>
  </a>
');
insert into a values (10,    12.3);
insert into b values (101,10,23.4);
insert into b values (102,10,34.5);
insert into b values (103,10,45.6);
insert select * from tab/@fld/k/m/n/a.b;

Изменение

Не вызывает затруднений изменение поля (рациональной таблицы)

update tab set fld=( select * from a.b.c                );
update tab set fld=( select * from a.b#ref1.c#lnk1      );
update tab set fld=( select * from t/@field/k/m/n/a.b.c );
update tab set fld='<a>...</a>';
или атрибута (xml-элемента)
update tab set @fld/p/q/r/@r1=( select * from a.b.c                );
update tab set @fld/p/q/r/@r1=( select * from a.b#ref1.c#lnk1      );
update tab set @fld/p/q/r/@r1=( select * from t/@field/k/m/n/a.b.c );
update tab set @fld/p/q/r/@r1='<a>...</a>';
Предлагаю обозначать содержимое xml-элемента как '@@', тогда его замена выглядит как показано в примере ниже. Рациональные деревья можно только удалять и создавать вновь.
update tab set @fld/p/q/@@=( select * from a.b.c                );
update tab set @fld/p/q/@@=( select * from a.b#ref1.c#lnk1      );
update tab set @fld/p/q/@@=( select * from t/@field/k/m/n/a.b.c );
update tab set @fld/p/q/@@='<a>...</a>';

Удаление

Добавляется возможность удалить конкретный xml-элемент (со всем его содержимым)

delete from tab/@fld/k/m/n;
xml-дерево
delete from tab/@fld/k.m.n;
рациональное дерево
delete from a.b.c;

Предикаты

Предикаты должны создаваться также для xml-аттрибутов.

where @fld/k/m/n/@n1 =  5;
where @fld/k/m/n/@n1 in (select a1 from a);
where @fld/k/m/n/@@  =  '<tag>...</tag>';
where @fld/k/m/n     in (select * from a/b/n);

Полномочия

Полномочия должны выдаваться также на xml-элементы.

grant  insert on tab/@fld/k/m/n to   UserName;
revoke delete on tab/@fld/k/m/n from UserName;

Триггеры

Триггеры должны создаваться также для xml-элементов.

create trigger TriggerName for tab/@fld/k/m/n
after insert as begin
  ...
end;;

Симметризация XPath и XTree

В XTree вложенные табменты не извлекаеются, если они не указаны. Например, если 'a' имеет вложенный табмент 'm', то выражением 'a.b.c' он извлечен не будет.

Аналогично XPath, в XTree возможны конструкции вида 'a.b | k.c', 'a.b*.c', 'a.b+.c', 'a.b?.c', 'a.b[@b1].c', 'a.b[@b1=5].c', 'a.b[0].c', 'a.(b | k)[0].c' и т.д. Гарантируется, что всегда сначала будет обработано звено с меньшим уровнем иерархии, и только затем с большим (это распространяется и на одноименные табменты под знаком '*' или '+').

XPath имеет выражения вида 'a//b' - предлагаю ввести симметричную конструкцию 'a..b' в XTree, которая означает табмент 'a', табмент 'b' и любое количество табментов любого имени между ними.

Кроме того, предлагаю использовать знак '!' для обозначения уровня иерархии в квадратных скобках (т.е. 'tab[!=5]'). Например, в выражении 'a.b.c.d.e' табмент 'a' имеет 0-й, 'b' имеет 1-й, 'c' имеет 2-й, 'd' имеет 3-й, 'e' имеет 4-й уровень. Все элементы списка (табменты одной секции) имеют одно и то же значение уровня иерархии.



Тюрин Дмитрий



Перечень статей   Терминология   Choose language