Советы и рекомендации по работе с XML
В SQL Server 2005 реализован широкий диапазон средств обработки XML-данных. Значения XML можно естественным образом хранить в столбце типа xml, который можно типизировать в соответствии с коллекцией XML-схем или оставить нетипизированным. XML-столбец можно индексировать. Кроме того, SQL Server 2005 поддерживает детализированную обработку данных с использованием языков XQuery и XML DML. Язык XML DML служит для модификации данных.
Веб-выпуски SQL Server 2000 и SQLXML предоставляют эффективные средства для работы с XML-данными. Основной их функцией является сопоставление реляционных и XML-данных. XML-представления реляционных данных могут быть определены при помощи аннотированной схемы XSD (AXSD), они обеспечивают XML-ориентированный подход с поддержкой массовой загрузки данных, запросов и возможностей обновления. Расширения языка Transact-SQL реализуют SQL-ориентированный подход для сопоставления XML-данных результатам реляционных запросов (с помощью инструкции FOR XML), а также для создания на основе XML-данных реляционных представлений (с помощью инструкции OPENXML). В SQL Server 2005 эти возможности расширены. В сочетании с естественной поддержкой XML-данных SQL Server 2005 формирует эффективную платформу разработки полнофункциональных приложений для управления частично структурированными и неструктурированными данными.
В этом разделе описаны принципы моделирования XML-данных и их использования в SQL Server 2005. Он делится на следующие подразделы.
- Моделирование данных
В SQL Server 2005 XML-данные можно хранить несколькими способами: с использованием естественного типа данных XML и XML-кода, распределенного по таблицам. В этом подразделе описываются принципы выбора оптимальных подходов к моделированию XML-данных. Кроме того, в нем рассматриваются такие темы, как индексирование XML-данных, продвижение свойств и типизация экземпляров XML. - Использование данных
В этом подразделе обсуждаются темы, связанные с использованием XML-данных, такие как загрузка XML-данных на сервер и выведение типов при компиляции запросов. В нем также объясняются сходства и различия тесно связанных функций и приводятся советы по их использованию. Рассматриваемый материал поясняется примерами.
Моделирование данных
В этом подразделе описываются обоснованные причины использования XML в SQL Server 2005. Кроме того, в нем приводятся принципы выбора между хранением XML-данных в естественном формате и технологией XML-представлений, а также даются рекомендации по моделированию данных.
Модель данных: реляционная или XML
Если данные хорошо структурированы и известна их схема, то для их хранения наверняка лучше всего подойдет реляционная модель. В SQL Server предусмотрены все необходимые для этого средства и функции. С другой стороны, если данные структурированы частично, неструктурированы или если их структура неизвестна, следует подумать о моделировании таких данных.
XML является удачным выбором, если нужна не зависящая от платформы модель, позволяющая гарантировать совместимость данных за счет применения структурной и семантической разметки. Кроме того, этот вариант уместен, если выполняются некоторые из следующих условий:
- данные разрежены, их структура неизвестна или их структура может значительно измениться в будущем;
- данные представляют иерархию контейнеров, а не ссылки между сущностями, и могут быть рекурсивными;
- данные упорядочены;
- требуется запрашивать данные или обновлять их фрагменты на основе их структуры.
Если никакие из этих условий не выполняются, следует выбрать реляционную модель данных. Например, если данные представлены в формате XML, но приложение пользуется базой данных только для их хранения и извлечения, то для этого вполне подойдет тип данных [n]varchar(max). Хранение данных в XML-столбце обеспечивает дополнительные преимущества. В их число входят проверка структуры и корректности данных, а также поддержка детализированных запросов и обновлений XML-данных.
Обоснованные причины хранения XML-данных в SQL Server 2005
Ниже приведены некоторые условия, при которых лучше использовать естественные XML-функции SQL Server 2005, а не управлять XML-данными, хранящимися в файловой системе.
- Требуется эффективно распространять, запрашивать и изменять свои XML-данные на основе транзакций. Большое значение имеет высокая гранулярность доступа к данным. Например, иногда нужно извлекать некоторые разделы XML-документа или вставлять в него новые разделы без замены всего документа.
- Предстоит иметь дело с реляционными и XML-данными, и необходимо обеспечить их совместимость в приложении.
- Необходима языковая поддержка запросов и модификации данных в приложениях, охватывающих несколько доменов.
- Требуется, чтобы сервер гарантировал корректность структуры данных и, возможно, проверял данные в соответствии с XML-схемами.
- Требуется проиндексировать XML-данные для оптимизации обработки запросов и улучшения масштабируемости и использовать эффективный оптимизатор запросов.
- Требуется обращаться к XML-данным, используя технологии SOAP, ADO.NET и OLE DB.
- Требуется использовать для управления XML-данными средства администрирования, реализованные в сервере баз данных. Примерами таких задач управления могут служить резервное копирование данных, их восстановление и репликация.
Если же ни одно из этих условий не выполняется, то для хранения данных лучше использовать отличный от XML тип больших объектов (например, [n]varchar(max) или varbinary(max)).
Варианты хранения XML-данных
SQL Server 2005 поддерживает несколько вариантов хранения XML-данных.
- Естественное хранение в виде типа xml
Данные при этом хранятся во внутреннем представлении, которое обеспечивает неизменность XML-содержимого данных. XML-содержимое определяет такие аспекты, как иерархия контейнеров, порядок документа и значения элементов и атрибутов. Точнее говоря, при этом обеспечивается неизменность InfoSet-содержимого XML-данных. Сведения о спецификации InfoSet можно найти по адресу: http://www.w3.org/TR/xml-infoset. InfoSet-содержимое не всегда идентично текстовым XML-данным, потому что следующая информация при этом не сохраняется: несущественное пустое пространство, порядок атрибутов, префиксы пространств имен и XML-декларация.
Для типизированного (то есть связанного с XML-схемой) типа данных xml модуль проверки после обработки схемы (PSVI), добавляет в информационный набор данные о типах и кодирует их во внутреннее представление. Это значительно ускоряет синтаксический анализ. Дополнительные сведения см. в спецификациях XML-схем, разработанных консорциумом W3C XML. Найти их можно по адресам http://www.w3.org/TR/xmlschema-1 и http://www.w3.org/TR/xmlschema-2. - Сопоставление XML-данных и данных, хранящихся в реляционном формате
Используя аннотированную схему (AXSD), можно разбить XML на столбцы одной или нескольких таблиц. Это обеспечивает корректность данных на реляционном уровне. В результате гарантируется сохранность иерархической структуры данных, хотя порядок элементов игнорируется. Схема не может быть рекурсивной. - Хранение больших объектов, [n]varchar(max) и varbinary(max)
При этом хранится идентичная копия данных. Это полезно в приложениях специального назначения, например в приложениях, обрабатывающих юридическую документацию. Большинству приложений точная копия данных не нужна — им хватает XML-содержимого (корректности элементов InfoSet).
Обычно используется сочетание этих подходов. Например, XML-данные можно сохранить в столбце типа xml, производя продвижение его свойств до уровня реляционных столбцов. Или же можно использовать технологию сопоставления для хранения нерекурсивных фрагментов в столбцах, отличных от XML, а в столбцах типа xml хранить только рекурсивные фрагменты.
Выбор XML-технологии
Выбор между естественным форматом XML и XML-представлениями обычно зависит от следующих факторов.
- Параметры хранения
Иногда XML-данные (например руководство по продукции) лучше хранить как большой объект, а в других ситуациях — в реляционных столбцах (например описание товара, преобразованное в формат XML). Каждый вариант хранения данных обеспечивает точность документа в разной степени. - Обработка запросов
Иногда один вариант хранения данных лучше другого соответствует природе и интенсивности запросов XML-данных. Степень поддержки детализированных запросов XML-данных, например оценки предикатов для XML-узлов, поддерживается двумя технологиями хранения данных в разной степени. - Индексирование XML-данных
Возможно, потребуется проиндексировать XML-данные, чтобы ускорить обработку XML-запросов. Возможности индексирования зависят от технологии хранения данных; для оптимизации рабочей нагрузки нужно выбрать более подходящий вариант. - Возможности модификации данных
Некоторые виды рабочей нагрузки сопряжены с детализированной модификацией XML-данных. Это может требоваться, например при добавлении нового раздела в документ, в то время как при решении других задач, таких как обработка веб-содержимого, это не нужно. Для разработчиков того или иного приложения большое значение может иметь поддержка языка модификации данных. - Поддержка схем
XML-данные можно описать при помощи схемы, которая может быть, а может и не быть документом XML-схемы. Поддержка связанных со схемой XML-данных зависит от XML-технологии.
Кроме того, технологии хранения XML-данных различаются по быстродействию.
Хранение XML-данных в собственном формате
XML-данные можно хранить на сервере в столбце типа xml. Этот вариант уместен, если выполняются следующие условия:
- необходим простой способ хранения XML-данных на сервере, при этом нужно сохранить порядок и структуру документа;
- нет схемы XML-данных;
- требуется запрашивать и изменять XML-данные;
- требуется проиндексировать XML-данные для ускорения обработки запросов;
- требуется использовать в приложении системные представления каталога для управления XML-данными и XML-схемами.
Механизм хранения XML-данных в естественном формате полезен, если есть XML-документы, имеющие разную структуру, или XML-документы, соответствующие разным или сложным схемам, которые слишком трудно сопоставить с реляционными структурами.
Пример: моделирование XML-данных с использованием типа данных xml
Допустим, мы имеем дело с руководством по продукции в формате XML, которое охватывает ряд тем, разделенных на несколько глав, включающих по несколько разделов. Раздел может содержать подразделы. Таким образом, элемент <section> является рекурсивным. Руководства по продукции содержат большой объем смешанной информации, диаграмм и технических характеристик; эти данные структурированы частично. Пользователям руководств может пригодиться возможность контекстного поиска интересующих их тем, например подраздела «кластеризованный индекс» в разделе «индексирование», и запроса технических характеристик.
Для хранения таких XML-документов прекрасно подходит столбец типа xml. Это позволяет сохранить InfoSet-содержимое XML-данных. Индексирование XML-столбца позволяет повысить эффективность обработки запросов.
Пример: сохранение точных копий XML-данных
Предположим, например, что правительственные законы требуют, чтобы хранились точные текстовые копии XML-документов. К этой категории могут относиться подписанные документы, юридические постановления и отчеты о биржевых операциях. Такие данные можно хранить в столбце типа [n]varchar(max).
При обработке запросов во время выполнения преобразуйте данные в тип xml и выполните для них запрос XQuery. Преобразование типов в период выполнения может быть связано со значительной тратой ресурсов, особенно если документ велик. При высокой частоте запросов документы можно дополнительно сохранять в столбце типа xml и индексировать именно его, а для возврата точных копий документа использовать столбец [n]varchar(max).
XML-столбец может быть столбцом, вычисляемым на основе столбца [n]varchar(max). Однако для вычисляемого XML-столбца, а также для столбцов типов [n]varchar(max) или varbinary(max), нельзя создать XML-индекс.
Технология XML-представлений
Определив соответствие между XML-схемами и таблицами базы данных, можно создать «XML-представление» хранимых данных. Чтобы заполнить базовые таблицы при помощи XML-представления, можно использовать операцию массовой загрузки XML-данных. Запрашивать XML-данные можно при помощи технологии XPath версии 1.0, при этом запрос преобразуется в SQL-запросы таблиц. Обновления также распространяются на эти таблицы.
Эта технология полезна в следующих ситуациях:
- требуется реализовать ориентированную на XML модель программирования, используя XML-представления существующих реляционных данных;
- есть XSD-схема или XDR-схема XML-данных, которую, возможно, предоставила внешняя партнерская организация;
- порядок данных не важен, данные таблиц нерекурсивны или максимальная глубина рекурсии не известна заранее;
- требуется запрашивать и изменять данные посредством XML-представления с использованием технологии XPath версии 1.0;
- требуется выполнять массовую загрузку XML-данных и распределять их между базовыми таблицами с использованием XML-представления.
Примерами данных, отвечающих этим условиям, могут служить реляционные данные, предоставляемые веб-службам и средствам обмена данными в форме XML, а также XML-данные с фиксированной схемой. Дополнительные сведения см. в библиотеке MSDN Online.
Пример: моделирование данных с использованием аннотированной XML-схемы (AXSD)
Предположим, что есть реляционные данные (например, сведения о заказчиках, заказах и товарах), которые нужно обрабатывать как XML. Определите в этом случае XML-представление, применив схему AXSD к реляционным данным. XML-представление позволяет выполнять массовую загрузку XML-данных в таблицы, а также запрашивать и обновлять реляционные данные. Эта модель особенно эффективна, если требуется обмениваться данными, содержащими XML-разметку, с другими приложениями без приостановления работы приложений SQL.
Комбинированная модель
Довольно часто для моделирования данных лучше всего подходит комбинация реляционных столбцов и столбцов типа xml . Некоторые значения XML-данных можно хранить в реляционных столбцах, а остальные или все значения XML — в XML-столбце. Это может привести к повышению производительности за счет более полного контроля над индексами, созданными для реляционных столбцов, и параметрами блокировки.
Значения, которые следует хранить в реляционных столбцах, зависят от рабочей нагрузки. Например, если извлекаются все XML-значения при использовании выражения пути /Customer/@CustId, то, выполнив продвижение значения атрибута CustId до реляционного столбца и осуществив его индексацию, можно, ускорить обработку запросов. С другой стороны, если XML-данные чрезмерно распределить по реляционным столбцам без дублирования, составление данных в единое целое может оказаться слишком дорогим.
В случае высокоструктурированных XML-данных, таких как содержимое таблицы, преобразованное в XML, можно сопоставить все значения с реляционными столбцами и, возможно, использовать технологию XML-представлений.
Моделирование данных с использованием типа данных xml
В этом разделе обсуждается моделирование данных в естественном формате XML. Сюда относится индексирование XML-данных, продвижение свойств и типизированный тип данных xml.
Та же таблица или другая
Столбец типа xml можно создать в таблице, содержащей другие реляционные столбцы, или в таблице, связанной посредством внешнего ключа с главной таблицей.
Столбец типа xml следует создавать в той же таблице, если выполняется одно из перечисленных ниже условий.
- Приложение извлекает данные из XML-столбца и не нуждается в том, чтобы для него был создан XML-индекс.
- Для столбца типа xml требуется создание XML-индекса, причем первичный ключ главной таблицы идентичен ее ключу кластеризации. Дополнительные сведения см. в разделе «Индексирование столбца типа xml».
Столбец типа xml следует создавать в отдельной таблице, если выполняются перечисленные ниже условия.
- Для столбца типа xml требуется создание XML-индекса, но первичный ключ главной таблицы отличается от ее ключа кластеризации, главная таблица не имеет первичного ключа или главная таблица является кучей (нет ключа кластеризации). Это может иметь место, если главная таблица уже существует.
- Не требуется замедлять просмотр таблицы из-за наличия в ней XML-столбца. Он занимает место независимо от того, хранится ли он в строке или вне строки.
Гранулярность XML-данных
Гранулярность XML-данных, хранимых в XML-столбце, очень важна при блокировках и в меньшей степени при обновлениях. В SQL Server используется одинаковый механизм блокировки XML-данных и данных, отличных от XML. Таким образом, при блокировке на уровне строки блокируются все экземпляры XML в строке. Если гранулярность велика, блокировка крупных экземпляров XML для выполнения обновлений сокращает производительность системы в многопользовательской среде. С другой стороны, при чрезмерной декомпозиции утрачивается инкапсуляция объекта и возрастают накладные расходы, связанные с воссозданием данных.
Чтобы создать эффективную систему, нужно достичь баланса между требованиями к моделированию данных и характеристиками блокировок и обновлений. Однако в SQL Server 2005 размер хранимых экземпляров XML имеет не такое большое значение.
Например, обновления экземпляра XML выполняются с использованием нового способа частичного обновления больших двоичных объектов (BLOB) и индексов, при котором существующий хранимый экземпляр XML сравнивается с его обновленной версией. При частичном обновлении большого двоичного объекта выполняется разностное сравнение двух экземпляров XML и обновляются только различающиеся данные. При частичном обновлении индекса изменяются только те строки XML-индекса, которые этого требуют.
Нетипизированный, типизированный и ограниченный тип данных xml
В SQL Server 2005 тип данных xml соответствует типу данных xml стандарта ISO SQL-2003. Таким образом, он позволяет хранить синтаксически корректные документы XML 1.0, а также так называемые фрагменты XML-содержимого с текстовыми узлами и произвольным числом элементов верхнего уровня в нетипизированном XML-столбце. SQL Server проверяет синтаксическую корректность данных, не требует, чтобы столбец был связан с XML-схемами, и отклоняет данные, не являющиеся синтаксически корректными в расширенном смысле. Это также верно для нетипизированных переменных и параметров типа XML.
Если есть XML-схемы, описывающие XML-данные, можно связать схемы с XML-столбцом, в результате чего получить типизированные XML-данные. XML-схемы используются для проверки данных, более точной в сравнении с нетипизированным XML проверки типов во время компиляции запросов и инструкций модификации данных, а также для оптимизации хранения данных и обработки запросов.
В следующих ситуациях следует использовать нетипизированный тип данных xml.
- Нет схемы XML-данных.
- Есть схемы, но нежелательно, чтобы сервер проверял данные. Это может иметь место в тех случаях, если приложение перед сохранением данных на сервере проверяет их на стороне клиента, если оно временно хранит XML-данные, которые не соответствуют схеме, или использует компоненты схемы, не поддерживаемые сервером (например key/keyref).
В следующих ситуациях следует использовать типизированный тип данных xml :
- есть схемы XML-данных и требуется, чтобы сервер проверял соответствие данных этим схемам;
- требуется оптимизировать хранение данных и обработку запросов на основе информации о типах;
- требуется в более полной мере использовать информацию о типах при компиляции запросов.
В типизированных XML-столбцах, параметрах и переменных можно хранить XML-документы или содержимое. Во время объявления необходимо указать при помощи флага, что хранится: документ или содержимое. Кроме того, необходимо предоставить системе коллекцию XML-схем. Укажите флаг DOCUMENT, если каждый экземпляр XML имеет ровно один элемент верхнего уровня. В противном случае укажите флаг CONTENT. Компилятор запросов использует флаг DOCUMENT при проверке типов во время компиляции запросов для определения одинарных элементов верхнего уровня.
Кроме типизации XML-столбца, к столбцам xml типизированного или нетипизированного типа можно применять реляционные ограничения (для столбцов или строк). Используйте ограничения в следующих ситуациях.
- Бизнес-правила не могут быть выражены в XML-схемах. Допустим, например, что адреса доставки заказов магазином цветов должны находиться не далее 90 километров от магазина. Это требование можно выразить в форме ограничения XML-столбца. Эти ограничения могут содержать методы для обработки данных типа xml.
- В ограничении фигурируют другие столбцы таблицы, имеющие тип XML или другой тип. В качестве примера можно привести требование, согласно которому идентификатор заказчика (/Customer/@CustId), хранимый в экземпляре XML, должен соответствовать значению из реляционного столбца CustomerID.
Определение типа документа (DTD)
Типизацию столбцов, переменных и параметров типа xml можно выполнять с использованием XML-схемы, но без использования DTD. Однако и с нетипизированными, и с типизированными XML-данными можно использовать встроенное определение DTD для указания значений по умолчанию и замены ссылок на сущности их расширенными формами.
Можно преобразовывать определения DTD в документы XML-схем при помощи инструментов других компаний и загружать эти XML-схемы в базу данных.
Индексирование столбцов типа данных xml
Для столбцов типа xml можно создавать XML-индексы. При этом индексируются все теги, значения и пути хранимых в столбце экземпляров XML и повышается эффективность обработки запросов. Применение XML-индекса может дать преимущества в следующих ситуациях.
- Часто выполняются запросы XML-столбцов. При этом нужно учитывать расходы на сопровождение XML-индекса во время модификации данных.
- XML-значения относительно велики, а извлекаемые XML-данные относительно малы. Создание индекса позволяет предотвратить синтаксический анализ всех данных в период выполнения, а также повышает эффективность обработки уточняющих запросов.
Первый индекс XML-столбца является первичным XML-индексом. При его использовании можно создать для XML-столбца вторичные XML-индексы трех типов с целью ускорения обработки часто применяемых запросов. Более подробная информация об этом приведена в следующем разделе.
Первичный XML-индекс
При создании первичного индекса индексируются все теги, значения и пути в экземплярах XML, хранимых в XML-столбце. Базовая таблица, т. е. таблица с XML-столбцом, должна включать кластеризованный индекс, связанный с первичным ключом таблицы. Первичный ключ используется для сопоставления строк индекса со строками базовой таблицы. При извлечении данных (например с помощью запроса SELECT *) из XML-столбцов извлекаются полные экземпляры XML. При обработке запросов используется первичный XML-индекс, и для возврата скалярных значений или поддеревьев XML применяется сам индекс.
Пример: создание первичного XML-индекса
В большинстве наших примеров используется таблица T (pk INT PRIMARY KEY, xCol XML) с нетипизированным XML-столбцом. Эти примеры можно легко расширить на типизированный XML. Дополнительные сведения об использовании типизированного XML см. в разделе Тип данных xml. Ради простоты запросы XML-данных описываются следующим образом:
<book genre="security" publicationdate="2002" ISBN="0-7356-1588-2">
<title>Writing Secure Code</title>
<author>
<first-name>Michael</first-name>
<last-name>Howard</last-name>
</author>
<author>
<first-name>David</first-name>
<last-name>LeBlanc</last-name>
</author>
<price>39.99</price>
</book>
Следующая инструкция создает для XML-столбца xCol таблицы T XML-индекс с именем idx_xCol.
CREATE PRIMARY XML INDEX idx_xCol on T (xCol)
Вторичные XML-индексы
После создания первичного XML-индекса, возможно, потребуется создать вторичные XML-индексы для ускорения обработки запросов различных категорий. Три типа вторичных XML-индексов — PATH, PROPERTY и VALUE — повышают эффективность соответственно запросов, основанных на путях, сценариев управления пользовательскими свойствами и запросов, основанных на значениях. В случае индекса PATH создается дерево B+ по паре (путь, значение) каждого XML-узла в порядке документа для всех экземпляров XML в столбце. При индексе PROPERTY создается дерево B+, кластеризованное по параметрам (PK, path, value) в каждом экземпляре XML, где PK — первичный ключ базовой таблицы. Наконец, при индексе VALUE создается дерево B+ по паре (value, path) каждого узла в порядке документа для всех экземпляров XML в XML-столбце.
Ниже приведены некоторые советы по созданию одного или более из этих индексов.
- Если при работе с XML-столбцами часто используются выражения пути, вторичный XML-индекс PATH, скорее всего, ускорит обработку данных. Типичный пример — выполнение метода exist() для XML-столбцов в предложении WHERE инструкции Transact-SQL.
- Если с использованием выражений пути извлекаются множественные значения из отдельных экземпляров XML, может принести пользу кластеризация путей в пределах каждого экземпляра XML в индекс PROPERTY. Этот сценарий обычно имеет место при работе с наборами свойств, когда извлекаются свойства объекта и известно значение его первичного ключа.
- Если запрашиваются значения экземпляров XML, не зная имен элементов или атрибутов, содержащих эти значения, следует подумать о создании индекса VALUE. Как правило, это имеет место при уточняющем запросе по осям нижних уровней (например //author[last-name="Howard", где элементы <author> могут встречаться на любом уровне иерархии). Кроме того, такая ситуация встречается при обработке запросов с символами-шаблонами (например /book [@* = "novel"], где в запросе выполняется поиск элементов <book>, имеющих некоторый атрибут со значением «novel»).
Пример: уточняющий запрос на основе пути
Предположим, что приходится часто выполнять следующий запрос:
SELECT pk, xCol
FROM T
WHERE xCol.exist ('/book/@genre[.="novel"]') = 1
Выражение пути /book/@genre и значение «novel» соответствуют ключевым полям индекса PATH. Таким образом, при таком типе рабочей нагрузки полезен вторичный XML-индекс типа PATH.
CREATE XML INDEX idx_xCol_Path on T (xCol)
USING XML INDEX idx_xCol FOR PATH
Пример: извлечение свойств объекта
Рассмотрим следующий запрос, извлекающий из каждой строки таблицы T свойства «genre», «title» и ISBN-номер книги:
SELECT xCol.value ('(/book/@genre)[1]', 'varchar(50)'),
xCol.value ('(/book/title/text())[1]', 'varchar(50)'),
xCol.value ('(/book/@ISBN)[1]', 'varchar(50)')
FROM T
В этом случае полезен индекс PROPERTY:
CREATE XML INDEX idx_xCol_Property on T (xCol)
USING XML INDEX idx_xCol FOR PROPERTY
Пример: запрос, основанный на значениях
В следующем запросе ось нижнего или этого же уровня (//) определяет частичный путь, так что эффективность уточняющего запроса на основе значения ISBN можно повысить за счет применения индекса VALUE:
SELECT xCol
FROM T
WHERE xCol.exist ('//book/@ISBN[. = "0-7356-1588-2"]') = 1
Индекс VALUE создается следующим образом:
CREATE XML INDEX idx_xCol_Value on T (xCol)
USING XML INDEX idx_xCol FOR VALUE
Полнотекстовый индекс XML-столбца
Для XML-столбцов можно создавать полнотекстовые индексы, индексирующие XML-значения, но игнорирующие XML-разметку. Полнотекстовая индексация значений атрибутов не выполняется, потому что они считаются частью разметки, а теги элементов используются как ограничители маркеров. Иногда можно совместно пользоваться полнотекстовым поиском и XML-индексом следующим образом:
- сначала отфильтруйте интересующие XML-значения, используя механизм полнотекстового поиска SQL;
- затем запросите XML-значения, которые используют XML-индекс, связанный с XML-столбцом.
Пример: комбинирование полнотекстового поиска с запросами XML-данных
После создания полнотекстового индекса для XML-столбца следующий запрос проверяет, что название книги содержит слово «custom»:
SELECT *
FROM T
WHERE CONTAINS(xCol,'custom')
AND xCol.exist('/book/title/text()[contains(.,"custom")]') =1
В методе contains() полнотекстовый индекс используется для выделения всех XML-значений, содержащих слово «custom». Предложение exist() гарантирует, что это слово входит в название книги.
Инструкции полнотекстового поиска, в которых используются метод contains() и одноименный метод языка XQuery, имеют различную семантику. Во втором случае выполняется сопоставление подстрок, а в первом — сопоставление маркеров с применением лемматизации. Таким образом, если искать строку, содержащую в заголовке слово «run», в число найденных вариантов войдут «run», «runs» и «running», потому что они соответствуют требованиям и метода contains() механизма полнотекстового поиска и метода contains() языка XQuery. Однако условия нашего запроса не соответствуют слову «customizable» в заголовке, хотя условия метода contains() языка XQuery были бы удовлетворены. Как правило, чтобы провести истинное сопоставление подстрок, метод contains() механизма полнотекстового поиска использовать не следует.
Кроме того, при полнотекстовом поиске выполняется лемматизация, а метод contains() языка XQuery осуществляет буквальное сопоставление. Это различие поясняет следующий пример.
Пример: полнотекстовый поиск XML-значений с использованием парадигматического модуля
Проверку contains() языка XQuery, выполненную в предыдущем примере, обычно устранить нельзя. Рассмотрим следующий запрос:
SELECT *
FROM T
WHERE CONTAINS(xCol,'run')
Слово «run» в документе соответствует условию поиска, потому что при этом осуществляется лемматизация. Кроме того, при использовании XQuery не проверяется контекст поиска.
Если при помощи схем XML-данные распределены по реляционным столбцам, для которых выполнено полнотекстовое индексирование, при обработке запросов XPath, адресованных XML-представлению, полнотекстовый поиск в базовых таблицах не выполняется.
Продвижение свойств
Если часто запрашивается небольшое количество значений элементов и атрибутов, можно выполнить их продвижение до реляционных столбцов. Это полезно, если запрос выполняется для небольшой части XML-данных, тогда как извлекается весь экземпляр XML. Создавать XML-индекс для XML-столбца не требуется. Вместо этого можно проиндексировать столбец, созданный на основе свойства. В запросах необходимо использовать этот столбец, потому что оптимизатор запросов не перенаправляет запросы XML-столбца столбцу, созданному на основе свойства.
Столбец, созданный на основе свойства, может быть вычисляемым столбцом в той же таблице или отдельным пользовательским столбцом таблицы. Если для каждого экземпляра XML выполняется продвижение одинарных значений, этого достаточно. Однако в случае многозначных свойств необходимо создать для свойства отдельную таблицу. Это описывается в следующем разделе.
Вычисляемый столбец, основанный на типе данных xml
Вычисляемый столбец можно создать при помощи пользовательской функции, вызывающей методы типа данных xml. Вычисляемый столбец может иметь любой тип SQL, в том числе XML. Это продемонстрировано в следующем примере.
Пример: вычисляемый столбец, основанный на методе типа данных xml
Создание пользовательской функции, возвращающей ISBN-номер книги:
CREATE FUNCTION udf_get_book_ISBN (@xData xml)
RETURNS varchar(20)
BEGIN
DECLARE @ISBN varchar(20)
SELECT @ISBN = @xData.value('/book[1]/@ISBN', 'varchar(20)')
RETURN @ISBN
END
Добавление в таблицу вычисляемого столбца для хранения значений ISBN:
ALTER TABLE T
ADD ISBN AS dbo.udf_get_book_ISBN(xCol)
Теперь вычисляемый столбец можно проиндексировать обычным способом.
Пример: запросы данных из вычисляемого столбца на основе методов типа данных xml
Чтобы получить элемент <book
> с ISBN-номером 0-7356-1588-2, можно выполнить следующее:
SELECT xCol
FROM T
WHERE xCol.exist('/book/@ISBN[. = "0-7356-1588-2"]') = 1
Запрос данных из XML-столбца можно переписать так, чтобы в нем использовался вычисляемый столбец:
SELECT xCol
FROM T
WHERE ISBN = '0-7356-1588-2'
Можно создать пользовательскую функцию, возвращающую тип данных xml, и вычисляемый столбец, основанный на пользовательской функции. Однако создать XML-индекс для вычисляемого XML-столбца нельзя.
Создание таблиц свойств
Иногда целесообразно выполнить продвижение некоторых многозначных свойств XML-данных до одной или нескольких таблиц, создать индексы для этих таблиц и перенаправить запросы к этим таблицам. Типичная ситуация, когда это полезно, имеет место тогда, когда в большинстве запросов обращение происходит к небольшому числу свойств. Можно сделать следующее.
- Создать одну или несколько таблиц для хранения многозначных свойств. Иногда удобно хранить одно свойство в каждой таблице и продублировать первичный ключ базовой таблицы в таблицах свойств для их соединения с базовой таблицей.
- Если требуется сохранить относительный порядок свойств, для этого нужно создать отдельный столбец.
- Создать триггеры для XML-столбца с целью обслуживания таблиц свойств. В триггерах сделайте что-либо одно из следующего перечисленного ниже.
- Используйте методы типа данных xml, такие как nodes() и value(), для вставки строк в таблицы свойств и их удаления.
- Создайте в среде CLR потоковые возвращающие табличное значение функции для вставки строк в таблицы свойств и их удаления.
- Напишите запросы, осуществляющие основанный на SQL доступ к таблицам свойств и основанный на XML доступ к XML-столбцу базовой таблицы, соединяя таблицы с использованием их первичного ключа.
Пример: создание таблицы свойств
Предположим, что требуется выполнить продвижение свойства, представляющего имена авторов. У книги может быть несколько авторов, поэтому данное свойство является многозначным. Каждое имя хранится в отдельной строке таблицы свойств. Первичный ключ базовой таблицы дублируется в таблице свойств ради обратного соединения таблиц.
create table tblPropAuthor (propPK int, propAuthor varchar(max))
Пример: создание пользовательской функции для создания набора строк на основе экземпляра XML
Следующая возвращающая табличное значение функция, udf_XML2Table, принимает значение первичного ключа и экземпляр XML. Она извлекает имена всех авторов из элемента <book
> и возвращает набор строк, состоящий из пар (первичный ключ / имя).
create function udf_XML2Table (@pk int, @xCol xml)
returns @ret_Table table (propPK int, propAuthor varchar(max))
with schemabinding
as
begin
insert into @ret_Table
select @pk, nref.value('.', 'varchar(max)')
from @xCol.nodes('/book/author/first-name') R(nref)
return
end
Пример: создание триггеров для заполнения таблицы свойств
Триггер вставки вставляет строки в таблицу свойств:
create trigger trg_docs_INS on T for insert
as
declare @wantedXML xml
declare @FK int
select @wantedXML = xCol from inserted
select @FK = PK from inserted
insert into tblPropAuthor
select * from dbo.udf_XML2Table(@FK, @wantedXML)
Триггер удаления удаляет строки из таблицы свойств на основе значения первичного ключа:
create trigger trg_docs_DEL on T for delete
as
declare @FK int
select @FK = PK from deleted
delete tblPropAuthor where propPK = @FK
Триггер обновления удаляет строки из таблицы свойств в соответствии с обновленным экземпляром XML и вставляет в таблицу свойств новые строки:
create trigger trg_docs_UPD
on T
for update
as
if update(xCol) or update(pk)
begin
declare @FK int
declare @wantedXML xml
select @FK = PK from deleted
delete tblPropAuthor where propPK = @FK
select @wantedXML = xCol from inserted
select @FK = pk from inserted
insert into tblPropAuthor
select * from dbo.udf_XML2Table(@FK, @wantedXML)
end
Пример: поиск экземпляров XML, включающих авторов с именем «David»
Можно составить такой запрос для XML-столбца или найти в таблице свойств записи с именами David и выполнить обратное соединение с базовой таблицей для возврата экземпляра XML. Например:
SELECT xCol
FROM T JOIN tblPropAuthor ON T.pk = tblPropAuthor.propPK
WHERE tblPropAuthor.propAuthor = 'David'
Пример: решение, основанное на использовании потоковой возвращающей табличное значение функции CLR
Данное решение состоит из следующих шагов:
- определение CLR-класса SqlReaderBase, реализующего интерфейс ISqlReader и формирующего потоковый возвращающий табличное значение вывод путем применения выражения пути к экземпляру XML;
- создание сборки и пользовательской функции языка Transact-SQL, запускающей класс CLR;
- определение с помощью пользовательской функции триггеров вставки, обновления и удаления для обслуживания таблиц свойств.
Чтобы реализовать это решение, создайте сначала потоковую функцию CLR. Тип данных xml представляется в ADO.NET как управляемый класс SqlXml с поддержкой метода CreateReader(), возвращающего объект класса XmlReader.
Примечание. |
---|
В примере данного раздела используются классы XPathDocument и XPathNavigator, которые вынуждают программиста загрузить в память все XML-документы. Используя подобный код в своих приложениях для обработки нескольких крупных XML-документов, знайте, что он плохо масштабируется. Старайтесь свести к минимуму число операций выделения памяти и используйте во всех возможных случаях потоковые интерфейсы. Дополнительные сведения о производительности см. в разделе Architecture of CLR Integration. |
public class c_streaming_xml_tvf {
public static ISqlReader streaming_xml_tvf
(SqlXml xmlDoc, string pathExpression) {
return (new TestSqlReaderBase (xmlDoc, pathExpression));
}
}
// Class that implements ISqlReader
public class TestSqlReaderBase : ISqlReader {
XPathNodeIterator m_iterator;
public SqlChars FirstName;
// Metadata for current resultset
private SqlMetaData[] m_rgSqlMetaData;
public TestSqlReaderBase (SqlXml xmlDoc, string pathExpression) {
// Variables for XPath navigation
XPathDocument xDoc;
XPathNavigator xNav;
XPathExpression xPath;
// Set sql metadata
m_rgSqlMetaData = new SqlMetaData[1];
m_rgSqlMetaData[0] = new SqlMetaData ("FirstName",
SqlDbType.NVarChar,50);
//Set up the Navigator
if (!xmlDoc.IsNull)
xDoc = new XPathDocument (xmlDoc.CreateReader());
else
xDoc = new XPathDocument ();
xNav = xDoc.CreateNavigator();
xPath = xNav.Compile (pathExpression);
m_iterator = xNav.Select(xPath);
}
public bool Read() {
bool moreRows = true;
if (moreRows = m_iterator.MoveNext())
FirstName = new SqlChars (m_iterator.Current.Value);
return moreRows;
}
}
Затем создайте сборку и пользовательскую функцию Transact-SQL с именем SQL_streaming_xml_tvf (не показана), соответствующую функции CLR streaming_xml_tvf. Пользовательская функция применяется для определения возвращающей табличное значение функции CLR_udf_XML2Table с целью создания набора строк:
create function CLR_udf_XML2Table (@pk int, @xCol xml)
returns @ret_Table table (FK int, FirstName varchar(max))
with schemabinding
as
begin
insert into @ret_Table
select @pk, FirstName
FROM SQL_streaming_xml_tvf (@xCol, '/book/author/first-name')
return
end
Наконец, определите триггеры так, как показано в разделе «Создание триггеров для заполнения таблицы свойств», заменив при этом функцию udf_XML2Table функцией CLR_udf_XML2Table. Триггер вставки показан в следующем примере:
create trigger CLR_trg_docs_INS on T for insert
as
declare @wantedXML xml
declare @FK int
select @wantedXML = xCol from inserted
select @FK = PK from inserted
insert into tblPropAuthor
select *
from dbo.CLR_udf_XML2Table(@FK, @wantedXML)
Триггер удаления идентичен версии, не использующей среду CLR. Однако триггер обновления просто заменяет функцию udf_XML2Table() функцией CLR_udf_XML2Table().
Коллекции XML-схем
Коллекция XML-схем — это сущность категории метаданных с областью действия, определяемой реляционной схемой. Она содержит одну или более XML-схем, которые могут быть связаны (например при помощи элемента <xs:import>) или не связаны. Отдельные XML-схемы в коллекции XML-схем идентифицируются по их целевому пространству имен.
Коллекция XML-схем создается с использованием инструкции CREATE XML SCHEMA COLLECTION (язык Transact-SQL) и предоставляет одну или более XML-схем. В существующую XML-схему можно добавлять дополнительные компоненты XML-схемы, а в коллекцию XML-схем — дополнительные схемы при помощи инструкции ALTER XML SCHEMA COLLECTION (язык Transact-SQL). Коллекции XML-схем можно защищать, как любые объекты SQL, используя модель обеспечения безопасности SQL Server 2005.
Столбцы с несколькими источниками типизации
Коллекция XML-схем C осуществляет типизацию XML-столбца xCol в соответствии с несколькими XML-схемами. Кроме того, флаги DOCUMENT и CONTENT определяют, можно ли хранить в столбце xCol деревья или фрагменты XML соответственно.
В случае флага DOCUMENT каждый экземпляр XML определяет целевое пространство имен его элемента верхнего уровня и типизируется и проверяется в соответствии с ним. С другой стороны, при флаге CONTENT каждый элемент верхнего уровня может определять любое из пространств имен схемы C. Экземпляр XML проверяется и типизируется в соответствии со всеми целевыми пространствами имен, встречающимися в экземпляре.
Расширение схем
Коллекция XML-схем используется для типизации XML-столбцов, переменных и параметров. Она обеспечивает механизм расширения XML-схем. Предположим, что добавляется XML-схема с целевым пространством имен BOOK-V1 в коллекцию XML-схем C. XML-столбец xCol, типизированный в соответствии с коллекцией C, может содержать XML-данные, соответствующие схеме BOOK-V1.
Далее предположим, что требуется расширить в приложении XML-схему новыми компонентами схемы, такими как определения сложных типов и объявления элементов верхнего уровня. Новые компоненты схемы можно добавить в схему BOOK-V1 без повторной проверки существующих XML-данных столбца xCol.
Допустим, что позднее требуется задействовать в приложении новую версию XML-схемы с целевым пространством имен BOOK-V2. Эту XML-схему можно добавить в коллекцию C. После этого в XML-столбце можно будет хранить экземпляры BOOK-V1 и BOOK-V2, а также выполнять запросы и модификацию данных в экземплярах XML, соответствующих этим пространствам имен.
Загрузка XML-данных
Перенос XML-данных из SQL Server 2000 в SQL Server 2005
Есть несколько способов переноса XML-данных в SQL Server 2005. Ниже приведены напримеры.
- Если в базе данных SQL Server 2000 данные хранятся в столбце типа [n]text или image, то эту таблицу можно импортировать в базу данных SQL Server 2005 при помощи служб SQL Server 2005 Integration Services (SSIS). Изменить тип столбца на XML можно с использованием инструкции ALTER TABLE.
- Массовое копирование данных SQL Server 2000 можно выполнить с использованием команды bcp out, после чего с помощью команды bcp in произвести массовую вставку данных в базу данных SQL Server 2005.
- Если в базе данных SQL Server 2000 данные хранятся в реляционных столбцах, необходимо создать новую таблицу со столбцом [n]text и, возможно, со столбцом первичного ключа для идентификации строк. Чтобы получить XML-данные, созданные на сервере при помощи инструкции FOR XML, и записать их в столбец [n]text, требуется программный код на клиентской стороне. Затем перенесите эти данные в базу данных SQL Server 2005, выбрав любую из вышеупомянутых методик. Можно записать XML-данные в XML-столбец базы данных SQL Server 2005 непосредственно.
Пример: изменение типа столбца на XML
Предположим, что в таблице R имеется столбец XYZ, тип которого нужно изменить с [n]text или image на нетипизированный XML. Для этого можно воспользоваться следующей инструкцией:
ALTER TABLE R ALTER COLUMN XYZ XML
- Тип столбца можно также преобразовать в типизированный XML, указав коллекцию XML-схем.
Массовая загрузка XML-данных
Можно выполнять массовую загрузку XML-данных на сервер, используя реализованные в SQL Server средства массовой загрузки, такие как bcp. Инструкция OPENROWSET позволяет загрузить данные в XML-столбец из файлов. Это показано в следующем примере.
Пример: загрузка XML-данных из файлов
Следующий пример показывает, как вставить строку в таблицу T. Значение XML-столбца загружается из файла «C:\MyFile\xmlfile.xml» как объект CLOB, а целочисленному столбцу назначается значение 10.
INSERT INTO T
SELECT 10, xCol
FROM (SELECT *
FROM OPENROWSET (BULK 'C:\MyFile\xmlfile.xml', SINGLE_CLOB)
AS xCol) AS R(xCol)
Кодировка текста
SQL Server 2005 хранит XML-данные в кодировке Юникод (UTF-16). XML-данные, извлекаемые из баз данных сервера, предоставляются в кодировке UTF-16. Если требуются данные в другой кодировке, извлеченные данные нужно преобразовать. Иногда XML-данные могут быть представлены в другой кодировке. Если это так, во время загрузки данных нужно быть внимательным, Примеры.
- Если текст XML представлен в кодировке Юникод (UCS-2, UTF-16), можно назначить его XML-столбцу, переменной или параметру без каких-либо проблем.
- Если кодировка отлична от Юникода и неявна из-за исходной кодовой страницы, строковая кодовая страница в базе данных должна быть той же самой или совместимой с элементами кода, которые следует загрузить. Если нужно, используйте предложение COLLATE. Если такой кодовой страницы на сервере не существует, необходимо добавить явную XML-декларацию с корректной кодировкой.
- Чтобы явно задать кодировку, воспользуйтесь типом varbinary(), поскольку он не работает с кодовыми страницами либо символьный тип для соответствующей кодовой страницы. После этого назначьте данные XML-столбцу, переменной или параметру.
Пример: явное указание кодировки
Предположим, что есть XML-документ vcdoc, хранящийся в типе данных varchar(max), который не объявлен явно как XML. Следующая инструкция добавляет объявление XML с кодировкой «iso8859-1», присоединяет к нему XML-документ, приводит результат к типу varbinary(max) (чтобы сохранить двоичное представление) и, наконец, приводит его к типу XML. Это позволяет процессору XML выполнять синтаксический анализ данных в соответствии с указанной кодировкой «iso8859-1» и создавать для строковых значений соответствующее представление UTF-16.
SELECT CAST(
CAST (('<?xml version="1.0" encoding="iso8859-1"?>'+ vcdoc) AS VARBINARY (MAX))
AS XML)
Язык XQuery и выведение типов
XQuery — это интегрированный в Transact-SQL язык, предназначенный для запросов данных типа xml. Этот язык разрабатывается консорциумом W3C (World Wide Web Consortium) совместно со всеми основными поставщиками решений для баз данных Microsoft. В качестве языка навигации в него входит XPath версии 2.0. Кроме того, в нем реализованы средства модификации данных типа xml. Дополнительные сведения о конструкциях, функциях и операторах XQuery, поддерживаемых в SQL Server, см. в разделе Применение функций XQuery к типу данных XML.
Модель ошибок
При обработке синтаксически некорректных выражений Xquery и инструкций XML DML возвращаются ошибки компиляции. На этапе компиляции проверяется корректность выражений XQuery и DML-инструкций в контексте статических типов и используются XML-схемы для вывода типов типизированного XML. Если выражение может завершиться неудачей в период выполнения из-за нарушения безопасности типов, компилятор формирует ошибки статических типов. Примерами статических ошибок являются добавление строки к целому числу и запрос несуществующего узла типизированных данных.
Ошибки XQuery периода выполнения преобразуются в пустые последовательности — это отклонение от стандарта W3C. В результате запроса эти последовательности могут быть представлены как пустой XML или как значение NULL, что зависит от контекста вызова.
Явное приведение к корректному типу позволяет предотвращать статические ошибки, хотя ошибки приведения типов в период выполнения будут преобразовываться в пустые последовательности.
В следующих разделах проверка типов описывается более подробно.
Одноэлементные проверки
Шаги определения расположения данных, параметры функций и операторы, которым нужны единственные значения, возвращают ошибку, если компилятор не может определить, будет ли в период выполнения гарантирована единственность элемента. Эта проблема часто возникает при работе с нетипизированными данными. Например, при уточняющем запросе атрибута необходима информация о единственном родительском элементе. Указать порядковый номер, выбирающий единственный родительский узел, недостаточно. Чтобы извлечь значения атрибутов при обработке комбинации node()-value(), указание порядкового номера, возможно, не потребуется. Это показано в следующем примере.
Пример: известный единственный экземпляр
В данном примере метод nodes() создает отдельную строку для каждого элемента <book
>. Метод value(), выполняемый для узла <book
>, извлекает значение @genre, которое, будучи атрибутом, является единственным.
SELECT nref.value('@genre', 'varchar(max)') LastName
FROM T CROSS APPLY xCol.nodes('//book') AS R(nref)
Чтобы проверить типы типизированного XML, используется XML-схема. Если в XML-схеме узел указан как единственный, компилятор это учитывает, и ошибки не возникают. В противном случае необходим порядковый номер, выбирающий единственный узел. В частности, применение оси нижнего или этого же уровня (//), такой как /book//title, ослабляет вывод единственности элемента <title>, несмотря на то, что в XML-схеме он указан именно так. Таким образом, следует переписать его как (/book//title)[1].
Важно знать о различии между выражениями //first-name[1] и (//first-name)[1] при проверке типов. Первое из них возвращает последовательность узлов <first-name>, в которой каждый узел является самым левым узлом <first-name> среди узлов с общим родителем. Второе возвращает первый единственный узел <first-name> в порядке документа в экземпляре XML.
Пример: использование метода value()
Следующий запрос данных из нетипизированного XML-столбца приводит к статической ошибке компиляции. Это объясняется тем, что метод value() ожидает в качестве первого аргумента единственный узел, а компилятор не может определить, будет ли в период выполнения существовать только один узел <last-name>:
SELECT xCol.value('//author/last-name', 'nvarchar(50)') LastName
FROM T
Вот решение этой проблемы, которое возможно как вариант:
SELECT xCol.value('//author/last-name[1]', 'nvarchar(50)') LastName
FROM T
Однако это решение не устраняет ошибку, потому что в каждом экземпляре XML могут содержаться несколько узлов <author
>. Следующий вариант работает:
SELECT xCol.value('(//author/last-name/text())[1]', 'nvarchar(50)') LastName
FROM T
Этот запрос возвращает значение первого элемента <last-name>
в каждом из экземпляров XML.
Родительская ось
Если тип узла не может быть определен, ему назначается тип anyType. Это не неявное приведение типа к любому другому типу. Особенно часто это имеет место при навигации с использованием родительской оси, например xCol.query('/book/@genre/../price'). Тип родительского узла определяется как anyType. Элемент также может быть определен как anyType в XML-схеме. В обоих случаях утрата более точной информации о типах часто приводит к ошибкам статических типов и требует явного приведения атомарных значений к их специфическому типу.
Методы доступа data(), text() и string()
Язык XQuery включает функцию fn:data() для извлечения скалярных типизированных значений из узлов, проверку узла text() для возврата текстовых узлов и функцию fn:string(), которая возвращает строковое значение узла. Их применение понятно далеко не всем. Ниже приведены советы по корректному использованию этих возможностей в SQL Server 2005. В качестве примера используется экземпляр XML <age>12</age>.
- Нетипизированный XML: Выражение пути /age/text() возвращает текстовый узел «12». Функции fn:data(/age) и fn:string(/age) возвращают строковое значение«12».
- Типизированный XML: Выражение /age/text() возвращает статическую ошибку для любого простого типизированного элемента <age>. Функция fn:data(/age) возвращает целое число 12, а функция fn:string(/age) строку «12».
Использование функций и операторов с объединенными типами
Работать с объединенными типами нужно особенно внимательно из-за проверки типов. Следующие примеры поясняют две из возможных проблем.
Пример: использование функции с объединенным типом
Взгляните на следующее определение элемента <r
> объединенного типа:
<xs:element name="r">
<xs:simpleType>
<xs:union memberTypes="xs:int xs:float xs:double"/>
</xs:simpleType>
</xs:element>
В контексте XQuery функция среднего значения fn:avg (//r) возвращает статическую ошибку, потому что компилятор XQuery не может сложить значения разных типов (xs:int, xs:float или xs:double) для элементов <r
>, определяемых аргументом fn:avg(). Чтобы решить эту проблему, перепишите вызов функции как fn:avg(for $r in //r return $r cast as xs:double ?).
Пример: использование оператора с объединенным типом
Оператор сложения ('+') требует использования точных типов операндов. Таким образом, выражение (//r)[1] + 1 возвращает статическую ошибку, которая имеет вышеуказанное определение типа элемента <r
>. Одно из решений этой проблемы — переписать выражение как (//r)[1] cast as xs:int? +1, где вопросительный знак определяет 0 или 1 вхождений элемента. SQL Server 2005 требует указания «cast as» и «?», потому что из-за ошибок периода выполнения результатом любого приведения типов может оказаться пустая последовательность.
Методы Value(), Nodes() и OpenXML()
Методы value() можно выполнять в инструкции SELECT для типа данных xml для создания набора строк извлеченных значений. Метод nodes() позволяет получить внутреннюю ссылку на каждый выбранный узел, которую можно использовать в дополнительных запросах. При создании набора строк из нескольких столбцов и, возможно, при высокой сложности используемых для этого выражений пути более эффективным подходом может оказаться сочетание методов nodes() и value().
Метод nodes() позволяет получать экземпляры специального типа данных xml, для каждого из которых контекст сопоставлен с другим выбранным узлом. Этот вид экземпляра XML поддерживает методы query(), value(), nodes() и exist() и может быть использован в статистических функциях count(*). Все другие способы его использования приводят к ошибке.
Пример: использование метода nodes()
Предположим, что требуется извлечь имена и фамилии авторов, которых зовут не «David». Кроме того, требуется извлечь эту информацию как набор строк, содержащий два столбца: FirstName и LastName. Используя методы nodes() и value(), можно сделать это следующим образом:
SELECT nref.value('(first-name/text())[1]', 'nvarchar(50)') FirstName,
nref.value('(last-name/text())[1]', 'nvarchar(50)') LastName
FROM T CROSS APPLY xCol.nodes('//author') AS R(nref)
WHERE nref.exist('first-name[. != "David"]') = 1
В данном примере метод nodes('//author')
получает набор строковых ссылок на элементы <author>
каждого экземпляра XML. Чтобы получить имена и фамилии авторов, вызываются методы value() относительно этих ссылок.
SQL Server 2000 позволяет создать набор строк на основе экземпляра XML при помощи метода OpenXml(). При этом можно указать реляционную схему набора строк и способ сопоставления значений экземпляра XML со столбцами набора строк.
Пример: использование метода OpenXml() с типом данных xml
Запрос из предыдущего примера можно переписать с методом OpenXml() так, как показано ниже. С этой целью создается курсор, считывающий каждый экземпляр XML в переменную XML и вызывающий для нее метод OpenXml():
DECLARE name_cursor CURSOR
FOR
SELECT xCol
FROM T
OPEN name_cursor
DECLARE @xmlVal XML
DECLARE @idoc int
FETCH NEXT FROM name_cursor INTO @xmlVal
WHILE (@@FETCH_STATUS = 0)
BEGIN
EXEC sp_xml_preparedocument @idoc OUTPUT, @xmlVal
SELECT *
FROM OPENXML (@idoc, '//author')
WITH (FirstName varchar(50) 'first-name',
LastName varchar(50) 'last-name') R
WHERE R.FirstName != 'David'
EXEC sp_xml_removedocument @idoc
FETCH NEXT FROM name_cursor INTO @xmlVal
END
CLOSE name_cursor
DEALLOCATE name_cursor
Метод OpenXml() создает представление данных в памяти и использует вместо обработчика запросов рабочие таблицы. В основе его работы лежит процессор XPath 1.0 кода MSXML 3.0, а не ядро XQuery. Рабочие таблицы не обобщаются между несколькими вызовами метода OpenXml(), даже если он выполняется для одного экземпляра XML. Это ограничивает его масштабируемость. Метод OpenXml() позволяет обращаться к формату краевой таблицы XML-данных без предложения WITH. Кроме того, он позволяет использовать оставшееся XML-значение в отдельном столбце «переполнения».
При объединении функций nodes() и value() эффективно используются XML-индексы. Таким образом, эта комбинация может оказаться более масштабируемой, чем метод OpenXml.
Использование предложения FOR XML для создания XML на основе набора строк
На основе набора строк можно создать экземпляр типа данных xml, используя инструкцию FOR XML с новой директивой TYPE.
Результат может быть назначен столбцу, переменной или параметру типа xml. Кроме того, инструкции FOR XML могут быть вложены друг в друга, создавая произвольные иерархические структуры. Благодаря этому создавать вложенные инструкции FOR XML намного проще, чем FOR XML EXPLICIT, но при большой глубине иерархии они могут оказаться менее эффективными. Предложение FOR XML поддерживает также новый режим PATH. Этот режим определяет в дереве XML путь к значению столбца.
Новая директива FOR XML TYPE позволяет определять для реляционных данных XML-представления, поддерживающие только чтение, используя синтаксис SQL. Как показано в следующих примерах, представление можно запрашивать при помощи инструкций SQL и встроенного механизма XQuery. К этим SQL-представлениям можно также обращаться в хранимых процедурах.
Пример: SQL-представление, возвращающее сформированный тип xml-данных
Следующий код создает XML-представление для реляционного столбца pk и заносит в него информацию об авторах, извлекаемую из XML-столбца:
CREATE VIEW V (xmlVal) AS
SELECT pk, xCol.query('/book/author')
FROM T
FOR XML AUTO, TYPE
Представление V содержит единственную строку с единственным столбцом xmlVal XML-типа .
. Его можно запрашивать так же, как и обычный экземпляр типа данных xml. Например, следующий запрос возвращает сведения об авторе по имени «David»:
SELECT xmlVal.query('//author[first-name = "David"]')
FROM V
Определения SQL-представлений в чем-то похожи на XML-представления, создаваемые с использованием аннотированных схем. Однако между ними есть важные различия. Определение SQL-представления поддерживает только чтение, а работать с ним нужно, используя встроенный механизм XQuery. XML-представления создаются при помощи аннотированной схемы. Кроме того, SQL-представление материализует XML-результаты перед применением выражения XQuery, тогда как при запросах XPath для XML-представлений выполняются SQL-запросы базовых таблиц.
Добавление бизнес-логики
Бизнес-логику можно добавить в XML-данные несколькими способами.
- Можно создать ограничения строк или столбцов, чтобы наложить ограничения, специфические для домена, во время вставки и модификации XML-данных.
- Можно написать для XML-столбца триггер, срабатывающий при вставке значений в столбец или при их обновлении. Этот триггер может определять специфические для домена правила проверки или заполнять таблицы свойств.
- Можно написать управляемые функции SQLCLR, принимающие XML-значения, и использовать средства обработки XML-данных, обеспечиваемые пространством имен System.Xml. Примером может служить применение преобразования XSL к XML-данным. Также можно десериализовать XML-данные в один или несколько управляемых классов и обработать их в управляемом коде.
- Можно создать хранимые процедуры и функции Transact-SQL, инициирующие обработку XML-столбца в соответствии с бизнес-потребностями.
Пример: применение преобразования XSL
Рассмотрим функцию CLR TransformXml(), которая принимает экземпляр типа данных xml и находящееся в файле преобразование XSL, применяет преобразование к XML-данным и возвращает результат преобразования. Вот ее основа, написанная на C#:
public static SqlXml TransformXml (SqlXml XmlData, string xslPath) {
// Load XSL transformation
XslCompiledTransform xform = new XslCompiledTransform();
XPathDocument xslDoc = new XPathDocument (xslPath);
xform.Load(xslDoc);
// Load XML data
XPathDocument xDoc = new XPathDocument (XmlData.CreateReader());
// Return the transformed value
MemoryStream xsltResult = new MemoryStream();
xform.Transform(xDoc, null, xsltResult);
SqlXml retSqlXml = new SqlXml(xsltResult);
return (retSqlXml);
}
После регистрации сборки и создания пользовательской функции Transact-SQLSqlXslTransform(), соответствующей TransformXml(), эту функцию можно вызывать из кода Transact-SQL, как показано в следующем запросе:
SELECT SqlXslTransform (xCol, 'C:\MyFile\xsltransform.xsl')
FROM T
WHERE xCol.exist('/book/title/text()[contains(.,"custom")]') =1
Результат выполнения этого запроса содержит набор строк преобразованных XML-данных.
Технология SQLCLR расширяет средства декомпозиции XML-данных на таблицы или продвижения свойств, а также запроса XML-данных с использованием управляемых классов из пространства имен System.Xml. Дополнительные сведения см. в электронной документации по SQL Server и пакету .Net Framework SDK.
Запросы между доменами
Если данные хранятся и в реляционных столбцах, и в столбцах типа xml, то возможно создание запросов, сочетающих обработку реляционных и XML-данных. Например, данные реляционных и XML-столбцов можно преобразовывать в экземпляр типа данных xml при помощи инструкции FOR XML, и запрашивать их при использовании XQuery. И наоборот, на основе XML-значений можно сформировать набор строк и запрашивать его, используя инструкции языка Transact-SQL.
Более удобным и эффективным способом написания запросов между доменами является использование значения переменной или столбца SQL в выражениях XQuery или XML DML:
- Метод sql:variable() позволяет использовать в выражении XQuery или XML DML значение переменной SQL.
- Функция sql:column() позволяет использовать в выражении XQuery или XML DML значения реляционного столбца.
На основе этих подходов в приложениях можно параметризовать запросы, как показано в следующем примере. Однако в функциях sql:variable() и sql:column() нельзя использовать тип XML и пользовательские типы.
Пример: запрос между доменами с использованием функции sql:variable()
Следующий запрос представляет собой измененную версию запроса, показанного в разделе «Пример: Запросы к вычисляемым столбцам на основе методов типа данных xml». В следующей версии конкретный ISBN-номер передается в функцию как SQL-переменная @isbn. Заменив константу вызовом функции sql:variable(), можно использовать этот запрос для поиска любого ISBN-номера, а не только 0-7356-1588-2.
DECLARE @isbn varchar(20)
SET @isbn = '0-7356-1588-2'
SELECT xCol
FROM T
WHERE xCol.exist ('/book/@ISBN[. = sql:variable("@isbn")]') = 1
Функцию sql:column() можно применять подобным образом, при этом она обеспечивает дополнительные преимущества. Для повышения эффективности могут использоваться индексы столбца — это решает оптимизатор запросов на основе траты ресурсов. В вычисляемом столбце можно хранить свойство, для которого было выполнено продвижение.
Представления каталога, обеспечивающие естественную поддержку XML
Представления каталога нужны для обеспечения метаданных об использовании XML. Некоторые из них обсуждаются в следующем подразделе.
XML-индексы
XML-индексы входят в представление каталога sys.indexes с индексом типа 3. Столбец name при этом содержит имя XML-индекса.
Кроме того, XML-индексы записываются в представление каталога sys.xml_indexes. Оно содержит все столбцы представления sys.indexes и некоторые специфические столбцы, полезные при работе с XML-индексами. Значение NULL в столбце secondary_type определяет первичный XML-индекс; значения 'P', 'R' и 'V' определяют соответственно вторичные XML-индексы PATH, PROPERTY и VALUE.
Информацию о пространстве, занимаемом XML-индексами, можно получить при помощи возвращающей табличное значение функции sys.dm_db_index_physical_stats. Она предоставляет такую информацию об индексах всех типов, как число занимаемых ими страниц на диске, средний размер строки в байтах и число записей. Эта функция поддерживает и XML-индексы. Получить такие данные можно для каждой секции базы данных. XML-индексы используют ту же схему секционирования и функцию секционирования базовой таблицы.
Получение коллекций XML-схем
Коллекции XML-схем перечислены в представлении каталога sys.xml_schema_collections. Коллекция XML-схем «sys» определяется системой. Она содержит предопределенные пространства имен, которые можно использовать во всех пользовательских коллекциях XML-схем, не загружая их явно. Этот список содержит пространства имен xml, xs, xsi, fn и xdt. Двумя другими представлениями каталога являются sys.xml_schema_namespaces, в котором перечислены все пространства имен каждой коллекции XML-схем и sys.xml_components, в котором перечислены все компоненты каждой XML-схемы.
Встроенная функция XML_SCHEMA_NAMESPACE, schemaName, XmlSchemacollectionName, namespace-uri, позволяет получить экземпляр типа данных xml. Этот экземпляр содержит фрагменты для XML-схем, содержащихся в коллекции XML-схем, за исключением предопределенных XML-схем.
Перечислить содержимое коллекции XML-схем можно двумя способами:
- написать запросы Transact-SQL, адресованные соответствующим представлениям каталога, связанным с коллекциями XML-схем;
- воспользоваться встроенной функцией XML_SCHEMA_NAMESPACE(). К результатам этой функции можно применять методы типа данных xml. Однако изменять базовые XML-схемы нельзя.
Все это поясняют следующие примеры.
Пример: перечисление пространств имен XML, входящих в коллекцию XML-схем
Выполните следующий запрос для коллекции XML-схем «myCollection»:
SELECT XSN.name
FROM sys.xml_schema_collections XSC JOIN sys.xml_schema_namespaces XSN
ON (XSC.xml_collection_id = XSN.xml_collection_id)
WHERE XSC.name = 'myCollection'
Пример: перечисление содержимого коллекции XML-схем
Следующая инструкция перебирает содержимое коллекции XML-схем «myCollection» реляционной схемы dbo.
SELECT XML_SCHEMA_NAMESPACE (N'dbo', N'myCollection')
Отдельные XML-схемы из коллекции можно получить как экземпляры типа данных xml, указав целевое пространство имен в качестве третьего аргумента функции XML_SCHEMA_NAMESPACE(). Это показано в следующем примере.
Пример: вывод конкретной схемы из коллекции XML-схем
Следующая инструкция выводит XML-схему с целевым пространством имен «https://www.microsoft.com/books» из коллекции XML-схем «myCollection» реляционной схемы dbo.
SELECT XML_SCHEMA_NAMESPACE (N'dbo', N'myCollection',
N'https://www.microsoft.com/books')
Запросы XML-схем
Запрашивать XML-схемы, загруженные в коллекцию XML-схем, можно перечисленными ниже способами.
- Написать адресованные представлениям каталога запросы Transact-SQL о получении пространств имен XML-схем.
- Создать таблицу со столбцом данных типа xml для хранения XML-схем и их загрузки в систему типов XML. Данные из XML-столбца можно запросить при помощи методов типа данных xml. Кроме того, можно создать для этого столбца XML-индекс. Однако при этом подходе в приложении нужно поддерживать согласованность между XML-схемами, хранимыми в XML-столбце, и системой типов XML. Например, при удалении пространства имен XML-схемы из системы типов XML для сохранения согласованности необходимо будет удалить его и из таблицы.
См. также
Справочник
Управление коллекциями XML-схем на сервере
Применение функций XQuery к типу данных XML
Основные понятия
Другие ресурсы
sys.dm_db_index_physical_stats
Введение в компонент Full-Text Search