Апдейтаграммы – это особый тип шаблонов, в котором вместо запроса на выборку данных хранится информация, необходимая для изменения данных. Синтаксис их таков:
<your_root xmlns:updg="urn:schemas-microsoft-com:xml-updategram" [mapping-schema="your_schema.xml"]> <updg:header> <updg:param name="param1_name" /> <updg:param name="param2_name" /> </updg:header> <updg:sync> <updg:before> xml-узлы </updg:before> <updg:after [updg:returnid="your_id"]> xml-узлы </updg:after> </updg:sync></your_root> |
Как видно из синтаксиса, апдейтаграмма может иметь параметры. Они, как обычно, задаются в URL и, как обычно, могут опускаться. Раздел header не является обязательным, но на практике без него обойтись трудно. Раздел before, по существу, определяет запись, над которой будет производиться действие. Он обязателен для изменения или удаления данных. В случае вставки его можно опустить. Раздел after определяет, как будет выглядеть найденная запись после выполнения апдейтаграммы. В случае удаления его указывать не нужно. Раздел sync определяет границы транзакции и буквально трансформируется в команды begin trans и commit trans. В случае неудачного выполнения шаблона транзакция откатывается с помощью rollback trans.
ПРИМЕЧАНИЕВсе аспекты работы апдейтаграмм хорошо видны в профайлере. Если у вас возникают вопросы, рекомендую сразу же им воспользоваться. |
В каком случае может возникнуть ошибка? Причин много:
Раздел before не может однозначно определить запись, над которой будет производиться действие. Например, если вы захотите удалить все записи, у которых поле age равно 33, то в ответ получите ошибку Ambiguous delete, unique identifier required. Другая ошибка возникает, когда такую запись невозможно найти совсем. В этом случае генерируется сообщение Empty delete, no deletable rows found для операции удаления, или Empty update, no updatable rows found для операции обновления.
Откат транзакции в триггере.
Ошибка ограничения (constraint violation).
Для того чтобы SQL Server понял, какие таблицы и поля ему изменять, вы можете использовать аннотированные схемы XDR или XSD. Если схема не используется, по умолчанию названия элементов соответствуют названиям таблиц, названия и значения атрибутов – названию и значению полей соответственно.
Добавление данных
Для вставки данных используется раздел after. Раздел before опускается. Если вы хотите получить на выходе значение колонки identity, укажите атрибут returned (и атрибут at-identity, см. далее). Для элемента, обозначающего таблицу, вы можете дополнительно указать три атрибута:
id – будет рассмотрен в разделе Обновление данных;
at-identity – позволяет получить в результирующий XML-документ значение вставленного поля для колонки identity. Например, пусть имеется такая таблица
create table test1(_id int identity,fld1 int) |
Вот шаблон, который вставляет в нее данные:
<ROOT xmlns:updg="urn:schemas-microsoft-com:xml-updategram"> <updg:sync> <updg:after updg:returnid="ident_field"> <test1 fld1="23" updg:at-identity="ident_field"/> </updg:after> </updg:sync></ROOT> |
Результатом выполнения команды «http://server/server_pubs/template/upd1.xml» будет следующий документ:
<ROOT xmlns:updg="urn:schemas-microsoft-com:xml-updategram"> <returnid><ident_field>1</ident_field></returnid></ROOT> |
guid – предназначен для генерации глобально уникального идентификатора (GUID).
Давайте рассмотрим более сложный пример с использованием схемы. Вот схема:
<?xml version="1.0" encoding="windows-1251" ?><xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:sql="urn:schemas-microsoft-com:mapping-schema"> <xsd:element name="Авторы" sql:relation="authors"> <xsd:complexType> <xsd:attribute name="Имя" type="xsd:string" sql:field="au_fname" /> <xsd:attribute name="Фамилия" type="xsd:string" sql:field="au_lname" /> <xsd:attribute name="Идентификатор" type="xsd:string" sql:field="au_id" /> <xsd:attribute name="Контракт" type="xsd:integer" sql:field="contract" default="1"/> </xsd:complexType></xsd:element></xsd:schema> |
Здесь мы явно использовали аннотации, так как названия XML-узлов составлены на русском. После выполнения следующего шаблона:
<?xml version="1.0" encoding="windows-1251" ?><ROOT xmlns:updg="urn:schemas-microsoft-com:xml-updategram"> <updg:sync mapping-schema="upd_schema.xml"> <updg:after> <АвторыИмя="alex" Фамилия="shirshov" Идентификатор="123-15-3452"/> </updg:after> </updg:sync></ROOT> |
мои имя и фамилия (да простят мне читатели нескромность) окажутся в таблице authors.
Обновление данных
Для обновления данных нужно использовать раздел before для определения обновляемых данных, и раздел after для определения конечных значений данных.
Давайте разберем пример:
<?xml version="1.0" encoding="windows-1251" ?><ROOT xmlns:updg="urn:schemas-microsoft-com:xml-updategram"> <updg:sync mapping-schema="upd_schema.xml"> <updg:before> <АвторыИмя="alex" Фамилия="shirshov" Идентификатор="123-15-3452" updg:id="for_update"/> </updg:before><updg:after> <Авторы Имя="Алексей" Фамилия="Ширшов" Идентификатор="123-15-3452" updg:id="for_update"/> <Авторы Имя="alex" Фамилия="shirshov" Идентификатор="123-15-3453" updg:id="for_insert"/> </updg:after></updg:sync></ROOT> |
Работает он так: сначала находится запись, однозначно идентифицируемая в разделе before (ее мы вставили в предыдущем примере). Этой записи, с помощью атрибута id, присваивается идентификатор. Зачем он нужен? Так как в разделе after мы указали две записи, то возникает неоднозначность: одну из них нужно использовать для вставки, а другую для обновления. Чтобы эту неоднозначность разрешить, в разделе after присутствует ссылка на найденную в разделе before запись посредством атрибута id. В результате выполнения шаблона будут исправлены имя и фамилия в записи с идентификатором 123-15-3452, а также добавлена новая запись.
Удаление
Это совсем тривиальная задача, главное помнить, что в разделе before запись должна однозначно определяться.
<?xml version="1.0" encoding="windows-1251" ?><ROOT xmlns:updg="urn:schemas-microsoft-com:xml-updategram"> <updg:sync mapping-schema="upd_schema.xml"> <updg:before> <АвторыИмя="alex" Фамилия="shirshov" Идентификатор="123-15-3453"/> </updg:before> </updg:sync></ROOT> |
В этом примере будет удалена запись с идентификатором 123-15-3453.
ПРИМЕЧАНИЕАтрибуты «Имя» и «Фамилия» приведены здесь для того, чтобы вам проще было понять, какая строка удаляется. Так как идентификатор уникально определяет запись, их можно было не указывать. |
Более подробную информацию об апдейтаграммах с примерами можно найти в MSDN или в [10].
Предположим, у вас имеется XML-документ, содержимое которого нужно «залить» в базу. Методов решения проблемы, как всегда, очень много, и вся сложность состоит в выборе наиболее подходящего. Вы можете загрузить документ в DOMDocument и в цикле, выбирая значения элементов и атрибутов, производить добавление данных с помощью инструкции SQL. Если вы не знакомы с объектной моделью DOM, то можете написать свой парсер. Такое лобовое решение обычно принимают самые отважные и «крутые» программисты, которые не боятся трудностей написания нового парсера, изучения SQL и начальства. Что ж, лично я (хотя начальства не особо боюсь) к такой категории себя отнести не могу. Мне нужно решение, которое опирается на уже существующие возможности и технологии. Второе, что приходит в голову – передать XML-документ в хранимую процедуру и с помощью OPENXML «залить» данные в таблицу. Решение здравое и наиболее эффективное в большинстве случаев. Но что делать, если у вас имеется большой документ? Конечно, его можно все так же передавать в хранимую процедуру в параметре text или ntext и также разбирать с помощью OPENXML. Но все дело в том, что OPENXML использует DOM, а обработка больших документов таким способом имеет кучу недостатков. Мало того, что расходуется большое количество драгоценных системных ресурсов, это еще и медленно! Для больших документов идеальным вариантом является XML Bulk Load.
XML Bulk Load – это обычный COM-сервер, размещенный в DLL, и использующий для анализа XML-документа SAX (Simple API for XML). Благодаря этому он обрабатывает документ по частям, намного менее ресурсоемок и более быстр. Для обновления данных XML Bulk Load должен знать, какие XML-узлы соотносятся с полями в таблицах, и каков их тип. Для этого он использует все те же аннотированные схемы, которые могут быть написаны на XDR или XSD.
Семантика объекта XML Bulk Load не может показаться сложной – объект содержит всего один метод и несколько свойств. Рассмотрим наиболее используемые свойства:
ConnectionString – строка соединения с базой данных (формат SQLOLEDB).
ConnectionCommand – позволяет использовать уже существующий объект ADODB.Connection.
BulkLoad – если установлено в true, кроме генерации схемы (таблиц) происходит также закачка данных. Если установлен в false – создаются только таблицы (см. свойство SchemaGen). По умолчанию – true.
ErrorLogFile – позволяет указать файл, в который будут записываться сообщения об ошибках.
Transaction – если установлено в true, все операции XML Bulk Load выполняются в контексте одной транзакции. По умолчанию – false.
TempFilePath – директория, в которой будет создан файл лога транзакции. Настоятельно рекомендую устанавливать его самостоятельно, так как по-другому у меня просто не получалось. Свойство имеет значение, только если Transaction установлено в true. По умолчанию создает файл в папке %temp%.