Professional
SQL Server 2000 XMI Paul J. Burke Sam Ferguson Denise Gosnell Paul Morris Jan D. Narkiewicz J. Michael Palermo IV Jon D. Reid Darshan Singh Brian Smith Karli Watson Carvin Wilson Warren Wiltsie
wrox
Wrox Press Ltd.
Пол Дж. Берк, Сэм Фергюсон, Дениза Госнелл, Пол Моррис Йен Д. Наркивиц, Дж. Майкл Палермо IV, Йон Д. Рейд, Даршан Сингх Брайан Смит, КарлиУотсон, Карвин Уилсон, Уоррен Уилши
SQL Server 2000 XML
Перевод с английского
Москва БИНОМ. Лаборатория знаний 2003
УДК 004.65 ББК 32.973 Б48 Переводчики: Хорев В. Д.: гл. 5—10; Приложения; Практические занятия 3-5; Грошев А. А.: гл. 1—4; Практические занятия 1-2
Б48
Берк Пол Дж. SQL Server 2000 XML / П. Дж. Берк, С. Фергюсон, Д. Госнелл и др.; Пер. с англ.; Под ред. С. М. Молявко. — М.: БИНОМ. Лаборатория знаний, 2003. — 638 с , ил. ISBN 5-94774-072-9 Подробно описана техника работы с базами данных SQL Server посредством XML-документов. Рассмотрен стандарт XML, техника извлечения данных из базы с помощью FOR XML, возможности представления XML-документов в виде реляционных наборов данных с помощью OPENXML. Уделено внимание XDR и XSD-схемам как средству получения данных посредством HTTP-запросов. Подробно обсуждается использование XPath-запросов к SQL Server и технология модификации баз данных с помощью апдейтограмм. Рассмотрены представления XML View как средство описания таблиц SQL Server. Продемонстрирована техника импорта XML-документов в базу данных с помощью массовой загрузки XML Bulk Load. Описаны новинки релиза Web Release 2. В книгу включены пять практических примеров, позволяющих читателю на собственном опыте убедиться в преимуществах XML-технологий при работе с SQL Server 2000. Книга рассчитана на опытных программистов SQL Server, которые хотят организовать XML-доступ к данным в базе SQL Server 2000, воспользовавшись новыми возможностями дополнений Web Release. УДК 004.65 ББК 32.973
По вопросам приобретения обращаться: В Москве «БИНОМ. Лаборатория знаний» (095)955-03-98, e-mail:
[email protected] в Санкт-Петербурге
«Диалект» (812)247-93-01, e-mail:
[email protected]
Copyright © 2001, Wrox Press, Ltd
ISBN 5-94774-072-9 (русск.) ISBN 1-861005-46-6 (англ.)
@
,Перев0Д н а р у с с к и й ЯЗЫК) БИНОМ. Лаборатория знаний, 2003
Торговые марки Издательство Wrox приложило все усилия к тому, чтобы обеспечить наличие информации о торговых марках всех компаний и продуктов, упоминаемых в этой книге. Тем не менее, издательство Wrox не может гарантировать точность и полноту этой информации.
Редакционный коллектив Авторы Пол Дж. Берк Сэм Фергюсон Дениза Госнелл Пол Моррис Йен Д. Наркивиц Дж. Майкл Палермо IV Йон Д. Рейд Даршан Сингх Брайан Смит Карли Уотсон Карвин Уилсон Уоррен Уилши Дополнительный материал Хоуп Хэтфилд Техническая архитектура Кейт Холл Технические редакторы Виктория Блекберн Клер Бритл Бенджамин Иген Пол Джеффкоут Дуглас Патерсон
•'
•
'
'
,
.
!
'
Технические рецензенты Рамеш Баладжи Мартин Бьюли Максим Бомбардир Бет Бриденбах Роберт Чанг Девид Фор Джефф Габриель Скотт Ханселмен Марк Хорнер Дениел Кент Шон Макереви Гледисон де Маседо Джон Д. Рейд Девид М. Скотт Роберт Вейра Ларри Уолл Джонатан Вайнер Дональд Ксай Сакр Янисс Дополнительные рисунки Крис Маттерфейс Указатель Эндриан Эксинт
Менеджер проекта Брюс Лоусон Администратор проекта Роб Хескет Агенты авторов Сара Бауерс Эйврил Корбин Менеджер по производству Саймон Хардвар Координатор производства Марк Бердетт Помощник по производству Эбби Форлетта Иллюстрации Шабнам Хуссейн Пол Гроув Обложка Крис Моррис
Об авторах Пол Дж. Берк Пол Дж. Берк является старшим разработчиком в одном из финансовых учреждений Сити (Лондон). Он специализируется в распределенных многоуровневых приложениях на основе COM/MTS, COM+ и J2EE. Он написал несколько журнальных статей о различных аспектах использования XML и СОМ+ в приложениях баз данных, и является участником телеконференций Microsoft по темам: SQL Server, Visual Basic и XML. С ним можно связаться через экспертов сайта http://www.experts-in-it.com/. Спасибо моим родителям, которые отправили меня в путь, Ричарду Уолдстайну и Алистеру Лоу-Норрису, заставляющим меня двигаться вперед, Майклу Льюису и Андрю Перри, не дающим мне покоя и заставляющим знать свое дело по настоящему хорошо. Также спасибо Брайану Ренделлу, Андрю Гейтору и Полу Кирби из Developmentor. Моей жене Джо за то, что дала мне возможность сделать это. Моей дочери Рози за то, что правильно расставила пробелы.
Сэм Фергюсон Сэм является IT-консультантом и разработчиком программного обеспечения, работает в Глазго, Шотландия. Сэм живет в Айршире и специализируется на технологиях, связанных с .NET. В свое свободное время (которого у него мало) он любит играть в гольф. Вскоре он собирается жениться (его невесту зовут Джеки) в идиллической местности Зел ам Зее в Австрии. У него есть две кошки (Лег и Чик), а также чудесная мать Катлин. Всем моим друзьям и коллегам.
Дениза Госнелл Дениза Госнелл, сертифицированный разработчик и сотрудник Microsoft, работает в качестве консультанта в Национальной консалтинговой группе MCS. Дениза обладает уникальным сочетанием технического и профессионального юридического опыта. Она получила степень бакалавра "компьютер сайенс" в университете Андерсона и степень доктора юриспруденции в университете Индианаполиса. Дениза - практикующий адвокат, лицензированный в штате Индиана, она также является активным участником профессиональных ассоциаций штата. Дениза работает в компьютерной промышленности около семи лет, недавно она (в соавторстве) выпустила книгу "Библия MSDE" (издательство IDG Books). Когда Дениза не работает и не учится, то совершает вместе со своим мужем Джейком путешествия по всему земному шару (Россия, Китай, Польша).
I
Моему чудесному мужу Джейку за его терпение в те выходные дни, которые я провела в работе над книгой вместо того, чтобы провести их с ним.
Пол Моррис Пол работает в области архитектуры баз данных и живет на Юго-Западе Лондона. Информационными технологиями он занимается уже более пятнадцати лет, причем последние десять он специализируется в технологиях Microsoft. Он проектирует и разрабатывает приложения "электронной коммерции". В настоящее время он работает над крупным проектом для одного из издательств в Соединенном Королевстве. Он является сертифицированным администратором баз данных Microsoft (MCDBA), разработчиком решений (MSCD) и системным инженером (MCSE+I).
Йен Д. Наркивиц Йен Д. Наркивиц является руководителем технического отдела компании Software Pronto, Inc. (
[email protected]). Йен начал свою карьеру в качестве разработчика Microsoft благодаря звезде баскетбола, Майклу Джордану. В начале 90-х годов Йен заметил, что вне зависимости от хода игры всегда побеждает та команда, в которой играет Джордан. И точно также, что бы ни случалось в мире технологии, всегда побеждает Microsoft (впрочем, этой стратегии уже десять лет, и, возможно, она нуждается в обновлении). Так или иначе, Йен понял, за какую команду ему нужно играть. Через несколько лет Йен разработал систему электронной почты, которая ныне используется на семнадцати миллионах компьютеров, помог автоматизировать работу на фабриках, где производят те самые джинсы, которые, вероятно, имеются в вашем гардеробе, и "приложил руку" к тому, чтобы небо над Абу Даби всегда оставалось свободным от вражеских самолетов. Все это он проделал при помощи таких технологий, как COM/DCOM, C0M+, C++, VB, C#, ADO, SQL Server, Oracle, DB2, ASP.Net, ADO.Net, Java, Linux и XML. В свободное время Йен - академический координатор по обучению Windows в различных организациях, автор статей для ASPToday, также он иногда играет в европейский футбол, иначе называемый соккером.
Дж. Майкл Палермо
IV
Дж. Майкл Палермо IV в настоящее время является консультантом в фирме Cunningham Consulting (сертифицированный GOLD-партнер Microsoft). Его пристрастия в технологии, это XML, SQL Server, ASP и .NET framework. Он - владелец сертификатов МСТ, MCSE, MCDBA и MCSD. В свободное время занимается аквариумами с коралловыми рифами.
Хочу no^oaapum>
всЮ
^
>
u*~~~.
Wrox3a
аеЛеНнуЮ
преаост
Ц
возможность участвовать в таком замечательном проекте. Я также благодарен моей жене Тошиа за жертвы, которые она принесла за время моего "отсутствия" в семье.
Ион Д. Рейд Йон руководит направлением средств разработки баз данных в технологическом отделе компании Micro Data Base Systems, Inc. (www.mdbs.com). Он также является редактором по компонентам C++ и Object Query Language (OQL) стандарта Object Data Management Group (ODMG) и участником предыдущих проектов издательства Wrox, включая 'Professional SQL Server 2000 Programming" и "Professional Windows DNA 2000 Development". Когда он не работает, не пишет и не катается на велосипеде, он проводит время со своей женой и двумя сыновьями.
Даршан Сингх Даршан Сингх - старший разработчик в компании InstallShield Software Corp., где он работает в группе, занимающейся разработкой программного обеспечения для Internet на основе ATL/COM+ и XML. На протяжении своей профессиональной карьеры он работал в компаниях Microsoft, Talisma, PSPL (Индия) и Spectrum (Индия), при этом в основном специализировался на базах данных и компонентных технологиях. Даршан также занят в XML-сообществе на сайте www.PerfectXML.com, с ним можно связаться по адресу
[email protected]. Я хотел бы поблагодарить своих родителей, без которых я никогда бы не смог достичь того, чего достиг. Я также благодарен моей возлюбленной жене, Сатвант, - просто обожаю ее улыбку. Большое спасибо чудесной команде издательства Wrox: Джеймсу Робинсону, Эйврилу Корбину, Бекки Стоуне, Кейт Холл и Робу Хескету.
Брайан
СМИТ
Брайан Смит в настоящее время работает в компании Tempo Ltd (независимый поставщик электроэнергии в Соединенном Королевстве), где заведует вопросами технической архитектуры. Его опыт профессионального разработчика программного обеспечения измеряется таким числом лет, что он не хочет даже их считать (первую ошибку в коде он нашел, отлаживая диагностическую программу на языке ассемблера для 8-разрядного микропроцессора Intel 8080). Разработчиком приложений VB и SQL Server он является в течение 10 лет, и считает, что никогда жизнь разработчиков не была такой насыщенной и интересной, как в наши дни. Он закоренелый киноман и фотограф-любитель, хотя и говорит, что изобретение Photoshop сделало занятие фотографией, скорее, кабинетной работой, а необходимость в затемнении отпала. Работа над этой книгой дала мне важный профессиональный опыт. Я бы хотел поблагодарить мою жену Мойру за то, что она терпела мое плохое настроение, и моего пса Сэма за участие в наших дискуссиях о форме и содержании.
Карли Уотсон Карли Уотсон - штатный автор издательства Wrox Press, отличающийся склонностью к разноцветной одежде. Он начинал с намерения сделаться самым знаменитым в мире специалистом по нанотехнологиям, так что однажды, возможно, вы встретите его имя в списке нобелевских лауреатов. В настоящее время Карли интересуется современными мобильными технологиями, такими, как С#. Его часто можно увидеть проповедующим эти технологии на конференциях. Еще Карли - энтузиаст сноу бор динга и хотел бы иметь дома кошку. Спасибо всей команде издательства Wrox за помощь в работе над книгой, и моей жене Донне — за то, что мирилась со мной в это время.
Карвин У ил сон Карвин Уилсон - владелец небольшой консалтинговой компании в Гиг Харбор, штат Вашингтон. Он специализируется на компонентной модели объектов, Visual Basic, SQL Server, Access, web-программировании на стороне сервера, технологии .NET и Delphi. С технологиями Microsoft и Borland он работает уже около 10 лет. Он с готовностью высказывает свое мнение и дает советы всем, кто этого желает. Вы можете обратиться к Карвину через его сайт www.harborviewsolutions.com.
Уоррен Уилши Уоррен в настоящее время - адъюнкт-профессор в университете Фарлей Дикинсон, где он вот уже 18 лет преподает курс компьютерных наук. Сейчас его усилия сосредоточены на профессиональном курсе Internet-программирования. До своего ухода из AT&T Уоррен занимал ряд должностей, связанных с управлением системой UNIX System V, аудитом сетей и систем в AT&T, управлением клиент-серверными системами на основе NT в департаменте внутреннего аудита, а также web-приложениями. Спасибо Эйврил, Кейт и Робу из Wrox за помощь начинающему автору, и моей жене Диане за то, что годами мирилась с моими "Сейчас не могу" и "Нет времени".
Содержание
Введение Темы этой книги Для кого эта книга? Что вам потребуется для работы над книгой Соглашения об обозначениях Поддержка пользователей Глава 1. Введение в XML Основы XML
1 3 3 3 4 7 9
Документы XML Элементы Атрибуты ИменавХМ!. Комментарии Специальные символы Пример Представление объектной модели документа (DOM) Порядок документа Действительный X M L
9 10 11 11 11 12 13 13 13 14
Определение типов документа (DTD) Объявления элемента Схемы XML
15 16 17
Пространства имен
18
XML для данных Представление данных
Использование XSLT для трансформирования XML
19 21
22
Как работает XSLT Элементы XSLT
22 23
Пример XSLT
24
Итоги главы
27
Содержание Глава 2. Обзор XML в SQL Server Выражение FOR XML Режим RAW Режим AUTO Режим EXPLICIT Параметры XMLDATA и ELEMENTS
Доступ к базе данных через HTTP
29 30 30 31 32
33
Простые з а п р о с ы HTTP URL
33
SQL-запросы в URL Указание корневого элемента Шаблоны Указание параметров для шаблонов Апдейтограммы
34 34 35 37 38
Запросы XPath
40
Массовая загрузка XML OPENXML Итоги главы Глава 3. FOR XML Использование FOR XML С Query Analyzer Синтаксис FOR XML FOR XML RAW Псевдонимы в запросах с FOR XML RAW XMLDATA в запросах FOR XML RAW Извлечение двоичных данных с FOR XML RAW FOR X M L A U T O Параметр ELEMENTS в запросах FOR XML AUTO Псевдонимы в запросах FOR XML AUTO XMLDATA в запросах FOR XML AUTO Извлечение двоичных данных с FOR XML AUTO Ограничения при использовании FOR XML AUTO Ограничения, касающиеся GROUP BY или обобщенных функций Ограничения, касающиеся двоичных данных Ограничения, связанные с вычисляемыми столбцами
Правила кодирования FOR X M L EXPLICIT Элементы, атрибуты и преобразования столбцов Пример двухуровневой иерархии Директива hide Директивы element и xml Директива xmltext ii
29
41 42 42 43 43 45 45 46 47 48 49 52 53 54 54 55 55 56 57
60 62 65 68 70 71 72
Содержание Директива cdata XMLDATA в запросах FOR XML EXPLICIT Директивы ID, IDREF и IDREFS BINARY BASE64 в запросах FOR XML EXPLICIT Пример трехуровневой иерархии Вызов запросов FOR XML с ADO ADO и шаблонный запрос ADO и вызовы хранимых процедур
Итоги главы Глава 4. OPENXML Обзор OPENXML Синтаксис OPENXML Параметр iDoc Параметр RowPattern Параметр Flags Правила отображения столбцов OPENXML Выражение WITH Связь с существующей таблицей Описание столбцов Отсутствие выражения WITH - таблицы компонентов
Написание запросов с применением OPENXML sp_xml_preparedocument sp_xml_rernovedocument Отображение, ориентированное на атрибуты Отображение, ориентированное на элементы Смешанное отображение Явное описание отображения столбца Метасвойства OPENXML Столбцы для избыточных данных
Поддержка ADO для OPENXML
76 76 77 81 82 84 85 88
90 93 93 94 94 95 95 96 97 98 98 99
102 104 106 107 112 113 115 116 117
118
Примеры ADO и OPENXML Пример 1 Пример 2
118 119 122
Вставка новых записей с применением OPENXML Изменение существующих записей с применением OPENXML Удаление записей с применением OPENXML Пример совместного выполнения разнородных действий
126 132 134 137
Применение OPENXML для изменения данных в базе данных SQL-сервера 125
Итоги главы
142
Содержание Глава 5. XDR-схемы
143
Что такое схемы?
143
Схемы против DTD XDR-схемы
Разработка XDR-схем XML-объявление Типы данных Пространство имен типов данных Методы проектирования
Разработка аннотированных XDR-схем
146 146 147 147 148 150 151 152 153 158
159
Схемы с аннотациями и просто схемы Управление виртуальными каталогами IIS для SQL Server Пространство имен для аннотаций Список аннотаций Соответствие по умолчанию Задание соответствия явным образом
159
Определение пространства имен для результирующего документа Управление именами элементов и атрибутов Представление отношений в результирующих XML-данных Управление структурой результирующих XML-данных Типы данных в XDR-схемах с аннотациями dttype sql:datatype Столбцы BLOB Получение данных из столбца переполнения Значения по умолчанию для атрибутов Уникальность идентификаторов Проблема разметки XPath-запросы и XDR-схемы с аннотациями XPath-запросы в URL Шаблон в URL Схема с аннотациями, встроенная в шаблон
165 166 169 175 181 181 182 183 184 185 186 188 189 189 189 190
Кэширование схем Итоги главы
IV
145 146
159 160 160 161 165
190 191
Содержание Глава 6. XSD-схемы
193
Что такое XSD-схемы?
193
Сравнение возможностей схем XSD и XDR Главные черты XSD-схем
194 195
Разработка XSD-схем
196
Объявление XML против Элемент Структурные альтернативы Ссылки на другие схемы Аннотации
196 196 197 198 203 206 209 211
Типы данных
212
Простые типы данных Создание собственных типов данных Фасеты
212 216 217
Верификация и использование XSD-схем
218
Поддержка XSD в SQL Server Верификация XSD-схем Трансляция XDR-схем в XSD-схемы
219 219 219
Итоги главы Глава 7 . Представления и шаблоны XML
220 221
Введение в шаблоны XML
221
Настройка виртуального каталога на IIS Внутреннее устройство шаблонов
222 224
Тэги SQL-запроса Вызов хранимых процедур в тэгах запроса Использование нескольких тэгов запроса
224 225 226
Тэги заголовков и параметров
229
Множественные параметры Значение параметра по умолчанию Передача параметров между операторами Тэги XPath-запросов Использование утилиты XML View Mapper
230 231 232 233 234
Применение таблиц стилей XSL
,
.
238
Обновления SQL Server: Web Release
240
Итоги главы
242
Содержание Глава 8 . XPath Почему "Path"? Поддержка XPath в системе SQL Server Использование XPath в SQL Server
246 247 247
Настройка SQL-сервера для работы с XPath
248
Образец XDR-схемы
248
Синтаксис XPath для SQL Server
249
Путь к местоположению - Location Path Сокращенная и несокращенная формы синтаксиса
250 252
Оси направлений Узел контекста "context" Ось потомка "child" Ось родителя "parent" Ось "self" Ось атрибута "attribute" Предикаты Операторы отношения Булевы операторы Арифметические операторы Множественные и последовательные предикаты Предикаты пути Вложенные предикаты Функции Булевы функции Функции преобразования Типы данных XPath и преобразование типов
252 253 253 253 253 254 254 255 255 256 256 257 257 258 258 259 259
Переменные XPath
Неподдерживаемый синтаксис Другие темы, связанные с SQL Server XPath
261
262 262
Ошибки
263
Безопасность
264
XPath-запросы и FOR XML EXPLICIT
264
Прямое связывание двоичных атрибутов
264
Будущее XPath Итоги главы Глава 9. Апдейтограммы XML for SQL Server 2000 Web Release 1 Обзор апдейтограмм Синтаксис апдейтограмм Три простых примера vi
245
265 266 267 267 268 269 271
Содержание Настройка среды Создание виртуального каталога IIS Пример 1: вставка
271 271 272
Пример 2: удаление
273
Пример 3: обновление
274
Еще несколько примеров Соответствие по умолчанию Соответствие по умолчанию, основанное на элементах Соответствие по умолчанию: атрибуты и элементы
275 275 275 276
Работа с множеством строк
277
Специальные случаи Работа со столбцами типа Identity Генерация GUID Работа со значениями NULL Обработка специальных символов Преобразования типов данных Передача параметров
280 280 283 284 285 286 286
Апдейтограммы и аннотированные XDR-схемы Отношения между таблицами и типы данных Использование оператора LIKE
Интеграция апдейтограмм в приложения
288 289 290
292
Апдейтограммы и HTML-формы
292
Отправка апдейтограмм из VB ADO и апдейтограммы Передача параметров в апдейтограмму при помощи ADO
294 296 297
Апдейтограммы и многопользовательская среда Ограничения апдейтограмм Итоги главы
299 300 300
Глава 10. Массовая загрузка XML-данных (Bulk Load)
301
Существующие методы массовой загрузки данных
302
Служба трансформации данных - Data Transformation Services
302
Утилита b c p BULK INSERT
304 305
X M L Bulk Load Объектная модель Bulk Load Метод Execute Свойства Настройка массовой з а г р у з к и данных XML Bulk Load Режимы Загрузка в несколько таблиц одновременно Ограничения XDR-схемы Потоки
3 0 6 309 309 309 313 313 315 318 319
VII
Содержание Создание схемы в процессе импорта
Итоги главы Практическое занятие I. Расширение существующей системы возможностями работы с XML
321
326 327
Существующее приложение
328
Получение отчетов в браузере
329
Редактирование информации в браузере
331
Проблемы проектирования объектов Достаточно ли быстр XML для наших нужд?
333 334
Классы, требуемые для теста ввода данных Запуск теста
335 338
Тест: получение данных с SQL-сервера Существующее приложение Интерфейсы Некоторые соображения о версиях документа
Веб-страница для создания заказов в режиме ON-LINE Дополнительная функция - автоматическое пополнение запасов Дополнительная функция - специальные сообщения Итоги практического занятия Практическое занятие I I . Построение системы ввода заказов на VB и X M L
341 344 345 347
353 355 357 358 361
Требования
362
Спецификации программы
362
Проектирование интерфейса пользователя Проектирование базы данных Таблица orders Таблица lines Таблица payments Таблица employees Таблица products Таблица pmt_types Примеры данных
Архитектура приложения
Проектирование XML
viii
363 364 365 366 367 367 368 368 368
369
371
Атрибуты или элементы
372
Значения данных Даты Числа с плавающей точкой
373 374 375
Содержание Текстовые поля Итоговые данные XML Образцы данных XML Сотрудники (Employees) Типы платежей (Payment Types) Товары (Products) Заказы (Orders)
Создание приложения XMLOrders
375 375 375 376 377 377 378
379
Запуск программы Ссылки DOM Ошибки XML Просмотр XML
379 380 384 387
Загрузка заказа Значения дат Значения NULL Перекрестные ссылки Распаковка элементов-потомков Д о с т у п к данным Ошибки SQL Хранимые процедуры
389 394 394 394 395 399 401 401
Модификация заказа Изменение платежей Форма frmPayments Удаление платежа Сохранение изменений Применение OPENXML Дескрипторы DOM Проверка данных Параллельное выполнение
Создание новых данных Добавление платежа Создание нового заказа
Бизнес - правила Анализ производительности Пропускная способность Верифицирующий анализатор DOM
Что далее? Итоги практического занятия
406 407 409 411 411 415 417 418 419
419 423 424
425 427 427 428
429 430
Практическое занятие III. Генерация отчетов при помощи XML 433 Что вам потребуется в этом практическом занятии 434 Постановка задачи 434 Основные проблемы в разработке систем генерации отчетов 435 IX
Содержание Архитектура "клиент-сервер" с точки зрения систем генерации отчетов Трудность настройки Усилия по поддержке Лицензии, требуемые ресурсы и стоимость
Как решить все эти проблемы? Visual Interdev Internet Information Server Поддержка XML на SQL Server Таблицы стилей XSL Бизнес-пример для практического занятия
Создание проекта Visual Interdev Создание базы данных
435 436 437 437
437 438 438 438 438 438
439 440
Выполнение сценариев создания базы Таблицы Хранимые процедуры
440 440 441
Создание XDR-схемы Создание XML-шаблонов Создание таблицы стилей XSL Разработка интерфейса Доступ к системе из VB Итоги практического занятия
441 444 445 448 456 458
Практическое занятие IV. Протокол SOAP Краткий обзор приложения Функциональность
Реализация серверной части системы Реализация клиентской части системы clsProduct clsProducts dsDepot cIsDepots Форма frmMain
Протокол SOAP и защита в маршрутизаторах Будущее SOAP Практическое занятие V. Практическое занятие по технологии .NET Проект
459 462 463
465 472 473 474 476 477 478
482 483 485 485
Содержание
SQL: Производительность и оптимизация
487
Производительность FOR XML
487
Хранимые процедуры
490
Производительность XMLDATA
Бизнес-уровень
493
495
Получение XML-данных, исходное приложение
495
Вставка данных (OPENXML), исходная версия
498
Исходные СОМ-объекты и .NET Этап 1 - перенос бизнес-уровня
502 502
Этап 2 - перенос бизнес-уровня
Клиентский уровень исходного приложения Клиентский уровень в исходном приложении, Customer Клиентский уровень в исходном приложении, Shippers Клиентский уровень в исходном приложении, Order
Уровень клиента в приложении .NET Уровень клиента в приложении .NET, Shippers Уровень клиента в приложении .NET, Customers Уровень клиента в приложении .NET, Orders
Итоги практического занятия Приложение А. Создание и настройка виртуальных каталогов Утилита MS Virtual Directory Management
506
510 511 513 515
517 518 520 521
522 523 523
Требования к системе
523
Установка
524
Создание виртуального каталога Вкладка General Вкладка Security Вкладка Data Source Вкладка Settings Вкладка Virtual Names Вкладка Advanced Обновление Web Release 1 Вкладка Settings Вкладка Advanced
524 525 525 526 527 528 530 531 532 532
Программные методы
532
Объектная модель Объект SQLVDirControl Семейство объектов SQLVDirs Объект SQLVDir Семейство VirtualNames Объект VirtualName
533 534 535 536 539 539
Практические советы
542
XI
Содержание Приложение В. Microsoft XML View Mapper 1.0 Как получить пакет XML View Mapper Требования к системе Требования к программному обеспечению Аппаратные требования Сеть Права доступа Загрузка п р о г р а м м н о г о обеспечения Установка XML View Mapper Процесс установки XML View Mapper Что было установлено, и где все это находится? Модификация, восстановление и удаление XML View Mapper Модификация (Modify) установленной программы XML View Mapper Восстановление (Repair) установленной программы XML View Mapper Удаление (Remove) установленной программы XML View Mapper
Архитектура X M L View Mapper
543 544 544 544 544 544 544 544 545 545 546 547 547 547 547
548
Исходные данные Схемы баз данных SQL Schema Importer XDR Schema XDR Schema Importer Окно проекта - Project Explorer
548 548 548 549 549 549
Результирующие данные XML View Exporter Схема XML View XDR-схема Два редактора и тестер Редактор карты - Map Editor Редактор XDR Тестер XPath-запросов - XPath Query Tester
550 550 550 550 550 550 550 550
Запуск XML View Mapper Ассоциация с типом файла Запуск из командной строки
Обзор визуальной среды разработки Панель инструментов Панель меню Сочетания "горячих" клавиш Общие сочетания клавиш Окно проекта Схемы и модули XML View Mapper Файлы XML View Mapper Окно редактора карт Панель SQL-схемы Панель XDR-схемы Манипуляции с модулями Соединение с базой данных xii I
I
550 551 551
551 552 553 553 553 555 556 556 557 557 560 564 565
Содержание Назначение соответствий Соответствия таблиц Соответствия столбцов Обработка переполнения Представление линий соответствия: цвет и стиль
565 565 566 566 567
Примеры задания соответствий Примеры задания соответствия для таблиц Примеры задания соответствия для столбцов Явное соответствие с указанием пути Примеры задания соответствия для данных переполнения Еще о соответствиях Панель карты соответствий Управление отображением линий соответствия Свойства линии соответствия Редактор XDR Автозавершение "Горячие клавиши" Проверка XDR-схемы Тестер XPath-запросов Правила завершенности Использование тестера XPath-запросов
568 568 569 570 571 571 571 573 574 574 575 576 576 576 576 577
Использование документации online Пакетные преобразования схем при помощи командного файла Командные элементы Командный элемент COMMANDS Элемент LOG Элемент CREATE_EMPTY Элемент IMPORT_XDR Элемент IMPORT_DB Элемент EXPORT_XDR Элемент EXPORT_XAS Выполнение командного файла
Обновление модулей Создание и применение командных файлов
578 579 579 579 580 581 581 583 585 586 587
587 588
Использование визуальной среды разработки
588
Не забывайте о соответствиях
588
Утилиты Преобразование DTD в XDR Генерация XDR-модуля Восстановление XDR-схемы из XML-документа
Возможные проблемы и их устранение Сообщения Сообщения об ошибках Предупреждения Информационные сообщения
588 588 588 589
589 589 589 589 590 xiii
Содержание
Ошибки XPath-запросов FAQ - ответы на часто задаваемые вопросы Раздел "Практические советы" Журнал Диалоговое окно журнала Другие источники
Настройка X M L View Mapper Вкладка General Раздел Project Раздел Map Editor Вкладка Mapping Filter Вкладка Mapping Format Вкладка Error Log
Поддержка разработчиков со стороны Microsoft
590 590 590 591 591 592
592 592 592 592 593 594 594
595
Приложение С. Обновление Web Release 2 - версия "бета 1 "
597
Предметный указатель
599
XIV
Введение Одна из важнейших новаций SQL Server 2000 заключается в том, что теперь в эту систему интегрированы средства поддержки технологии XML. Корпорация Microsoft признала, что следующее поколение Web-приложений и приложений уровня предприятия будет активно использовать XML для передачи данных и поэтому энергично интегрирует средства поддержки XML в SQL Server. Стандартный вариант установки SQL Server 2000 включает в себя следующие XML-средства: • a
•
поддержка раздела FOR XML в выражениях SELECT OPENXML
XML-представления (XML Views)
Консорциум W3C (World Wide Web Consortium) создает стандарты в области XML с такой скоростью, что Microsoft теперь выпускает обновления для SQL Server 2000 через Web. В середине февраля 2001 года был объявлен выпуск обновления Web Release 1. Это обновление существенно усиливает средства поддержки XML в SQL Server, добавляя новые функциональные возможности: • •
апдейтограммы (Updategrams); массовая загрузка XML (XML Bulkload).
В конце апреля, когда эта книга уже готовилась к выпуску, было объявлено о выпуске бета-версии обновления Web Release 2. Этот web-релиз значительно расширяет поддержку XSD-схем.
Темы этой книги Мы начнем наше знакомство с XML с краткого обзора этой технологии, который поможет вам, если вы новичок, и не будет лишним, если вы уже сталкивались с XML. В главе 2 мы опишем простые примеры использования стандартных XML-средств SQL Server.
Введение Глава 3 посвящена разделу FOR XML оператора SELECT. Раздел FOR XML позволяет получать XML-документы в результате обычного запроса к таблицам в реляционной базе данных добавлением всего лишь одиннадцати символов в конец запроса. Ключевые словаRAW, AUTO и EXPLICIT позволяют управлять структурой возвращаемых XML-данных. Мы также познакомимся здесь с XML-схемами и научимся обращаться с двоичными данными. В главе 4 рассматривается обратный процесс - получение реляционных данных из XML-документа при помощи OPENXML. Результирующие данные можно использовать точно таким же образом, как и любой другой набор результирующих строк, полученный из таблиц или представлений. Мы можем управлять структурой получаемых данных, которые можно использовать для удаления или обновления записей в базе данных. XDR-схемам посвящена глава 5. Эти схемы позволяют задавать структуру XML-документа и специфицировать типы данных. При помощи XDR-схем мы также можем получать данные из базы через HTTP-запросы без установления соединения с SQL-сервером. Глава 6 посвящена XSD-схемам и сравнению их с XDR-схемами. Стандарт XSD выпущен консорциумом W3C и не зависит от платформы. Кроме того, этот стандарт предусматривает более широкие в сравнении с XDR функциональные возможности. Корпорация Microsoft, как явствует из Web Release 2, намерена интегрировать поддержку XSD-схем в SQL Server 2000. XML-представления и шаблоны являются темой главы 7. XML-представления дают возможность описать, как таблица на SQL-сервере выглядит с точки зрения XML. XML-шаблоны позволяют повысить эффективность запросов, одновременно обеспечив более высокий уровень безопасности. Глава 8 посвящена XPath. Ограничение XML-документа состоит в том, что выполнять навигацию по нему или обращаться к нему с запросами при помощи одного только XML невозможно. XPath - это навигационный язык запросов, специфицированный консорциумом W3C для поиска данных в XML-документах. Механизм апдейтограмм (updategrams) был впервые представлен в обновлении Web Release 1, и ему посвящена глава 9. Апдейтограммы представляют собой блоки специальных XML-тэгов, которые описывают данные до и после выполнения апдейтограммы. Этот механизм позволяет простым и ясным способом обновлять данные посредством HTTP, и, обеспечивая высокую производительность, требует минимума программирования. Глава 10 описывает механизм массовой загрузки XML-данных (XML Bulk Load) - еще одну новацию из обновления Web Release 1. Механизм массовой загрузки позволяет быстро и эффективно импортировать в базу значительное число (потенциально, даже миллионы) записей из XML-документов. Десять основных глав дополняются затем пятью практическими занятиями, которые должны дать вам представление о путях реализации в приложениях всех этих функциональных возможностей. Наконец, книга завершается тремя Приложениями. Приложение "А" посвящено настройке виртуальных каталогов, в Приложении "В" мы изучаем утилиту XML View Mapper, а в Приложении "С" описывается обновление Web Release 2.
Введение
Для кого эта книга? Эта книга создана для разработчиков, обладающих опытом работы с SQL Server 2000 и, в особенности, с T-SQL. Базовое знание технологии XML было бы полезным, хотя глава 1 предназначена как раз для полных новичков в этой области.
Что вам потребуется для работы над книгой Для работы над основными главами книги вам потребуется следующее: •
SQL Server 2000.
•
Обновление "XML for SQL Server Web Release 1". В главе 9 приведены инструкции по загрузке и установке этого обновления.
•
Утилита "Microsoft XML View Mapper 1.0". В Приложении "В" описано, как ее получить.
Для выполнения практических занятий вам также необходимо иметь VB б, ASP, C++ 6 и С#. Заметим, однако, что эти пять практических занятий - не главное в книге.
Исходный код Все исходные коды, используемые в книге, можно загрузить с сайта http://www.wrox.com.
Соглашения об обозначениях Для того чтобы облегчить вам восприятие текста, мы используем ряд соглашений. Например: Так обозначается важная информация, которая имеет прямое отношение к окружающему тексту, и на которую необходимо обратить особенное внимание.
Так выглядят комментарии "по ходу дискуссии", не имеющие прямого отношения к обсуждаемой теме. В тексте мы используем различные стили: •
Когда мы представляем новое понятие, то выделяем ключевые слова.
•
Сочетания клавиш на клавиатуре мы обозначаем так: Ctrl-A.
• •
Имена файлов и фрагменты программного кода в тексте мы обозначаем: SELECT * Текстовые обозначения в пользовательском интерфейсе, а также текст URL-ссылок выглядит следующим образом: Menu.
3
Введение Программный код у нас представляется несколькими способами. Определения методов и функций обозначаются так: OPENXML(iDoc [WITH
i n t [in],RowPattern
(SchemaDeclaration
nvarchar[in],[Flags
b y t e [ i n ] ] )
\ TableName)]
Пример исходного кода приведен ниже:
I
В примерах исходного кода так обозначается важный, новый фрагмент, тот код, на который необходимо обратить особенное внимание. А это код, который уже фигурировал в примерах, или код, по сути своей второстепенный.
Поддержка пользователей Нам важно знать, что вы думаете об этой книге: что вам понравилось, что не понравилось, и что, по-вашему, в следующий раз можно было бы сделать лучше. Вы можете прислать нам свои замечания по электронной почте (
[email protected]). Пожалуйста, не забудьте упомянуть в своем письме название книги.
Ошибки Мы приложили все усилия к тому, чтобы в тексте и исходном коде не осталось ни одной ошибки. Тем не менее, человеку свойственно ошибаться, и поэтому мы понимаем, что существует необходимость информировать вас об обнаруженных ошибках. Списки обнаруженных ошибок для всех книг нашего издательства вы найдете на сайте http://www.wrox.com. Если вы встретитесь с ошибкой, которая не включена в список, пожалуйста, дайте нам знать.
Поддержка посредством электронной почты Если вам требуется совет эксперта по проблеме, с которой вы столкнулись в этой книге, отправьте сообщение на адрес электронной почты
[email protected] с указанием названия книги и последних четырех цифр в номере ISBN в поле 'Subject". Типичное сообщение электронной почты должно включать в себя: •
Название книги, последние четыре цифры номера ISBN, а также номер страницы, где вы столкнулись с проблемой, в поле Subject.
•
Ваше имя, контактная информация и описание сути проблемы в теле сообщения.
Мы не отправляем ненужные и бесполезные сообщения. Нам необходимо знать детали для того, чтобы сэкономить ваше и наше время. Когда вы пришлете нам сообщение электронной почты, оно пройдет по следующим этапам процесса поддержки: •
Поддержка пользователей - ваше сообщение попадет в отдел поддержки пользователей, где уже имеются ответы на часто задаваемые вопросы. Эти люди немедленно ответят на вопросы общего характера о книге или web-сайте.
•
Редакторы - более сложные вопросы будут переданы техническому редактору, отвечающему за данную книгу. Редакторы обладают опытом в той области, которой посвящена книга, и поэтому способны ответить на сложные технические вопросы.
Введение После того как проблема будет решена, редактор, возможно, поместит новое сообщение в список обнаруженных ошибок на web-сайте. •
Авторы - наконец, в том маловероятном случае, когда редактор не сможет ответить на вопрос, он передаст ваше сообщение автору. Мы стараемся оберегать своих авторов от излишней и ненужной переписки, однако, действительно сложные вопросы мы им передаем. Все наши авторы помогают нам в поддержке своих книг. Автор ответит пользователю и редактору, а в результате выиграют все читатели.
P 2 P . W R O X . C O M Мы также поддерживаем списки рассылки SQL Server. Наша уникальная система осуществляет поддержку "от программиста к программисту" (programmer to programmer™) при помощи списков рассылки, форумов и групп новостей. Вы можете быть уверенными в том, что ваш запрос прочитает не только штатный сотрудник отдела поддержки, но также и авторы нашего издательства, и многие другие профессионалы, участвующие в наших списках рассылки. По адресу p2p.wrox.com вы найдете целый ряд списков, предназначенных для программистов SQL Server, которые помогут вам не только в отношении работы с этой книгой, но вообще в ваших проектах.
Чем хороша эта система поддержки Вы можете присоединиться к любому списку рассылки на выбор или же получать еженедельный дайджест. Если у вас нет времени на чтение рассылки, вы можете воспользоваться средствами поиска в архивах. Бессмысленные и бесполезные сообщения удаляются, а ваш адрес электронной почты будет защищаться уникальной системой Lyris. Запросы на включение вас в список (так же, как и на исключение из него) отсылайте по адресу
[email protected].
Введение в XML Необходимость в обмене данными возникла еще на заре компьютерной эры. В прошлом применялось множество различных методов форматирования данных, обычно зависящих от конкретного приложения. Поэтому различные приложения, работающие под разными операционными системами, часто не могли расшифровать данные друг друга. В таком подходе не было ничего ошибочного, и, безусловно, существуют ситуации, когда создание собственного формата обмена данными для каждого приложения оправдано. Вместе с тем, при разработке приложений, особенно приложений масштаба предприятия, важно иметь тщательно проработанную спецификацию форматирования данных. Наличие такой спецификации могло бы значительно облегчить жизнь разработчикам программного обеспечения. Ведь сложные схемы обмена данными значительно увеличивают время разработки программного обеспечения. Кроме этого, благодаря упрощению обмена данными, облегчается дальнейшая модификация и доработка существующего программного обеспечения. В последние годы особенно много внимания уделяется одной из технологий форматирования данных, а именно языку XML, или extensible Markup Language (Расширяемый язык гипертекстовой разметки). Спецификация языка XML разработана консорциумом W3C (World Wide Web Consortium, http://www.w3c.org/) в 1998 году в результате усилий по стандартизации языков разметки текстов. Фактически это подмножество языка SGML - Standard Generalized Markup Language (Стандартный обобщенный язык разметки), - детально описанного в стандарте ISO 8879, опубликованном в 1986 году, но более простое и не требующее знания стандарта SGML. Лучше всего объяснить термин "разметка текста" на примере. Рассмотрим фрагмент текста и прокомментируем его. Earlier in the day Jenkins had met Smith in Fenchurch Street. He was a tall man with striking features and eyes that always seemed to be looking at you in an unnerving way. The spark of devious intelligence was obvious, and most people quickly learned to choose their words with care around him. Not Jenkins, though, who had discovered his weakness. He had always carried a bar of chocolate with him when he knew he would encounter this strange individual. Now, Smith was dead. It was up to Jenkins to find out how - and why.
Глава 1 Если этот параграф помещается на отдельной странице, можно "разметить" текст. Note: not sure about this name. Change?
New paragraph. Chapter 1 paragraph 2.
Earlier in the day Jenkins had met Smith in Fenchurch Street. He was a tall man with striking features and eyes that always seemed to be looking at you in an unnerving way. The spark of devious intelligence was obvious, and most people quickly learned to choose their words with care around him. Not Jenkins, though, who had discovered his had always carried a bar of chocolatcfwith him when he knew he would encounter this strange individual.
Note: chocolate sounds a bit silly. A different weakness,perhaps?
Now, Smith was dead. It was up to Jenkins to find out how - and why.
Use italics for last two words.
В языке разметки текста действия по разметке текста включаются в сам текст в виде дополнительной информации. Примером может служить язык HTML. При использовании этого языка текст, отображаемый в браузере, и информация об отображении этого текста объединяются в едином файле. Например, для того, чтобы написать в приведенном выше примере два последних слова курсивом с помощью HTML, необходимо просто окружить их открывающим и закрывающим тэгами : | Now, Smith was dead. I t was up to Jenkins to find out how - and why. Второй тэг начинается с косой черты (/) чтобы указать, что это закрывающий тэг. Соответствующая программа чтения текста, например HTML-браузер, может создать графическое представление текста на основании дополнительной информации, содержащейся в нем. Язык XML аналогичен HTML, но с одним важным отличием. HTML предназначен для отображения, форматирования текстов, встраивания графики и тому подобное. XML же дает исключительно структурную и контекстуальную информацию о данных. Такое смещение акцентов делает XML идеальным для обмена данными стандартным способом, хотя этот язык можно использовать и для более традиционных задач разметки текстов, в чем мы убедимся позднее. В этой главе мы рассмотрим:
8
•
Основы XML.
•
Представление объектной модели документа (DOM) в XML-файле.
Введение в XML •
Создание XML-файла в соответствии с описанной грамматикой, использующей определения типов документов (DTD) и схемы.
Q
Пространства имен XML.
•
Основы трансформирования XML с использованием таблицы стилей (XSLT).
•
Использование XML для хранения данных в противоположность разметке текстов.
Основы XML В этом разделе мы познакомимся с синтаксисом XML и детально обсудим различия между хранением текста и данных.
Документы XML XML-файлы называются также документами и содержат такие разделы: •
Необязательное объявление XML сообщает, что документ является XML, а также использованную версию XML, кодировку символов и, если требуется, дополнительную информацию.
•
Необязательное определение типа документа (DTD), описывающее структуру документа XML и допустимый синтаксис. DTD может описываться внутри документа или ссылаться на внешнее описание.
•
Тело документа.
Объявление обычно выглядит следующим образом:
•
Это пример инструкции обработки - специального тэга, заключенного в разделители и предназначенного для выдачи инструкций по обработке документа приложением. В приведенном выше примере мы сообщили, что использовался XML версии 1.0. Метод указания этой информации о версии включает в себя использование атрибута, называемого v e r s i o n . В ближайшее время мы обсудим атрибуты более детально. Сейчас мы пропустим раздел DTD и вернемся к нему в этой главе позже. Для объяснения DTD необходимо детально понимать структуру документа XML. Тело документа содержит единственный элемент-документ. Он должен быть только корневым элементом. Документ XML может просматриваться двумя способами: человеком, например, в текстовом редакторе, или приложением. Когда приложение загружает документ XML, то обычно производится синтаксический разбор документа, при котором документ подвергается нескольким проверкам для того, чтобы убедиться в корректности документа. В следующем разделе мы рассмотрим составляющие части документа XML и правила, которым мы должны следовать для создания корректного документа с корректным синтаксисом. Давайте более детально рассмотрим понятие "элемент".
2—1683
Глава 1 Элементы В XML элемент состоит из начального тэга, содержимого элемента и конечного тэга. Начальный и конечный тэги выглядят аналогично тэгам HTML и содержат текст, заключенный в угловые скобки (символы < и >). Кроме того, в начале конечного тэга добавляется символ косой черты ("слеш"). Каждый элемент должен быть описан полностью. Это означает, что каждому начальному тэгу должен соответствовать конечный тэг. Исключение составляют пустые элементы, которые мы рассмотрим ниже. Имя элемента используется в начальном и конечном тэгах и располагается непосредственно внутри тэга. Например, элемент может выглядеть следующим образом:
I
Элемент может содержать: •
другие элементы;
•
текстовые данные;
•
текстовые данные и другие элементы одновременно.
Кроме того, элемент может быть пустым, то есть не иметь содержимого. У такого элемента могут быть атрибуты, как мы увидим в следующем разделе. Пустые элементы могут быть описаны как обычным способом с помощью начального и конечного тэгов, так и упрощенным способом. При описании упрощенным способом применяется единственный тэг с левой наклонной чертой в конце. Например: Ц Заметьте, что элементы не могут перекрываться. Это означает, что если один элемент содержит начальный тэг другого элемента, он должен также содержать и конечный тэг этого же элемента. Это существенное отличие от HTML, в котором допускается перекрытие элементов: |
This text is
a mix of
normal, italic and bold italic t e x t .
Приведенный выше код вызовет ошибку в XML. Элемент, входящий в другой элемент, называется также вложенным. В иерархии документа элементы могут быть представлены как "родители", "потомки" или "одноуровневые". Проиллюстрируем это в виде такой структуры: •
Здесь для элемент является родителем, является потомком, и элемент является одноуровневым.
10
Введение в XML Атрибуты В начальный тэг элемента могут включаться атрибуты, которые представляют собой пару имя-значение. Атрибуты описываются путем указания имени атрибута, следом за ним знака равенства (=) и значения атрибута в кавычках. Выбор одинарных или двойных кавычек определяется содержимым значения, и если значение содержит одинарные кавычки, то для разделения выбираются двойные кавычки и наоборот. Например:
Имена в XML При определении имен атрибутов и элементов XML необходимо следовать правилам: •
Первый символ должен быть буквой или символом подчеркивания.
•
Последующие символы могут быть буквами, символами подчеркивания, числами, точками или тире.
Примеры корректных имен: _Person I
Root-Element Section42
Примеры некорректных имен: 69Dude
1 -F1 Имена XML учитывают регистр символов. Это означает, что имена и различны и могут содержаться в одном документе, хотя такое соседство может быть нежелательным.
Комментарии Документ XML может содержать комментарии аналогично языкам программирования, таким как C++. Комментарии применяются для сообщения дополнительной информации человеку, изучающему текст программы, и не дают никакой дополнительной информации приложению, использующему XML. Синтаксис комментария в XML аналогичен HTML и выглядит следующим образом: Ц§ Все символы между открывающим < ! - - и закрывающим --> литералами игнорируются синтаксическим анализатором. Комментарии должны располагаться вне элемента-документа (но не перед объявлением XML, которое должно стоять первым в XML-файле). Заметим, что комментарии в XML-документе также подвергаются синтаксическому анализу. Следовательно, большое количество комментариев может быть причиной снижения производительности. Однако это не значит, что необходимо полностью исключать 11
Глава 1 комментарии, просто их нужно применять осознанно и делать короче. Хорошо комментированный документ обладает преимуществами, если необходимо его изучение человеком.
Специальные символы Теперь стало ясно, что в XML некоторые символы, такие как < или >, имеют особое значение для синтаксического анализатора. Следовательно, может возникнуть вопрос "Как же применять такие символы в тексте?" Мы имеем две возможности. Можно применить символьные ссылки или разделы CDATA. Символьные ссылки представляют собой последовательность символов, интерпретируемых анализатором как другие символы. В таблице перечислены допустимые символьные ссылки: Символьная ссылка
Результирующий символ
&атр;
&
>
>
<
<
' " Все ссылки начинаются символом "&" и заканчиваются ";". Фактически ссылки представляют собой особый случай компонентов XML (мы детально рассмотрим их позже). В приведенной выше таблице показана только небольшая выборка из обширного набора символьных ссылок. В более общей форме можно применять символы из полного набора символов ISO\IEC 10646. Для этого создается символьная ссылка, включающая код символа в десятичном или шестнадцатеричном виде, которому предшествует строковый литерал, соответственно, " & # " или "х". Описание символьной ссылки завершается точкой с запятой (;). Например, текст, содержащий: Ш You & me a r e C; h i m .
в результате грамматического разбора будет представлен в виде: Щ You & me are > him. В качестве альтернативы мы можем явно описать раздел текста CDATA, который не содержит ничего, что могло бы рассматриваться как разметка текста. Описание раздела CDATA производится с помощью разделителей " " . Весь текст после первого разделителя будет интерпретироваться "как есть", с тем только ограничением, что он не может содержать строку "] ] >", поскольку такая строка рассматривается как окончание раздела CDATA. Например, текст: Ц
будет интерпретироваться: You & me
12
Введение в XML Пример В качестве примера рассмотрим следующий документ XML. Этот документ соответствует всем рассмотренным нами правилам. < ! — Здесь комментарий вне элемента-документа --> Здесь текст внутри элемента-документа. Здесь текст внутри элемента, содержащегося в элементе-документе. Еще немного текста внутри элемента-документа.
Подведем итоги: •
Присутствует единственный элемент-документ,
•
Элемент-документ может содержать одновременно и текст и другие элементы. Элементы не перекрываются.
•
Каждый элемент завершен и имеет начальный и конечный тэги, или пуст.
Q
Все использованные имена (для элементов и атрибутов) корректны.
Документ, соответствующий перечисленным выше правилам, называется формальноправильным документом.
Представление объектной модели документа (DOM) Структура XML иерархична, и это фундаментальное положение необходимо принимать во внимание. Наиболее концептуальное представление иерархичной структуры известно как объектная модель документа (DOM). В этой модели документ XML рассматривается как набор узлов в древовидной структуре. Узлы могут быть элементами, атрибутами, текстом, комментариями или инструкциями обработки. В модели DOM присутствует единственный узел, известный как корневой узел документа. В этом узле содержится все в документе XML. Корневой узел - это не то же самое, что элемент документа в нашем документе XML, который содержится внутри корневого узла документа на том же уровне иерархии, что и объявление XML. С использованием концепции DOM мы можем представить документ XML из последнего примера следующим образом (см. рис. на след. стр.). Графическое представление будет очень важно несколько позже, когда мы начнем рассматривать таблицу стилей (XSLT).
Порядок документа Заметим, что представление документа XML в виде DOM сохраняет порядок документа. Это означает, что узлы древовидной структуры точно представляют документ. Поэтому мы можем ссылаться на элементы посредством указания их имен или их положения. Например, "второй элемент-потомок ". 13
Глава 1
Узел
документа
Узел XML-деклараций
PI
Узел комментария
Узел элемента
Узел текста
Узел элемента
Узел атрибута
Узел текста
Узел текста Узел текста
/зел элемента
Узел атрибута
Узел текста Узел комментария
В то же время, для атрибутов порядок следования не применим. Мы не можем предположить, что порядок атрибутов в элементе будет сохранен, поскольку такое положение не включено в спецификацию XML.
Действительный XML В этой главе мы уже встречались с формально-правильным XML. Это означает, что такой документ XML соответствует всем синтаксическим правилам спецификации XML и может быть понят синтаксическим анализатором. В этом разделе мы рассмотрим другое понятие действительный XML. 14
Введение в XML Действительный XML - это формально-правильный XML, который соответствует дополнительному набору правил. Этот набор правил может быть указан в документе или в другом месте. Верифицирующий анализатор представляет собой анализатор, способный проверять эти дополнительные правила, он отвергает документ XML в случае обнаружения ошибки. Концепция действительного XML очень важна. Получение от делового партнера формально-правильного XML-документа не решает проблемы. Предположим, мы запросили документ с детальной информацией о потребителе и получили следующее: Billy Robertson 13
Другой партнер прислал что-то подобное: Billy Robertson 13
В обоих случаях мы имеем дело с формально-правильными XML-документами, но для согласования этих документов могут потребоваться дополнительные усилия. Если мы проверим правильность этих документов путем сообщения правил структурирования документов XML, то мы сможем предотвратить эти проблемы. Можно сказать, например: "элемент должен содержать полное имя в элементе ", и второй документ примера будет признан некорректным. Однако мы можем просто позвонить партнеру по телефону и сообщить правила. В настоящее время существуют два способа задания правил корректности документов XML. •
Определение типов документа (DTD).
•
Схемы XML.
В следующих двух разделах мы рассмотрим оба метода.
Определение типов документа (DTD) Определение типов документа состоит из последовательности объявлений для элементов и связанных с ними атрибутов; они могут включаться в документ для проверки его правильности. Если документ содержит другие элементы или атрибуты, или объявленные элементы или атрибуты применены неверно, то в результате верификации документа будет обнаружена ошибка. Фактически, определение типов документа задает грамматику для верификации документа. 15
Глава 1 Определение типов документа (DTD) может находиться или непосредственно в документе XML, или в отдельном файле, доступном через URL. Определение типов документа связывается с документом XML с помощью объявлениж! DOCTYPE>. Это объявление описывает имя DTD, которое должно совпадать с именем элемента-документа в документе XML, а также URL для ссылки на внешний файл с DTD или собственно DTD. Можно задать и внешние и внутренние DTD. В этом случае сначала обрабатываются внутренние DTD, затем внешние. Дублирование определений во внешнем файле вызовет ошибку. Для описания внешнего DTD используются ключевые слова SYSTEM или PUBLIC: | Использование ключевого слова SYSTEM предписывает анализатору загрузить файл DTD из указанного URL. Ключевое слово PUBLIC используется для обозначения широко известных определений типов документов. Этот метод разрешает приложению использовать собственные методы для доступа к объявлениям. Однако индивидуальный подход каждого анализатора в реализации доступа к широко известным определениям может снизить производительность системы и ограничивает применение этой технологии. Внутренние DTD заключаются в квадратные скобки: Ц
Синтаксис определений типов документов не зависит от того, внешние они или внутренние. Исключение заключается в том, что внешние DTD могут содержать разделы, обрабатываемые по условию. Однако такие разделы применяются редко, и мы не будем их здесь рассматривать.
Объявления элемента Допустимое содержимое данного элемента описывается в DTD с помощью объявления < !ELEMENT>. В основном, содержимое элемента подпадает под одну из следующих категорий: •
Пустой - элемент не может иметь никакого содержимого.
G
Элемент - элемент содержит другие элементы.
•
Смешанный - элемент содержит другие элементы и/или текстовые данные.
•
Произвольный - элемент может содержать все что угодно.
Элементы с пустым и произвольным содержимым описываются соответственно с помощью ключевых слов EMPTY или ANY следующим образом: I
Элементы, содержащие другие элементы, и элементы со смешанным содержимым требуют более детального описания. Обычно мы перечисляем каждый из потомков, который элемент может иметь, или по имени (для элементов-потомков) или с помощью*PCDATA для текста. Простейший способ для описания заключается в перечислении списка потомков, разделенного запятыми. 16
Введение в XML Например: Щ
Это означает, что должен иметь двух потомков и . Потомки должны следовать в указанном порядке. Элементы со смешанным содержимым описываются аналогично: §§§ В последнем примере в содержимом должен быть текст между элементами и . Кроме описания потомков в списке, разделенном запятыми, можно использовать оператор | для указания того, что допустим только один элемент. Например: Ц
Запись, приведенная выше, означает, что элемент может содержать один элемент или один элемент . Иногда необходимо указать, сколько раз элемент может встретиться в списке потомков родительского элемента. Для этого в DTD предусмотрены операторы меры количества, с помощью которых можно указать: •
? - элемент может появляться только один раз или не появляться вовсе.
•
* - элемент может появляться несколько раз или не появляться вовсе.
Q
Н— элемент может появляться один или несколько раз.
Например: Ц
Это означает, что может содержать один или несколько элементов , либо несколько или ни одного элементов . В системе присутствуют ограничения. Например, мы не можем указать минимальное количество потомков, отличное от нуля или одного, и мы не можем указать максимальное количество элементов. Это одно из упущений, исправляемых с помощью схем XML. Для получения дополнительной информации о DTD обратитесь к книге "Beginning XML", написанной David Hunter et at. и изданной Wrox Press (ISBN 1861003412).
Схемы XML В качестве альтернативы DTD для определения структуры файла XML мы можем применять схемы XML. Схемы XML представляют собой последнее дополнение к XML и предназначены для исправления многих из упущений, имеющихся в DTD. В частности: •
Схемы описываются в терминах XML, следовательно, нет необходимости изучать совершенно другой синтаксис. 17
Глава 1 •
• •
Схемы обеспечивают более мощные средства для создания структур, в частности с использованием типов для упрощения ситуации, когда необходима стандартная структура. Схемы достаточно гибки и мощны, и не ограничены тремя операторами DTD. Схемы позволяют типизировать данные элементов.
Спецификации схем имеются на сервере консорциума W3C (http://www.w3c.org). В настоящее время они доступны в виде предварительных рекомендаций, в марте 2001 года. Документ содержит три части. В первой части объясняется назначение схем и их отличие от DTD, во второй детально описывается объявление структурной информации XML, и в третьей обсуждаются типы данных. Спецификация схем очень сложна, и обсуждение сейчас даже ключевых положений спецификации не оправдано. Мы детально рассмотрим схемы в главах 5 и 6. Заметим только, что раздел типов данных спецификации схемы будет удобно применять при перемещении данных из базы данных в XML, поскольку он дает нам возможность ограничивать значения аналогично тому, как это делается в базе данных. Поэтому корректный документ XML может помещаться в базу данных без опасений потери или изменения значений в процессе передачи.
Пространства имен Пространства имен XML дают возможность документам использовать имена из нескольких DTD и/или схем одновременно. При этом имена классифицируются, и соответственно могут различаться идентичные имена, описанные в различных DTD или схемах. Например, рассмотрим два DTD для файла XML со списком продуктов: один с грызунами, второй с компьютерным оборудованием. В обоих этих DTD должен быть описан элемент . Ниже приведен имеющий отношение к делу фрагмент из Rodents . d t d :
А вот фрагмент ComputingEquipment. dtd: IDREF CDATA ID
#Required #Required #Required
Другой документ XML, например счет, должен иметь возможность ссылаться на оба этих DTD. Возникает вопрос: как? Пространства имен обеспечивают решение проблемы^ посредством привязки элементов к DTD и схемам. Такая привязка выполняется с помощью атрибутаxmlns, который может применяться к любому элементу. Например, для привязки элемента к R o d e n t s . d t d мы должны написать такие строки:
18
Введение в XML
Все элементы внутри этого элемента получат доступ к именам в Rodents .dtd. Следовательно, если мы хотим иметь здесь элемент , то он должен быть описан в соответствующем DTD. В общем случае, мы можем связать префиксы с пространствами имен. Код, приведенный ниже, создает два префикса имен rodent и сотр и привязывает префиксы к двум DTD:
i
Если мы хотим использовать имена из этих пространств имен, мы должны применять полное описание имени, которое включает префикс имени, двоеточие и собственно имя.
Обратите внимание, что объявления xmlns имеют ограниченную видимость: они действуют только для элемента, к которому применены, и любых элементов, содержащихся в этом элементе. Элементы, находящиеся в иерархии вне элемента, не получат доступа к пространствам имен rodent и сотр. Если пространства имен содержат глобальные атрибуты (возможно в схемах, а не в DTD), то мы можем применять полностью описанные имена для доступа к ним из любого элемента, где эти пространства имен видны. Например, у нас есть схема, содержащая глобальный атрибут c o l o r . Мы могли бы использовать такой код:
XML для данных В этой книге мы преимущественно рассматриваем XML как способ обмена данными. В то же время, многие приложения XML используются более традиционным способом: для разметки текстов. Хорошим примером этого является XHTML, который является модифицированной версией HTML, отвечающей стандарту XML. Раздел XHTML выглядит следующим образом:
My Page Welcome one and all! This is the page you've been looking for!
19
Глава 1 Важно точное расположение данных в файле. Изменение порядка элементов и текста может разрушить результат обработки файла. is the page you've been looking for! My Page This Welcome one and all!
Файлы XML используются для данных и более снисходительны. Для такого документа:
изменение порядка узлов не изменит содержащейся в нем информации, и результат обработки файла не изменится:
20
Введение в XML
Представление данных Обратите внимание, что в приведенном выше примере атрибуты используются для определения данных, то есть реальная информация в документе противопоставляется его структуре. Можно заменить структуру документа равноценной структурой, но с использованием элементов: Bob addrl 555-12 34 itemOOK/ID> car Big blue gas guzzler. 15000 CAR00042 item002 furniture Black l e a t h e r r e c l i n e r . < / D e s c r i p t i o n > 45 CHR0032K/CatalogReference>
Измененный документ содержит точно ту же информацию, что и исходный, хотя отформатирован несколько по-иному. Давайте кратко проанализируем следствия представления в форме, ориентированной на элемент, и в форме, ориентированной на атрибут. Хотя реконфигурация представленного выше документа никоим образом не изменяет хранящуюся в нем информацию, такая реконфигурация может стать причиной проблем в приложении, обращающемся к данным по их положению. Например, при запросе третьего потомка второго элемента реконфигурация может стать причиной нарушений в работе приложения. Применение атрибутов для представления данных несколько упрощает задачу, поскольку порядок следования атрибутов совершенно произволен (в XML не существует системы адресации, в которой можно запросить "третий атрибут элемента X", например). Кроме того, применение идентификаторов атрибутов облегчает выбор различных элементов. Преимущество атрибутов заключается еще и в том, что они могут быть "введены" легче, чем элементы. Это важно в тех случаях, например, когда в приложениях хранения данных требуется ручная подготовка документов. Если вы решите использовать атрибуты для указания данных, то обнаружите, что данные базы данных отображаются лучше, поскольку структура и содержимое хранятся раздельно. 21
Глава 1 Ведь при наличии нескольких структурных элементов (таких KaK из примера) и нескольких элементов хранения данных (как, например, ), разобраться в документе несколько сложнее. Кроме того, использование атрибутов уменьшит документ, поскольку потребуется меньшее количество элементов. Однако использование атрибутов может затруднить трансформацию, если она потребуется, поскольку синтаксис адресации атрибутов несколько запутан. (Трансформации мы рассмотрим в следующем разделе.) Обобщая вышесказанное, можно сказать, что не существует окончательных аргументов при выборе представления данных в виде элементов или атрибутов. Зачастую выбор той или иной формы полностью субъективен.
Использование XSLT для трансформирования XML Применение определений типов документа и схем для жесткого описания структуры документа безусловно хорошо, но часто возникают ситуации, когда деловые партнеры (или даже подразделения одного предприятия) должны представлять одинаковые данные разными способами, как мы видели ранее. В таких ситуациях не всегда желательно (или даже возможно) уйти от различий и согласовать единую структуру для всех заинтересованных сторон. Даже при достижении компромисса, все равно останутся проблемы преобразования существующих данных к другой структуре. К счастью существует технология, которая может прийти нам на помощь. В одной из спецификаций W3C описан XSL, или расширяемый язык стилей (eXstensible Stylesheet Language). Этот язык обеспечивает способ преобразования XML как из одной структуры в другую, так и к другому языку визуализации, например HTML. В XML присутствуют два различных подхода к решению проблемы трансформации. Первый из них, XSLF (XSL Formatting - форматирование XSL), предназначен исключительно для преобразования XML к объектам форматирования, которые далее могут быть преобразованы в формат визуализации, такой как HTML или PDF. Другая технология XSL, более интересная нам, называется XSLT (XSL Transformation - XSL-трансформация). XSLT может трансформировать XML в другую структуру с использованием вспомогательной таблицы стилей. Конечным результатом такой трансформации может быть XML, но может быть также чистый текст, HTML или любой другой формат по вашему желанию. Фактически XSLF использует XSLT для выполнения действий по трансформации. В этом разделе мы сконцентрируемся на трансформации XML в другой словарь XML.
Как работает XSLT XSLT работает с применением структуры объектной модели XML-документа, как было ранее описано в этой главе. Вначале исходный документ XML загружается в жходное дерево узлов, а таблица стилей XML (написанная также на XML) загружается в дерево узлов таблицы стилей. Затем они комбинируются для создания результирующего дерева узлов. Комбинация включает в себя последовательность шаблонов в таблице стилей, каждый из 22
Введение в XML которых сообщает процессору XSLT, как трансформировать узел из дерева исходных узлов в результирующее дерево узлов. Каждый шаблон в таблице стилей соответствует одному или нескольким узлам в дереве исходных узлов. Это соответствие, как мы вскоре увидим, использует синтаксис XPath (синтаксис XPath детально описывается в этой книге позднее, сейчас же мы рассмотрим только его основные положения). Обработка XSLT начинается с поиска шаблона, соответствующего корневому узлу документа исходного XML (помните, что корневой узел документа не то же самое, что элемент документа в исходном документе XML - корневой элемент документа содержит документ XML целиком, включая объявления XML и так далее). Шаблоны для корневого узла документа затем начнут запись узлов в результирующее дерево узлов. В процессе записи могут подключаться и другие шаблоны. После использования всех шаблонов создание результирующего дерева узлов завершается, и оно может быть записано на диск. Заметьте, что это не то же самое, что: "После того, как все узлы исходного дерева трансформированы...". Зачастую мы будем использовать только некоторые узлы исходного дерева, отфильтровывая нужную нам информацию.
Элементы XSLT После того, как мы получили представление о процессе трансформации, давайте рассмотрим структуру таблиц стилей XSLT. Как упоминалось выше, таблицы стилей XSLT написаны на XML. Поэтому они начинаются со стандартного объявления XML: §§ Далее мы описываем в таблице стилей элемент-документ:
Этот элемент, загружаемый из пространства именхБ1, просто указывает используемую версию XSLT, которая в настоящий момент должна быть всегда 1.0. Внутри элемента мы указываем выходной формат для результирующего дерева узлов. Для структурной трансформации XML, должен всегда указываться XML: щ
Остальная часть таблицы стилей включает элементы < x s l : template>, содержащие шаблоны, используемые для трансформации. Каждый из шаблонов должен иметь атрибут match, описывающий в синтаксисе XPath соответствие узлу (или узлам). В общем случае в первом шаблоне будет описано соответствие для элемента-документа с использованием простого выражения XPath "/". Например:
Р
23
Глава 1 В шаблоне мы можем указать литерал XML для вывода, например:
I
Приведенный выше код полностью корректен для выполнения трансформации XSLT, хотя на практике он вряд ли будет полезен. Независимо от содержимого исходного XML, в результирующем дереве появится:
I
В XSLT мы можем применять множество других элементов и конструкций для того, чтобы делать более интересные вещи. Цель этой главы - дать общий обзор возможностей, а не детальное их рассмотрение. Поэтому лучший способ - проиллюстрировать возможности XSLT на примере и обсудить его.
Пример XSLT Рассмотрим две различные структуры счета, приведенные в последнем разделе. Один из этих счетов использует для представления данных атрибуты, тогда как другой ориентирован на элементы. Вообразим, что наша компания применяет атрибуты, и нам необходимо прочитать счет, подготовленный нашим партнером. При этом счет партнера ориентирован на элементы. Один из способов решения задачи может заключаться в использовании таблицы стилей XSLT для преобразования XML в желаемый формат.
24
Введение в XML
Простейший способ испытания кода заключается в использовании процессора XT XSLT, созданного Джимом Кларком (Jim Clark) и доступного для загрузки с http://www.jclark.com/xml/xt.htmL После проверки вы сможете убедиться, что результат в точности соответствует приведенному ниже. Давайте условно разобьем пример на фрагменты и изучим, как они работают. Прежде всего, шаблон, соответствующий элементу-документу:
Большинство строк кода отсюда будут просто переписаны прямо в результирующее дерево, но в двух строках мы применили элемент < x s l : apply-templates>. Тем самым мы приказали процессору применять шаблоны к любому узлу, описанному с использованием атрибута s e l e c t . Узел выбирается с помощью XPath. Здесь мы просто называем элементы, которые хотим обработать, с помощью оператора XPath "//"• Применение этого оператора означает: "выбрать все элементы с тем же именем в полном дереве исходных узлов". Здесь можно применить и относительные пути с указанием в дереве исходных узлов исходной точки, откуда процессор XSLT начнет работу в настоящий момент. Это важная точка - во 25
Глава 1 время обработки любого шаблона XSLT работает с определенной точкой в исходном дереве узлов. Эта точка известна как контекст. Контекст, по сути, является корнем документа во время обработки шаблона корневого элемента. Поэтому, если мы используем относительную адресацию, нам необходимо ввести такие строки: Этот фрагмент кода работает аналогично фрагменту кода, приведенному выше, но требует большего объема ввода. Однако относительная адресация может быть полезна при обработке нескольких элементов с одинаковым именем по всему документу. Это могли бы быть элементы , которые являются потомками, например, - , в этом случае применение "//Item" даст возможность осуществить их выбор. Далее, в результате обработки первого элемента < x s l : apply-templates> процессор попытается найти шаблон, соответствующий узлу элемента . Далее мы можем увидеть шаблон:
Этот шаблон в результате создает элемент и присваивает ему некоторые атрибуты с использованием элементов < x s l : a t t r i b u t e d Каждый из этих элементов создает имя атрибута, используя атрибут name, и значение атрибута из его значения. Здесь мы извлекаем текстовое содержимое названных компонентов с применением элементов < x s l : value-of >, каждый из которых имеет атрибут s e l e c t , снабженный выражением XPath. Выражение XPath указывает на требуемый узел. Обратите внимание, что здесь мы можем применить относительную адресацию. Контекст в этом шаблоне является обрабатываемым узлом , поэтому запись "Name" интерпретируется как "потомок с именем в текущем контексте", что соответствует — "потомок с именем ". Процесс добавления атрибутов повторяется для каждого элемента, который мы хотим преобразовать в атрибуты. Затем мы закрываем элемент и шаблон завершен. Шаблон, соответствующий элементам , работает совершенно аналогично.
26
Введение в XML В этой главе мы только прикоснулись к теме XSLT. Если вы хотите изучить ее более детально, обратитесь к изданию XSLT Programmer's Reference (Wrox Press, ISBN 1861003129).
Итоги главы В этой главе мы рассмотрели основы XML и связанные с XML технологии. Далее в книге нам потребуются эти знания как фундамент для более подробного изучения технологий, описанных здесь. В следующей главе мы будем применять XML совместно с SQL-сервером и вкратце рассмотрим доступные методы использования XML перед детальным их описанием.
27
w
Обзор XML в SQL Server В предыдущей главе мы кратко рассмотрели XML и некоторые связанные с ним технологии. В этой главе мы начнем изучение возможностей SQL Server 2000, связанных с XML. В частности мы рассмотрим: •
Выражение FOR XML в операторах SELECT.
•
Простые запросы HTTP URL.
•
Апдейтограммы.
•
Массовая загрузка XML.
•
OPENXML.
Некоторые из этих технологий недоступны в базовой версии SQL Server 2000. Для получения доступа к ним необходимо установить XML для SQL Server 2000 Web Release 1, доступный для свободной загрузки с http://msdn.microsoft.com/downloads/. Более детально этот продукт описан в главе 9. Обратите внимание, что многие из этих технологий требуют наличия Internet Information Server 5 (IIS) или более старшей версии. Как правило, IIS входит в состав стандартных программных средств систем с SQL Server 2000, поэтому в этой главе мы будем полагать, что IIS установлен.
Выражение FOR XML Наиболее общий способ извлечения данных в XML из базы данных SQL-сервера заключается в применении выражения FOR XML в SQL-запросах. Это выражение информирует SQL-сервер о том, что результат необходимо представить в виде документа XML. При использовании этого выражения нам необходимо указать один из трех режимов его использования: •
RAW - выдает простой результирующий XML с минимальным форматированием;
•
AUTO - выдает результирующий XML и обладает большими возможностями форматирования;
•
EXPLICIT - дает возможность явно отформатировать результирующий XML.
Глава 2 В следующих разделах мы рассмотрим допустимые режимы несколько более детально. Мы продолжим изучение рассматриваемых здесь технологий в главе 3.
Режим RAW При указании режима RAW XML мы получим несколько элементов , каждый из которых обозначает строку таблицы. Эти элементы будут иметь атрибуты для каждого столбца таблицы, содержащего данные. Например, мы можем запросить базу данных Northwind SQL-сервера следующим способом: Щ SELECT CategoryName,Description FROM Categories FOR XML RAW В результате получим такой XML: Некоторые запросы в этом режиме будут выполняться некорректно. Например: §§ SELECT * FROM Categories FOR XML RAW
Даст в результате: Server: Msg 6829, Level 16, State 1, Line 1 "FOR XML EXPLICIT and RAW modes currently do not support addressing binary data as URLs in column 'Picture'. Remove the column, or use the BINARY BASE64 mode, or create the URL directly using the 'dbobject/TABLE[@PK1="V1"]/@COLUMN' syntax". Причина заключается в том, что поле P i c t u r e требует дополнительной обработки, и не связана с *-синтаксисом, который работает в значительно более простых ситуациях. Ошибочный результат выполнения запроса подсказывает нам, что для того чтобы корректно выполнить этот запрос, нам необходимо использовать режим AUTO.
Режим AUTO В результате применения режима AUTO мы получим дополнительное форматирование результирующего XML. Наиболее существенный результат заключается в том, что элементы заменяются элементами с контекстно-зависимыми именами. Например, выполняя первый запрос предыдущего раздела в режиме AUTO: Ц SELECT CategoryName,Description FROM Categories FOR XML AUTO в результате получим: 30
Обзор XML в SQL Server Здесь была создана последовательность из нескольких URL для получения доступа к двоичному содержимому столбца P i c t u r e . Позже мы детально рассмотрим эту возможность и то, как мы можем использовать возвращенные URL. В большинстве простых случаев режим AUTO обеспечивает вполне достаточные функциональные возможности, но мы можем также использовать и режим EXPLICIT.
Режим EXPLICIT Режим EXPLICIT предоставляет нам большие возможности управления форматированием возвращаемого XML, но его синтаксис более сложен. В процессе обработки происходит трансформирование строк XML в формат универсальной таблицы. С помощью этого специального формата происходит точное управление созданием элементов и порядком размещения данных (в атрибуты или элементы). Например, для повторения одного из рассмотренных нами простых запросов необходимо использовать такой запрос: SELECT 1 AS Tag, NULL AS Parent, CategoryName AS [Category!1!CategoryName], Description AS [Category!1!Description] FROM Categories FOR XML EXPLICIT
Результат будет таким:
31
Глава 2 Различие заключается в том, что здесь присутствует элемент , который был специально назван в запросе.
Параметры XMLDATA и ELEMENTS С помощью параметра XMLDATA можно просмотреть созданную схему. Применить этот параметр можно в любом из рассмотренных нами режимов. Например, мы можем использовать код: Щ SELECT C a t e g o r y N a m e , D e s c r i p t i o n FROM C a t e g o r i e s FOR XML RAW,XMLDATA
В результате получим схему: :^j Done
eg o l cal n itranet
Так решается проблема нескольких корневых элементов.
Шаблоны Если мы хотим получить более сложную иерархию документа или документ, содержащий результаты нескольких запросов, мы можем применить шаблоны для форматирования результатов. Мы увидим, что шаблоны применяются во всей книге, а глава 7 целиком посвящена им. Сейчас же мы рассмотрим их основные особенности. Шаблон представляет собой файл XML с размещенными в элементах запросами, которые берутся из пространства имен urn:schemas-microsoft-com:xml-sql. Внутри этих элементов мы можем размещать полные запросы SQL и даже вставлять результаты работы хранимых процедур. Ниже приведен пример простого шаблона с использованием запроса: SELECT * FROM Categories FOR XML AUTO
Этот шаблон предписывает SQL-серверу поместить результат запроса напрямую в структуру XML.
35
Глава 2 Мы можем выполнить этот шаблон, поместив его целиком в URL следующим образом: http://localhost/northwind?template== You',
INSERT INTO ('I
[Extra/.Stuff]) 0x8)
[Categories]
([CategoryName], VALUES
'You&Me',
[Description],
Am < You',
'"Lets
just
[Extra/.Stuff]) be f r i e n d s ' " ,
0x9)
Столбцы CategoryName и D e s c r i p t i o n содержат специальные символы XML, такие как "знак больше", "знак меньше" и "апостроф". Ниже представлен запрос FOR XML AUTO для извлечения имени столбца Extra/ . Stuff и соответствующих значений данных:
I
SELECT [CategorylD], [CategoryName], [Description], [Extra/.Stuff] FROM [Categories] WHERE [CategoryName] LIKE 'I Am%' FOR XML AUTO
Документ XML, созданный этим запросом, выглядит следующим образом: Причина, по которой эти атрибуты созданы как часть схемы XML-Data, очевидна. Директива IDREFS ( [Customers ! 1! OrderlDs ! IDREFS] ) вызывает создание атрибута name со значением OrderlDs. Директива ID ( [Orders ! 2 ! OrderlD! ID] ) вызывает создание атрибута name со значением OrderlD. Фрагмент документа XML, созданного этим запросом, приведен ниже (без схемы XML-Data): Maria Anders Alfreds Futterkiste «Drders OrderlD="ID-10643"> 1997-08-25T00:00:00 80
FOR X M L
1997-10-03T00:00:00 1997-10-13T00:00:00 Обратите внимание, что данные, связанные с ContactName, CompanyName и OrderDate, содержатся в элементах, а не в атрибутах. Это связано с применением директивы element для каждого из этих столбцов в запросе FOR XML EXPLICIT. Элемент Customers содержит атрибут OrderlDs. Значение этого атрибута: "ID-10643 ID-10692 ID-10702 Ю-10835 ID-10952 ID-11011" имеет тип IDREFS, и каждый элемент списка ссылается на атрибут типа ID. Данные атрибута были созданы в таком виде благодаря применению директивы IDREFS к столбцу OrderlD: 'ID-'+CAST(O.OrderID AS
VARCHAR(IO))
Пример ссылки атрибута OrderlD на атрибут ID может быть найден в элементе Orders: OrderID="ID-10643"
Помните, что каждый ID должен быть уникален. А что произойдет, если запрос содержит более чем один тип ID: OrderlD, CategorylD и ShipperlD? Каждый из них должен быть уникален в документе XML. Разумно выполнить некоторые преобразования для обеспечения уникальности, такие как: •
'OrID-'+CAST(OrderlD AS VARCHAR(IO))
•
'CaID-'+CAST(CatetoryID AS VARCHAR(IO))
•
'ShID-'+CAST(ShipperlD AS VARCHAR(IO))
BINARY BASE64 в запросах FOR XML EXPLICIT При включении двоичных данных в запрос, содержащий FOR XML EXPLICIT, для кодирования данных должна применяться директива BINARY BASE64. В этом разделе мы детально рассмотрим применение этой директивы. Двоичные данные применяются достаточно широко не только при выполнении запросов FOR XML. Например, инсталляционная программа пакета прикладных программ может быть поставлена в виде XML. Каждый элемейт в этом случае может представлять собой библиотеку, которая является или обязательной или дополнительной. Библиотека (в данном случае DLL) должна быть включена в виде двоичных данных (атрибутМс^и1е). Инсталляционная программа может отображать имена библиотек (атрибутЫате), функциональное назначение библиотеки (атрибут Desc), а также текст подсказки с вопросом о необходимости установки библиотеки DLL: I
Name="MerryDiskUtil.DLL" Desc="Optimizes Disk Speed" Optional="Y" Module="LMQRZAIA ... ADHrDD+"
81
Глава 3 Представьте себе каталог регионального представительства по продаже автомобилей в виде документа XML. Каталог предназначен для привлечения покупателя к покупке автомобиля: §§ Color="Red" Speed="Fast"/> Естественно, что с таким описанием автомобилей Феррари мы не продадим много автомобилей. Но если мы добавим двоичные данные, такие как фотография автомобиля, объемы наших продаж взлетят как ракета: Щ Color="Red"
Speed="Fast" Picture="FRwvAAIA ... ADHrQX+"/>
Пример трехуровневой иерархии В заключение нашего обсуждения мы рассмотрим FOR XML EXPLICIT с трехуровневой иерархией. В этом примере мы продемонстрируем гибкость запросов FOR XML вида EXPLICIT, а также покажем, что разработка таких запросов требует тщательного продумывания и осторожности. Запрос, который создает трехуровневый документ XML, выглядит следующим образом: SELECT I AS Tag, О AS Parent, Firstname AS [EmployeeTable!1!FirstName], LastName AS [EmployeeTable!1!LastName], 0 AS [EmployeeTerritoriesTable!2!TerritorylD] , NULL AS [EmployeeTerritoriesTable!2!TerritoryDesc], 0 AS [RegionTable!3!RID] , NULL AS [RegionTable!3!RDesc] FROM Employees
UNION ALL SELECT 2, 1, FirstName, LastName, ET.TerritorylD, RTRIM(TerritoryDescription), 0, NULL FROM Employees, EmployeeTerritories AS ET, Territories AS T WHERE Employees.Employeeld = ET.EmployeelD AND Et.TerritorylD = T.TerritorylD UNION ALL SELECT 3, 2, FirstName, LastName, ET.TerritoryID, NULL, R.RegionID, RTRIM(R.RegionDescription)
82
FOR XML FROM Employees, EmployeeTerritories AS ET, Territories AS T, Region AS R WHERE Employees.Employeeld = ET.EmployeelD AND Et.TerritorylD = T.TerritorylD ORDER BY [EmployeeTable!l!FirstName], [EmployeeTable!1!LastName] , [EmployeeTerritoriesTable!2!TerritorylD] , [RegionTable!3!RID] FOR XML EXPLICIT
Применение функции RTRIM в этом запросе необходимо потому, что столбцы T e r r i t o r y D e s c r i p t i o n и RegionDescription имеют тип SQL nchar, в котором представляются символьные данные фиксированной длины в кодировке Unicode Такие столбцы создают данные XML фиксированной длины, и RTRIM удаляет все пробелы в конце строки. Запрос также косвенно использует отношение между таблицами для создания трехуровневой иерархии SQL. Используемые отношения: •
Employees и EmployeeTerritories
•
EmployeeTerritories и Territories
•
Territories и Region
Фрагмент документа XML, создаваемого этим запросом: Babylon Nightblindness This Years Love Were Not Right White Ladder Say Hello Wave Goodbye
103
Глава 4 Supposed Former I n f a t u a t i o n
Junkie
Front Row Baba Thank You Are You S t i l l Mad Sympathetic Character That I Would Be Good The Couch Cant Not UR I Was Hoping One Would Not Come Unsent So Pure Joining You
|
Heart Of The House Your Congratulations
Этот пример кода очень прост. После корневого элемента следует набор элементов, которые содержат тип носителя, название звукозаписи и список композиций, которые содержит звукозапись. Приведенный выше код будет использоваться на протяжении всего раздела с небольшими изменениями. Изменения будут вноситься для того, чтобы подчеркнуть наиболее важные понятия. В рассмотренном нами примере параметр iDoc не был описан, и ему не было присвоено значение. Как указано в разделе описания синтаксиса, параметр iDoc является дескриптором внутреннего представления потока XML, который мы хотим обрабатывать с помощью OPENXML. Для того чтобы получить дескриптор, мы применяем системную хранимую процедуру с именем sp_xml_preparedocument. В следующем разделе мы обсудим эту процедуру, а также родственную ей процедуру sp xml removedocument. s p . x m l j p r e p a r e d o c u m e n t Процедура sp_xml_preparedocument выполняет подготовку XML для использования его с OPENXML. Пример применения этой процедуры показан ниже: I
EXEC sp_xml_preparedocument @hDoc OUTPUT, @DocIn, ' < r o o t xmlns:xyz="run:MyNamespace"/>'
Здесь @hDoc является дескриптором для обработки документа и @DocIn является потоком XML для обработки. Третий параметр дает нам возможность описать пространство имен. Этот параметр необязателен, и если он опущен, то применяется такое значение по умолчанию:
104
OPENXML
Здесь @hDoc является целым значением и указывает на расположенное в памяти внутреннее представление документа, описанного параметром @ Doc In. Параметр @ Doc In является текстовым представлением исходных данных XML. Обратите внимание, что параметр@hDoc является выходным (OUPUT) параметром процедуры sp_xml_preparedocument. sp_xml_preparedocument берет исходный XML, осуществляет его синтаксический разбор и создает в памяти внутреннее представление документа. Представление является древовидной структурой, определяющей узлы XML. Например, в операторе: sp_xml_preparedocument @hDoc OUTPUT, @DocIn переменная @ Doc In присвоена такому XML:
Achtung Baby Zoo Station Even Better Than The Real Thing One Till The End Of The World Whose Gonna Ride Your Wild Horses Tryin' To Throw Your Arms Around The World Heart Of The House Your Congratulations
— We now call sp_xml_preparedocument to render the source XML in a -- fashion consumable by OPENXML.
109
Глава 4 EXEC sp_XML_preparedocument @hdoc OUTPUT, @cDoc -- Now call the OPENXML function to select a list of the artists from — the source XML. We shall SELECT artist [Name] from OPENXML, using — table Artist as the template. [SELECT [Name] |FROM OPENXML (@hdoc, JITH Artist
1
/ROOT/Artist' , 1)
C sp_xml_removedocument @hDoc Строки кода без серого фона взяты из исходного документа XML. Обратите внимание на применение выражение WITH, синтаксис которого мы обсуждали ранее при изучении OPENXML. Результирующее множество приведено ниже: Name
U2 David Gray Alanis Morissette (3 row(s) affected) Просто, не так ли? Разберемся, как работает этот пример. Сперва мы загрузили исходный документ XML в переменную, которую передали процедуре sp_xml_preparedocument. Полученный в результате дескриптор применяется совместно с выражением XPath для извлечения различных узлов исходного XML. Мы можем применять существующую таблицу как шаблон для отображения между исходным XML и результирующим множеством. Таблица A r t i s t , по определению, содержит два столбца. Если мы применим SELECT * в отличие от SELECT [Name], мы получим тот же результат. Это связано с тем, что мы не можем указать столбец с уникальным сгенерированным значением в списке SELECT или операторе FROM OPENXML. Следовательно, в случае применения SELECT * столбец с уникальным сгенерированным значением A r t i s t ID игнорируется. Выражение XPath, определенное в примере как • /ROOT/Artist', предписывает OPENXML обработать циклически все узлы A r t i s t в нашем исходном документе XML. Мы также определили тип отображения равным 1, что соответствует ориентированному на атрибуты отображению. Это означает, что оператор SELECT извлекает только атрибуты имен всех элементов A r t i s t . Давайте применим другие созданные нами таблицы для получения значений атрибутов других узлов в исходном документе XML. Добавьте следующий код после Bbi6opaArtist и перед процедурой удаления документа и выполните скрипт: SELECT * 1 FROM OPENXML (@hdoc, '/ROOT/Artist/Media ,1) WITH Media SELECT * FROM OPENXML (@hdoc, '/ROOT/Artist/Media/TrackList' , 1)
110
OPENXML
WITH Album SELECT * 1 |FROM OPENXML (@hdoc, '/ROOT/Artist/Media/TrackList/Track ,1) JITH Track Результирующее множество показано ниже: Name U2 David Gray Alanis Morissette (3 row(s) affected) Type CD Album Album CD (4 row(s) affected) ArtistID
MedialD
Name
NULL NULL NULL NULL
NULL NULL NULL NULL
NULL NULL NULL NULL
NoOfTracks 12 12 10 17
(4 row(s) affected) AlbumID
Pos
Name
NULL NULL NULL
1 2 3
NULL NULL NULL
NULL NULL NULL
15 16 17
NULL NULL NULL
(51 row(s) affected) Среди результатов присутствует много значений NULL. Это связано с применением операторов SELECT * и с тем, что фактически в таблицах, перечисленных в выражении WITH, описаны столбцы, которые не соответствуют атрибутам в исходном XML. В следующих примерах мы увидим, как извлекаются значения элементов. 111
Глава 4 Итак, суммируя вышесказанное, следует отметить, что мы можем применять значение 1 для того, чтобы дать предписание OPENXML извлекать значения атрибутов из исходного XML на основании выражения XPath. В этом примере мы применяем таблицу для описания столбцов, на которые отображается исходный документ XML. Столбцы с уникальными сгенерированными значениями не могут описываться в списке выражения SELECT и не возвращаются в результирующее множество, даже если в запросе указаны все столбцы.
Отображение, ориентированное на элементы Давайте детально рассмотрим элементы Media, Name, TrackList и Track. Для отображения столбцов на результирующее множество мы будем применять два различных типа выражений WITH. Причину этого мы увидим несколько позднее. Для выполнения примера просто замените оператор SELECT из примера ориентированной на атрибуты обработки на следующий код: SELECT * FROM OPENXML (@hdoc, 1/ROOT/Artist/Media', 2) WITH Album --([Name] varchar (100)) SELECT Track 'Name1 FROM OPENXML (@hdoc, '/ROOT/Artist/Media/TrackList/Track' WITH (Track varchar (100) '.')
2)
Перед тем, как приступать к обсуждению, давайте взглянем на результаты работы этого кода: ArtistID
MedialD
Name
NoOfTracks
NULL NULL NULL NULL
NULL NULL NULL NULL
Achtung Baby All That You Cant Leave Behind White Ladder Supposed Former Infatuation Junkie
NULL NULL NULL NULL
(4 row(s) affected) Name Zoo Station Even Better Than The Real Thing One Joining You Heart Of The House Your Congratulations (51 row(s) affected) Как вы и могли ожидать, первый оператор SELECT применяет таблицу Media как шаблон для отображения на исходный документ XML. Мы устанавливаем значение флага в OPENXML равным 2, описывая, таким образом, ориентированное на элементы отображение, и остаток столбцов в результирующем множестве имеет значение NULL.
112
OPENXML
Вы можете заметить, что во втором операторе SELECT мы не применяем таблицу в качестве шаблона для отображения столбцов. Это связано с тем, что таблица Track и исходный элемент XML Track имеют не одинаковые имена столбцов. В таблицу входит столбец с именем Name, а исходный элемент XML называется Track. Поэтому мы явно описываем отображение столбца исходного XML на строку результирующего множества с применением XPath и выражения WITH: SELECT Track 'Name' FROM OPENXML (@hdoc, '/ROOT/Artist/Media/TrackList/Track' WITH (Track varchar(lOO) '.')
2)
Здесь мы с помощью выражения XPath описали путь к узлу, который мы хотим обработать. Затем в данном случае мы описываем имя столбца, тип данных и выражение XPath для узла, который мы хотим отобразить. Поскольку речь идет о текущем узле, то его XPath задается точкой.
Смешанное отображение Наш исходный XML может быть отформатирован так, что не позволит применить для извлечения данных ориентированное на элементы или атрибуты отображение результирующего множества. В некоторых случаях нам необходимо применять смешанный способ отображения. Мы можем применять оба типа отображения в операторе SELECT посредством формирования значения Flags с помощью операции логического OR для описания отображения, ориентированного и на атрибуты и на элементы. Пример приведен ниже: DECLARE ShDoc int DECLARE @cDoc varchar(8000) SET @cDoc = ' Achtung Baby Zoo Station Even Better Than The Real Thing One Till The End Of The World Whose Gonna Ride Your Wild Horses So Cruel
r.
113
Глава 4 The Fly Mysterious Ways Trying To Throw Your Arms Around The World Ultra Violet(Light My Way) Acrobat Love Is Blindness — Create an internal representation of the XML document. EXEC sp XML preparedocument @hdoc OUTPUT, @cDoc
— SELECT the appropriate fields using a table to map to — the source XML SELECT * FROM OPENXML (@hdoc, '/ROOT/Artist/Media/TrackList/Track1 , 3) WITH Track EXEC sp_XML_removedocument @hdoc
Результатом выполнения этого кода будет: AlbumID
Pos
Name
NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
1 2 3 4 5 6 7 8 9 10 11 12
Zoo Station Even Better Than The Real Thing One Till The End Of The World Whose Gonna Ride Your Wild Horses So Cruel The Fly Mysterious Ways Trying To Throw Your Arms Around The World Ultra Violet (Light My Way) Acrobat Love Is Blindness
(12 row(s) affected)
114
OPENXML
Явное описание отображения столбца К сожалению, применение отображения, ориентированного на атрибуты, на элементы или смешанного отображения с таблицами в качестве шаблонов, или явным описанием столбцов не всегда соответствует нашим требованиям. Возьмите, например, выполненный нами пример. Вообразите, что нам необходимо знать об альбоме все - исполнителя, название альбома, количество композиций, порядок следования композиций, их названия. Для решения этой задачи с помощью методов, приведенных выше, потребуется несколько операторов SELECT, которые возвратят результаты в различные результирующие множества. Более эффективный путь получения единого результирующего множества заключается в том, чтобы явно описать всю требуемую нам информацию посредством явного отображения столбцов необходимого результирующего множества на элементы и атрибуты исходного XML. Для этого мы описываем выражение XPath, идентифицирующее значение, которое необходимо извлечь. Код, приведенный ниже, показывает, как это сделать: — SELECT the appropriate fields using an explicit mapping to -- the source XML SELECT * FROM OPENXML (@hdoc, '/ROOT/Artist/Media/TrackList/Track', 1) ../../../@Name', WITH (Artist varchar (30) ../../@Type', [Media Type] varchar(10) ../@NoOfTracks', [No Of Tracks] int ../../Name', [Album Name] varchar(50) @Pos', [Track Pos] int [Track Name] varchar(100) '.')
Давайте посмотрим, как работает этот код. Выражение XPath, которое передается оператору OPENXML, указывает на самый последний узел, из которого мы хотим извлечь значение. После этого в выражении WITH мы легко можем извлекать узлы-предки. Собственно выражение WITH содержит определения для столбцов, которые мы хотим извлечь, плюс выражение XPath для значения, которое следует взять из исходного XML. Первым из столбцов является A r t i s t , который находится на верхнем уровне в повторяющейся группе исходного XML. Из этого узла нам необходимо имя исполнителя, поэтому мы выбираем атрибут @Name. Далее, мы переходим на следующий уровень, где описан тип носителя, и извлекаем значение атрибута @Туре и так далее. Столбцы могут описываться в любом порядке, а запись " . . / " в выражении XPath приказывает синтаксическому анализатору перейти на один уровень вверх от текущей позиции, т. е. к элементу-родителю. Результат выполнения этого оператора SELECT для исходного кода XML, приведенного в начале раздела "Отображение, ориентированное на атрибуты" показан ниже: Artist
Media Type
No Of Tracks Album Name
Track Pos
Track Name
U2 U2 U2 U2 U2 U2 U2
CD CD CD CD CD CD CD
12 12 12 12 12 12 12
1 2 3 4 5 6 7
Zoo Station Even Better Than The... One Till The End Of The World Whose Gonna Ride... So Cruel The Fly
Achtung Achtung Achtung Achtung Achtung Achtung Achtung
Baby Baby Baby Baby Baby Baby Baby
115
Глава 4 CD U2 U2 CD U2 CD U2 CD U2 CD David Gray Album David Gray Album David Gray Album
12 12 12 12 12 10 10 10
Achtung Baby Achtung Baby Achtung Baby Achtung Baby Achtung Baby White Ladder White Ladder White Ladder
8 9 10 11 12 1 2 3
Mysterious Ways Trying To Throw Your... Ultra Violet(Ught My Way) Acrobat Love Is Blindness Please Forgive Me Babylon My Oh My
(51 row(s) affected)
Метасвойства OPENXML Метасвойства OPENXML дают информацию об элементах DOM, содержащихся в исходном документе XML. В исходном документе XML эта информация представлена не в текстовом виде. Оператор OPENXML генерирует эту метаинформацию как часть данных в ходе обычной обработки. Мы можем применять эти метасвойства при создании результирующих множеств для принятия решения об обработке, отслеживания родства узлов, создания уникального ключа и тому подобное. Метасвойство
Описание
@mp:id
Уникальный ID узла в исходном XML. ID корневого узла равен нулю (0). ID существует до тех пор, пока документ загружен в память. Если документ перезагружается, тот же узел может не иметь то же ID, что и при первой загрузке в память
@тр:parentid
Уникальный ID родителя текущего узла. Корневой узел имеет @mp:parentid, равный NULL
@тр:localname
Имя текущего узла. Так, если узел , то @mp: localname имеет значение Customer
@тр: parentlocalname
Имя родителя текущего узла
@тр:namespaceuri
URI пространства имен текущего элемента
@тр: parentnamespaceuri
URI пространства имен родителя текущего элемента
@mp:prefix
@mp:parentprefix
116
Дает префикс URI пространства имен для метасвойства, приведенного выше. Устанавливается в NULL, если URI пространства имен отсутствует. Префикс отсутствует, если пространство имен в @mp: namespaceuri является пространством имен по умолчанию Префикс, относящийся к URI пространства имен родителя текущего узла. Для этого метасвойства действуют те же правила, что и для @mp:prefix
OPENXML
@mp:prev
Применяется для определения порядка узлов. Это свойство описывает @mp:id предыдущего элемента, находящегося на том же уровне иерархии, что и текущий узел. Если узел является первым на этом уровне иерархии, значение @mp:prev равно NULL
@mp:XMLtext
Этот узел применяется в основном для столбцов с избыточными данными. Мы рассмотрим столбцы для избыточных данных в следующем разделе
Мы не будем обсуждать упомянутые метасвойства, за исключением @mp:XMLtext, поскольку они детально описаны в SQL Server Books Online в разделе Specifying Metaproperties in OPENXML.
Столбцы для избыточных данных Метасвойство @mp:XMLtext помогает нам применять такую особенность OPENXML, как столбцы для избыточных данных (overflow columns). Столбцы для избыточных данных дают нам возможность хранить данные, которые в настоящий момент не применяются в операторе OPENXML, отсюда выражение "избыточные данные". Избыточные данные превращаются в свойство @mp: XMLtext и доступны нам для использования тем же способом, что и любой другой столбец, включенный в выражение WITH. Давайте вначале рассмотрим небольшой пример. Загрузите нашу музыкальную библиотеку, рассмотренную ранее, и выполните следующий оператор SELECT FROM OPENXML:
I
SELECT * FROM OPENXML (@hdoc, WITH ([Album Name] [No Of Tracks] [Overflowed]
'/ROOT/Artist/Media/TrackList',8) varchar(50) '../Name', int '@NoOfTracks', varchar(8000) '@mp:XMLtext')
Мы получим результирующее множество, которое содержит название альбома, количество композиций и список композиций в альбоме, как показано ниже (обратите внимание на то, что строки "завернуты" из-за малой ширины страницы): Album Name
No Of Tracks
OverflowCol
Achtung Baby 12 Zoo StationEven Better Than The Real ТЫпдLove Is Blindness -- Generate a handle to the XML document. EXEC sp_XML_preparedocument @hdoc OUTPUT, @cDoc — Update the length field of the track based on source XML values UPDATE Track SET TrackLength = о.Length FROM ( SELECT Length, AlbumName, TrackName FROM OPENXML (@hdoc, '/ROOT/Artist/Media/TrackList/Track1,1) WITH ( Length varchar(5) '@Length', AlbumName varchar(50) '../../Name1, TrackName varchar(50) '.') ) o,
Album a WHERE o.AlbumName = a . [ N a m e ] AND Track.AlbumID = a.AlbumID AND o.TrackName = Track.[Name] --Remove t h e document handle from memory EXEC sp_XML_removedocument @hdoc
Если мы сейчас просмотрим таблицу Track, то увидим, что столбец TrackLength заполнен значениями атрибута Length узлов Track исходного XML. •
A
l
b
u
m
I
D
P
o
s
N
1
3
Z
CNJ
3
3
O
3
4
T
5
3
6
3
7
3
8
3
9
3
3
1
2
1
1
r
o
w
(
s
)
a
f
o
1
A
2
L
B
f
S
e
c
t
e
d
T
h
o
s
i
t
o
t
n
e
y
t
r
t
i
4
r
T
h
a
n
T
h
e
R
e
a
l
T
h
i
n
g
v
u
c
o
i
a
i
k
f
T
a
R
h
i
e
d
W
e
o
Y
r
o
l
u
d
u
s
o
o
W
T
l
W
i
l
d
H
o
r
s
e
s
B
u
I
:
e
t
a
h
(
y
r
L
o
i
s
Y
h
t
o
M
u
r
y
A
W
r
a
m
y
s
A
r
o
u
n
d
T
h
e
W
)
o
r
l
d
l
l
n
i
D
A
n
d
n
a
M
e
s
s
o
m
e
n
Y
o
u
C
a
n
t
G
e
t
O
u
t
O
f
5
5
8
4
:
2
0
:
4
:
1
8
:
3
2
:
3
4
2
6
3
t
2
1
5
5
:
:
L
e
n
g
t
h
1
3
0
4
y
k
:
5
w
g
c
5
5
s
f
:
4
r
t
I
t
O
n
a
0
:
4
T
V
e
u
d
n
l
i
g
b
n
o
r
:
4
y
r
n
o
a
e
l
a
r
o
u
e
r
E
G
F
s
l
e
e
C
r
t
t
e
5
l
y
e
a
B
e
c
t
n
e
h
U
0
2
5
S
T
1
(
h
M
3
2
l
W
T
S
e
n
i
e
o
v
T
1
m
o
E
3
3
a
4
5
9
N
U
L
L
N
U
L
L
)
1
3
3
Глава 4 White Ladder This Years Love Sail Away Say Hello Wave Goodbye Supposed Former Infatuation Junkie Yes Front Row Baba Thank You Are You Still Mad Sympathetic Character That I Would Be Good The Couch Cant Not UR I Was Hoping One Would Not Come Unsent So Pure Joining You Heart Of The House Your Congratulations -- Generate a handle to the XML document. EXEC sp_xml_preparedocument @hdoc OUTPUT, @cDoc DELETE FROM Album WHERE AlbumID IN( SELECT AlbumID FROM Album, ( SELECT Available, AlbumName FROM OPENXML (@hdoc, '/ROOT/Artist/Media',1) WITH (Available varcharp) 'Available', AlbumName varchar(50) 'Name') ) о WHERE ltrim(rtrim(Album.[Name])) = ltrim(rtrim(o.AlbumName)) AND o.Available = 'No') --Remove the document handle from memory EXEC sp xml removedocument @hdoc
Если мы сейчас просмотрим таблицы Album и Track, то увидим, что данные для albumID, равного 2, удалены из таблицы.
136
OPENXML
Пример совместного выполнения разнородных действий Теперь, оценив в действии функции изменения базы данных, давайте разберем более сложный пример. Рассматриваемые в нем данные относятся к ветеринарной хирургии и хранятся в таблицах Owner, P e t s (которая содержит OwnerlD) и Туре (здесь содержится вид животного - кошка, собака и так далее). Прежде всего, выполните SQL, который создаст схему для этих таблиц: CREATE TABLE [dbo].[Owner] ( [OwnerlD] [int] NOT NULL , [OwnerName] [varchar] (50) NULL , [Address] [varchar] (50) NULL ) ON [PRIMARY] GO CREATE TABLE [dbo].[Pets] ( [PetID] [int] NOT NULL , [OwnerlD] [int] NOT NULL , [PetType] [varchar] (50) NULL , [PetName] [varchar] (20) NULL ) ON [PRIMARY] GO CREATE TABLE [dbo].[Type] ( [TypelD] [int] NOT NULL , [Type] [varchar] (50) NULL ) ON [PRIMARY] GO SET QUOTED IDENTIFIER OFF GO SET ANSI_NULLS ON GO CREATE TRIGGER trgjDwner ON [dbo].[Owner] FOR DELETE AS DELETE FROM Pets WHERE OwnerlD in( SELECT OwnerlD FROM DELETED) GO SET QUOTED_IDENTIFIER OFF GO SET ANSI_NULLS ON GO Попробуем разобраться, к а к п р и м е н я т ь оператор OPENXML для того, чтобы и н ф о р м а ц и я , х р а н я щ а я с я в базе д а н н ы х , соответствовала и н ф о р м а ц и и , х р а н я щ е й с я в исходном документе XML.
6—1683
137
Глава 4 Исходный документ XML выглядит следующим образом: Sam 25 Hillfoot Dr, Howwod Peggy Cat Jacquie 3 Station Court, Glengarnock Chick Cat Louis Reptile Glen 21 Corseford Ave, Wishaw Max Dog |
Наша цель - облегчить изменения в базе данных, которые будут отражать изменения в исходном XML. Для этого мы создадим три хранимые процедуры с именами sp_UpdateOwners, sp_UpdatePets и sp_UpdatePetType. Эти процедуры в качестве входного параметра будут получать исходный документ XML. Мы можем применять эти процедуры с приведенным выше документом XML для загрузки начальной информации о домашних животных и их владельцах. Процедуры будут применять OPENXML для создания результирующего множества. Процедура sp UpdateOwners будет проверять наличие записи о владельце, и если она не существует, то владелец будет добавлен. Если запись владельца уже существует, то процедура выполнит обновление. При операциях вставки и изменения процедура sp_UpdateOwners проверит наличие в базе данных того владельца животного, о котором нет сведений в XML.
138
OPENXML
Те же действия будут выполняться для животных и видов животных, хотя триггер удаления таблицы Owner будет следить за удалением информации о животных, если данных об их владельце больше нет. Исходный код этих процедур показан ниже: CREATE PROCEDURE sp_UpdateOwners AS
@cDoc
text
DECLARE @hDoc int
-- Create an internal representation of the XML document. EXEC sp_xml_preparedocument @hdoc OUTPUT, @cDoc -- INSERT the owner if they do not already exist. INSERT Owner SELECT OwnerlD, OwnerName, OwnerAddress FROM OPENXML(@hDoc, 'ROOT/OWNER1, 1) WITH (OwnerlD int '@ID', OwnerName varchar(50) 'NAME', OwnerAddress varchar(50) 'ADDRESS') WHERE OwnerlD Not In ( SELECT OwnerlD FROM Owner) —UPDATE all owner details. UPDATE Owner SET OwnerName = o.OwnerName, Address = о.OwnerAddress FROM ( SELECT OwnerlD, OwnerName, OwnerAddress FROM OPENXML(@hDoc, 'ROOT/OWNER', 1) WITH (OwnerlD int '@ID', OwnerName varchar(50) 'NAME', OwnerAddress varchar(50) 'ADDRESS') )o WHERE Owner.OwnerlD = o.OwnerID --DELETE any redundant owners. DELETE FROM Owner WHERE OwnerlD NOT IN( SELECT OwnerlD FROM OPENXML(@hDoc, 'ROOT/OWNER', 1) WITH (OwnerlD int '@ID')) GO CREATE PROCEDURE spJJpdatePets @cDoc AS
text
DECLARE @hDoc int -- Create an internal representation of the XML document. EXEC sp__xml_preparedocument @hdoc OUTPUT, @cDoc -- INSERT the owner if they do not already exist.
139
Глава 4
INSERT P e t s
SELECT PetID, OwnerlD, PetType, PetName FROM OPENXML(@hDoc, 'ROOT/OWNER/PETS/PET' WITH (PetID int '@ID', OwnerlD int '../../@ID', 1 PetType varchar(50) 'TYPE , PetName varchar(50) 'NAME') WHERE PetID Not In ( SELECT PetID FROM Pets) —UPDATE all pet details. UPDATE Pets SET PetType = o.PetType, PetName - о.PetName FROM ( SELECT PetID, OwnerlD, PetType, PetName FROM OPENXML(@hDoc, 'ROOT/OWNER/PETS/PET WITH (PetID int '@ID', OwnerlD int '../../@ID', PetType varchar(50) 'TYPE1, PetName varchar(50) 'NAME')
1)
1)
WHERE Pets.PetID = o.PetID —DELETE redundant pet records. DELETE FROM Pets WHERE PetID NOT IN( SELECT PetID FROM OPENXML(@hDoc, 'ROOT/OWNER/PETS/PET', 1) WITH (PetID int '@ID')) GO CREATE PROCEDURE sp_UpdatePetType @cDoc text AS DECLARE @hDoc int — Create an internal representation of the XML document EXEC sp_xml_preparedocument @hdoc OUTPUT, @cDoc — INSERT the owner if they do not already exist. INSERT Type SELECT DISTINCT * FROM OPENXML(@hDoc, 'ROOT/OWNER/PETS/PET/TYPE') WITH (TypelD int '@ID', Type varchar(20) '.') WHERE TypelD Not In ( SELECT TypelD FROM Type) —UPDATE all pet details. UPDATE Type SET Type = o.Type
140
OPENXML
FROM
(
SELECT TypelD, Type FROM OPENXML(@hDoc, 'ROOT/OWNER/PETS/PET/TYPE') WITH (TypelD int '@ID', Type varchar(20) '.') )o WHERE Type.TypelD = o.TypelD —DELETE redundant pet records. DELETE FROM Type WHERE TypelD NOT IN( SELECT TypelD FROM OPENXML(@hDoc, 'ROOT/OWNER/PETS/PET/TYPE') WITH (TypelD int '@ID'))
После выполнения этого SQL-кода, мы можем выполнить следующие хранимые процедуры:
E
XEC sp_UpdateOwners 'Source XML' XEC sp_UpdatePets 'Source XML 1 XEC sp_UpdatePetType 'Source XML'
Используя исходный документ XML, показанный ранее, вставьте исходные данные XML между одинарными кавычками в операторы EXEC, приведенные выше. Мы можем теперь испытать механизм обработки данных, изменив данные в исходном документе XML и снова выполнив хранимые процедуры. Пример измененных данных XML показан ниже:
Щ
Sam 25 Hillfoot Dr, Howwod The Leg Cat Jacquie 3 Station Court, Glengarnock Chick Cat Louis Reptile
141
Глава 4 «DWNER ID="4"> Sue 7 Station Court, Glengarnock Chico Cat
Выполнение приведенных процедур соответствующим образом изменит наши таблицы.
Итоги главы Оператор OPENXML представляет собой средство, с помощью которого мы можем создавать результирующее множество из исходного документа XML. Мы можем применять этот оператор везде, где можно применять другие источники результирующих множеств, такие как таблицы или представления. Компоненты исходного документа XML могут быть отображены для создания результирующего множества посредством описания типа отображения, которое мы хотим применить в операторе OPENXML (ориентированное на элементы или атрибуты отображение), или посредством явного описания отображения в выражении WITH. Мы также можем описать таблицы как шаблоны для создания столбцов результирующего множества, которые должны быть отображены, с параметрами, определяющими отображение, ориентированное на атрибуты или элементы. Наиболее гибкая возможность явное определение отображения. Мы можем описать имена столбцов, типы столбцов и выражение XPath, которое выбирает узел, который мы хотим отобразить на столбец. Если мы не определили выражение WITH, OPENXML будет заполнять таблицу компонентов, что даст нам возможность просмотреть информацию о каждом из узлов исходного документа XML. При этом используются метасвойства OPENXML, применяемые для обеспечения уникальности идентификаторов и получения информации о происхождении исходных узлов XML. Можно применять ADO 2.6 для выполнения операторов OPENXML и возврата созданных результирующих множеств в виде объектов ADO Recordset. Главное преимущество применения функции OPENXML в SQL Server 2000 состоит в возможности изменять базу данных на основе информации, содержащейся в исходном документе XML. С помощью оператора OPENXML мы можем выполнить все обычные операции с данными, а именно - вставку, изменение и удаление. Мы также можем применять SELECT INTO, объявлять курсоры и присваивать значения переменным на основании результатов, полученных из оператора OPENXML. Будем надеяться, что эта глава ввела вас в богатый мир новых функциональных возможностей, обеспечиваемых в SQL Server 2000 посредством OPENXML. Фактически, для манипулирования данными может применяться единый способ коммуникации. Такой способ коммуникации, наряду с другими средствами поддержки XML, предоставляемыми в SQL Server 2000, обеспечивает применение OPENXML. В рамках этого подхода средства поддержки XML скоро войдут в разряд обычных инструментов разработки взаимодействующих приложений. 142
I
XDR-схемы Мы знаем теперь, каким образом такие возможности SQL-запросов, как FOR XML и OPENXML можно использовать для получения данных в формате XML Такого же результата можно достичь использованием XDR-схем (XML-Data Reduced, усеченные XML-данные), при этом доступ к данным осуществляется посредством HTTP-запросов, без явной установки соединения с SQL-сервером и без посылки SQL-запросов. Вполне вероятно, что вы сочтете схемы более простым в использовании инструментом, чем SQL-запросы с использованием FOR XML EXPLICIT. В этой главе мы изучим: •
что такое XML-схемы;
•
как создаются и используются XDR-схемы в системе SQL Server 200Q
• • •
аннотации к XDR-схемам, позволяющие задать соответствие данных; типы данных XML в сравнении с SQL-типами; кэширование схем;
•
ключевые моменты, о которых необходимо помнить при работе с аннотированными XDR-схемами.
Что такое схемы? Одна из целей технологии XML состоит в том, чтобы обеспечить обмен информацией между различными организациями и приложениями. Это становится возможным лишь при условии, что формат XML-документов точно определен и согласован со всеми сторонами. И тут важную роль способны сыграть схемы. Вот простейшее определение схемы: схема это определение действительного XML-документа, согласованное со всеми заинтересованными сторонами. XML-документ может быть хорошо сформированным, но означает ли это, что он соответствует бизнес-правилам? Схемы обеспечивают XML-синтаксис, позволяющий описать допустимые для документа элементы и атрибуты. Определив согласованный набор тэгов, схема задает общий словарь (вокабулярий), который используется обеими сторонами. Например, компания в США может обозначать единицу складского хранения при помощи слова "inventory", в то время как английское предприятие
Глава 5 будет использовать для этого слово "stock". При помощи согласованной с обеими сторонами схемы можно определить общие правила и задать один термин, который будет использоваться для обозначения данного понятия. В результате обе компании будут использовать один словарь и смогут надежно обмениваться информацией при условии использования одной и той же схемы. Давайте рассмотрим это на примере. Представьте себе ситуацию, в которой разные учебные заведения используют разные способы представления средних оценок успеваемости студентов (GPAs - grade point averages) в своих записях. Например, в структуре записей одной школы это выглядит следующим образом: 12345 3.5 67890 4.0
В то время как другая школа использует следующий формат: 3.5 4.0
Если возникнет нужда в передаче данных между этими двумя школами, то им придется согласовать некий стандартный способ представления этих данных. Это и будет схема. Допустим, что школы решили принять первый вариант в качестве стандарта. Тогда это соглашение можно было бы оформить при помощи несложной схемы:
1 4 4
XDR-схемы Мы разберем код этой схемы немного позднее. Пока же давайте рассмотрим некоторые основные концепции. Прежде всего, необходимо усвоить, что схема сама по себе является XML-документом. Далее, определения допустимых элементов должны содержаться в элементе . В данном случае элементы только объявлены при помощи тэгов и . Элемент содержит в себе два вложенных элемента-потомка: и . Наконец, сам элемент содержится в элементе .
Схемы против DTD Схемы - не первый способ определения синтаксиса для XML-документов. На ранних этапах развития XML существовало только одно стандартное средство для решения этой задачи DTD-определения (определение типов документа, Document Type Definition). Язык DTD использовался в спецификации W3C XML 1.0 в качестве средства верификации структуры XML-документов. На самом деле язык DTD был изобретен еще до XML и применялся он в документах SGML (Standard Generalized Markup Language) Поскольку XML практически представляет собой упрощенное подмножество SGML, DTD-определения можно с таким же успехом использовать для задания правил верификации XML-доку ментов. Давайте рассмотрим DTD-определение, описывающее структуру записи для данных о студенте в примере, который мы рассматривали выше:
I
Student (ID, GPA)> ID (#PCDATA)> GPA (#PCDATA)>
В этом DTD определен элемент типа , причем указано (при помощи знака "+"), что он может содержать в себе один или более элементов THna. Каждый элемент типа содержит два вложенных элемента: и , причем каждый из них содержит данные типа #PCDATA (в сущности, это обычные символьные данные). Если это определение сохранить в файле с именем Students .dtd, то на него можно будет сослаться в XML-документе: Ц
12345 3.5 67890 4.0
В чем отличие такого подхода от использования схем? Прежде всего, заметим, что в DTD используется синтаксис, отличный от XML. Для разработчика это означает необходимость в знании еще одного языка. Второе: в DTD не поддерживается механизм типов данных (хотя поддержаны "атрибуты-типы", такие как перечисления). В приведенном выше DTD элемент содержит секцию #PCDATA. Следовательно, в примере с нашими студентами следующий фрагмент действителен, если верифицировать его при помощи данного DTD:
145
Глава 5 Л
12345 Hello!
Еще одно важное отличие DTD и схем состоит в том, что к XML-документу можно присоединить только одно DTD-определение, в то время как схем можно использовать несколько. Кроме того, одна схема может ссылаться на другую, что позволяет избежать дублирования описаний общих элементов или атрибутов. Благодаря всем этим преимуществам многие разработчики отдали предпочтение схемам, быстро интегрировав этот механизм в свои приложения.
XDR-схемы XDR-схема представляет собой подмножество обычной XML-схемы. Аббревиатура XDR означает "XML-Data Reduced", то есть ограниченные или усеченные XML-данные. XDR-схемы были предложены как альтернатива DTD-определений, позволяющая избавиться от их недостатков, дающая одновременно разработчикам дополнительные возможности. Вот краткое перечисление основных черт XDR-схем: •
XDR-схема является XML-доку ментом, поэтому вам не придется изучать дополнительный синтаксис для того, чтобы ее использовать.
•
XDR-схемы, в отличие от DTD, допускают существование неопределенных элементов и тэгов в XML-документе, представляя таким образом "открытую" модель содержимого.
•
Типы данных можно задавать как для элементов, так и для атрибутов.
•
XDR-схемы обладают свойством расширяемости, поскольку одна XDR-схема может ссылаться на другую.
•
Несмотря на тот факт, что в большинстве случаев XDR-схемы применяют к корневому элементу XML-документа, их можно использовать и для описания произвольных элементов документа.
XDR-схемы описывают формат, содержимое и данные в XML-документе. Например, когда XML-анализатор фирмы Microsoft (Microsoft XML Parser - MSXML) верифицирует документ при помощи XDR-схемы, на которую тот ссылается, то проверяется соответствие документа критериям, имеющимся в схеме. Если верификация обнаруживает несоответствие, то анализатор генерирует ошибку.
Разработка XDR-схем Давайте вернемся к нашему примеру со студентами. Каждому студенту соответствует ID и GPA. Как можно было бы описать этот документ при помощи XDR-схемы? Давайте рассмотрим несколько необходимых для этого понятий и определим собственную XDR-схему.
XML-объявление Как вы помните, XDR-схема является XML-документом, и поэтому первой строкой будет стандартное XML-объявление: §§ 146
XDR-схемы В данном примере единственным атрибутом будет указание версии XML. Другие атрибуты, такие как "encoding" и " s t a n d a l o n e " , определяют кодирование символов и отношение документа к внешним источникам.
Следующая строка нашей схемы определяет собственно корневой элемент. В XDR-схеме корневым элементом всегда должен быть элемент . Для того чтобы анализатор MSXML мог идентифицировать элемент, как XDR-схему, в качестве атрибута этого элемента используется ссылка на специальное пространство имен. Пространство имен для XDR-схем, это u r n : schemas-microsof t-com: xml-data. Пространство имен уникальным образом идентифицирует элемент или атрибут для анализатора. Таким образом, элемент, объявленный при помощи пространства имен Microsoft XDR, будет обрабатываться иначе, чем с пространством имен XSD (о котором речь пойдет в следующей главе). Следующий пример представляет собой типичное объявление элемента с пространством имен XDR:
В этом примере пространство имен по умолчанию для корневого элемента, а также для всех его потомков определено как пространство XDR. Обратите внимание на то, что элемент позволяет также задать имя схемы. < E l e m e n t T y p e > Элемент определяет характеристики описываемого элемента при помощи атрибутов: •
name Обязательный атрибут, задает имя элемента.
•
content Этот атрибут определяет, какие значения могут содержаться в элементе. Вот список возможных значений для этого атрибута:
Значение
Описание
mixed
Значение по умолчанию. Содержимое элемента может быть смешанным: произвольные сочетания вложенных элементов и значений
textOnly
Элемент содержит только значения (текстовые) и не содержит элементов-потомков
eltOnly
Элемент содержит только элементы-потомки, значений нет
empty
Элемент не может содержать, ни значений, ни элементов-потомков. Пустой элемент все же может обладать атрибутами
147
Глава 5
model Этот атрибут определяет модель содержимого для элемента. Здесь возможны два варианта и, соответственно, два возможных значения для этого атрибута: Значение
Описание
open
Значение по умолчанию. Элемент может содержать данные, не определенные в схеме
closed
Элементу позволено содержать только те данные, что описаны в схеме
•
order Этот атрибут задает меру количества для вложенных элементов-потомков. Возможны три варианта:
Значение
Описание
many
Значение по умолчанию. Элемент может не включать в себя ни одного элемента-потомка, включать все или часть из определенных для него потомков в произвольном порядке
seq
Элемент должен содержать в себе все элементы-потомки в точности в том порядке, в котором они для него определены
one
Элемент должен содержать в себе только один элемент-потомок
Прежде чем рассмотреть пример построения XDR-схемы, мы должны еще изучить элемент типа , который близко соотносится с . < e l e m e n t > Элемент типа ссылается на экземпляр элемента и может присутствовать внутри другого элемента . Задавая значения для следующих атрибутов, можно управлять поведением этого элемента: •
type Обязательный атрибут. Его значением должно быть имя объявленного элемента типа .
•
minOccurs При помощи этого атрибута элемент можно сделать необязательным. Существуют два возможных значения:
Значение
Описание
0
Элемент необязателен
1
Элемент должен встретиться хотя бы однажды
148
XDR-схемы
maxOccurs Этот атрибут определяет максимально допустимое число элементов. Тут также возможны два значения: Значение
Описание
1
Элемент может появиться лишь однажды
*
Элемент может встретиться в документе произвольное число раз
Теперь, разобравшись в устройстве элементов и , давайте взглянем на обновленный вариант нашей схемы:
При помощи этой схемы задаются следующие правила: •
Текст может содержаться только в элементах и .
•
Элемент должен содержать в себе только по одному экземпляру элементов и .
•
В элементе может содержаться произвольное число элементов либо ни одного.
•
Никакие дополнительные элементы или атрибуты не разрешены.
Иными словами, мы определили такой порядок, что удовлетворяющие нашей схеме документы могут описывать произвольное число студентов при условии, что для каждого указаны значения ID и GPA. Это соглашение можно теперь распространить на все организации, желающие обмениваться информацией о студентах-в согласованном формате. Но если продолжить реализацию нашего примера, то каким образом мы применим свою схему к создаваемым документам? В большинстве случаев это достигается ссылкой на схему в пространстве имен корневого элемента. Хотя, строго говоря, схему можно применить и к произвольному элементу или фрагменту. Вот пример XML-документа, исходящий из предположения, что описанная выше схема находится в том же каталоге:
I
12345 3.5
149
Глава 5
67890 4.0
В нашем случае схема сохранена в файле с именем StudentsSchemal. xdr. Этот пример относится к "элементо-центрическому" типу, то есть мы избегаем здесь использования атрибутов. Предположим, что возникла необходимость сделать не потомком элемента , а его атрибутом. Это может потребоваться по соображениям экономии пространства или для соответствия некоторой технической спецификации. Давайте реализуем использование атрибутов в документе, но вначале мы должны познакомиться с элементами XDR-схемы, служащими для определения атрибутов в XML-доку ментах. Определение атрибутов при помощи XDR-схемы очень похоже на определение элементов. В первую очередь необходимо объявить атрибут при помощи элемента, a затем описать его, задав значения для атрибутов элемента. В отличие от вложенных в элемент элементов-потомков, конкретный атрибут может либо встретиться в элементе один раз, либо не встретиться вовсе. На порядок следования атрибутов никаких ограничений не накладывается. Вот список атрибутов, которые можно использовать в элементе : •
name Обязательный атрибут, задает имя для того атрибута, который мы описываем.
•
required
Значение
Описание
yes
Атрибут обязателен и должен встретиться в элементе
по
Атрибут необязателен •
default Позволяет задать значение по умолчанию для определяемого атрибута.
Элемент , объявленный в пределах элемента , определяет атрибуты для элементов данного типа. Если элемент определен вне какого-либо элемента , то на него могут ссылаться несколько элементов . Иными словами, объявляйте элемент внутри , если соответствующий атрибут будет использоваться только в элементах данного типа. Вот пример типичного oбъявлeния: Щ
После того, как мы объявили , мы можем связать его с элементом типа .
150
XDR-схемы
Отношения между элементами < a t t r i b u t e > и походят на отношения и . Элемент < a t t r i b u t e > ссылается на объявленный ранее в области видимости этого элемента. "Поведение" элементов < a t t r i b u t e > аналогично "поведению" . Ниже приведен список атрибутов, которые можно использовать с этим элементом: •
type Обязательный атрибут. Значение этого атрибута должно указывать на объявленный ранее элемент типа . Проще говоря, здесь должно содержаться имя соответствующего элемента .
•
required Определяет обязательность/необязательность атрибута. Сам по себе этот атрибут необязателен, если соответствующее значение было задано в объявлении .
Значение
Описание
yes
Атрибут обязателен и должен встретиться в элементе
по
Атрибут необязателен •
default Позволяет задать значение по умолчанию для определяемого атрибута. Обратите внимание: заданное здесь значение будет иметь приоритет перед аналогичным значением, определенным в объявлении соответствующего .
Некоторые сочетания значений атрибутов элемента могут привести к интересному эффекту в процессе анализа документа. Например, если при объявлении элемента мы зададим атрибуту required значение "yes", а затем зададим значение по умолчанию, то получим следующий эффект: значение по умолчанию станет обязательным для использования. В сущности, это даст в результате константу, поскольку любые другие значения будут анализатором отвергаться. Теперь мы применим полученные знания к нашему примеру. Если нам требуется, чтобы элемент превратился в атрибут элемента , то в схему придется внести следующие изменения:
151
Глава 5 Вот в чем суть произведенной трансформации: • J
Элемент типа теперь должен обладать атрибутом ID. Атрибут ID нельзя использовать в элементах других типов, поскольку он объявлен внутри элемента , служащего для описания элементов .
Если в дальнейшем потребуется дополнить схему другими элементами, где нужен будет атрибут ID, мы просто объявим вне элемента , чтобы сделать его доступным для всей схемы. Если сохранить приведенную выше схему в файле с именем StudentSchema2 .xdr, то экземпляр XML-документа, содержащего сведения о студентах, будет выглядеть следующим образом:
I Ц
Ord-1 Ord-3 Ord-5
XDR-схемы Ord-2 Ord-4 custID="Cust-2"/> custID="Cust-l"/> custID="Cust-2"/> custID="Cust-l"/>
Согласно этому фрагменту, заказ с идентификатором *t>rd-4" был размещен в 11:13 утра 2 апреля 2001 года.
157
Глава 5
Методы проектирования При работе над схемами следует руководствоваться несколькими важными принципами: •
Область действия Прежде чем объявить новый элемент или атрибут, нам следует рассмотреть возможность использования уже имеющихся объявлений. Например, мы создали атрибут "ID" для элемента . Теперь, если нам потребуется аналогичный по типу идентификатор для элементов Tnna, то лучше будет объявить атрибут "ID" вне пределов какого-либо элемента - это позволит всем элементам пользоваться определением этого атрибута.
LJ
Минимизация
Для того чтобы уменьшить физический размер XML-документов, можно использовать несколько методов. Один из путей состоит в том, чтобы использовать атрибуты тогда, когда их значения находятся с соответствующим элементом в отношении "один-к-одному". В то же самое время избегать дублирования данных можно при помощи ссылок на элементы. Например, к одному клиенту могут относиться несколько заказов. Вместо того чтобы помещать сведения о клиенте в каждый заказ, мы можем использовать в нашей схеме id, i d r e f и i d r e f s для того чтобы ссылаться на сведения о клиенте, не дублируя их. •
Расширяемость Схемы могут ссылаться на другие схемы с тем, чтобы использовать некие общие определения. Предположим для примера, что в одной схеме необходимо определение элемента , который должен содержать элементы-потомки , , , и . Если эти элементы уже определены в некоей схеме, то первая схема могла бы просто сослаться на нее, не повторяя этих определений. Пусть схема с определением элемента называется Info . xdr. Вот как другая схема, расположенная в том же каталоге, может на это определение сослаться:
I
•
Документирование Разработка схем требует предусмотрительности и тщательного планирования. По завершении проекта, вам, как разработчику (а также, возможно, и другим людям) будет очень полезно иметь хорошо документированную схему, где в комментариях поясняется назначение каждого узла. Это делается при помощи тэга . Вот небольшой пример:
I
158
This is the student element!
XDR-схемы
Все, с чем мы познакомились до настоящего момента, специфично для XDR-схем, как таковых. В следующем разделе мы обратимся к вопросу о том, как XDR-схемы поддерживаются средствами SQL Server 2000.
Разработка аннотированных XDR-схем В SQL Server 2000 предусмотрены так называемые "аннотации" к языку XDR-схем, благодаря которым становится возможным получать от сервера данные в виде XML-документов точно определенного формата. Аннотации позволяют XDR-схеме поставить элементы и атрибуты документа в соответствие таблицам и столбцам базы данных. Мы можем применить здесь свое знание XDR-схем и задать структуру документа, который будет наполнен данными из базы.
Схемы с аннотациями и просто схемы Ключевое различие между обычной XDR-схемой и схемой с аннотациями (annotated) заключается в той роли, которую они играют. Схема с аннотациями формирует данные в XML-документ, в то время как простая схема верифицирует уже существующие структуры и типы. В то же время обе эти схемы можно рассматривать как своеобразный "договор" о том, как следует представлять данные. Аннотации позволяют XML-документу представлять реляционные данные. При помощи XPath (подробней об XPath см. в главе 8) для схемы с аннотациями можно специфицировать запрос.
Управление виртуальными каталогами IIS для SQL Server Для того чтобы продемонстрировать возможности схем с аннотациями, необходимо будет создать виртуальный каталог для выполнения запросов XPath. Для настройки виртуального каталога и виртуальных имен, пожалуйста, обратитесь к Приложению "А". Для работы с примерами этой главы создайте виртуальный каталог с именем NW, служащий для доступа к учебной базе данных Northwind ("Борей"). Затем создайте виртуальное имя XDR типа schema. Каталог, который ассоциируется с виртуальным именем XDR, предназначен для хранения наших схем с аннотациями. Для того чтобы использовать шаблоны (как это предусмотрено в нашем примере), создайте еще одно виртуальное имя типа template с именем Template и ассоциируйте его с тем же каталогом. При конфигурировании виртуального каталога NW установите параметры "Allow XPath" и "Allow template queries". Кроме того, при желании, вы можете отключить кэширование схем (параметр "Disable caching of mapping schemas") с тем, чтобы внесенные в схему изменения в течение примерно двух минут не вступали в силу. В процессе разработки такое ожидание затрудняет тестирование схем. 159
Глава 5
Пространство имен для аннотаций Для того чтобы создать схемы с аннотациями, нам необходимо указать соответствующее пространство имен. Нам также требуется элементарное понимание того, как функционирует механизм аннотаций, и какие аннотации доступны для использования. Чтобы аннотировать XDR-схему, мы должны специфицировать определенное пространство имен: "urn : schemas-microsof t-com: xml-sql". Так же, как и в случае пространства имен типов данных, это обычно делают в пределах элемента. Вот пример объявления пространства имен для аннотаций:
I
В нашем примере префикс пространства имен s q l используется для того, чтобы отличать аннотации в этом пространстве имен от аннотаций в других пространствах.
Список аннотаций Уже благодаря простому объявлению пространства имен для аннотаций становится возможной постановка элементов и атрибутов в соответствие (mapping) таблицам и представлениям. Далее приведен список аннотаций, позволяющих управлять этим соответствием: Аннотация
Описание
sql:relationship
Определяет вид отношения между XML-элементами. Действие этой аннотации аналогично действию оператора JOIN в языке T-SQL. Используйте следующие атрибуты для установки отношения: key, k e y - r e l a t i o n , foreign-key и foreign-relation
sql:field
Соответствие между XML-элементом или атрибутом и полем базы данных. Имя элемента или атрибута может при этом отличаться от имени соответствующего поля. Эту аннотацию используют также в случаях, когда имя столбца содержит пробелы или символы, недопустимые в XML-схеме
sql:id-prefix
Сцепляет префикс с полем базы данных для того, чтобы получилось действительное значение i d , i d r e f или idref s. Использование этой аннотации предотвращает возникновение конфликтов в XML-документе
sql:is-constant
Используется в случаях, когда значение должно фигурировать в XML-документе, притом, что оно не связано со столбцом или таблицей. Включение этой аннотации в элемент и установка ее значения в " 1 " (True) приведет к тому, что в результате запроса будет сгенерировано значение-константа
160
XDR-схемы
Аннотация
Описание
sql:key-fields
Задает поле (или поля), которые уникальным образом идентифицируют строки таблицы. Это гарантирует правильный порядок вложенности элементов в результирующем XML-документе
sql:limit-field
Эта аннотация используется с тем, чтобы указать столбец в таблице, используемый в качестве фильтра. Похожая по своим функциям на предложение WHERE в T-SQL-выражениях, она представляет имя столбца в выражении сравнивания. Совместно с аннотацией s q l : l i m i t - v a l u e это обеспечивает фильтрацию результирующих значений
sql:limit-value
Используется совместно с s q l : l i m i t - f i e l d для фильтрации результирующих значений
sql:map-field
Позволяет исключать элементы схемы из результата. Для того чтобы исключить узел из результирующих XML-данных, добавьте эту аннотацию и установите ее значение в " 1 " (True)
sql:overflow-field
Используется для указания столбца, предназначенного для хранения данных в случае переполнения
sql:relation
Установка соответствия между XML-элементом или атрибутом, и таблицей базы данных. Имя элемента или атрибута может при этом отличаться от имени соответствующей таблицы. Эту аннотацию используют также в случаях, когда имя таблицы содержит пробелы или символы, недопустимые в XML-схеме
sql:targetnamespace
Используется для того, чтобы поместить результаты выполнения запроса в пространство имен, отличное от используемого по умолчанию
sql:url-encode
При помощи этой аннотации указывают URI для ссылки на BLOB-данные
sql:use-cdata
Позволяет специфицировать разделы СБАТАдля использования в определенных элементах XML-документа
Все эти аннотации будут более подробно рассмотрены в оставшейся части главы.
Соответствие по умолчанию После того как в XDR-схеме объявлено пространство имен для аннотаций, установка соответствия по умолчанию начинает действовать автоматически. Это означает, что при совпадении имени элемента с именем таблицы или представления, а имени атрибута с именем поля они автоматически ставятся в соответствие друг другу. Вот пример XDR-схемы с использованием соответствия по умолчанию:
Сохраните эту схему в файле с именем CategoriesSchemal .xdr в каталоге, ассоциированном с виртуальным именем XDR. Откройте окно Internet Explorer и введите следующий URL (имя "localhost" можно заменить именем IIS-сервера): http://localhost/NW/XDR/CategoriesSchema1.xdr/Categories?root=root Результат должен выглядеть следующим образом: Hhttp://iocdhost/NW/XDR/Cate M a n j i m u p Dried Apples
173
Глава 5 Мы познакомимся с другими примерами использованиямql: r e l a t i o n s h i p > в оставшейся части главы.
Фильтрация данных До сих пор единственная фильтрация данных, с которой мы встретились, была осуществлена при помощи XPath-запроса. Возвращаемый набор реляционных данных можно ограничить также при помощи двух аннотаций: s q l : l i m i t - f i e l d и s q l : l i m i t - v a l u e . Эти атрибуты определены для элементов и < a t t r i b u t e > , где имеется аннотация «Drder OrderlD=" 10249" CustomerlD="TOMSP" EmployeelD="6"> Tofu «Drder Order!D=" 10250" CustomerlD="HANAR" EmployeelD="4" /> «Drder OrderlD=" 10251" CustomerlD="VICTE" EmployeelD="3" /> Tofu
Управление структурой результирующих XML-данных Аннотация s q l : k e y - f i e l d s задает столбец или столбцы, которые уникально идентифицируют строки в отношении, определенном при помощи элемента. Если для идентификации используется более одного столбца, то имена столбцов должны быть разделены пробелами. Эту аннотацию можно использовать либо в элементе , либо в . Применение s q l : k e y - f i e l d s обеспечивает правильный порядок вложенности в результирующем документе. Вот пример, поясняющий эту мысль:
В этой схеме использовано соответствие по умолчанию, таким образом, элемент будет соответствовать таблице Employees. Элементом-потомком для является , в котором содержится атрибут EmployeelD. Сохраните эту схему под именем EmployeesSchema3 .xdr в рабочем каталоге и выполните XPath-запрос при помощи следующего URL: http://localhost/NW/XDR/EmployeesSchema3.xdr/Employees?root=root
175
Глава 5 Вот результирующие данные: Seattle Tacoma Kirkland Redmond London London London Seattle London Полученная в ответ информация сама по себе верна, однако вложенность элементов ошибочна. Простое изменение схемы устраняет эту проблему. Добавьте аннотацию s q l : k e y - f i e l d s в определение для следующим образом:
I
Сохраните внесенные изменения и повторите запрос. Результат будет выглядеть следующим образом: Seattle Tacoma Kirkland Redmond 176
XDR-схемы London London London Seattle London Отчего такая разница? Все дело в том, что спецификация столбцаEmployeelD в качестве ключевого принудительно делает вложенными элементы (в которых, напомним, содержится атрибут EmployeelD). Это дает нам контроль над порядком вложенности элементов. Давайте рассмотрим теперь другой пример. Эта схема определяет сложное отношение, в котором продукты группируются по категориям, а для каждого продукта идентифицируется его поставщик:
177
Глава 5
Сохраните эту схему под именем CPSJoinSchemal .xdr в рабочем каталоге и выполните XPath-запрос при помощи URL: http://localhost/NW/XDR/CPSJoinSchema1.xdr/Category?root=root Вот фрагменты р е з у л ь т и р у ю щ и х д а н н ы х : Exotic Liquids Exotic Liquids
Полученные данные, как очевидно, нарушают порядок вложенности. Поставщик Exotic Liquids должен фигурировать в качестве элемента-потомка для Chai и для Chang, а не дважды для Chang. Дело, видимо, в том, что Exotic Liquids поставляет два продукта категории Beverages. Чтобы добиться правильной вложенности элементов, добавьте аннотацию s q l : k e y - f i e l d s : Ш Сохраните изменения и повторно выполните запрос. Вот как выглядят результирующие данные: Exotic Liquids Exotic Liquids Теперь для каждого продукта поставщик идентифицируется правильно. Для того чтобы задать надлежащий порядок вложенности элементов в результирующем документе, рекомендуется во всех схемах специфицировать аннотацию s q l : k e y - f i e l d s .
178
XDR-схемы Элементы, которым нет соответствия в базе данных Аннотированные XDR-схемы называют "схемами соответствия (mapping schemas)" неслучайно - все элементы и атрибуты в такой схеме соответствуют либо таблице, либо представлению, либо столбцу. Но что, если нам потребуется использовать в результирующем XML-документе элемент, которому нет никакого соответствия в базе данных? Тут нам и пригодится аннотация s q l : i s - c o n s t a n t . Эту аннотацию можно использовать только в элементе . Действительными значениями для этого атрибута являются либо " 1 " (True), либо "О" (False). В следующей схеме показано типичное использование аннотации s q l : i s - c o n s t a n t : ii
Здесь выражение s q l : i s - c o n s t a n t = " l " представляет собой только флаг, обозначающий элемент-константу. Сохраните схему под именем EmployeesSchema4 . xdr в рабочем каталоге. Выполните следующий XPath-запрос при помощи URL: http://localhost/NW/XDR/EmployeesSchema4.xdr/Employees Замечаете какое-нибудь отличие от предыдущего запроса? Здесь нет необходимости включать в строку запроса указание корневого элемента "?root=root". Почему? Потому что элемент не имеет соответствия в базе данных, и по этой причине в результирующем документе он встретится лишь однажды. Однако элемент соответствие в базе данных уже имеет и будет многократно встречаться внутри элемента , как видно из результирующего документа: Categories
Обратите внимание на две новых аннотации, специфицированных в элементе: аннотация s q l : id обеспечивает уникальное имя для схемы, на которое ссылается атрибут mapping-schema элемента .
Кэширование схем Ранее в этой главе мы советовали отключить кэширование схем на период отладки и тестирования по той причине, что внесенные в схему изменения при включенном кэшировании будут вступать в силу с задержкой около двух минут. Осталось объяснить, почему это так. В первый раз, когда XPath-запрос выполняется с использованием аннотированной XDR-схемы, схема сохраняется в памяти, и там же строятся необходимые структуры данных. Если кэширование разрешено, то схема остается в памяти, улучшая тем самым производительность при обработке последовательности запросов. При внесении в схему
190
XDR-схемы изменений требуется около двух минут на то, чтобы сохраненная в памяти схема была заменена новым вариантом. Для того чтобы включить или отключить кэширование схемы, используйте флажок Disable caching of mapping schemas при настройке параметров виртуального каталога. Вы можете задать размер кэша, добавив в системный реестр следующий ключ: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\Client\SQLXMLX\SchemaCacheSize. Значение по умолчанию для SchemaCacheSize равно 31. Рекомендуется задавать значение большее, чем число используемых схем. Для увеличения производительности полезно увеличить размер кэша. И наоборот, для экономии памяти его следует уменьшить.
Итоги главы Аннотированные XDR-схемы представляют собой отличный механизм для получения данных от SQL Server 2000 в формате XML. Давайте вспомним основные темы, освещенные в этой главе: •
XDR-схемы являются хорошей альтернативой другим технологиям. В отличие от DTD-определений, XDR-схемы поддерживают типизацию данных и сами по себе основываются на синтаксисе XML.
•
Схему с аннотациями можно использовать не только для верификации, но также и для генерации XML-доку мента, который отражает состояние объектов базы данных в соответствии с XPath-запросом.
•
Аннотации предоставляют разработчику средство контроля над установкой соответствия данных, структурой, отношениями и типами данных.
Вы, вероятно, согласитесь с тем, что аннотированные XDR-схемы не слишком трудны для изучения, и что использование их на практике не таит в себе никаких сложностей. Лишь небольшие затраты времени требуются для того, чтобы разработать схему с аннотациями и получить XML-данные в том формате, который вам нужен. Захватывающие перспективы, не правда ли? Единственное, что неизменно в нашей отрасли - это перемены. И стандарты XMLHe стали тут исключением. Следующая глава будет посвящена новым, недавно появившимся элементам этого языка: XSD-схемам.
191
XSD-схемы При помощи аббревиатуры XSD обозначают стандарт "XML Schema Definition", предложенный консорциумом W3C. В предыдущей главе мы увидели, как можно получить данные в формате XML при помощи XDR-схем. Корпорация Microsoft создала XDR-схемы с тем, чтобы иметь возможность немедленно приступить к использованию XML-схем, несмотря даже на то, что работа консорциума W3C над стандартом XSD была еще далека от завершения. Как таковой, стандарт XDR базируется на "мгновенном снимке" состояния работ над XSD на март 1999 года, и в полной мере является продуктом корпорации Microsoft. С той поры стандарт XSD прошел большой путь, и теперь он обеспечивает значительно больше возможностей, чем XDR. По этой причине синтаксис XSD достаточно важен, чтобы его знать, несмотря на то, что он в настоящее время в SQL Server не поддерживается. В этой главе мы рассмотрим следующие темы: •
что представляют собой XSD-схемы;
•
сегодняшнее состояние XSD;
•
сравнение возможностей схем XSD и XDR;
•
как разрабатывать и применять XSD-схемы;
•
как верифицировать XSD-схему;
•
SQL Server и XSD-схемы;
•
некоторые средства для трансформации XDR в XSD.
Что такое XSD-схемы? Нужда в новых стандартах для определения XML-документов появилась вследствие ограничений, которыми страдают DTD-определения (Document Type Definition - DTD). В марте 2001 года стандарты ("рекомендация") W3C "XML Schema Definition" (XSD) из "кандидатской" стадии ("Candidate Recommendation Phase") перешли в стадию "предложения" (Proposed Recommendation Phase). Как только директор W3C одобрит эти стандарты, они получат статус "полной рекомендации". До тех пор стандарты XSD могут
Глава 6 еще подвергаться ревизиям и пересмотрам. Таким образом, информация, которую вы найдете в этой главе, неокончательна. Тем не менее, согласно утверждениям участников разработки стандарта, последний шаг в принятии стандарта будет играть чисто административную роль и настоящее состояние Рекомендации достаточно близко к окончательному. Пожалуйста, посетите сайт W3C по адресу http://www.w3.org для того, чтобы получить актуальную и достоверную информацию о состоянии стандарта XSD. Для лучшего ознакомления с XSD рекомендуем изучить документацию на сайте http://www. w3. org/TR/xmlschema-O.
Сравнение возможностей схем XSD и XDR В предыдущей главе мы узнали, что схемы используют для определения элементов и атрибутов синтаксис XML. Мы увидели примеры создания схем на основе синтаксиса XDR schema. Но в чем же разница между XDR и XSD? Как мы уже говорили, XDR представляет собой версию Microsoft, основанную на состоянии версии W3C на март 1999 года. XSD обеспечивает значительно более широкие функциональные возможности и является полностью независимым от производителей стандартом. Давайте на простом примере сравним XSD и XDR. Не беспокойтесь пока о том, что детали синтаксиса XSD будут вам непонятны. Взгляните на XML-документ из предыдущей главы: 12345 3.5 67890 4.0
Предположим, что приведенные выше XML-данные представляют собой действительный XML-документ. XDR-схема для определения такого документа выглядела бы следующим образом:
194
XSD-схемы
Теперь сравните это с аналогичной схемой, использующей синтаксис XSD: Как легко увидеть при беглом изучении этих двух примеров, между синтаксисами XDR и XSD имеются серьезные различия. Синтаксис XSD мы подробно рассмотрим далее в этой главе.
Главные черты XSD-схем XSD-схемы обеспечивают все функциональные возможности XDR-схем, DTD-определений и многое сверх того. Вот краткий обзор важнейших черт XSD-схем: •
Подобно XDR-схемам, схемы XSD являются XML-документами, поэтому вам не придется осваивать другой синтаксис, чтобы их использовать (в отличие от DTD-определений).
•
Так же, как и в XDR-схемах, типы данных в XSD-схемах можно задавать и для элементов, и для атрибутов.
•
XSD-схемы позволяют определять новые типы данных вдобавок к более чем четырем десяткам встроенных типов.
•
XSD-схемы позволяют определять уникальные ключи для элементов данных.
•
XSD-схемы поддерживают объектно-ориентированный стиль наследования, когда одна схема может наследовать свойства другой. Это огромное преимущество, позволяющее многократно использовать схемы.
•
XSD-схемы позволяют определять элементы, способные заменять друг друга.
•
XSD-схемы позволяют определять элементы с содержимым типа Null.
Подобно XDR, XSD-схемы определяют формат, содержимое и данные в XML-доку менте. Если документ ссылается на схему, то при верификации его анализатором, который поддерживает XSD, документ будет проверен на соответствие схеме. При обнаружении несоответствия анализатор сгенерирует ошибку.
195
Глава 6
Разработка XSD-схем Давайте изучим в деталях всю XSD-схему, созданную ранее, чтобы понять, как она работает.
Объявление XML Поскольку XSD-схема является XML-документом, в первой ее строке мы видим стандартное объявление XML: | В данном примере единственным атрибутом является номер версии XML. Другие атрибуты, такие, как "encoding", можно указать при необходимости. < s c h e m a > В следующей строке нашей XSD-схемы начинается собственно корневой элемент. Для XSD-схем таким элементом всегда должен быть элемент . Чтобы идентифицировать корневой элемент для XML-анализатора, в него необходимо поместить ссылку на пространство имен:
I
Пространство имен, на которое ссылается это объявление - это рекомендация W3C от 2001 года (Proposed Recommendation version of XSD). В более ранних примерах можно встретить ссылки на рекомендацию-"кандидат" (Candidate Recommendation version of XSD) от 2000 года: Заметим, что префикс xsd в этих примерах использован для обозначения типа схемы, однако на самом деле здесь можно использовать произвольный префикс. Он должен только соответствовать тому префиксу, который использован в объявлении пространства имен, как показано ниже: Назначение этих префиксов состоит в том, чтобы показать, что содержимое схемы относится к словарю (вокабулярию) назначенного пространства имен (XSD) и отличить ее тем самым от схем других типов. Стоит заметить, что это пространство имен можно сделать пространством по умолчанию, и тогда не будет необходимости в использовании этих префиксов вообще. Однако следует сознавать, что использование пространства имен по умолчанию может привести к проблемам, например, при импорте схем. Даже в примерах консорциума W3C пpeфикcxsd указывается явно, без задания пространства имен по умолчанию.
196
XSD-схемы
После того как тип схемы обозначен, мы можем перейти к сердцу схемы - к ее элементам. Мы многое узнали об элементах в предыдущей главе, посвященной XDR. Поэтому здесь мы сразу перейдем к синтаксису элементов XSD. Вот список атрибутов, значения которых управляют "поведением" элемента: •
name Обязательный атрибут. Указывает имя элемента.
•
type Обозначает простой тип (например, xsd: s t r i n g ) или имя сложного типа. Атрибут type можно использовать в объявлении простого типа (если не наложены ограничения), но в случае сложного составного типа дело обстоит иначе, как будет показано ниже.
•
minOccurs Этот атрибут определяет обязательность появления элемента. Сам по себе он необязателен. По умолчанию его значение равно 1. Таблица, приведенная ниже, описывает возможные значения:
Значение
Описание
0
Элемент необязателен
Целое > 0
Элемент должен встретиться в документе, по меньшей мере, указанное число раз
maxOccurs Этот атрибут задает максимальное число элементов данного типа в документе. Этот атрибут необязателен, по умолчанию его значение равно 1. Таблица, приведенная ниже, описывает возможные значения: Значение
Описание
Целое > 0
Элемент может встретиться в документе не более указанного числа раз
Unbounded
Элемент может встречаться в документе неограниченное число раз
Вооружившись этим знанием об , давайте внимательно рассмотрим наш пример:
197
Глава 6
Рассматривая эту схему, мы заметим следующие правила, которые из нее вытекают: •
В элементах и содержатся строковые значения.
•
Элемент должен содержать в себе только один набор элементов и (и для , и для предусмотрены значения minOccurs и maxOccurs, равные 1). Заметим, что явное указание этих значений не было необходимым, поскольку в обоих случаях значение по умолчанию равно 1.
•
Число элементов может быть произвольным: ни одного или неограниченное количество (атрибуту minOccurs присвоено значение 0, a maxOccurs - unbounded).
•
Благодаря объявлению элементы и должны встречаться в документе именно в той последовательности, в которой они определены в схеме.
Иными словами, мы определили, что удовлетворяющий этой схеме документ может содержать сведения о произвольном числе студентов при том условии, что значения ID и GPA указаны для каждого студента. Этот своеобразный "договор" о представлении данных позволит теперь обмениваться информацией о студентах в согласованном формате многим организациям. Давайте более внимательно изучим синтаксис объявлений всех этих элементов.
против В предыдущей главе мы видели, как в схемах XDR элемент используется для описания характеристик элемента в отношении содержимого (может ли элемент содержать в себе другие элементы и т. д.). Для этого просто менялось значение атрибутов (например, "textOnly", "eltOnly" и др.). В схемах XSD используется противоположный подход. Здесь вы явным образом объявляете элемент, как принадлежащий к типусотр1ех type (сложный ТИП), ИЛИ же к simple type (простой тип). В каких случаях, и какой из этих вариантов выбрать? Элементы используются в случаях: •
когда ваш элемент будет содержать в себе элементы-потомки, и/или;
•
когда ваш элемент будет обладать атрибутами.
Элементы используются в случаях: •
когда вы хотите создать новый тип данных на основе встроенного простого типа, и/или;
Q
когда ваш элемент не будет содержать в себе элементы-потомки или атрибуты.
Чтобы проиллюстрировать разницу между этими двумя видами элементов, давайте вернемся к нашим примерам. В примере со студентами каждый студент обладал ID и GPA. Следовательно, если студент - это элемент, то он должен обладать вложенными элементами-потомками. По этой причине мы должны объявить элемент принадлежащим к сложному типу, так, как показано ниже: 198
XSD-схемы
I
O - «DrderDetail ProductID="Product-42" UnitPrice="9.8" Quantity="10"> O - 0 -
^ - n r r i o r n o t - a i l D r n r l i i r r t n = " P r n r l n r + - 1 4 - " I l n i t P r i r o = " 1 R fi" П | i a n t i t w = " Q ' 4
; ^ ] Opening page
http:/ii.jcalhost/NW/XDRitry_)(pabh,xdr/ordetDetaii?root=root...
Ось "self" Одиночная точка представляет собой ссылку на текущий узел, то есть "на себя" (несокращенная форма: " s e l f : : " ) . Эта ось редко используется в XPath-запросах к SQL Server.
253
Глава 8 Ось атрибута "attribute" Ось "attribute" специфицируется символом "@" (полная форма: " a t t r i b u t e : : " ) . Следующий запрос: Ц Customer/@ContactName выберет атрибуты ContactName из элементов Customer в образцовой схеме. Если мы попытаемся открыть такой запрос при помощи браузера, то получим сообщение об ошибке:
Fje £dit $ew Favorites look Цв1р . • •; ^Bacl • J J ^ ^Search ^Favoiites jHslory -_ji Address j#] htp://o l cah l ost/MW/XDR/try_xpath.xdr/Cus:lomer/@ContactName?root=root
_J
ERROR: 500 Internal Server Error HResult: 0x80004005 Source: Microsoft X M L Extensions to S Q L Server Description: XPath: only direct object access may select an attribute. Either change the XPath to select an element, or else use direct object access :*O Done
;ыЁ Local intranet
Что ж, попытаемся последовать совету, который заключается в этом сообщении, и укажем атрибуты в прямом запросе к объектам базы данных: http://localhost/NW/dbobject/Customers/@ContactName?root=root Теперь мы получим какой-то результат, но полезным он не выглядит:
"3 Maria AndersAna TrujilloAntonio MorenoThomas HardyChristina BerglundHanna MoosFrederique CiteauxMartin SommerLaurence LebihanElizabeth LincolnVictoria AshworthPatricio SimpsonFrancisco ChangYang WangPedro AfonsoElizabeth BrownSven OttliebJanine LabruneAnn DevonRoland MendelAria CruzDiego RoelMartine RanceMaria LarssonPeter FrankenCarine SchmittPaolo AccortiLino RodriguezEduardo SaavedraJose Pedro FreyreAndre FonsecaHoward SnyderManuel PereiraMario PontesCarlos HernandezYoshi LatimerPatricia McKennaHelen BennettPhilip CramerDaniel ToniniAnnette RouletYoshi TannamuriJohn SteelRenate MessnerJaime YorresGarlos
Прямая ссылка на атрибуты бывает необходимой лишь в одном случае. Прямая ссылка позволяет адресовать двоичные данные, например, графические. Далее в этой главе мы к этому еще вернемся. Во всех остальных случаях, для того чтобы осуществлять доступ к атрибутам, необходимо использовать предикаты.
Предикаты Пути местоположения сами по себе обладают небогатыми возможностями. Для расширения этих возможностей необходимо задавать в запросе какие-то условия поиска. Эти условия в терминологии XPath именуются предикатами. Если вы знакомы с SQL, то можете рассматривать предикаты как аналог раздела WHERE в SQL-запросе - результирующие 254
XPath
данные отбираются по соответствию некоторым критериям. Предикаты в XPath-запросе можно задать для каждого элемента, входящего в путь поиска, при этом условное выражение заключается в квадратные скобки. Например, для того чтобы среди всех элементов Orders выбрать те, у которых значение атрибута Quantity превышает 10, используем следующий запрос: Ц
Customer/Order/OrderDetail[@Quantity>10]
Kpath^dr/Custon^ Edit Jfiew Favorites l o o k jHistory ^ , J k ' J j ^ ^ | Address WSJ http://bcalhosUNW/>EiR/try_xpath.xdr/Custonier/Order/OrderDetaH@iQiiantity>10]?root=root
>
>
l
n
=
c
<
o
/
d
'
u
D
i
c
t
I
D
s
c
o
u
=
n
"
t
P
r
o
d
Q
"
2
"
>
>
>
Щ Local intranet
Допустимы также и последовательные предикаты, то есть ряд предикатов, примененных к одному и тому же элементу запроса. Например, следующий запрос вернет элементы, отобранные по значениям сразу двух атрибутов: Ц OrderDetail[@Quantity>10][@UnitPrice
< 5]
Это выражение эквивалентно конструкции с использованием логического AND, поскольку оба предиката должны вернуть значение TRUE для того, чтобы выраженное ими условие считалось выполненным. 256
XPath
Предикаты пути Предикаты могут включать в себя путь к элементу, при этом предикат возвращает значение TRUE или FALSE в зависимости от того, существует указанный путем элемент, или нет. Например, следующий запрос вернет записи всех заказчиков, обладающих заказами: Ш Customer[Order] Путь, указанный внутри предиката, может возвращать множество элементов. Например, немного ранее мы выполняли запрос, возвращающий элементыOrderDetails с обусловленным значением атрибута Quantity. Допустим, нам требуется увидеть при этом заказы (элементы Order), связанные с полученными O r d e r D e t a i l s . Достичь такого результата можно, указав путь внутри предиката: I
Customer/Order[OrderDetail/@Quantity>10]
Fie gdit Ve i w Favorites Tools hjelp •^Back - " - ^ i 3 ^Search jy Favorites i^HKtory -_J-
_J iAddre5stejhttp://localhost/NW/XDR/try_xpath.xdr/Custorner/Order[OrderDetail/@)Qijantit:y>10]?root=root - - - 0.25 - 0.25 - O.25 *Ц Opening page http://iocalhost/M»V/XDR/try_xpath,xdr/Customer/Order[OrderC'ei;al/fbQuantity>10]?root=fool:.
Может возникнуть вопрос - почему мы тут видим элемент OrderDetail со значением Quantity, равным 2? Это не ошибка: SQL Server вернул в точности то, что было запрошено. Были отобраны все элементы Order, в которых имелся, по меньшей мере, один элемент OrderDetail со значением атрибута Quantity, большим 10.
Вложенные предикаты Еще один способ реализации предыдущего запроса заключается в использовании вложенного предиката: •
Customer/Order[OrderDetail[@Quantity>10]]
•
2
5
7
Глава 8
|lhtЯ pе://o lc lti osVte /iN .Kec £a dh wW/FXaD voRrte i/stry_>lo< op lsathH plrl/C.:.u»to't»«ir/ 4-"Bdck Jf _£] 4} ^Search ^Favorites ^History | Rj» ! Address ^]htp:/o l cah l ost/NW/XDR/try_xpath.xdr/Customer/Order[OrderDeta[@ li Quantyi>10]?root=root - - 0.25 - O.25 + - • •• «Drder 0rderID="Order-10405" OiderDate="1997-01-06T00:00:00"> - O - . Блок идентифицирует единственную запись, подлежащую обновлению, в то время как блок содержит новые данные для этой записи. Так же, как и в операции удаления, должна найтись ровно одна запись, удовлетворяющая условиям. Варианты "несколько записей" или "ни одной записи" приведут к генерации ошибки. Итак, чему же мы научились? Надеемся, эти три простых примера дали вам общее представление об апдейтограммах, о том, как функционирует данный механизм. Давайте продолжим обучение еще несколькими примерами.
Еще несколько примеров В этом разделе мы рассмотрим еще несколько примеров, которые помогут вам лучше разобраться в механизме апдеитограмм и подготовят вас к следующему разделу. Вы можете оставить открытым окно Query Analyzer с тем, чтобы при помощи запроса SELECT * FROM Shippers наблюдать происходящие в результате работы апдеитограмм изменения.
Соответствие по умолчанию Ранее мы убедились, что при модификации таблицы имя элемента задает таблицу, а имена атрибутов — столбцы в таблице. Это называют основанным на атрибутах соответствием по умолчанию. При этом каждый элемент в блоках и соответствует некоторой таблице, а его атрибуты - столбцам в таблице.
Соответствие по умолчанию, основанное на элементах Вместо того чтобы использовать атрибуты, вы для указания столбцов можете определить вложенные элементы-потомки. Такой подход называют соответствием по умолчанию, основанным на элементах. Вот простой пример:
275
Глава 9
I
Sky Packages (503) 555-1234
Введите этот код и сохраните его в файле i n s e r t 2 .xml в каталоге С: \TestUpdg\template. Выполните затем апдейтограмму при помощи URL: http://localhost/TestUpdg/template/insert2.xml Если все сделано правильно, то вы увидите, что апдейтограмма выполнила ту же операцию, что i n s e r t l .xml, то есть, иными словами, вставила запись в таблицу Shippers. При этом любопытно отметить, что здесь CompanyName и Phone являются вложенными элементами, а не атрибутами.
Соответствие по умолчанию:
атрибуты и элементы
Можно создать такую апдейтограмму, где соответствие по умолчанию будет основываться на атрибутах и элементах одновременно. Взгляните на следующий пример:
I
(503) 555-9988
Сохраните этот код в файле i n s e r t 3 . x m l в каталоге subdirectory C:\TestUpdg\template и выполните его, открыв URL http://localhost^estUpdg/template/insert3.xml Эта апдейтограмма добавляет новую запись в таблицу Shippers. При этом, как видите, для спецификации столбцов используются атрибут (CompanyName) и вложенный элемент (Phone) одновременно. Использование атрибутов или элементов по большей части зависит от личных пристрастий разработчика и представляет собой дилемму, скорее религиозную, чем техническую. Тем не менее, в некоторых ситуациях действительно следует отдавать предпочтение элементам (например, невозможно иметь несколько одноименных атрибутов). Если вас интересует вопрос о предпочтительности использования атрибутов или элементов, рекомендуем посетить сайт http://www.oasis-open.org/cover/elementsAndAttrs.html, где вы также узнаете много полезного об атрибутах и элементах вообще.
276
Апдейтограммы До сих пор мы полагались на автоматическую установку соответствия по умолчанию, когда имя каждого элемента в пределах блоков и точно соответствовало имени таблицы, а имена атрибутов или элементов-потомков - именам столбцов. Вскоре мы узнаем, как специфицировать собственную схему для установки нужного соответствия между апдейтограммой и объектами базы данных явным образом.
Работа с множеством строк Во всех примерах до этого момента мы имели дело с одной строкой таблицы. В этом разделе мы узнаем, как модифицировать множество строк при помощи одной апдейтограммы. Давайте начнем с примера, добавляющего в таблицу Shippers несколько строк одновременно: Company Three (503) 333-3333
Сохраните этот код в файле с именем i n s e r t 4 .xml в каталоге С: \TestUpdg\template, a затем откройте его URL. В этой апдейтограмме полностью пропущен блок, а блок заключен в один блок , то есть все операции будут выполнены в рамках одной транзакции. Данная апдейтограмма вставляет три записи в таблицу Shippers. Заметьте, как здесь используется соответствие по умолчанию. В последнем примере все три элемента были заключены в единственный блок . Вы можете поместить их в отдельные блоки . Это ничего не изменит в конечном результате, но может оказаться полезным, когда вы выполняете несколько операций в одной транзакции. • Помните, что при использовании нескольких блоков и в рамках одного , можно делать их пустыми, но совсем опускать — нельзя. Следующая апдейтограмма выполняет в точности такие же операции, что n i n s e r t 4 .xml, однако достигает этого при помощи нескольких блоков :
277
Глава 9 Company Three (503) 333-3333
Для выполнения этой апдейтограммы сохраните текст в файле i n s e r t 5 . xml в каталоге C:\TestUpdg\template, и откройте URL http://locaihos^estUpdg/template/insert5.xml Для того чтобы понять, в чем состоят выгоды использования нескольких блоков и , давайте создадим пример обновления и удаления записей в одной транзакции:
Для выполнения этой апдейтограммы сохраните текст в файлеmultiupdatel .xml в каталоге С: \TestUpdg\template и откройте URL http://localhost/TestUpdg/template/multiupdate1.xml Данная апдейтограмма выполняет две модификации в рамках одной транзакции. Первая из них состоит в обновлении столбцов CompanyName и Phone для строки, где ShipperlD = б. Затем производится удаление записи, где ShipperlD = 5. Если любая из этих операций потерпит неудачу, все транзакция будет отменена. В предыдущем примере мы использовали множественные пары блоков и для выполнения нескольких операций. Однако можно сделать то же самое лишь с одной парой блоков и . В этом случае нам нужно поместить в эти блоки несколько элементов. Для того чтобы различать эти элементы между собой, необходимо использовать атрибут updg:id. Взгляните на следующий пример:
278
Апдейтограммы
Для выполнения этой апдейтограммы сохраните текст в файле idex amp l e i . xml в каталоге C:\TestUpdg\template и откройте URL http://localhost^estUpdg/template/idexample1.xml Эта апдейтограмма выполняет все три модификации данных - обновление, вставку и удаление. Обновляется поле Phone в записи, где ShipperlD равно 1, затем удаляется запись с ShipperlD, равным 6 и, наконец, добавляется запись с данными компании R e l i a b l e Shippers. Атрибут "updg: i d " и его значение используется для того чтобы обозначить попарное соответствие элементов в блоках и . Поскольку элемент с атрибутом i d , равным "rowl", присутствует в обоих блоках, и в , и в , речь идет об обновлении. А вот для элемента с атрибутом id, равным "row2", из блока , соответствия в блоке нет, что указывает на операцию удаления. Наконец, элемент с атрибутом id, равным "newRow", имеется только в блоке , благодаря чему распознается операция вставки. Без атрибутов updg: i d подобная апдейтограмма работать не будет. Довольно забавно наблюдать скрытое выполнение операторов T-SQL, происходящее при обработке апдейтограммы. Запустите SQL Profiler и посмотрите, как динамическая библиотека "SQL ISAPI extension" вызывает операторы T-SQL для того чтобы выполнить операции, специфицированные в апдейтограмме. Наконец, для того чтобы выполнить все эти операции в раздельных транзакциях, мы используем раздельные блоки . Если одна из транзакций потерпит неудачу и будет отменена, последствия ошибки распространятся только на неудачную транзакцию, результаты остальных транзакций останутся в силе. Вот пример, иллюстрирующий реализацию множественных транзакций в одной апдейтограмме:
2 7 9
Глава 9
Как всегда, для выполнения этой апдеитограммы сохраните этот код в файле m u l t i s y n c l . xml в каталоге С: \TestUpdg\template и откройте URL http://localhost/TestUpdg/template/nnultisync1.xml В этой апдейтограмме реализуются две транзакции. Первая пытается удалить запись с Shipper ID, равным 999, а затем вторая обновляет поле CompanyName в записи с ShipperlD = 3. При выполнении этой апдейтограммы первая транзакция потерпит неудачу, поскольку такой записи в таблице нет, и вы увидите в окне браузера сообщение об ошибке: Empty delete, no deletable rows found Transaction aborted, но это не помешает успешному выполнению второй транзакции, в чем легко убедиться при помощи Query Analyzer.
Специальные случаи Во всех операциях вставки данных, которые мы до сих пор реализовывали, определялись значения только для столбцов CompanyName и Phone, а значение для столбца ShipperlD, относящегося к типу i d e n t i t y , генерировались SQL-сервером без нашего участия. Но что, если нам потребуется узнать, какое именно значение задал SQL Server для вновь вставленной строки в результате выполнения нашей апдейтограммы? Что ж, в этом разделе мы займемся этим, и другими подобными вопросами, касающимися специальных случаев в работе с апдейтограммами: •
как получить значение identity-столбца, сгенерированное сервером;
•
генерация GUID;
•
работа со значениями NULL;
•
символы, допустимые в SQL Server, но недопустимые в XML;
•
преобразования типов данных.
Давайте начнем со значений identity-столбцов.
Работа со столбцами типа Identity При выполнении операций вставки над таблицами, в которых имеется ключевой столбец типа identity, SQL Server автоматически генерирует значение для этого столбца. При работе с сервером посредством апдейтограмм вы можете получить сгенерированное значение, и либо использовать его в апдейтограмме, либо передать приложению-клиенту. Мы рассмотрим оба случая.
280
Апдейтограммы Перехват значения
столбца Identity для использования
в апдейтограмме
В первом нашем примере мы узнаем, как получить значение identity-столбца для использования в той же самой апдейтограмме. Столбец ShipperlD, относящийся к типу i d e n t i t y , является первичным ключом таблицы Shippers, а также играет роль внешнего ключа в таблице Orders, где его значения фигурируют в столбце ShipVia. В следующем примере мы вставим новую запись в таблицу Shippers, и затем обновим полученным identity-значением поле ShipVia в таблице Orders, в строке с идентификатором OrderlD, равным 10248: «Drders OrderID="10248" />
Для выполнения этой апдейтограммы сохраните текст в файле i d e n t i t y l . xml в каталоге C:\TestUpdg\template и откройте следующий URL: http://localhos^estUpdg/template/identity1.xml В этой апдейтограмме имеются две пары блоков и . Первый блок пуст, в то время как корреспондирующий с ним блок включает вложенный элемент, соответствующий таблице Shippers и содержащий значения для столбцов CompanyName и Phone - ясно, что это операция вставки. Важно отметить здесь атрибут updg: a t - i d e n t i t y , который используется для перехвата возвращаемого сервером значения i d e n t i t y . Это значение для только что вставленной записи сохраняется в переменной v a r l d e n . Затем значение переменной используется при обновлении столбца ShipVia в таблице Orders. Помните, что в системе SQL Server для таблицы допускается лишь один столбец типа identity. По этой причине нам нет нужды указывать имя identity-столбца в апдейтограмме. SQL Server автоматически распознает нужный столбец, поскольку других столбцов такого типа в таблице быть не может. Возвращение значения Identity приложению-клиенту Кроме использования сгенерированного сервером значения i d e n t i t y в самой апдейтограмме, это значение можно вернуть клиенту. Такая операция реализуется использованием атрибута updg: r e t u r n i d в блоке совместно с атрибутом updg: a t - i d e n t i t y . Вот пример, это иллюстрирующий: I
281
Глава 9 Сохраните эту апдейтограмму в файле i d e n t i t y 2 . xml в каталоге the С: \TestUpdg\template и откройте URL http://localhost/TestUpdg/template/identity2.xml В этой апдейтограмме пропущен блок , и имеется лишь один блок , специфицирующий операцию вставки данных в таблицу Shippers. На этот раз в блоке использован атрибут updg: r e t u r n i d , действие которого заключается в том, что полученное от SQL-сервера новое значение i d e n t i t y для вставленной строки передается клиенту. При выполнении этой апдейтограммы вы увидите это значение в окне своего браузера: 15 Первый раз за все время мы получили результат в окне браузера, отличающийся от стандартного сообщения. Возвращенное значение (в данном случае 15) содержится в потомке элемента < r e t u r n i d > . Поскольку эта апдейтограмма вставила в таблицу лишь одну строку, в ответ мы получили единственное число в единственной переменной (IdenVal), как значение атрибутов updg: r e t u r n i d и updg: a t - i d e n t i t y . Возможно использование ряда переменных, разделенных одиночными пробелами, для получения identity-значений при вставке нескольких записей. Применение
таблицы стилей
К полученному результату можно применить таблицу стилей XSL для того, чтобы представить его в более легкой для восприятия форме. Чтобы увидеть, как это делается, сохраните текст следующей таблицы стилей в файле с именем i d e n t i t y 2 . x s l в каталоге С:\TestUpdg\template: Your Shipper ID is: Затем, для того чтобы применить эту таблицу стилей к результатам работы апдейтограммы i d e n t i t y 2 . x m l , просто укажите ее имя в URL http://localhost/TestUpdg/template/identity2.xml?xsl=template/identity2.xsl 282
Апдейтограммы Тут нет ничего удивительного, ведь апдейтограммы являются шаблонами, точно такими, как те, с которыми мы имели дело в главах 2 и 7. Собственно говоря, ссылку на таблицу стилей можно задать в корневом элементе апдейтограммы, вместо того чтобы указывать ее в URL. Если вам интересны темы, связанные с применением таблиц стилей, то рекомендуем прочитать великолепную книгу Майкла Кея "XSLT Programmers Reference", Wrox Press, ISBN 1-861003-12-9.
Генерация GUID GUID (Globally Unique Identifier) - глобально уникальный идентификатор, представляющий собой 16-байтовое двоичное значение, для которого гарантируется уникальность "в мировом масштабе". Значения GUID используются для надежной идентификации сущностей (в нашем случае, записей). SQL Server поддерживает тип данных u n i q u e i d e n t i f i e r , предназначенный для хранения значений GUID. Предположим, что в некоторой таблице имеется столбец типа u n i q u e i d e n t i f i e r , и что мы хотим вставить строку в эту таблицу при помощи апдейтограммы. Каким образом мы должны задать значение для столбца u n i q u e i d e n t i f i e r ? Ответ заключается в следующем: необходимо использовать атрибут updg:guid. Давайте посмотрим на примере, как это делается, а затем разберемся в сути выполняющихся операций. Поскольку в базе данных Northwind нет таблицы, в которой уже имелся бы столбец типа u n i q u e i d e n t i f i e r , нам придется создать новую таблицу для этого примера. Вообразим, что мы разрабатываем web-сайт, и нам нужна таблица для хранения пользовательских откликов. Давайте создадим таблицу WebFeedback. Запустите Query Analyzer и создайте таблицу, используя следующий T-SQL-оператор: USE Northwind Go CREATE TABLE WebFeedback (Feedld UNIQUEIDENTIFIER, FeedText NTEXT) Go
Теперь сформируем код апдейтограммы, предназначенной для вставки записи в эту таблицу: gVal Great s i t e !
Для того чтобы выполнить апдейтограмму, сохраните этот код в файле i n s e r t 6 . xml в каталоге С: \TestUpdg\template и откройте URL
283
Глава 9 http://localhost/TestUpdg/template/insert6.xml Эта апдейтограмма добавляет строку в таблицу WebFeedback. В ней используется атрибут updg:guid, который генерирует значение GUID и помещает его в переменную gVal. Затем значение этой переменной присваивается полю Feedld. Когда библиотека SQL ISAPI extension встречает атрибут updg:guid, она вызывает встроенную в T-SQL функцию NEWIDO . Подобно значениям типа i d e n t i t y , значения GUID также можно вернуть в виде отображаемых браузером XML-данных при помощи атрибута updg: r e t u r n i d в блоке . Следует помнить, что GUID-переменная (gVal в нашем случае) доступна в рамках всего блока , и в этих пределах к ней можно обращаться без ограничений.
Работа со значениями
NULL
В SQL Server значения NULL используются в случаях, когда значение неизвестно, неприменимо или будет добавлено позднее. Помните, что значения NULL не имеют ничего общего ни с пустой строкой, ни с числом 0: значение NULL - это просто отсутствие значения. В рамках XML вы не можете явно использовать значения NULL, и если вы специфицируете пустой элемент или атрибут, то значением будет пустая строка, а не NULL. Но при помощи апдейтограмм можно задать для столбца значение NULL посредством атрибута updg: n u l l v a l u e , как это демонстрируется в следующем примере. В этом примере в таблицу Shippers добавляется еще одна строка. На этот раз нам известно значение для столбца CompanyName, но телефонного номера мы не знаем, поэтому должны в столбец Phone поместить значение NULL:
Надеемся, вы уже выучили наизусть процедуру выполнения апдейтограммы - сохраните код в файле i n s e r t 7 . x m l , в каталоге C:\TestUpdg\template, и откройте соответствующий URL: http://localhost^estUpdg/template/insert7.xml Заметьте, что u p d g : n u l l v a l u e является атрибутом тэга , и ему в качестве значения можно присвоить произвольную строку (мы использовали "NA"). А затем точно такую же строку мы присвоили полю Phone, указывая тем самым, что в него необходимо записать значение NULL. Мы вернемся еще в этой главе к атрибуту n u l l v a l u e , когда речь зайдет о передаче параметров в апдейтограмму.
284
Апдейтограммы Обработка специальных символов В базе данных на SQL Server невозможно иметь два объекта с одинаковыми именами (за исключением случая, когда у одноименных объектов разные владельцы). Например, в базе могут быть две таблицы с именем, скажем "Т1", и это не будет проблемой, если таблицы созданы разными пользователями, скажем, dbo и Joe. В этом случае предполагается, что мы будем использовать полные имена таблиц, включающие, в виде префикса, имя владельца: "[dbo] .Т1"и " [ J o e ] .T1". Обратите внимание на тот факт, что символы " [ " и " ] " недопустимы в именах XML-элементов. Кроме того, в именах таблиц на SQL Server могут присутствовать пробелы (например, в учебной базе данных Northwind есть таблица с именем "Order D e t a i l s " ) , в то время как в именах XML пробелы недопустимы. Следовательно, мы не можем использовать такие элементы, как, скажем, или . Как же в таком случае апдейтограмма сможет обновить таблицу? Решение проблемы заключается в использовании кодировки UCS-2 для преобразования специальных символов в формат _хНННН_ , где НННН представляет собой шестнадцатеричный эквивалент специального символа (старший бит впереди). В результате пробел будет преобразован в значение _х0020_, открывающая квадратная скобка в_х005В_, а закрывающая - в _x005D_, и так далее. Если вас интересуют подробности о кодах UCS-2, рекомендуем посетить сайт http://www. Unicode, org/charts/index.html. Взглянем теперь на пример обновления таблицы [Order D e t a i l s ] :
Выполните апдейтограмму, сохранив текст в файле update2 .xml в каталоге C:\TestUpdg\template и открыв URL http://locaIhos^estUpdg/template/update2.xml Эта апдейтограмма находит запись со значением OrderlD, равным 10248, и ProductID, равным 42. Затем она обновляет поле Discount значением 0 . 575 (точнее сказать, 0.57499999, ближайшим вещественным числом). Заметьте, что символы пробела в имени элемента заменены кодом _х002 0_. Еще здесь важно отметить, что мы обновляем этой апдейтограммой поле Discount, которое относится к вещественному типу (число с плавающей точкой). Значение 0 .57 5 передается препроцессору (SQL ISAPI extension DLL, s q l i s a p i . d l l ) в виде строки, а затем оно передается SQL-серверу в виде значения типа nvarchar (символы Unicode). Поскольку SQL Server способен неявно преобразовывать символьное представление числа в вещественный 285
Глава 9
числовой тип, апдейтограмма успешно выполняется. Но что, если бы требовалось обновить поле такого типа, для которого требуется явное преобразование (например, money)? Чтобы ответить на этот вопрос, мы перейдем к последнему подразделу, в котором обсудим преобразования типов данных.
Преобразования типов данных В справочной системе "SQL Server Books Online" есть великолепная диаграмма, иллюстрирующая явные и неявные преобразования типов. Согласно этой диаграмме, SQL Server автоматически преобразует данные nvarchar в char, datetime, decimal, real, и т.д. Но преобразование nvarchar в money или nvarchar в smallmoney, например, должно осуществляться явно, с использованием функций CAST или CONVERT. Если явное преобразование типа требуется в апдейтограмме, то его реализуют, предваряя значение атрибута символом "$". В качестве иллюстрации к сказанному, давайте изменим нашу апдейтограмму так, чтобы она обновляла значение U n i t P r i c e , которое относится к типу money:
Ц
Сохраните к о д в u p d a t e 3 . x m l и выполните http://localhost/TestUpdg/template/update3.xml
Значение атрибута U n i t P r i c e здесь предваряется символом "$". Попытайтесь удалить этот символ, и вы обнаружите, что без него апдейтограмма потерпит неудачу. Заметим, что это необходимо лишь до тех пор, пока мы пользуемся автоматической установкой соответствия по умолчанию, когда имена элементов и атрибутов прямо соответствуют именам объектов в базе данных. Далее в этой главе мы познакомимся со схемами соответствия, при использовании которых нужда в символе "$" отпадет.
Передача параметров Во всех примерах до этого момента мы использовали неизменяемые значения, специфицированные прямо в тексте. В реальных приложениях, конечно, нам потребуется передавать в апдейтограмму параметры, для того, чтобы она вносила изменения, "заказанные" пользователем. Мы знаем уже, что апдейтограмма есть не что иное, как шаблон. Еще в главе 2 мы узнали, как при помощи блоков и можно передавать в шаблон параметры. Аналогичным образом мы можем передать параметры в апдейтограмму. Давайте рассмотрим это на простом примере:
286
Апдейтограммы
Сохраните этот код под именем paraml. xml и выполните http://localhostAestUpdg/template/param1.xnnl?ShlD=2&ShPhone=(509)%20123-4455 Помните, что имена параметров чувствительны к регистру. Если бы в примере, который приведен выше, мы указали имя "SHID" вместо "ShID", то получили бы сообщение об ошибке, поскольку без значения параметра ShID ни одной записи не нашлось бы. В нашем примере используются два параметра ShID и ShPhone. Апдейтограмма обновляет поле Phone в записи, идентифицируемой значением поля ShipperlD, которое задается параметром ShID. Значения параметров можно передать в составе URL (методGET), или как часть HTTP-заголовка (метод POST). Далее в этой главе мы увидим, как параметры передаются методом POST, когда заполненная web-форма передается апдейтограмме. Если вы используете SQL Profiler для того чтобы наблюдать происходящие в недрах сервера события, то заметите, что когда библиотека "SQL ISAPI extension" встречается в апдейтограмме с параметром, она вызывает хранимую системную процедуру sp_executesql вместо того чтобы использовать прямые вызовы T-SQL. Наконец, так же, как и в случае шаблонов, апдейтограммы позволяют передавать в параметрах значение NULL. Для этого необходимо использовать атрибут пи 11 v a l u e в блоке , указав тем самым, что апдейтограмме разрешено принимать 3Ha4eHHflNULL в параметрах. Давайте немного изменим апдейтограммуparaml .xml:
Сохраните измененный текст как param2 .xml и выполните http://localhos^estUpdg/template/param2.xnnl?ShlD=1&ShPhone=NA
287
Глава 9 Заметьте, что, в отличие от блока , атрибут "nullvalue" здесь использован без префикса пространства имен. Причина в том, что тот же самый атрибут используется в блоке в стандартных шаблонах. Когда вы выполните последнюю апдейтограмму (param2 . xml) и передадите для параметра ShPhone значение NA, в поле Phone для записи с ShID=l будет помещено значение NULL.
Апдеитограммы и аннотированные XDR-схемы До сих пор мы пользовались соответствием по умолчанию (его также называют неявным соответствием), когда имена элементов в блоках и точно соответствуют именам таблиц или представлений, а имена атрибутов и вложенных элементов - именам столбцов. Такой способ установки соответствия не очень удобен, если вы обновляете много таблиц, связанных первичными и внешними ключами, а также в случаях, когда вам требуется явное преобразование типов данных. В этом разделе мы сосредоточимся на использовании схем соответствия, при помощи которых соответствие элементов апдеитограммы и объектов базы данных задается явным образом. Для того чтобы сослаться на аннотированную XDR-схему (схему соответствия) в апдейтограмме, используют атрибут mapping-schema в блоке . При наличии этого атрибута апдейтограмма не использует соответствие по умолчанию, а обращается к заданной схеме. Давайте изменим наш первый пример (файл i n s e r t l .xml) так, чтобы в нем использовалась аннотированная XDR-схема: Щ
§|
Сохраните этот код в файле i n s e r t 8 . x m l в каталоге C:\TestUpdg\template. Обратите внимание на атрибут mapping-schema в блоке . Этот атрибут ссылается на файл schl .xml, в котором должна содержаться схема соответствия. Вот содержимое файла s c h l . xml (его необходимо поместить в тот же каталог С : \TestUpdg\template):
I
288
- j У ; Ш Й? 1
Q§ P,
Connection a
^ $ и Microsoft OLE DB . mm Task v$ «§ %} H i
Ё
З
a Add New Regions
®
Ф ©Щ 4 j i^r ^Ж1 j is t i
Утилита bcp Утилита bcp (bulk copy program - программа массового копирования) представляет собой старейший механизм массовой загрузки данных в SQL Server. В наши дни она используется редко. Многие администраторы баз данных испытывают двойственные чувства к этой утилите, столь необходимой для импорта данных, и столь капризной в использовании. Утилита bcp представляет собой утилиту командной строки, предусматривающую небольшую гору параметров: bcp
{ [[database_name.] [owner] .]{table_name I view_name) {in | out | queryout | format} data_file [-m max_errors] [-f format_file] [-e e r r _ f i l e ] [-F first_row] [-L last_row] [-b batch_size] [-n]
[ - c ] [-w] [ - N ] [-V ( 6 0 |
65 |
7 0 ) ]
|
"query"}
[-6]
[-q] [-C code_page] [-t field_term] [-r row_term] [-i input_file] [-o output_file] [-a packet_size] [-S server_name[\instance_name]] [-U login_id] [-P password] t - T ] [ - v ] [ - R ] [ - k ] [ - E ] [ - h "hint [,...n]"]
Назначение большинства из этих параметров очевидно, и мы не будем вдаваться в детали синтаксиса этой утилиты. Единственное, о чем стоит упомянуть, это о том, что утилита различает в параметрах прописные и строчные буквы, поскольку происходит она все же из мира Unix! Давайте разовьем предыдущий пример и добавим несколько территорий, описывающих регион "Европа". Например, мы можем импортировать файл с описанием европейских стран в таблицу T e r r i t o r i e s :
304
Массовая загрузка XML-данных (Bulk Load)
Fe li Edti Format Hep l
99000,united Kingdom,5 99001,France,5 99002,Germany,5 99003,Belgium,5 99004,Greece,5 99005,Italy,5
"Щ
При помощи командной строки мы можем вызвать утилиту Ьср и поручить ей выполнение импорта: | Ьср Northwind.dbo.Territories in "c:\New Territories.txt" -с -Т -t,
Вначале мы указали имя таблицы, в которую необходимо импортировать данные, далее следует слово " i n " , указывающее на тот факт, что речь идет об импорте данных, а не об экспорте. Затем мы указали имя файла с данными. Ключ "-с" показывает, что речь идет о текстовом файле. Ключ "-т" используется для того, чтобы зарегистрироваться на SQL-сервере на правах "trusted connection". Здесь можно было бы использовать ключи "-U" и " - р " для того, чтобы задать реальный пользовательский идентификатор и пароль. Наконец, ключ " - t " указывает, что в качестве разделителя полей в исходном файле используется запятая вместо табуляции. Для посылки данных на удаленный сервер нам пришлось бы задать параметр "-S", а его отсутствие говорит о том, что мы имеем дело с сервером локальным. B U L K
I N S E R T
И, наконец, наша краткая экскурсия по традиционным методам массовой загрузки данных завершается оператором BULK INSERT. Этот оператор появился, как альтернатива использованию утилиты Ьср, в версии SQL Server 7.0. Его главное преимущество заключается в том, что массовая загрузка выполняется как внутренний процесс SQL Server, и работает немного быстрее. Ранее, при необходимости выполнить загрузку данных в коде SQL, мы должны были использовать хранимую процедуру xp_cmdshell (или ей подобную), которая запускала утилиту Ьср. Неудивительно, что оператор BULK INSERT в своих параметрах почти полностью повторяет ключи Ьср, за исключением того обстоятельства, что имена параметров здесь намного легче запомнить: BULK INSERT [['database name'.]['owner'].]{'table name' FROM 'data file'} [WITH ( [BATCHSIZE [= batch size]] [[,] CHECK_CONSTRAINTS] [ [ , ] CODEPAGE [= ' A C P ' | ' O E M ' I ' R A W | ' c o d e p a g e 1 ] ] [[,] DATAFILETYPE [={'char'|'native'|'widechar'|'widenative'}]] [[,] FIELDTERMINATOR [= 'field terminator1]] [[,] FIRSTROW [= first row]] [[,] FIRE_TRIGGERS]
305
Глава 10 [[,] FORMATFILE - 'format file path'] [[,] KEEPIDENTITY] [[,] KEEPNULLS] [[,] KILOBYTES_PER_BATCH [= kilobytes per batch] [[,] LASTROW [= last row]] [[,] MAXERRORS [= max errors]] [[,] ORDER ({column [ASCIDESC]}[,...n])] [[,] ROWS_PER_BATCH [-= rows per batch]] 1 [[,] ROWTERMINATOR [= 'row terminator ]] [[,] TABLOCK]
Продолжая все тот же пример, давайте импортируем еще один набор записей. Пусть, описав территории, мы теперь хотим назначить ответственных за территории сотрудников. Один из них, живущий в Лондоне, будет отвечать за европейский регион, и, следовательно, нам требуется импортировать файл вида: File Edit Format Help 9,99000 9,99001 9,99002 9,99003 9,99004 9,99005
Для того чтобы импортировать этот файл при помощи оператора BULK INSERT, мы выполним следующий код:
I
BULK INSERT Northwind.dbo.EmployeeTerritories FROM 'C:\New EmployeeTerritories.txt' WITH (FIELDTERMINATOR = ',')
Смысл этого SQL-выражения вполне прозрачен: файл "New E m p l o y e e T e r r i t o r i e s . t x t " импортируется в таблицу E m p l o y e e T e r r i t o r i e s с использованием настроек по умолчанию и заданным разделителем полей в файле (запятая). Мы весьма бегло рассмотрели все эти средства массовой загрузки с той лишь целью, чтобы у вас сложилось представление о традиционных механизмах, "на фоне" которых вы лучше сможете понять XML Bulk Load. Все это более детально описано в книге "Professional SQL Server 2000 Programming", Wrox Press, ISBN 1861004486.
X M L
B u l k
L o a d
Теперь, когда у вас есть общее понимание процессов массовой загрузки, мы можем перейти к изучению механизма массовой загрузки XML-данных - XML Bulk Load. XML Bulk Load всегда выполняется при помощи объекта SQLXMLBulkLoad, - вначале необходимо настроить свойства этого объекта, а затем вызвать его метод Execute. Давайте рассмотрим небольшой пример, чтобы увидеть все это в действии, прежде чем мы углубимся в технические детали. Чтобы не усложнять задачу, мы загрузим в таблицу T e r r i t o r i e s список стран, на этот раз азиатских. XML-документ с исходными данными выглядит следующим образом: 306
Массовая загрузка XML-данных (Bulk Load)
99100 Thailand 6 9910K/TerritoryID> Cambodia 6 99102 Malaysia 6 99103 India 6 9910 4 Pakistan 6
Сохраните этот текст в файле T e r r i t o r i e s . xml в новом каталоге C:\sqlxml\chapterlO. Как мы уже говорили, XML Bulk Load загружает XML-документ в базу данных, основываясь на аннотированной XDR-схеме. XDR-схемы описывались в главе 5, поэтому мы не будем здесь их рассматривать (за исключением нескольких специфичных для XML Bulk Load моментов, речь о которых впереди). Для того чтобы загрузить наш документ, потребуется следующая схема:
307
Глава 10 Сохраните текст в файле T e r r i t o r i e s S c h e m a . xml в том же самом каталоге. Затем следует создать DTS-пакет для выполнения импорта. Создайте в DTS новый пакет и добавьте туда элемент ActiveX Script Task. Введите в диалоговом окне следующий сценарий на языке VBScript: .-l.nl.xj
•; ActiveX Script Task Properties Description: Language: JVB Script Langua Functions: Abs And Array Asc Atn Call Case Case Else CBool CByte CCur CD ate CDbl Chr CInt CLng Entry function: JMan i Language 1 ti
I Import Asian Countries into Territories Visual Basic ActiveX Script Function Main(] 1 Create the new XML Bulk Load object d S et myObe j ct = CreateObe j ct("SQLXMLBuk l Load.SQLXMLBuk l Load") 1 Set the connection string in order to connect to the Northwind database ' on the local server using integrated security myObject.ConnectionString • ''providers QLOLEDВ. 1 ;data source=(local);'' _ & "database=Northwind;lntegrated Security=SSPI" 1
Log any errors myObject.ErrorLogFile • "c:\sqlxml\chapter10\errorlog.xml" ' And perform the data import rnyObject. Execute "c:\sqlxml\chapter1 OVTemtoriesSchema.xml", _ "cAsqlxml\chaptet10\Territories.xml" ' Tidy up at the end Set myObject=Nothing Main = DTSTaskExecResult_Success End Function
:j Undo
Cancel
Ln4.CoM 5 Help ]
Щелкните на OK, закрыв окно диалога, и запустите затем пакет. После его успешного выполнения откройте окно Query Analyzer и выполните запрос: I
SELECT * FROM Territories WHERE RegionID = 6
В результате мы увидим, что записи действительно импортированы в таблицу: TerritorylD
TerritoryDescription
99100 99101 99102 99103 99104
Thailand Cambodia Malaysia India Pakistan
RegionID
(5 row(s) affected) Как видите, все достаточно просто. Однако тут есть множество способов управлять деталями выполнения импорта. Прежде чем мы займемся изучением более сложных примеров, давайте рассмотрим объектную модель XML Bulk Load
308
Массовая загрузка XML-данных (Bulk Load)
Объектная модель Bulk Load Объектная модель XML Bulk Load довольно проста и состоит всего лишь из одного объекта SQLXMLBulkLoad, - который обладает одним методом и пятнадцатью свойствами. Как мы вскоре увидим, в большинстве своем эти свойства играют ту же самую роль, что и ключи и параметры в традиционных методах массовой загрузки, которые мы обсуждали ранее в этой главе. Это не случайность - на самом деле XML Bulk Load неявно обращается к этим методам, и, фактически, именно ими мы и пользуемся посредством этой объектной модели. В каком-то смысле объект SQLXMLBulkLoad можно рассматривать как программный интерфейс для обычного оператора BULK INSERT.
Метод Execute Метод Execute - это единственный метод объекта SQLXMLBulkLoad. Как легко догадаться, этот метод, собственно, и выполняет сам процесс загрузки данных. Вызов этого метода сопровождается двумя параметрами вида: .Execute SchemaFileName, DataFileName \ Stream Object Здесь вы передаете методу имя аннотированной XDR-схемы и имя XML-документа (или же объекта ADO 2.6 Stream). Все это демонстрировалось в предыдущем примере.
Свойства Объект SQLXMLBulkLoad обладает пятнадцатью свойствами, значения которых влияют на процесс загрузки данных. BulkLoad Свойство BulkLoad используется для того, чтобы разрешить или запретить выполнение массовой загрузки. Наличие такого свойства может показаться странным, но эта возможность полезна, когда необходимо лишь создать таблицы, не загружая в них данные. Значением этого свойства может быть True или False - в случае True загрузка данных выполняется. True - это значение по умолчанию. CheckConstraints Значение этого свойства определяет, будут ли данные при загрузке проверяться на соответствие ограничениям целостности. Значение по умолчанию здесь - F a l s e , что соответствует загрузке данных без проверки. Сама загрузка при этом происходит быстрее, и, кроме того, в этом случае нет нужды беспокоиться о последовательности записей-предков и их потомков. ConnectionString Свойство C o n n e c t i o n S t r i n g содержит стандартную строку соединения OLE DB в таком же формате, что и в ADO. Обычно строка состоит из следующих компонентов:
309
Глава 10
Компонент
Описание
provider=SQLOLEDB.1;
Имя провайдера OLE DB, в данном случае SQL Server
data
Имя SQL-сервера, о соединении с которым идет речь
source=servername;
database=databasename;
Имя базы данных на сервере
...а также включает в себя один из следующих элементов: Компонент
Описание
Integrated Security=SSPI;
Используется соединение с аутентификацией Windows
uid=username; pwd=password;
Используется соединение с аутентификацией SQL Server
В результате должна получиться строка вида: myObject.ConnectionString = "provider=SQLOLEDB.1; data source=(local);" & "database=Northwind;Integrated Security=SSPI" ConnectionCommand
Свойство ConnectionCommand представляет собой альтернативу свойству C o n n e c t i o n S t r i n g , описанному выше. Вместо строки соединения это свойство принимает в качестве значения объект ADO Command, при этом, если уже существует соединение с базой данных, вы можете им воспользоваться, вместо того чтобы создавать новое. Вот фрагмент кода, иллюстрирующий использование этого свойства: ' Create the objects Dim myConnection As New ADODB.Connection Dim myCommand As New ADODB.Command Dim myBulkLoad As New SQLXMLBulkLoad 1
Make the connection Set myConnection.ConnectionString = "provider=SQLOLEDB.1;" & "data source= (local);database=Northwind;Integrated Security=SSPI" myConnection.Open
' Set up the Command object for use Set myCommand.ActiveConnection = myConnection ' Use the Command object's connection for bulk loading myBulkLoad.ConnectionCommand = myCommand
Если оба этих свойства (ConnectionCommand и ConnectionString) используются одновременно, то преимуществом обладает то из них, которому значение присвоено последним.
310
Массовая загрузка XML-данных (Bulk Load) ErrorLogFile Свойство ErrorLogFile используется для того, чтобы задать полное имя файла, куда будут записываться сообщения об ошибках в процессе загрузки. При каждой новой загрузке файл удаляется, а если ошибок не было, то он не создается. Вот пример использования этого свойства: myObject.ErrorLogFile
= "C:\SQLXML\ChapterlO\errorlog.xml"
•
Файл можно сохранить в формате XML или как обычный текст: C
:
\
s
File
q
l
n
m
Edit
l
\
c
h
a
p
View
•
-
t
e
r
I
O
\
e
r
r
o
Favorites:
r
l
o
g
.
x
Tools
m
l
-
M
i
c
r
o
s
o
f
Help
* (ф 4j) O < S o u r c e > M i c r o s o f t OLE DB P r o v i d e r f o r SQL S e r v e r < / S o u r c e > - - 0x80004005 23000 l 1 4 < / S e verity > < S o u r c e > M i c r o s o f t OLE DB P r o v i d e r f o r SQL S e r v e r < / S o u r c e > -
Q
F
o
r
c
e
T
a
b
l
e
L
o
c
M
y
C
o
m
p
u
t
e
r
k
Свойство ForceTableLock определяет, следует ли блокировать в монопольном режиме те таблицы, в которые производится загрузка данных. Это позволяет повысить скорость загрузки, однако никто не сможет обратиться к таблицам, пока процесс не завершится. По умолчанию это свойство имеет значение F a l s e , что соответствует отсутствию блокировки. IgnoreDup/icateKeys По умолчанию, свойство IgnoreDuplicateKeys имеет значение False, благодаря чему любая попытка добавить запись с неуникальным (дублирующим) значением ключа потерпит неудачу, и, в результате, потерпит неудачу вся операция массовой загрузки. 311
Глава 10 В нетранзакционном режиме (различные режимы обсуждаются далее) это приведет также к тому, что не будут вставлены некоторые из предыдущих записей. Задав этому свойству значение True, мы обеспечим следующее положение: когда операции встретится неуникальный ключ, соответствующая запись будет проигнорирована, операция же в целом будет завершена успешно. В этом режиме импорт происходит значительно медленнее, поскольку каждая запись вставляется в отдельной транзакции. KeepNulls Свойство KeepNulls отвечает за то, какие значения попадают в таблицу в случае, когда значение для столбца в XML-документе или схеме соответствия явно не задано. По умолчанию это свойство имеет значение False, что соответствует использованию значений по умолчанию, определенных для таблицы. В противном случае будет вставлено значение NULL. Keepldentity Значение этого свойства определяет правила "поведения" операции загрузки в отношении столбцов типа identity. В случае значения True (значение по умолчанию) SQL Server использует identity-значения из документа, вместо того чтобы генерировать их. Если же в этом свойстве содержится значение F a l s e , SQL Server генерирует identity-значения самостоятельно, игнорируя значения в XML-доку менте. SchemaGen Свойство SchemaGen используют для того, чтобы создавать таблицы в базе данных в процессе загрузки. При SchemaGen = True схемы таблиц генерируются на основании аннотированной XDR-схемы с использованием имен и типов данных, в ней заданных. В случае False (значение по умолчанию) таблицы не создаются. SGDrop Tables Свойство SGDropTables используется совместно со свойством SchemaGen для того, чтобы удалять существующие таблицы перед созданием новых. При SGDropTables = False (значение по умолчанию) таблицы не удаляются. SGUseld Это свойство также используется совместно с SchemaGen. Если в свойстве SchemaGen содержится значение True, то это свойство определяет поля, составляющие первичный ключ создаваемой таблицы. Если свойство содержит значение True, то в качестве первичного ключа будут использоваться атрибуты с d t : t y p e = " i d " . По умолчанию оно имеет значение False. TempFilePath Свойство TempFilePath используется лишь в том случае, когда в свойстве T r a n s a c t i o n содержится значение True, при этом оно переопределяет каталог по умолчанию, используемый для хранения временных файлов. По умолчанию его значение -NULL, при этом временный каталог определяется значением переменной окружения TEMP. 312
Массовая загрузка XML-данных (Bulk Load) Для того чтобы использовать это свойство, ему необходимо присвоить строковое значение вида: g
.TempFilePath = "C:\SQLXML\ChapterlO"
Transaction
Это свойство содержит булево значение (по умолчанию - False). Оно управляет режимом загрузки, как описывается немного ниже. XMLFragment По умолчанию свойство XMLFragment содержит значение False, что соответствует импорту формально-правильного XML-документа с единственным корневым элементом. Присвоив ему значение True, мы разрешаем импорт частичной XML-структуры без корневого элемента.
Настройка массовой загрузки данных XML Bulk Load В этом разделе мы опишем различные способы управления "поведением" массовой загрузки, и начнем мы с описания доступных режимов загрузки. •
Режимы Существуют два основных режима загрузки XML-данных: транзакционный и нетранзакционный. По умолчанию применяется нетранзакционный режим. Этот режим проще в использовании, однако, с ним могут быть связаны различные потенциальные опасности. Транзакционный режим Загрузка в транзакционном режиме гарантирует, что либо все данные будут загружены успешно, либо, в случае возникновения ошибок, вся операция загрузки будет полностью отменена. В этом режиме для каждой таблицы, куда должны вставляться записи, согласно аннотированной XDR-схеме, создается временный файл. Затем производится импорт данных в эти временные файлы. После завершения импорта во временных файлах будут содержаться стандартные данные импорта, готовые к загрузке при помощи оператора BULK INSERT, каковая затем и производится. Использовать транзакционный режим следует в тех случаях, когда возможны ошибки в данных, и когда вы не можете позволить себе оставить таблицы в частично загруженном состоянии. Транзакционный режим требует больше времени для загрузки и выполняется в два этапа. К тому же, в случае XML-документа большого размера следует учесть добавочное дисковое пространство, используемое в этом режиме для хранения временных файлов. Для того чтобы использовать транзакционный режим, достаточно задать свойству Transaction объекта SQLXMLBulkLoad значение True. При этом вы должны для соединения с сервером воспользоваться свойством ConnectionCommand, а не ConnectionString. Причина кроется в том, что в последнем случае устанавливается собственное соединение с базой данных, которое не завершает транзакции автоматически в случае, когда Transaction = True.
313
Глава 10 Будем надеяться, что в следующих релизах можно будет использовать транзакционный режим также и со свойством ConnectionString. Теперь давайте модифицируем пример сценария VBScript в элементе ActiveX Script Task следующим образом: Function Main() ' Create the new XML Bulk Load object Set myObject = CreateObject("SQLXMLBulkLoad.SQLXMLBulkLoad") Set myConnection = CreateObject("ADODB.Connection") Set myCommand = CreateObject("ADODB.Command")
I
' Set the connection string in order to connect to the Northwind 'database on the local server using integrated security myConnection.ConnectionString = _ "provider=SQLOLEDB.l;data source= (local);" & "database=Northwind;Integrated Security=SSPI" myConnection.Open ' Set up the command object for use Set myCommand.ActiveConnection = myConnection ' Use the command object's connection for bulk loading myObject.ConnectionCommand = myCommand ' Log any errors myObject.ErrorLogFile = "c:\sqlxml\chapterlO\errorlog.xml" myobject.Transaction = True 1
And perform the data import myObject.Execute "c:\sqixml\chapterlO\TerritoriesSchema.xml", "c:\sqlxml\chapter10\Territories.xml" ' Tidy up at the end Set myObject=Nothing Set myConnection = Nothing
I
Set myCommand = Nothing Main = DTSTaskExecResult_Success End Function
Обратите внимание на то, что ранее вставленные строки необходимо предварительно удалить. Нетранзакционныи
режим
Нетранзакционныи режим является режимом по умолчанию и используется, когда вы не задаете значения свойству T r a n s a c t i o n . Этот режим полностью противоположен транзакционному: он не гарантирует, что все данные будут добавлены в таблицы или отвергнуты целиком. Его вполне безопасно использовать, если речь идет о загрузке данных в пустые таблицы - вы всегда можете очистить таблицы и начать импорт заново.
314
Массовая загрузка XML-данных (Bulk Load) Главное преимущество нетранзакционного режима состоит в его большей скорости и меньшем используемом пространстве на диске. Данные загружаются прямо в таблицы базы данных. Нетранзакционный режим использует для добавления записей интерфейс I Rows e t Fast Load в режиме "без регистрации", что еще более увеличивает скорость загрузки. Чтобы достичь наибольшей скорости импорта, используйте либо импорт в пустые таблицы без создания индексов, либо импорт в таблицы с уникальным индексом (последний вариант немного медленнее). Обратите внимание: при загрузке двоичных данных можно использовать только нетранзакционный режим. Для того чтобы использовать нетранзакционный режим в своем коде, либо не задавайте значения свойству T r a n s a c t i o n , либо явно задайте ему значение False.
Загрузка в несколько таблиц одновременно При наличии корректной аннотированной XDR-схемы операция XML Bulk Load может загружать данные из одного XML-документа в несколько таблиц одновременно. Однако прежде чем перейти к рассмотрению этой техники, мы должны познакомиться с несколькими новыми понятиями. •
Область видимости узлов Моменты, когда узел входит в область видимости, и когда покидает ее, определяют возможность вставки записи в таблицу. Узел входит в область видимости, когда встречается его начальный тэг. Например, если вспомнить наш предыдущий пример (документ T e r r i t o r i e s .xml), узел войдет в область видимости, когда будет прочитан тэг < T e r r i t o r i e s > : Д
9 910 0 Thailand 6
Узел покидает область видимости со считыванием конечного тэга, в данном случае это < / T e r r i t o r i e s > . В сочетании с данными из XDR-схемы прочитанного будет достаточно, чтобы построить запись и, в случае нетранзакционного режима, добавить ее прямо в таблицу. Если данные их XML-документа записываются в несколько таблиц, то область видимости учитывается аналогичным образом. Например, если мы возьмем таблицы Orders и Order D e t a i l s (для простоты несколько полей проигнорированы), то XML-документ для загрузки данных одновременно в две таблицы будет выглядеть следующим образом:
I
12 00 0
315
Глава 10 AROUT 9 2001-04-07 2001-04-30 4 40.00 Around The Horn
120 Hanover Sq London WAl lDP UK 4 22.00 100 5 21.35 100
В этом случае узел Orders попадает в область видимости первым из всех, и остается там до своего конечного тэга. Первый узел OrderDetails проходит область видимости по мере считывания своих начального и конечного тэгов. То же самое происходит со вторым узлом O r d e r D e t a i l s . Пока эти два узла проходят область видимости, узел Orders находится в ней. Если вы запустите трассировку при помощи SQL Profiler, то увидите, что по мере прохождения разных узлов через область видимости записи добавляются в базу данных немного по-разному. Запись Orders создается первым оператором BULK INSERT, а вслед за ней в базу данных отправляются две записи O r d e r D e t a i l s при помощи второго оператора BULK INSERT. Это делается для того, чтобы предотвратить нарушение ограничений ссылочной целостности, которое произойдет при попытке вставить 3anHCb0rderDetails, которой не соответствует ни одна из записей Orders. В этом документе содержится один заказ на два продукта. Далее приведена аннотированная XDR-схема, служащая для загрузки подобного документа (OrdersSchema.xml):
316
Массовая загрузка XML-данных (Bulk Load)
Запуск теста Теперь нам необходимо создать некое приложение, из которого будут вызываться классы. Код такого приложения имеется на веб-сайте Wrox (timingl .exe). Последовательность тестирования обоих классов такова: • • •
Создается локальная копия исходной таблицы Order D e t a i l s из базы данных Northwind. Для каждой строки таблицы создается либо объект c O r d e r D e t a i l s , либо объект cOrderDetailsXML. Каждый объект заполняется значениями соответствующим образом, и выполняется переход к следующей строке.
Нам необходимо создать приложение СОМ+, которое будет сервером приложений для наших объектов и выполнять связанный тест на другой машине (поскольку мы пытаемся доказать, 338
Расширение существующей системы возможностями работы с XML что уменьшение количества сетевых запросов сделает интерфейс на базе XML более эффективным). Мы измеряем время (в секундах), необходимое для полного завершения копирования таблицы Order D e t a i l s по строкам для имитации реального применения. Заметьте, что, как мы уже сказали, этот тест выполняется на обычной офисной машине, поэтому не следует ожидать фантастических абсолютных значений. Нас интересует относительная производительность. Тест
cOrderDetails
cOrderDetailsXML
Исходный тест
72 (с)
78 (с)
Как видите, в случае применения XML производительность несколько ниже за счет дополнительных затрат времени на упаковку данных в XML и дополнительной работы на сервере с переданным документом XML. Однако если мы глубже рассмотрим физический уровень, то увидим, что сеть не является узким местом в приложении: мы применяем коммутируемую сеть со скоростью передачи данных 100 Мбит/с, и другой сетевой трафик, за исключением нашего приложения, отсутствует. Теперь сформируем умеренный сетевой трафик. Результаты будут такими: Тест
cOrderDetails
cOrderDetailsXML
Умеренная загрузка сети
265 (с)
146 (с)
Это уже лучше и в большей степени соответствует тому, что мы ожидали увидеть. Но что получится, если мы серьезно нагрузим сеть? Тест
cOrderDetails
cOrderDetailsXML
Сильная загрузка сети
371 (с)
177 (с)
Результат достаточно убедителен. Если мы сведем результаты в одну таблицу, то увидим, что применение OPENXML в SQL Server 2000 может существенно увеличить производительность: Тест
cOrderDetails
cOrderDetailsXML
Исходный тест
72
78
Умеренная загрузка сети
265
146
Сильная загрузка сети
371
177
Рассмотренный пример сильно упрощен, он не выполняет проверки данных и обработки ошибок. Если мы примем во внимание, что этот тип объекта принадлежит к уровню доступа к данным, а все основные проверки должны выполняться на уровне бизнес-логики, тогда наш подход верен.
339
Практическое занятие Однако если мы подробно рассмотрим хранимую процедуру, мы заметим побочный эффект (преднамеренный или нет). Обратите внимание, что для столбца THnanvarchar установлен предел в 4000 символов, отличный от принятого в SQL Server, а в процедуре ничто не мешает нам передавать в одном запросе сложные детали заказа. Если мы перепишем наш тест так, чтобы запрашивать упорядоченную noOrderlD таблицу Order D e t a i l s целиком, то можем одной операцией послать все записи Order D e t a i l s с одинаковым значением столбца OrderlD. Результат измененного теста представлен ниже: Тест
cOrderDetailsXML
cOrderDetailsXML(batch)
Исходный тест
78
36
Умеренная загрузка сети
146
68
Сильная загрузка сети
177
81
Мы видим, что если приложение передает нам все строки Order D e t a i l s со значением OrderlD, равным 10250, в таком формате:
то мы получаем три дополнительные строки и возвращенное значение, идентичное приведенному ниже: NorthwindCRUD.cOrderDetailsXML Это означает, что применение XML с SQL Server 2000 имеет большой потенциал с точки зрения увеличения производительности приложений. Если мы применяем XML как структуру для передачи данных в многоярусном приложении, то первый вопрос, который мы могли бы задать, таков: "А где в приложении я должен применить XML?"
340
Расширение существующей системы возможностями работы с XML На этот вопрос есть несколько ответов, и тесты, которые мы выполнили, помогут вам определить, к какому же из ответов подходит ваше собственное приложение. А вот и возможные ответы: •
Нигде, двоичные форматы быстрее и я не люблю угловые скобки.
•
Нигде, но мы должны применять его, это требование рынка. Почему рынок требует от нас так замедлить приложения?
•
Он нашел место в передаче информации браузерам и другим приложениям, и этого достаточно.
•
Для обращения к базе данных для обеспечения кроссплатформенного взаимодействия.
•
Между уровнями представления и бизнес-логики, поскольку они физически различны.
•
Везде.
Попытаемся показать, что в условиях применения больших наборов данных со сложными преобразованиями или обработкой, требующей активного применения DOM, мы можем увеличить производительность с применением XML. Кроме того, структура приложения будет отличаться простотой и гибкостью. Мы уже исследовали случай размещения данных на SQL-сервере, и продемонстрировали возможность применение XML для повышения производительности. Далее мы рассмотрим пример извлечения данных с SQL-сервера.
Тест: получение данных с SQL-сервера Приведенные ниже три хранимые процедуры получают в качестве входного параметра единственную величину (CustomerlD) и возвращают соответствующее значение CompanyName. После изучения этих процедур можно было бы ожидать, что все они покажут примерно равную производительность: CREATE PROCEDURE dbo.prcCustomerlnfo @CustomerID varchar(5) AS SELECT CompanyName FROM Customers WHERE CustomerlD = @CustomerID GO
I
CREATE PROCEDURE prcCustomerlnfoOUT @CustomerID varchar(5), @MyCompanyName varchar(40) OUT AS SELECT @MyCompanyName = CompanyName FROM Customers WHERE CustomerID=@CustomerID GO CREATE PROCEDURE prcCustomerlnfoXML @CustomerID varchar(5) AS SELECT CompanyName
341
Практическое занятие FROM Customers WHERE CustomerlD = @CustomerID FOR XML RAW GO
Если мы снова создадим небольшое объединяющее приложение для теста (timings2 .exe), которое вызывает каждую из хранимых процедур для каждого потребителя в нашей таблице Customers, то для всех методов мы получим удивительно похожие результаты. Если мы забудем, что сделали только 91 вызов, и представим, сколько вызовов мы можем выполнить за пять секунд, то получим несколько отличающиеся результаты. Очевидно, в этом случае, чем больше число запросов, тем больше производительность: 1 ti Relative Timings 1 M ResutlSet ;
• •
0*pui
\
XML Stream
• XMLRS
ResultSet (или результирующее множество записей) применяетprcCustomerlnfо и стандартный код ADO. Output применяет prcCustomerlnfoOUT и стандартный код ADO.
•
XML RS применяет prcCustomerlnfoXML и стандартный код ADO для возврата в качестве результирующего множества записей.
LJ
XML Stream применяет prcCustomerlnfoXML и потоки для возврата интересующего нас результата.
Потоки - это относительно новая функция (доступна с версии ADO 2.5), предназначенная для извлечения данных с SQL-сервера и быстрой передачи их любому другому объекту, поддерживающему потоки. Мы можем направить потоки к объектам Microsoft XML Parser и IIS Response. Примеры кода для всех этих тестов доступны для загрузки с веб-сайта Wrox. Для небольших наборов записей нет существенных различий между любым из перечисленных механизмов извлечения данных. А что произойдет, если мы станем использовать большие наборы данных? Например, такие, в которых более 2000 строк? Будут ли в этом случае существенные различия в производительности? На приведенном ниже снимке экрана видна потенциальная проблема. Вариант с ADO работает значительно быстрее, чем Stream-вариант. Здесь возникает и другая проблема, специфичная для XML. Эту проблему мы называем раздувание XML (XML bloat). Для представления данных в виде XML необходима дополнительная информация, которая не нужна при двоичном представлении данных в виде результирующего множества записей ADO. Следовательно, передаваемый объем данных в случае применения XML возрастает.
j ResutSei ; \ ,„....:.,; „.,' 1 1 I 1 I 342
XMLRS 4
•ВНГЛгПП : XMLSrIeam !: |
-
0
.
<
\
§
m
S
:
:
i
.
:
v
•
:
;
.
l
• :
.
•
•
.
:
:
:
•
:
;
:
-
;
:
;
;
:
:
i
.
;
-
:
;
Расширение существующей системы возможностями работы с XML В тесте timings2 измеряется эффективность каждого метода при извлечении информации из базы данных. Вы видите, что применение набора данных ADO выглядит более эффективным, а преобразование данных в XML занимает второе место по эффективности. Однако здесь имеется другая причина снижения общей производительности. Эта причина не имеет отношения к базе данных. Мы будем передавать данные нашему приложению, и для этого есть два реальных способа. Один из способов заключается в передаче данных в виде XML (мы будем передавать его в виде строк), другой способ заключается в передаче несвязанного (disconnected) набора данных ADO. Какой из них более эффективен? Мы создаем два класса и серверное приложение для них в виде СОМ+. У каждого класса по одному методу. Методы представляют данные в виде XML-строк или в виде набора данных ADO. Далее мы создаем объединяющее приложение теста для представления результатов в соответствующем виде и передачи информации по сети. Каждый тест будет запускаться трижды, и каждый раз будет отображаться число использованных запросов. В первом примере мы выполним следующие запросы: SELECT * FROM Customers FOR XML RAW SELECT * FROM Customers
47Э1 1211 •
'
7276
'
''•*'••*
W
Deate new ybsecl each time
XML
..
JSELECT " FROM Customers FOR XML RA
Как видим, относительная производительность передачи информации в XML не велика. Но если мы ограничим запросы одной строкой, как показано ниже SELECT * FROM Customers WHERE CustomerID='ALFKI' FOR XML RAW SELECT * FROM Customers WHERE CustomerID='ALFKI'
то увидим, что относительная производительность стала несколько лучше:
|5497 !• ' Create new object each time 1
WHERE CustometD l -A ' LFKI FOR XML ]0M Customers WHERE CustomalD-'ALFKI1!
3 4 3
Практическое занятие I Однако наилучшую производительность мы получим, когда установим реальные ограничения в запросе: SELECT CompanyName FROM C u s t o m e r s WHERE CustomerID='ALFKI'FOR XML RAW SELECT CompanyName FROM C u s t o m e r s WHERE C u s t o m e r I D = ' A L F K I '
I
А вот результаты выполнения тестов с приведенными выше запросами: Ш Relative Timings 3 ADO 2434 15116
4693
7788
6768
i П?. Create new object each time X
M
A
D
L
0
S
J
E
S
L
E
E
L
C
E
C
T
T
c
o
m
p
a
n
y
n
a
m
e
F
R
O
M
C
u
s
t
o
m
e
r
s
c
o
m
p
a
n
y
n
a
m
e
F
R
O
M
C
u
s
t
o
m
e
r
s
Как видно из приведенных выше примеров, XML не является лучшим выбором для передачи больших объемов данных по сети. XML должен применяться в ситуациях, когда мы работаем с небольшими наборами данных, хотим построить гибкое приложение и готовы получить низкую производительность. Это одна из основных характеристик большинства приложений COM/MTS или DNA - мы не должны передавать большие объемы данных, и должны получить максимум от одного вызова.
Существующее приложение Как следует их приведенных выше тестов, оптимальной областью применения XML будут операции с одной или несколькими строками. Другими словами, типичные операции обработки транзакций в реальном времени (OLTP - online transaction processing). Мы продолжим расширять существующее приложение дополнительными функциями, например функцией добавления заказов, с применением новых возможностей SQL Server 2000 по работе с ХМЬ.Так выглядит форма заказа: '
[ ©Add Order
riTinimiiiriiяпмштимнмиммммммя
Custoffle/ . .,,.. jviNET
OK •
Required By |200012-17 Set Shipping Wom^'ronJ Product
Price 4.00 : Singaporean Hokkien Fried Mее 9.80 ; МоггагеНа di Giovanni 34.00
QuanSy: 12 10 5.00
:
DtSCQWli
3
4
4
• . .•
....
•: •
a
A
d
d
e
l
n
e
d
I
t
e
m
0.00 0.00
D
e
t
e
I
t
e
m
0.00 „
!
:
.
.
.
.
£
d
4
l
t
e
m
j
Расширение существующей системы возможностями работы с XML Нас больше всего интересует, что происходит в момент нажатия кнопки ОК. В существующей версии происходит проверка правильности заказа, и затем его данные заносятся в базу данных. Для получения кода, использующего XML, мы должны заменить код существующего клиентского приложения для кнопки ОК кодом, приведенным ниже: Private Sub cmdOK_Click() Dim strXML As String Dim oAddOrder As XMLEventlnterface.IXMLEvent strXML = GenerateOrderXMLO Set oAddOrder = CreateObject("NorthWindEvents.AddOrderEvent") ValidateOutput (oAddOrder.XMLEvent(strXML)) Set oAddOrder = Nothing End Sub
Теперь давайте рассмотрим модификации, необходимые для внедрения возможностей XML и SQL Server 2000. Прежде всего, мы собираемся разработать простой событийный механизм, основанный на XML, для достижения некоторых выгод, обсуждавшихся ранее - гибкости, простоты добавления дополнительных функций. Для этого нам необходимо модифицировать хранимую процедуру вставки заказа для применения поддержки OPENXML. В заключение необходимо оценить, как проделанная нами дополнительная работа упростит дальнейшие модификации приложения. В частности, мы собираемся рассмотреть расширение возможностей приложения операцией автоматического пополнения запасов с некоторыми торговыми партнерами и применение этой инфраструктуры для дополнения приложения возможностями специальных почтовых сообщений. Обе модификации могут быть выполнены без изменения существующего кода (при наличии событийной инфраструктуры на базе XML). •
Необходимо создать интерфейс с именем XMLEventlnterf асе . IXMLEvent.
•
Необходимо создать класс с именем NorthWindEvents .AddOrderEvent для реализации этого интерфейса.
•
Необходимо создать документ XML на основе входных данных и сохранить его в strXML.
•
Мы получим результаты метода XMLEvent класса NorthWindEvents .AddOrderEvent и оценим эти результаты.
Сейчас мы рассмотрим каждый из этих шагов по очереди.
Интерфейсы Интерфейсы - один из подходов (и самый лучший) к проектированию объектов СОМ. Вместо того, чтобы включать все в интерфейс по умолчанию, мы можем создать специальный интерфейс, в котором будет обеспечена полная поддержка группы объектов. Проиллюстрируем сказанное примером. Если мы создадим библиотеку ActiveX DLL (с именем npoeKTaXMLEventlnterf асе) с единственным классом (IXMLEvent) и приведенным ниже кодом в теле созданного класса, то мы не сделаем чего-то по-настоящему полезного. Это просто прототип. Но давайте пройдем чуть дальше: 345
Практическое занятие I Public Function XMLEvent(ByVal EventString As String) As String End Function Public Function XMLEvent_Rollback(ByVal End Function Public Function XMLNotification(ByVal End Function
EventString As String) As String
EventString As String)
Сейчас мы создадим другой проект ActiveX DLL с именемNorthWindEvents, который будет реализовать этот интерфейс (нам необходимо добавить ссылки на предыдущий проект XMLEventlnterfасе), с единственным (по меньшей мере) классом с именем AddOrderEvents. При добавлении к классу AddOrderEvents приведенного ниже оператора Щ I m p l e m e n t s IXMLEVENT
мы получим три дополнительных (private) метода - те самые, которые мы перед этим определили в нашем интерфейсе (см. экранный снимок). Обратите внимание, что с интерфейсами не наследуется реализация функциональности. Каждый класс, который реализует интерфейс, должен обеспечить собственную реализацию. Это причина, по которой программисты предпочитают делегирование: *, NorthwindEvents - Microsoft Visual Basic [design] File Edit Ve i w Project Format Debug Run Query Da i gram Toosl Add-lns Wn i dow JHeSp
Option Explicit Implements IXHLEvents
Private Function IXHLEvents_XMLEvent(ByVal EventString Аз String) As String End Function
Мы создаем XML на основании информации, полученной из формы. Пример документа XML приведен ниже. Обратите внимание, что этот XML применяется исключительно в нашем приложении, и его структура минимизирована с точки зрения объема работы, необходимой для передачи данных между уровнями, в нашу базу данных и из нее: 346
Расширение существующей системы возможностями работы с XML
K/Major> 0 K/Revision>
0 K/Revision> O r d e r OrderID="" CustomerID="VINET" EmployeeID="5" OrderDate="1996-07-04 00:00:00.000" RequiredDate="1996-08-01
J
Эта страница берет построенный нами XML и передает его странице с именем p l a c e o r d e r . asp. Исходный текст этой страницы показан ниже: 0 Then strReturn = SendBiztalk(strBiztalkMessage) End If IXMLEvent_XMLEvent = strReturn End Function
Кроме этого нам понадобится хранимая процедура p r c B i z t a l k O r d e r (которая просто запрашивает изделия, запас которых мы должны пополнить, если он ниже установленного 356
Расширение существующей системы возможностями работы с XML уровня). Эта процедура работает совместно с процедурой BiztalkMessageForProducts (основанной на коде, взятом из книги о BizTalk). Нам также потребуется функция SendBiztalk (текст которой приведен ниже). Необходимо отметить, что мы применяем интерфейс XMLHHTP30, который является частью библиотеки MSXML. Этот интерфейс предназначен для применения на клиентской стороне и безопасен для работы с ASP. Private Function SendBiztalk (ByVal pstrBiztalkMessage As String) As String On Error GoTo ERR_SENDBIZTALK Dim oHTTP As XMLHTTP30 Set oHTTP = CreateObject ("XMLHTTP30") oHTTP.open "POST", strBiztalkURL oHTTP.setRequestHeader "Content-Type", "text/xml" oHTTP.send psrtBiztalkMessage strReturn = "" & pstrBiztalkMessage & "" EXIT_SENDBIZTALK: Set oHTTP = Nothing SENDBIZTALK = strReturn Exit Function ERR_SENDBIZTALK: strReturn = "" & pstrBiztalkMessage & "" Resume EXIT_SENDBIZTALK End Function
Дополнительная функция специальные сообщения Допустим, что мы столкнулись с проблемой - мы заказали слишком много джема Grandma Boysenberry и очень хотим быстро сбыть его. Поэтому мы вынуждены послать каждому покупателю, который мог бы купить этот джем, специальное сообщение по электронной почте с предложением купить баночку (или пару дюжин). Обратите внимание, что когда придет время удалять данное правило, мы можем просто удалить соответствующую строку таблицы. Нам необходимо выполнять проверку всякий раз при добавлении заказа. Поэтому мы создаем класс NorthWindEvents .MailshotValuedCustomer, в который встроен интерфейс IXMLEvent, определенный нами ранее, и вносим два изменения. Первое заключается в выполнении следующего запроса SQL к базе данных Northwind: INSERT tblEVENT VALUES GO
I
prcListProglDsForEvent
('AddOrder',1,3,'NorthWindEvents.MailshotValuedCustomer')
'AddOrder',1
357
Практическое занятие I ProgID NorthWindEvents.ProcessAddOrder NorthWindEvents.BiztalkOrder NorthWindEvents.MailshotValuedCustomer (3 row(s) affected) Второе изменение состоит в написании кода iuiaccaMailshotValuedCustomer и реализации метода XMLEvent для поддерживаемого им интерфейса IXMLEvent. Псевдокод этого класса приведен ниже, а полный код приведен на веб-сайте Wrox: Private Function IXMLEvent_XMLEvent(ByVal EventXML As String) As String Dim strCustomer As String Dim strEmail As String Dim strReturn As String strReturn = "" ' Get the customerlD strCustomer = GetCustomerlD(EventXML) ' Determine the Email (if any) from this strEmail = EmailFromCustomerlD (strCustomer) ' Any email address? If strEmail "" Then strReturn = SendMail(strEmail) End If
Uses CDO
IXMLEvent_XMLEvent = strReturn End Function
Большинство из применяемых здесь процедур (за исключением SendMail) уже были рассмотрены раньше в этом практическом занятии. Здесь требуется сочетание функций быстрого поиска в таблице (EmailFromCustomerlD возвратит адрес электронной почты потребителя) и возврата текстового значения корректного элемента из DOM (GetCustomerlD). Процедура SendMail применяет CDO для отправки стандартных сообщений электронной почты по определенным нами адресам. Как и во всех других приведенных примерах, создание реально работающего приложения не самый главный вопрос. Важно продемонстрировать, что основанная на событиях архитектура обеспечивает ряд выгод, в том числе гибкость и простоту замены объектов.
Итоги практического занятия В этом практическом занятии мы рассмотрели ряд важных вопросов, пытаясь показать, какую выгоду может принести в наших приложениях XML. При правильном применении XML (с учетом выбора оптимальной производительности) и постоянном поиске нового инструментария (новые версии верифицирующих анализаторов XML появляются достаточно регулярно, и их производительность все время растет), XML можно применять для расширения функциональных возможностей приложений без снижения производительности. 358
Расширение существующей системы возможностями работы с XML Как видно из приведенных выше примеров, XML имеет вполне конкретные преимущества. Гибкость и расширяемость XML в сочетании с полиморфизмом поведения в рамках СОМ и СОМ+, дают возможность создавать гибкие и мощные приложения. Эти факторы в сочетании с необходимостью создания XML для других приложений (BizTalk, Web Services и других) делают SQL Server 2000 и его средства для работы с XML исключительно мощными и простыми в применении. Основной вопрос, который мы хотели осветить в этих примерах, заключается в том, как мало кода нужно писать для SQL Server и как много у него функциональных возможностей.
Построение системы ввода заказов на VB и XML В этом практическом занятии мы будем разрабатывать приложение на Visual Basic для демонстрации представления всех данных приложения исключительно на XML. Данные приложения будут храниться в стандартном реляционном виде в безе данных SQL Server 2000. Приложение будет простой системой ввода заказов для торговой точки и называться XMLOrders. В практическом занятии мы не ставим перед собой задачу реализовать реальные функции, модель данных и бизнес-правила настоящей системы. Вместо этого, мы сознательно выбрали дизайн приложения таким, чтобы продемонстрировать специальные технологии, удобные приемы и указать на возможные источники возникновения проблем. В результате мы получим работоспособное приложение, но его нельзя будет рассматривать как шаблон для будущих разработок. Приложение XMLOrders является концептуальной структурой, пользуясь которой системные аналитики и программисты на VB исследуют новые идеи и технологии применения XML. В конце главы приведены рекомендации по дальнейшему развитию приложения. Это практическое занятие не включает в себя весь окончательный код программы, поэтому желательно загрузить код программы. Загрузочный пакет включает две полных версии проекта. Одна версия представляет собой структуру приложения без какого-либо кода (VB1) а вторая — полная система (VB2). Кроме этого, в пакет включены все SQL-скрипты и примеры XML.
13—1683
Практическое занятие И
Требования Для построения приложения требуется следующее операционное окружение: •
Win32 desktop (Windows 2000, SP1 или Windows NT 4.0 SP5);
•
Microsoft Visual Basic 6.0 (SP4);
•
Microsoft XML Parser 2.6, 3.0 или более поздней версии;
•
Microsoft ADO 2.6;
Q •
Microsoft SQL Server 2000 (локальный или удаленный); клиент для Microsoft SQL Server 2000 (Enterprise Manager, Query Analyzer, SQL Profiler).
Разработчики должны быть знакомы со следующими технологиями: •
создание компонентов ActiveX COM на VB;
•
применение ADO для доступа к данным SQL Server;
•
применение Transact-SQL для разработки хранимых процедур.
Спецификации программы Предположим, что мы провели обычный процесс изучения требований к системе и выполнения системного анализа, и согласовали с конечным потребителем набор функций, которые должна выполнять система. Мы также разработали прототипы форм, которые демонстрируют пользователям функциональные возможности системы. Следовательно, мы находимся на стадии, когда функциональные и технические спецификации готовы. Основные задачи программы таковы: •
создание нового заказа на покупку;
•
изменение текущих заказов.
Бизнес-правила выглядят следующим образом:
362
• • •
Заказы могут содержать только продукты из базы данных товаров компании. В каждом заказе должен быть хотя бы один товар. Заказы должны быть оплачены одним из допустимых методов платежей.
•
Комиссионные от завершенной сделки должны быть зачислены конкретному сотруднику, и исчисляться на основании суммы продаж.
•
Заказы могут изменяться в день их создания, поскольку процессы выполнения внешних обязательств и согласований выполняются ночью.
•
Потребители распознаются только по простому текстовому полю произвольной формы.
Построение системы ввода заказов на VB и XML
Проектирование интерфейса пользователя В приложении построен простой интерфейс пользователя (с именем frmMain). Он содержит текстовые окна, комбинированные окна, некоторые управляющие элементы и пару элементов MSFlexGrid. Эта глава не посвящена созданию интерфейса пользователя. Функции описанного здесь интерфейса пользователя минимально достаточны для выполнения демонстрационного примера, не более того. Код на VB минимален и обеспечивает необходимую навигацию и обработку событий. Исключение составляют вопросы применения XML.
Ш1
*. XML Orders File. XML
Order Date
Order No: Empo l yee: Customer
j
Shipping:
f
Charge:
Shpiped On:
L
Ln ie
Paymens t Product [Descrp ito in |Qty . j Unti Prc ie | Total
Commsiso in:
Total: Cancel
Некоторые подробности дизайна формы: •
Переключение полей по клавише Tab начинается с левого верхнего поля и выполняется вниз по форме.
•
Поля Order Date, Shipped On, Commission и Total заблокированы (Locked), их значения устанавливаются программно (вместо этого мы могли бы применить метки, но они могут потребовать в дальнейшем технической поддержки).
•
Изменение данных в таблице выполняется с помощью дополнительной (подчиненной) формы. Мы рассмотрим этот вопрос несколько позже.
363
Практическое занятие II
Проектирование базы данных Структура базы данных полностью традиционна, спроектирована с учетом размещения на SQL Server 2000, хотя и не требует никаких новых возможностей этой версии. Намерение применять XML не влияло на проектирование базы данных! При проектировании базы данных не учитывалось, что текст в формате XML будет храниться в базе данных. Хотя SQL Server 2000 выполняет запросы XML, его сильная сторона заключается в работе с реляционными данными, и не существует убедительных аргументов в пользу отказа от выгод, предоставляемых RDBMS. А выгоды эти таковы: •
развитая система оптимизации запросов;
•
развитый стандартизованный язык SQL;
•
изобилие дополнительных инструментальных средств и технологий;
•
и, что немаловажно, большой опыт, накопленный программистским сообществом в разработке приложений.
Теперь начнем создавать базу данных. Ниже приведен скрипт (сценарий) для создания таблиц. Создайте базу данных с именем wroxdb: USE master
GO CREATE DATABASE wroxdb COLLATE SQL_Latinl_General_CPl_CS_AS GO
I
Самый важный момент здесь заключается в описании сравнения с учетом регистра для базы данных. Из-за того, что в XML различается регистр, необходимо убедиться, что мы воспринимаем данные соответствующим образом. Применение сравнения с учетом регистра, возможно, станет источником дополнительных ошибок в разрабатываемом вами коде. Конечно, нет необходимости делать этот выбор всегда - другие системы, применяющие базы данных, могут применять другие варианты сравнения. Следует учесть основные особенности применения сравнения в документе XML, созданном в SQL-сервере. Имена атрибутов будут взяты автоматически из определения схемы базы данных и формата запроса SELECT. В базе данных, различающей регистр, независимо от текущей схемы сервер позволяет применять в запросах имена таблиц и столбцов в выбранном вами формате. Эта возможная несовместимость будет всегда источником проблемы при обработке XML. Если мы применяем сравнение с учетом регистра, сервер заставит применять корректные имена в вашем SQL и поможет избежать проблем. Ниже приведена схема взаимосвязей объектов, отображающая данные, с которыми мы собираемся работать:
364
Построение системы ввода заказов на VB и XML
orders order no
lines order_no (FK) line no
orderjdate emp_no (FK) customer deliver shippingjchange shipdate adm_rev_no adm time
payments order_по (FK) pmt_no
prodjd(FK) qty unit_price unit_cost commission
products
[__
prodjd descr price cost
pmtJype(FK) amount
pmtjypes l _
pmtjype descr
employees L_
H- emp_no name commission rate
Обратите внимание, что для всех имен выбраны строчные буквы! Это помогает в проверке достоверности информации и делает манипуляции с XML более предсказуемыми, Схема достаточно сложна, чтобы гарантировать, что наше приложение разрешит все проблемы, возникающие при разработке решений OLTP, но в то же время несколько упрощена, чтобы помочь нам избежать создания повторяющегося кода. Остаток кода DDL опущен, чтобы сэкономить место. Полная база данных может быть создана при помощи скрипта wroxdb. sql из загрузочного пакета.
Таблица orders Основные данные системы XMLOrders содержатся в таблице заказов o r d e r s : Столбец
Тип
Комментарии
order no
int
Номер заказа, свойство IDENTITY применяется для присваивания уникального номера
order date
datetime
Дата создания заказа - время не включается
emp no
int
Номер сотрудника, которому должны быть начислены комиссионные. Смотри таблицу employees
customer
varchar(30)
Может быть пустым
365
Практическое занятие И
Столбец
Тип
Комментарии
deliver
tinyint
0 = не доставлено; 1 = доставлено
shipping char ge
money
ship date
datetime
Может иметь значение NULL. Устанавливается внешней системой!
adm rev no
int
Дает механизм для оптимистической стратегии блокирования при параллельном выполнении. Значение этого поля будет объяснено позже
adm time
datetime
Записывает время и дату внесения последних изменений в заказ
Общая сумма заказа
У таблицы o r d e r s две связанные с ней подчиненные таблицы: l i n e s и payments. Теперь давайте рассмотрим их структуру.
Таблица lines Таблица l i n e s содержит одну строку для каждого продукта, купленного в каждом заказе. Столбец
Тип
Комментарии
order no
int
Номер заказа
line no
smallint
Номер строки в рамках каждого заказа. Обычно начинается с единицы
prod id
int
Идентификатор продукта - смотри таблицу продуктов products
qty
int
Количество
unit price
money
Цена за единицу на момент продажи (цена может изменяться)
unit cost
money
Стоимость (себестоимость) единицы (для продавца) этого продукта на момент продажи (стоимость может изменяться)
commission
money
Комиссионные, причитающиеся сотруднику за этот продукт (процент комиссионных commission rate может изменяться)
Мы храним цену, стоимость и размер комиссионных в таблице l i n e s потому, что они хоть и берутся из других таблиц, не являются постоянными величинами. Следовательно, мы должны сохранить значения, вычисленные на момент создания запроса. Общая стоимость заказа, например, заказа с номером 1000, может быть рассчитана таким образом:
366
Построение системы ввода заказов на VB и XML SELECT o.order_no, shipping_charge + SUM(qty * unit_price) AS Total FROM orders AS о INNER JOIN lines AS 1 ON о.order_no=l.order no WHERE o.order_no - 1000 GROUP BY o.order_no, shipping__charge
Тогда как сумма комиссионных: SELECT SUM(qty * commission) AS Total_Commission FROM lines WHERE order_no = 1000
Таблица payments Каждый заказ имеет одну или несколько платежных записей. Столбец
Тип
Комментарии
order no
int
Номер заказа
pmt no
smallint
Номер платежа в заказе. Обычно начинается с единицы
pmt type
char(1)
Тип платежа - смотрите таблицу pmt types
amount
money
Сумма, перечисленная этим способом
Общая сумма платежей по заказу может быть вычислена с помощью такого выражения SQL: SELECT SUM (amount) AS Total__Payment FROM payments WHERE order_no = 1000
Очевидно, мы ожидаем, что общая сумма, вычисленная в результате запроса к таблице l i n e s , в точности совпадет с результатом аналогичного запроса к тaблицepayments.
Таблица employees Таблица employees содержит список всех работающих сотрудников. В системе не должно существовать заказов с некорректным или неизвестным номером сотрудника (поскольку внешний ключ мы собираемся поместить в таблице orders). Столбец
Тип
Комментарии
emp no
int
Номер сотрудника
name
varchar(30)
Имя сотрудника
commission r a t e
float
Процент комиссионных, получаемых при каждой совершенной сделке. Иными словами, значение 2.0 означает 2% от цены товара
Несмотря на то, что процент комиссионных commission_rate может изменяться, наше приложение не имеет функции изменения этой таблицы. 367
Практическое занятие И Таблица products Каждый товар для продажи должен быть определен в таблице p r o d u c t s : Столбец
Тип
Комментарии
prod id
int
Идентификатор продукта
descry
varchar(50)
Описание продукта
price
money
Цена данного товара
cost
money
Себестоимость данного товара
Несмотря на то, что цена и себестоимость товара может изменяться, наше приложение не имеет функции изменения эп^ой таблицы.
Таблица pmtjtypes Каждый метод платежа требует отражения в виде записи в таблицеpmt
types:
Столбец
Тип
Комментарии
pmt type
char (1)
Код типа платежа. Это ПЕРВИЧНЫЙ КЛЮЧ
descry
varchar(15)
Описание
Примеры данных Давайте вставим несколько начальных тестовых данных в базу данных. Сначала подчиненные таблицы: -- employees INSERT employees VALUES (1,'Fred Brown',2.0) INSERT employees VALUES (2,'Bill Jackson',1.2) INSERT employees VALUES (3,'Charlie Watts',0.8) GO -- payment types INSERT pmt_types VALUES ('C , 'Cash' ) INSERT pmt_types VALUES('R','Credit Card') INSERT pmt_types VALUES('D','Debit Card 1 ) go -- products INSERT products VALUES(100,'Bright red jacket',123.99, 88.0)
368
Построение системы ввода заказов на VB и XML INSERT products VALUES(101,'Black trousers',36.99, 24.55) INSERT products VALUES(201,'Men''s trainers',69.50, 48.12) INSERT products VALUES(202,'Ladies trainers',65.00, 41.40) INSERT products VALUES(301,'Cotton Socks',7.49, 5.30) GO
Как видите, мы решили, что наша компания будет продавать предметы одежды. Сейчас мы создадим первый заказ. Поскольку мы решили применять в качестве номера заказа IDENTITY-значение, мы должны выполнить небольшой фрагмент кода для фиксации IDENTITY-значения в качестве номера заказа в подчиненных таблицах. В конечном счете, мы поместим этот фрагмент кода в транзакцию, чтобы гарантировать целостность данных. Если это первый созданный заказ, то он должен иметь номер 1000. -- sample order DECLARE @ord int INSERT orders (order_date,emp no,customer,deliver,shipping charge, adm_rev_no,adm_time) VALUES ('2001-03-10', 1, 'Mrs Jane Smith', 1, $10, 2, GETDATEO) SELECT @ord = @@IDENTITY INSERT lines VALUES(@ord, 1, 101, 1, 36.99, 24.55, ROUND(36.99*0.02,2)) INSERT lines VALUES(@ord, 2, 201, 2, 69.5, 48.12, ROUND(69.5*0.02,2)) INSERT payments VALUES(@ord, 1, ' C , 185.99) GO
Теперь мы имеем полнофункциональную базу данных для тестирования наших запросов во время разработки программы.
Архитектура приложения Структура приложения XMLOrders соответствует стандарту для многоярусных приложений Microsoft Windows DNA. Если вы не знакомы с этим типом архитектуры, обратитесь к книге "Professional Windows DNA" издательства Wrox Press (ISBN 1861004451). В этом практическом занятии показано, что выбор XML в качестве стандарта формата внутренних данных приложения дает существенный эффект. Ключевым вопросом в проектировании приложения является правильный выбор места создания и манипулирования XML. Давайте рассмотрим многоярусную архитектуру, выбранную для приложения XMLOrders. На приведенной ниже схеме показана типичная трехярусная архитектура в Visual Modeler. На схеме изображены основные компоненты, которые нам необходимо реализовать, хотя некоторые из них можно опустить:
369
Практическое занятие II
ЩClass Diagram: Logical View / Three-Tiered Service Model {read-only} U s e r Services
B u s i n e s s Services
Data Services
fj
COrdersDAL frmMain
> COrderBOL %2etOrder() •GetAHEmployeesO •GetProductQ •GetAllPayTypesO *GetReferences()
,.y ^etchOrder()
~"-£i
> C P stDA Lu • Fec thOrdPaymenstO > CLinesDAL
*C Fae c tshO rdo Ld n iu els)(>>
В результате мы получили явное указание - Pos: 1, сообщающее о том, что первый символ тэга employees вызвал проблему. Хотя явная обработка ошибок не будет рассматриваться в оставшейся части главы, ясно, что после любой операции loadXML следует всегда вызывать ParseError.
385
Практическое занятие II Возвратимся к LoadRef s. Нам теперь необходимо заполнить комбинированное окно с информацией о сотруднике данными из employees, представленными в DOM. И здесь разумно создать групповую функцию, поместить ее в модульXMLHelp.bas и затем применять эту функцию заполнения любого комбинированного поля: 1 fill Public
a combo box with the s p e c i f i e d a t t r i b u t e values Sub PopulateCombo(oNode As IXMLDOMNode, cboCombo As ComboBox, sPath As S t r i n g , s A t t r i b As S t r i n g )
Dim
oElem As IXMLDOMNode
Dim
oList As IXMLDOMNodeList
cboCombo.Clear Set oList = FindNodes (oNode, sPath & "/@" & sAttrib) If oList Is Nothing Then Exit Sub For Each oElem In oList cboCombo.Addltern oElem.Text Next End Sub
Обратите внимание на вызов процедуры FindNodes - это простая вызывающая функция для метода selectNodes DOM, предназначенная для изоляции любых ошибок DOM, которые могут возникнуть из-за неверного описания XPath. Теперь добавьте эти процедуры в XMLHelp.bas следующим образом: | ' find a single node using selectNodes syntax Public Function FindNode(oRoot As IXMLDOMNode, sPath As String) As _ IXMLDOMNode On Error GoTo errout Set FindNode = oRoot.selectSingleNode(sPath) Exit Function errout: Set FindNode = Nothing End Function ' find all matching nodes using selectNodes syntax Public Function FindNodes(oRoot As IXMLDOMNode, sPath As String) As IXMLDOMNodeList On Error GoTo errout Set FindNodes = oRoot.selectNodes(sPath) Exit Function errout: Set FindNodes = Nothing End Function
Первая процедура FindNode возвращает ссылку на единственный узел или Nothing, если узел не найден. Вторая процедура FindNodes возвращает ссылку на список узлов или Nothing, если узлы не найдены.
386
Построение системы ввода заказов на VB и XML При использовании функций, аналогичных этим, для запроса DOM, проверка наличия узла (узлов) жизненно важна для предотвращения ошибок выполнения. В обеих функциях для поиска подходящих записей применяется стандартный синтаксис запроса XPath. Давайте вернемся к процедуре LoadRef s в f rmMain и завершим процесс инициализации, заполнив комбинированное поле информацией о сотрудниках: sXML = oBOL.GetReferences oRefDOM.loadXML sXML
I
PopulateCombo oRefDOM.documentElement, cboEmp, "/refs/employees/employee", "name" End Sub
Этот код загрузит в комбинированное поле значения атрибута name всех найденных записей employee. Если вы теперь выполните программу, то сможете увидеть три имени в выпадающем списке, как показано ниже: т, XML Orders File
XML
Ordei No:
Employee:
Shipped On:
Payments Line
|Product
1 Description |Qty
[UnitPrice
|Total
Просмотр XML Во время работы над проектом нам будет необходимо часто просматривать код XML, с которым мы работаем. В окончательной версии приложения возможность просмотра, возможно, будет не нужна пользователям, но ее легко добавить или удалить. Итак, попробуем создать вторую форму с именем f rmXML в приложении XMLOrders. Эта форма содержит простое многострочное текстовое поле. В форме будет единственное Public-свойство для ссылки на существующий документ DOM или любой DOM-узел, и реформатирования XML для отображения с применением рекурсивной функции обхода дерева:
I
Public Property Set XMLDOM(oDOM As IXMLDOMNode) txtXML.Text = FormatXML(oDOM) End Property 1
build a new DOM as a copy of the original 387
Практическое занятие II Private Function FormatXML(oNode As IXMLDOMNode) As String Dim oNew As DOMDocument If oNode Is Nothing Then Exit Function Set oNew = New DOMDocument oNew.loadXML oNode.xml FormatXMLDoc oNew, " " FormatXML - oNew.xml End Function 1 recursive descent into each element node Private Sub FormatXMLDoc(oDOM As IXMLDOMNode, ByVal slndent As String) Dim oChild As IXMLDOMNode Dim oNew As IXMLDOMNode
If oDOM.childNodes.length > 0 Then For Each oChild In oDOM.childNodes FormatXMLDoc oChild, slndent & slndent If oDOM.nodeType = NODE_ELEMENT Then Set oNew = oDOM.ownerDocument.createNode(NODE_TEXT, vbNullString, vbNullString) oNew.nodeValue = vbCrLf & slndent Set oNew = oDOM.insertBefore(oNew, oChild) Set oNew = Nothing End If Next If oDOM.nodeType = NODE_ELEMENT Then Set oNew = oDOM.ownerDocument.createNode(NODE_TEXT, vbNullString, vbNullString) oNew.nodeValue = vbCrLf & Left(slndent, Len(slndent) / 2) Set oNew - oDOM.appendChild(oNew) Set oNew = Nothing End If End If End Sub
Для применения новой формы мы должны добавить некоторые пункты в систему меню frmMain:
Caption: jShowbRefs Name:
| mnShowRefs
Index:
Hep l ContextID: JO Г Checked
f? Enabled
•E&xit XML •Show 8tOrder ••Show (^Template
3 8 8
Shortcut: [(None) j Negota i tePostion: jo-None ^ W Visible
Г WindowUst
Построение системы ввода заказов на VB и XML
Добавим следующий код к f rmMain:
I
Private Sub mnShowRefs_Click() frmXML.Show Set frmXML.XMLDOM = oRefDOM End Sub
Теперь при выполнении программы мы можем щелкнуть по пункту Show Refs меню XML для отображения данных:
< empo l yee emp_no="1" name="Fe i d Blown" commission_rate="2.0000'7> < empo l yee emp_no="2" name="Bil Jackson" commsi $o i n_rate="1.2000'7>
Мы можем использовать ту же технологию для отображения других источников XML, применяющихся в приложении. Форма f rmXML полностью переносима в другое приложение, требующее такой функции.
Загрузка заказа Теперь мы готовы приступить к работе с данными заказа. Как и ранее, мы применяем UML-схему для объяснения процесса (см. илл. на след. стр.). Что же изображено на схеме? •
Последовательность инициируется после ввода пользователем номера заказа в поле txtOrderNo.
•
txtOrderNo V a l i d a t e вызывает функцию LoadOrder.
• •
LoadOrder создает экземпляр COrderBOL и вызывает метод GetOrder. Метод GetOrder из COrderBOL предназначен для сбора данных из трех различных источников: COrdersDAL, CLinesDAL и CPaymentsDAL. Это очень похоже на последовательность, которая используется в методе GetReferences, поэтому, очевидно, мы можем применить аналогичную технологию для выполнения этой задачи.
389
Практическое занятие II
txtOrderNo()
CPavmentsDAL LoadOrderf) getOrdert)
FetchOrderf) XML: FetchOrdLinesO I
^
XML: , j I FetchOrdPayments() \- XML:
XML: UnpackOrderi) •
Как и ранее, первоначально мы будем избегать реализации объектов уровня доступа к данным путем возврата из GetOrder написанного вручную кода XML. В этот раз возвращаться будет единственный фрагмент кода (с применением образца, который мы создали ранее).
В модуль класса COrderBOL введите следующий код: Public
Function GetOrder(OrderNo As Long)
As
String
' hand-coded xml GetOrder = " 0 Then If Not oDOM.loadXML(sXML) Then MsgBox GetDomError(oDOM) Exit Function End If Else Beep MsgBox "Unknown Order No" txtOrderNo.SetFocus Exit Function End If Call UnPackOrder LoadOrder = True cmdSave.Enabled = True bEditing = True End Function
Как видите, мы предусмотрели случай, когда XML не будет возвращен. В этом случае пользователь получит сообщение Unknown Order No. Мы вызываем LoadOrder из обработчика события V a l i d a t e для txtOrderNo в frmMain: Private Sub txtOrderNo_Validate(Cancel As Boolean) Static bBusy As Boolean 1 prevent double invocation If bBusy Then Exit Sub bBusy = True If Val(txtOrderNo.Text) > 0 Then Call LoadOrder End If bBusy = False End Sub
Поскольку м ы загрузили объект oDOM, нам необходимо вызвать процедуру UnPackOrder (входящую в frmMain) для заполнения формы: | Private Sub UnPackOrder() Dim oElem As IXMLDOMElement Dim oEmp As IXMLDOMElement Dim sEmpNo As String Set oElem = FindNode(oDOM.documentElement, "/order") With oElem txtOrderNo.Text = .GetAttribute("order_no") txtCustomer.Text = .GetAttribute("customer") txtOrderDate - XMLDateToField(.GetAttribute("order_date")) sEmpNo = .GetAttribute("emp_no") chkDeliver.Value = Val(.GetAttribute("deliver")) txtShipCharge.Text = .GetAttribute("shipping_charge") UpdateShipCharge 1 may be null txtShipDate.Text = XMLDateToField(GetAttribute(oElem, "ship date"))
392
Построение системы ввода заказов на VB и XML
End With txtOrderNo.Enabled = False
prevent modification during Edit phase
' look up the employee name from emp_no Set oEmp = FindNodeByAttribute(oRefDOM.documentElement, "/refs/employees/employee", "emp_no", sEmpNo) If Not oEmp Is Nothing Then cboEmp.Text - oEmp.GetAttribute("name") fCommisRate = CSng(GetAttribute(oEmp, "commission_rate")) End If Call GetProducts Call GetPayments End Sub
В UnPackOrder мы применили FindNode для обнаружения корневого элемента . Мы могли просто применить следующий код: Set oElem = oDOM.childNodes(0)
Если мы уверены, что наш DOM будет содержать только один корневой элемент , то вполне можем применять метод childNodes. Это более эффективный подход. Главное неудобство данного метода состоит в его скрытости, - что такое childNodes (0)? Мы можем ответить на этот вопрос, только проанализировав D0M, тогда как метод FindNode очевиден. Если структура нашего XML когда-либо изменится, то метод FindNode выдаст сообщение об ошибке (что вполне справедливо), тогда как childNodes (0) будет существовать всегда. Однако это может быть вовсе не то, что вы ожидаете. Создадим объект элемента (oElem), поскольку это дает нам возможность простого доступа к атрибуту по имени: With oElem txtOrderNo.Text = .GetAttribute("order_no") txtCustomer.Text - .GetAttribute("customer") txtOrderDate = XMLDateToField(.GetAttribute("order_date")) sEmpNo = .GetAttribute("emp_no") chkDeliver.Value - Val(.GetAttribute("deliver")) txtShipCharge.Text = .GetAttribute("shipping_charge") UpdateShipCharge ' may be null txtShipDate.Text = XMLDateToField(GetAttribute(oElem, "ship_date")) End With
На атрибуты необходимо всегда ссылаться по имени, а не по порядку childNode. MSXML не гарантирует, что порядок атрибутов останется без изменений во время всего жизненного цикла DOM, поэтому применять численные смещения и неразумно и бесполезно. Не поддавайтесь соблазну!
14—1683
3 9 3
Практическое занятие II Значения дат Ранее мы решили, что все даты в нашем XML будут представлены в формате YYYY-MM-DD. Но мы хотим, чтобы пользователь видел даты в привычном для него формате. Следовательно, в XMLHelp нам необходима специальная функция для преобразования из формата YYYY-MM-DD к локальному укороченному формату (если в программе допускается изменение даты, то нам потребуется функция и для обратного преобразования): Public Function XMLDateToField(sYYMD As String) As String On Error Resume Next XMLDateToField = "" ' empty string default If Len(sYYMD) = 10 Then XMLDateToField = Format(CDate(sYYMD), "Short Date") End If End Function
Значения NULL Пока все идет нормально. Однако когда мы перейдем к атрибуту s h i p _ d a t e , мы встретим проблему - s h i p _ d a t e может иметь значение NULL, и значение NULL опускается при создании атрибута в XML оператором FOR XML. Если мы применяем метод G e t A t t r i b u t e DOM, то в случае, если значение атрибута равно NULL, возникнет ошибка, как и в случае с образцом XML, возвращаемым COrderBOL.GetOrder. Мы могли бы не обрабатывать такую ошибку и представить соответствующее значение пустой строкой. Однако давайте добавим к нашему модулю XMLHelp более дружественную функцию: Public Function GetAttribute(oElem As IXMLDOMElement, Name As String) As String Dim sValue As String 1
if the a t t r i b u t e doesn't exist then return an empty string On Error Resume Next sValue = oElem.getAttributeNode(Name).Text GetAttribute = sValue End Function Сейчас мы можем надежно использовать значение пoляship_date, независимо от того, равно ли его значение NULL или нет (не забывая, что XMLDateToField возвращает в качестве значения пустую строку).
Перекрестные ссылки В приведенном выше коде вы можете отметить, что мы избегали применения номера сотрудника. Мы извлекли значение emp_no во временную переменную sEmpNo: sEmpNo = .GetAttribute("emp_no")
Нам необходимо оформить соответствие sEmpNo элементам списка комбинированного поля, в котором необходимо найти нужное имя. Для этого необходимо выполнить поиск BoRefDOM (из процедуры UnPackOrder): 1
look up the employee name from emp_no Set oEmp = FindNodeByAttribute(oRefDOM.documentElement, "/refs/employees/employee", "erap_no", sEmpNo) If Not oEmp Is Nothing Then
394
Построение системы ввода заказов на VB и XML
cboEmp.Text = oEmp.GetAttribute("name") fCommisRate = CSng(GetAttribute(oEmp, "commission_rate")) End If
Ниже приведена групповая функция для модуля XMLHelp: ' find a particular node by searching attribute values Public Function FindNodeByAttribute(oRoot As IXMLDOMNode, Path As String, Attrib As String, Value As String) As IXMLDOMNode Dim oEl As IXMLDOMNode Dim sQuery As String Set FindNodeByAttribute = Nothing On Error GoTo errout ' search for first matching node using XPath query sQuery = Path & "[@" & Attrib & "='" & Value & " ' ] " Set FindNodeByAttribute = oRoot.selectSingleNode(sQuery) Exit Function errout: Err.Raise Err.Number, "FindNodeByAttribute", Err.Description Set FindNodeByAttribute = Nothing End Function
Как видите, мы снова применили запрос XPath для поиска. Сейчас, однако, мы не ищем элемент просто по имени, нам необходим элемент с определенным атрибутом, равным указанному значению. Эта функция упрощает задачу для разработчиков, поскольку знания синтаксиса XPath для поиска атрибутов не требуется.
Распаковка элементов-потомков Давайте создадим новую процедуру f rmMain с именем Get Products. Основная задача этой процедуры состоит в распаковке элементов l i n e s из DOM заказа. Прежде всего, вспомним, как выглядит XML в oDOM: < employees) tlbimp /out:TheLegacyBusTierPhasel.dll
TheLegacyBusTier.tlb
Tlblmp - TypeLib to .NET Assembly Converter Version 1.0.2204.21 Copyright (C) Microsoft Corp. 2000. All rights reserved. Typelib imported successfully to Thel_egacyBusTierPhase1.dll
Разработчики приложений .NET привыкли использовать команду XCOPY, при помощи которой все библиотеки просто копируются, как часть дистрибутива. Исходную СОМ-библиотеку нельзя просто скопировать. Даже на платформе .NET необходимо зарегистрировать СОМ-сервер. Сгенерированная утилитой библиотека 502
Практическое занятие по технологии .NET TheLegacyBusTierPhasel. d l l использует обычные средства обращения к объектам, специфицированным в исходной библиотеке типов TheLegacyBusTier. t l b . Если создана и зарегистрирована отладочная версия исходного приложения, то загружаться будет именно эта версия СОМ-сервера. Если создана и зарегистрирована рабочая версия исходного приложения, то эта версия библиотеки будет загружаться при любом обращении к СОМ-объектам исходного приложения. Если (по какой бы то ни было причине) библиотека типов претерпит изменения (например, сменится версия ADO), утилиту Tlblmp необходимо будет запустить вновь с тем, чтобы сгенерировать библиотеку "прокси-сборщика .NET" заново. Если этого не сделать, то старый вариант библиотеки не будет знать об изменениях, произошедших в библиотеке типов. Для того чтобы убедиться в доступности СОМ-объектов исходного приложения из С#, будет разработано консольное приложение С#. Это консольное приложение, TheBusTierTestPhasel, создано в среде Visual Studio.NET. Для того чтобы приложение получило доступ к бизнес-уровню исходного приложения, необходимо задать ссылки при помощи диалогового окна, открываемого командой Project | Add Reference в визуальной среде Visual Studio.NET. Окно диалога выглядит следующим образом: [Add Reference .NET Framework COM I Projects | Component Name : Version Configuration System Objects o.o.o.q cscornpmgd 0.0.0.0 CustomMarshae l rs 1.0.2204.21 IEHost 0.0.0.0 In Memory Compe li r 7.0.0.9030 ISymWrapper 0.0.0.0 7.0.0.9031 ipt Engn ie osoft.ComServices 1.0.2204.21 osoft.VisualBasic 1.0.0.0 osoft .VisualBasic. Compati...1.0.0.0 osoft.VisualC 7.0.9030.0 snfr.VicisKhirin.HII Selected Components:
С :\WINNT\Microsof t, NET\Fra С: \WINNT\Microsof t. NET\Fra С: \WINNT\Microsof t, NET\Fra С: \WINNT\Microsof t, NET\Fra С: \WIIMNT\Microsof t. NET^Fra С: \WINNT\Microsof t, NET\Fra C:\WINNT\Microsoft.NET\Fra С: \WINNT\Microsof t. NET\Fra С: \WINNT\Microsof t, NET\Fra С: \WINNT\Microsof t, NET\Fra С: \WINNT\Microsof t. NET\Fra Г: 1 WTNNT\Mirrn Alfreds Futterkiste Maria Anders ANATR Ana Trujillo Emparedados у helados Ana Trujillo На рисунке изображен пример таблицы, отображенной при помощи CustomerTable. asp:
512
Практическое занятие по технологии .NET Ц http://localhost/TheLegacydient/CustomepTabte,esp • I File
Edit
View
Favorites
Tools
,J j ] $
I 4->Back -
Help
^Search ^*j Favorites
-^History
i Address ж ] http://localhost/TheLegacyClient/CustomerTable,asp ICustomerlDj iALFKI
Company Name jAlfreds Futterkiste
iCustomerlD! jANATR
Company Name jAnaTrujillo Emparedados у helados
jCustomerlDj |ANTON
Company Name
[Around the Horn
iCustomerroj jBERGS
Contact Name |АпаТгц]Шо
Company Name
jAntomo Moreno Taqueria
jCustomer Ш | jAROTJT
Contact Name ;Maria Anders
Company Name
iBerglunds snabbkop
Contact Name 1 Antonio Moreno ;|
Contact Name
jThornas Hardy Contact Name IChnstma Berglund
Клиентский уровень в исходном приложении, Shippers В исходном приложении ASP-файл S h i p p e r L i s t .asp отвечает за отображение XML-данных, сгенерированных для объекта Shippers. Эти XML-данные генерируются при помощи FOR XML RAW в хранимой процедуре GetShippersRaw. Хранимая процедура не вызывается напрямую из ASP-страницы, ее вызов осуществляется через посредство бизнес-уровня (объект Shipper и его метод Get). Результирующие XML-данные отображаются в форме списка (сценарий на стороне клиента генерирует HTML). Ниже приведен код S h i p p e r L i s t . asp: Dim xmlDocument Dim rootNode, childNode Dim xmlData Set xmlDocument = ShippersDatalsland.xmlDocument xmlDocument.resolveExternals=false xmlDocument.async=false Set rootNode = xmlDocument.documentElement For each childNode in rootNode.childNodes xmlData - document.all("MyDataList").innerHTML document.all("MyDataList").innerHTML = xmlData & _ "
" & childNode.getAttribute("ShipperlD") & ", " & childNode.getAttribute("CompanyName") & ", " &
513
Практическое занятие V childNode.getAttribute("Phone") & "" Next
Сценарий на стороне сервера, как часть ASP - активной страницы сервера ( S h i p p e r L i s t . asp), решает следующие задачи: •
Создает СОМ-объект типа Shipper: Set shipper =
•
Server.CreateObject("TheLegacyBusTier.Shipper")
В область внутри HTML-страницы записывается нужный идентификатор: Response.Write " "
•
Записывает данные в объект Response: Response.Write strShipper
При загрузке HTML-страницы выполняется сценарий на стороне клиента:
Этот сценарий выполняет следующие манипуляции: •
Получает XML-данные: Set xmlDocument = ShippersDatalsland.xmlDocument
514
Практическое занятие по технологии .NET •
Извлекает данные, связанные с корневым узлом : Set rootNode = xmlDocument.documentElement
•
Просматривает все узлы-потомки корневого узла: For each childNode in rootNode.childNodes
•
Для каждого элемента-потомка отображает список атрибутов. В нашем примере документ называется MyDataList, атрибуты извлекаются следующим образом: childNode.getAttribute("ShipperlD").
HTML-код, генерируемый страницей ShipperList. asp, основывается на списке, генерируемом сценарием клиентской стороны. Ключевая строка HTML -
указывает, что связанный со списком ID создан сценарием на клиентской стороне. Вот образец XML-данных, сгенерированных страницей S h i p p e r L i s t . asp: На рисунке вы видите пример страницы, отображаемой S h i p p e r L i s t .asp: 'Ihttp^/Iocalhost/Thel • - 1ai -~j : File Edit View; Favorites ;: Tools Hep l Ф-B i ack •> '4 3 Ji] tjl ' IT* Personal Bar -^Search w Address ( ^ http://localhosti(TheLegacyClient/Sriipp]^j p^Go j Links w 1, Speedy Express, (503) 555-9831 2, United Package, (503) 555-3199 3, Federal Shipping, (503) 555-9931
W Local intranet
Клиентский уровень в исходном приложении, Order ASP-файл OrderRecordset. asp в исходном приложении служит для отображения XML-данных, сгенерированных для объекта Order. Эти XML-данные генерируются при помощи FOR XML AUTO в хранимой процедуре GetOrdersAuto. Хранимая процедура вызывается через метод Get объекта Order на бизнес-уровне. Результирующий XML использует набор записей ADO на стороне клиента. Вот код страницы OrderRecordset .asp: На рисунке показан пример отображения данных страницей OrderRecordset. asp: •1 http^/localhost/ThelegacyCWent File Edit; Ve i w Favorites Tools Hep l ф»Васк ••* Ф •' ^ [|) fj} ; f£Personal Bar ЩSearch 4.)Favorites Address jifQ http://localhost/TheLegacyClient/OrderRecordset.asp j j f^Go : OrderlD: 10248 Customer ID: VINET Employee Ш: 5 Previous OrderlD
iig]Done'
NextOrderlD _LJ £ iEE Local intranet
При каждом щелчке на кнопке Next OrderlD, текущим в наборе становится следующий элемент Order. Атрибуты этого элемента (OrderlD, CustomerlD и EmployeelD) отображаются на экране. При щелчке на кнопке Previous OrderlD текущим становится предыдущий элемент набора.
Уровень клиента в приложении .NET В цели нашего переноса не входит обязательное использование всех удивительных и экзотических возможностей технологии ASP.NET. Нас вполне удовлетворит простой перенос функций от обычных ASP-файлов к их "коллегам" в ASP.NET. Клиентский уровень в .NET реализован в ASP.NET-проекте с именем ClientDotNet. Среди файлов этого проекта интерес представляют следующие: •
S h i p p e r L i s t . aspx - отображает в виде списка .NET-версию данных объекта Shipper. Этот файл соответствует ASP-файлу S h i p p e r L i s t . asp.
•
CustomerTable.aspx - отображает в виде таблицы .NET-версию XML-данных объекта Customer. Этому файлу в исходном приложении соответствует CustomerTable.asp.
О
OrderRecordset. aspx - отображает .NET-версию XML-данных объекта Order, используя при этом набор записей на стороне клиента. Как легко догадаться, его "кузеном" в исходном приложении является OrderRecordset. asp.
Первоначально, после своего создания, клиентский уровень .NET не ссылался на бизнес-уровень, TheBusTierDotNet. В этом нет ничего удивительного -Visual Studio.NET действительно является фантастическим по своим возможностям инструментом, но чудес все 517
Практическое занятие V же не бывает. На бизнес-уровень необходимо ссылаться из версии 3 клиента при помощи меню Project в среде Visual Studio.NET. Важно отметить, что здесь мы не приводим рисунков для иллюстрации работы клиентского уровня .NET по той простой причине, что они ничем не отличаются от версии ASP (Visual Interdev 6.0), поскольку в версии ASP.NET (Visual Studio.NET) пользовательский интерфейс не изменился. В обеих версиях работает одинаковый HTML-код, ничем не отличаются и сценарии на клиентской стороне. Единственный изменившийся элемент - это сценарии на стороне сервера.
Уровень клиента в приложении .NET, Shippers ASP.NET-файл S h i p p e r s L i s t .aspx отображает XML-данные, сгенерированные для объекта .NET Shippers. Тот факт, что S h i p p e r s L i s t . aspx является клоном ASP-файла в исходном приложении, легко обнаружить, изучив его содержимое: void Page_Load(Object sender, EventArgs EvArgs) { TheBusTierDotNet.Shipper shipper = new TheBusTierDotNet.Shipper(); string strShipper; shipper.Get (out strShipper); Response.Write(""); Response.Write(strShipper); Response.Write("") ; }
Dim xmlDocument Dim rootNode, childNode dim xmlData Set xmlDocument = ShippersDatalsland.xmlDocument xmlDocument.resolveExternals=false xmlDocument.async=false Set rootNode = xmlDocument.documentElement For each childNode in rootNode.childNodes xmlData = document.all("MyDataList").innerHTML document.all("MyDataList").innerHTML = xmlData & "- " & childNode.getAttribute("ShipperlD") & ", " &
518
Практическое занятие по технологии .NET childNode.getAttribute("CompanyName") & ", " & _ childNode.getAttribute("Phone") & Next
Начало файла указывает на язык, использованный в сценарии на стороне сервера (С#):
Пространство имен версии 3 бизнес-уровня обозначается второй строкой файла ShippersList.aspx: После загрузки ASP.NET-страницы генерируется событие Page_Load. Аналогичным образом возникает событие Page_Unload в момент, когда страница выгружается. В файле S h i p p e r s L i s t .aspx содержится обработчик события Page_Load. Этот код выполняется на стороне сервера (заметьте, мы не сказали "сценарии на стороне сервера") и выглядит следующим образом: void Page_Load(Object sender, EventArgs EvArgs) { //**Set shipper = Server.CreateObject("TheLegacyBusTier.Shipper") TheBusTierDotNet.Shipper shipper = new TheBusTierDotNet.Shipper(); //**Dim strShipper string strShipper;
//**strShipper = shipper.Get shipper.Get(out strShipper); //**Response.Write " ••••; dt ©>•••: sql 4
Customer S Ё! Order E j ; CustomerlD Q J j CornpanyName ? 5 ! i ContactName py i City Д | | Fax f|••••; Orders § j • I Order § f f l OrderDetail § S Discount 5 ; Employee Э ffi
Click to import an SQL schema or Drag an SQL module from the Project Explorer.
c^ SampleSchemal §• £3 SQL Modules : '••• i^I Northwind (Northwind.smt) Si Q XDR Modules i : [