Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Использование XML совместно с SQL.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
1.39 Mб
Скачать

Вложенные запросы for xml

Раньше нельзя было выполнять вложенные запросы, возвращающие xml. Теперь это стало возможным. Например:

select name, object_id,

(select name, index_id

from sys.indexes [index]

where [index].object_id = object.object_id

for xml auto, type

)

from sys.objects object

for xml auto

Вложенный запрос возвращает тип xml, и серверу абсолютно безразлично, как он был получен. Т.е. с тем же успехом можно написать так:

declare @xml xml

set @xml = '<root/>'

select name, object_id, @xml

from sys.objects object

for xml auto

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

XSINIL

Теперь пустое значение можно представлять с помощью стандартного механизма, принятого в XML для обозначения значения NULL. Суть проблемы заключается в том, что в режиме for xml auto, elements поля со значением NULL просто не попадают в результирующий XML-фрагмент. Например:

select top 4 object_id,

case when object_id % 2 = 0 then null

else object_id end obj

from sys.indexes

for xml auto, elements

Возвращает:

<sys.indexes>

<object_id>133575514</object_id>

</sys.indexes>

<sys.indexes>

<object_id>677577452</object_id>

</sys.indexes>

<sys.indexes>

<object_id>677577452</object_id>

</sys.indexes>

<sys.indexes>

<object_id>693577509</object_id>

<obj>693577509</obj>

</sys.indexes>

Как видите, для первых трех строк элемент obj отсутствует, потому что равен NULL. Для того чтобы передать информацию о том, что он равен NULL, в XML-документ, необходимо добавить ключевое слово xsinil следующим образом:

select top 4 object_id,

case when object_id % 2 = 0 then null

else object_id end obj

from sys.indexes

for xml auto, elements xsinil

Теперь все в порядке (изменения выделены):

<sys.indexes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<object_id>133575514</object_id>

<obj xsi:nil="1" />

</sys.indexes>

<sys.indexes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<object_id>677577452</object_id>

<obj xsi:nil="1" />

</sys.indexes>

<sys.indexes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<object_id>677577452</object_id>

<obj xsi:nil="1" />

</sys.indexes>

<sys.indexes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<object_id>693577509</object_id>

<obj>693577509</obj>

</sys.indexes>

Генерация xsd на лету

Раньше создать схему на лету можно было только в формате XDR с помощью конструкции for xml , xmldata. Так как спецификация XRD, которая никогда не была официальным стандартом, уже давно устарела, разработчики, наконец, ввели поддержку генерации схемы в формате XSD по результатам выборки. Например, нехитрый запрос:

select top 4 name, object_id

from sys.indexes

for xml auto, xmlschema, elements xsinil

возвращает следующий, довольно хитрый, результат:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"

targetNamespace="http://schemas.microsoft.com/sql/2002/types">

<xsd:simpleType name="nvarchar">

<xsd:restriction base="xsd:string"/>

</xsd:simpleType>

<xsd:simpleType name="int">

<xsd:restriction base="xsd:int"/>

</xsd:simpleType>

</xsd:schema>

<xsd:schema targetNamespace="urn:schemas-microsoft-com:sql:SqlRowSet8"

xmlns:schema="urn:schemas-microsoft-com:sql:SqlRowSet8"

xmlns:xsd="http://www.w3.org/2001/XMLSchema"

xmlns:sqltypes="http://schemas.microsoft.com/sql/2002/types"

elementFormDefault="qualified">

<xsd:import namespace="http://schemas.microsoft.com/sql/2002/types"/>

<xsd:element name="sys.indexes">

<xsd:complexType>

<xsd:sequence>

<xsd:element name="name" nillable="1">

<xsd:simpleType>

<xsd:restriction base="sqltypes:nvarchar"

sqltypes:localeId="1049"

sqltypes:sqlCompareOptions="IgnoreCase IgnoreKanaType IgnoreWidth">

<xsd:maxLength value="128"/>

</xsd:restriction>

</xsd:simpleType>

</xsd:element>

<xsd:element name="object_id"

type="sqltypes:int"

nillable="1"/>

</xsd:sequence>

</xsd:complexType>

</xsd:element>

</xsd:schema>

<sys.indexes xmlns="urn:schemas-microsoft-com:sql:SqlRowSet8"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<name>pk_dtproperties</name>

<object_id>133575514</object_id>

</sys.indexes>

<sys.indexes xmlns="urn:schemas-microsoft-com:sql:SqlRowSet8"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<name xsi:nil="1"/>

<object_id>677577452</object_id>

</sys.indexes>

<sys.indexes xmlns="urn:schemas-microsoft-com:sql:SqlRowSet8"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<name>IX_composite</name>

<object_id>677577452</object_id>

</sys.indexes>

<sys.indexes xmlns="urn:schemas-microsoft-com:sql:SqlRowSet8"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<name xsi:nil="1"/>

<object_id>693577509</object_id>

</sys.indexes>

Вложенные запросы не могут генерировать схему. Это может делать только запрос верхнего уровня. Все вложенные XML-фрагменты такого многоуровневого запроса представляются типом xsd:anyType. Кроме этого, нельзя генерировать XSD-схему на лету в режиме explicit.

sql:variable и sql:column

Может сложиться ситуация, когда в запросе XQuery нужно получить значение поля sql-запроса или внешней переменной. Для таких целей в SQL Server-е введены две функции расширения: variable и column, которые должны находиться в неком пространстве имен с префиксом sql. Если в запросе объявляется какое-либо пространство имен с тем же префиксом sql, выше обозначенные функции становятся недоступными. Надеюсь, эту ошибку, как и бесчисленное множество других, разработчики поправят к релизу.

В функцию sql:variable передается один параметр – строковый литерал, значение которого трактуется как имя переменной простого типа во внешней области видимости. Функция возвращает скалярное значение этой переменной. Например:

declare @xml xml

set @xml = '

<fam>

<husband income="180">alex</husband>

<wife income="161">rosa</wife>

<son income="90">dima</son>

</fam>'

declare @i int

set @i = 150

select @xml::query('/fam/*[@income < sql:variable("@i")]')

Здесь выбираются все дочерние элементы fam, у которых атрибут income меньше значения внешней переменной i.

Похожим образом используется функция sql:column. Она позволяет получить значение поля из внешнего запроса.

Например, следующий запрос содержит в себе XQuery-подзапрос, обращающийся к колонке id внешнего SQL-запроса.

select fam::query('<fam id = "{sql:column("id")}"> {/fam/*} </fam>')

from fams

Результат:

<fam id="1">

<husband income="180">alex</husband>

<wife income="161">rosa</wife>

<son income="90">dima</son>

</fam>

С помощью этой функции нельзя обращаться к колонкам XML-типа.

OPENXML

Как уже говорилось в первой части этой статьи [6], при использовании функции openxml можно было не указывать структуру и типы полей результирующей выборки, опуская ключевое слово with. В этом случае результат возвращался в так называемом edge table формате. Например,

declare @h int

exec sp_xml_preparedocument @h out, '<a><b/></a>'

select * from openxml(@h, '/')

exec sp_xml_removedocument @h

Возвращает:

id parentid nodetype localname prefix namespaceuri datatype prev text

-- -------- -------- --------- ------ ------------ -------- ---- ----

0 NULL 1 a NULL NULL NULL NULL NULL

2 0 1 b NULL NULL NULL NULL NULL

Теперь можно получать доступ к внутренней информации, использующейся в процессе создания edge table, при формировании структуры результирующей выборки. Это делается с помощью атрибутов метасвойств (metaproperty attributes), которые находятся в пространстве имен urn:schemas-microsoft-com:xml-metaprop, которому назначен префикс mp. Список всех метаатрибутов я приводить не буду: достаточно взглянуть на шапку предыдущей таблицы. Вот пример их использования:

declare @h int

exec sp_xml_preparedocument @h out,

'<a attr="it is"><b>text1</b><b>text2</b></a>'

select * from openxml(@h, '/a/b|/a')

with(

nodeid int '@mp:id',

parentid int '@mp:parentid',

nodename varchar(10) '@mp:localname',

text1 varchar(100) '../@attr',

text2 varchar(100) 'text()'

)

exec sp_xml_removedocument @h

Здесь выбирается три строки - два элемента b и элемент а. Вот результат:

nodeid parentid nodename text1 text2

------ -------- -------- ----- -----

0 NULL a NULL NULL

3 0 b it is text1

4 0 b it is text2