Большие объемы данных неэффективно "заливать" в базу чередой последовательных insert'ов.
Для этого применяются средства массовой загрузки. Аналогом bcp / BULK INSERT при работе
с XML-файлами выступает XML Bulk Load. Он не грузит весь документ целиком в память, а
прочитывает его по отдельным узлам, понимая на основе аннотированной схемы, когда запись
заканчивается и ее можно отправить SQL Server'у для вставки. Использование аннотированных
схем позволяет осуществлять наполнение одновременно нескольких связанных таблиц.
XML Bulk Load не есть утилита с графическим интерфейсом, а всего лишь СОМ-компонент,
устанавливаемый SQLXML веб-релизами. Массовая закачка XML-документа осуществляется
программным путем - достаточно написать VB-скрипт из нескольких строчек. Я надеюсь, что эта
идея вас не пугает, потому что если вы дочитали до этого места, значит, вы, скорее, разработчики,
нежели пользователи или администраторы. Для работы из-под .NET Framework необходимо
импортировать библиотеку типов Microsoft SQLXML Bulkload 3.0 Type Library
(...\Program Files\Common Files\System\Ole DB\xblkld3.dll).
Объектная модель XML Bulk Load тривиальна. Она состоит из единственного объекта -
SQLXMLBulkLoad с единственным методом Execute, принимающим два параметра: аннотированную
схему и сам XML-документ. Аннотированная схема, как всегда, задает правила разноски XML-
содержания: в какое поле какой таблицы положить тот или иной элемент или атрибут. Собственно,
все.
Вот XML, который требуется перенести в БД:
<Книги_по_XML>
<Книга Название="The Guru's Guide to SQL Server Stored Procedures,
XML, and HTML" ISBN="0201700468" Страниц<font color=blue>="576">
<Порядковый_номер>1</Порядковый_номер>
<Издательство>Wesley Professional</Издательство>
<Цена_на_Амазоне>34.99</Цена_на_Амазоне>
<Дата_выхода>2001-12-21</Дата_выхода>
<Авторы>
<Автор Имя="Ken" Фамилия="Henderson" />
<Автор Имя="Ron" Фамилия="Soukup" />
</Авторы>
</Книга>
<Книга Название="Programming Microsoft SQL Server 2000 With XML
(Pro-Developer)" ISBN="0735613699" Страниц="400">
<Порядковый_номер>2</Порядковый_номер>
<Издательство>Microsoft Press</Издательство>
<Цена_на_Амазоне>41.99</Цена_на_Амазоне>
<Дата_выхода>2001-06-01</Дата_выхода>
<Авторы>
<Автор Имя="Graeme" Фамилия="Malcolm" />
</Авторы>
</Книга>
...
</Книги_по_XML>
Вот аннотированная схема, которую я для него определил (схема должна находиться в отдельном
файле, размещение ее в самом XML-документе не допускается).
<?xml version="1.0" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:ms="urn:schemas-microsoft-com:mapping-schema">
<xs:annotation>
<xs:appinfo>
<ms:relationship name="Книга_Авторы" parent="Book"
parent-key="BookID" child="Author" child-key="BookID" />
</xs:appinfo>
</xs:annotation>
<xs:element name="Книга" ms:relation="Book">
<xs:complexType>
<xs:sequence>
<xs:element name="Порядковый_номер"
ms:field="BookID" ms:datatype="int" />
<xs:element name="Издательство"
ms:field="Publishing" ms:datatype="varchar(50)" />
<xs:element name="Цена_на_Амазоне"
ms:field="Price" ms:datatype="numeric(6,2)" />
<xs:element name="Дата_выхода"
ms:field="IssueDate" ms:datatype="smalldatetime" />
<xs:element name="Авторы"
maxOccurs="1" ms:is-constant="1">
<xs:complexType>
<xs:sequence>
<xs:element name="Автор" minOccurs="0"
maxOccurs="unbounded" ms:relation="Author"
ms:relationship="Книга_Авторы">
<xs:complexType>
<xs:attribute name="Имя"
ms:field="FirstName"
ms:datatype="nvarchar(50)" />
<xs:attribute name="Фамилия"
ms:field="LastName"
ms:datatype="nvarchar(50)" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="Название" ms:field="Title"
ms:datatype="nvarchar(200)" />
<xs:attribute name="ISBN" ms:field="ISBN"
ms:datatype="char(10)" />
<xs:attribute name="Страниц"
ms:field="NumPages" ms:datatype="smallint" />
</xs:complexType>
</xs:element>
</xs:schema>
И вот скрипт, который осуществляет загрузку:
class BulkExample
{
static void BulkLoad_File_SQLXML()
{
SQLXMLBULKLOADLib.SQLXMLBulkLoad3Class bl =
new SQLXMLBULKLOADLib.SQLXMLBulkLoad3Class();
bl.ConnectionString = "Provider=SQLOLEDB;...";
bl.SchemaGen = true;
bl.SGDropTables = true;
bl.KeepNulls = true;
bl.Transaction = true;
bl.ErrorLogFile = "..\\BulkCopy\\XMLDocForBulkLoad.err";
bl.Execute("..\\BulkCopy\\XMLDocForBulkLoad.xsd",
"..\\BulkCopy\\XMLDocForBulkLoad.xml");
}
[STAThread]
static void Main()
{
BulkLoad_File_SQLXML();
}
}
Скрипт 13
Следует обратить внимание на атрибут [STAThread], поскольку компонент пока работает только
в однопоточном режиме. Свойство SchemaGen, определяет, должны ли таблицы под загрузку
создаваться или вставка идет в уже имеющиеся. Чтобы просто создать структуры и не переносить
при этом сами данные, нужно еще поставить BulkLoad = false. Имена таблиц и полей, их типы и
прочие метаданные определяются аннотированной схемой
( ms:relation, ms:field,
ms:datatype).
Если таблицы уже существуют, то SGDropTables = true заставляет их пересоздаться. Удаление
таблиц происходит в том порядке, в котором они упоминаются в XML-файле и может приводить
к конфликту с ограничениями primary key/foreign key. Рассмотренный в п.11 ms:inverse при этом
не помогает. В том случае, если таблица есть и для поля определено значение по умолчанию, оно
будет использоваться, когда в XML-файле соответствующий элемент или атрибут пропущен, но
если поставить KeepNulls = true, то значение по умолчанию будет проигнорировано и в поле будет
поставлен Null. Аналогично действует свойство KeepIdentity. Если вставка идет в поле типа identity,
то false заставляет игнорировать значения для этого поля в XMLном файле и использовать
автоинкремент, определенный для него на сервере. CheckConstraints определяет, будут ли
проверяться constraints (primary key/foreign key и пр.) при загрузке данных. Свойство Transaction
заставляет все действия по загрузке проходить в масштабе единой транзакции, так что ежели что
случится, то все будет откачено. Его нельзя использовать при загрузке BLOBов. ForceTableLock
устанавливает табличную блокировку на таблицы, задействованные в ходе bulk load. Если в
аннотированной схеме поле помечено атрибутом dt:type="id"
( xmlns:dt="urn:schemas-microsoft-com:xml:datatypes"),
то свойство SGUseID = true приведет к тому,
что при создании таблицы на него будет создано ограничение primary key. Если в схеме в
<xs:element name<font color=blue>="Книга"
ms:relation="Book">
поставить атрибут, ms:key-fields="BookID", то
между таблицами Book и Author будет создано ограничение primary key/foreign key по полю
BookID. Независимо от этого указание в схеме отношения между таблицами
<ms:relationship
name="Книга_Авторы"
parent="Book" parent-key="BookID"
child="Author" child-key="BookID" />
обеспечивает во время массовой загрузки автоматическое заполнение поля BookID в дочерней
таблице Author значениями BookID из родительской записи
таблицы Book, как происходит в
нашем случае:
BookID |
Publishing |
Price |
IssueDate |
NumPages |
ISBN |
Title |
1. | Wesley Professional | 34.99 | 2001-12-21 00:00:00 | 576 | 0201700468 |
The Guru's Guide to SQL Server Stored Procedures, XML, and HTML |
2. | Microsoft Press | 41.99 | 2001-06-01 00:00:00 | 400 | 0735613699 |
Programming Microsoft SQL Server 2000 With XML (Pro-Developer) |
... | ... | ... | ... | ... | ... | ... |
LastName |
FirstName |
BookID |
Henderson | Ken | 1 |
Soukup | Ron | 1 |
Malcolm | Graeme | 2 |
Griffin | John | 3 |
Williams | Kevin | 4 |
... | ... | ... |
Методу Execute можно передавать не только название XML-документа, но и ADODB.Stream:
...
StreamReader sr = File.OpenText("..\\BulkCopy\\XMLDocForBulkLoad.xml");
ADODB.StreamClass sc = new ADODB.StreamClass();
sc.Charset = "UTF-8";
sc.Open(System.Type.Missing, ADODB.ConnectModeEnum.adModeUnknown,
ADODB.StreamOpenOptionsEnum.adOpenStreamUnspecified, "", "");
sc.WriteText(sr.ReadToEnd(), ADODB.StreamWriteEnum.stWriteChar);
sc.Position = 0;
bl.Execute("..\\BulkCopy\\XMLDocForBulkLoad.xsd", sc);
sr.Close(); sc.Close();
что позволяет загружать в БД динамически сгенерированный внутри кода XML без
необходимости его промежуточного сохранения. Для загрузки XML-фрагмента (набора элементов
без корневого, не являющегося каноническим well-formed документом) необходимо использовать
свойство XMLFragment.
|