Книги, научные публикации Pages:     | 1 | 2 | 3 | 4 | 5 |   ...   | 6 |

том1 а л ь м а н а х программиста Тематический сборник материалов MSDN Library и MSDN Magazine Microsoft ADO.NET ...

-- [ Страница 3 ] --

Двоичная ADO.NET-сериализация в период выполнения.NET Framework предоставляет два предопределенных форматирующих объекта в пространстве имен System.Runtime.Serialization.Formatters: дво ичный и форматирующий объект SOAP. Их классы Ч соответственно BinaryFormatter и SoapFormatter. Первый генерирует более компактный код, а второй рассчитан на более широкое взаимодействие и создает опи сание класса в формате SOAP.

Следующий код иллюстрирует, что нужно сделать для сериализации объ екта DataTable с применением двоичного форматирующего объекта:

BinaryFormatter bin = new BinaryFormatterO;

StreamWriter dat = new StreamWriter(outputFile);

bin.Serialize(dat.Basestream, dataTable);

dat.CloseO;

Метод Serialize форматирующего объекта заставляет последний записы вать содержимое сериализуемого объекта в двоичный поток. Метод Dese rialize выполняет обратную операцию: считывает ранее созданный двоич ный поток, воссоздает объект и возвращает его:

DataTable table = new DataTableO;

BinaryFormatter bin = new BinaryFormatterO;

StreamReader reader = new StreamReader(sourceFile);

table = (DataTable) bin.Deserialize(reader.BaseStream);

reader.CloseC);

Пока все замечательно. Однако при запуске этого кода произойдет кое-что неожиданное. Если вы попытаетесь сериализовать объект DataTable или DataSet с помощью двоичного форматирующего объекта, то двоичный файл, конечно, получите Ч только он окажется весьма большим, с тонной XML-данных. К сожалению, XML-данные занимают много места в двоич ном файле. Десериализация таких файлов может потребовать нескольких секунд и гораздо больше памяти, чем нужно на самом деле. В результате, выбрав двоичную сериализацию ADO.NET-объектов в стремлении полу чить более компактный файл, вы не достигнете своей цели. Двоичная се риализация применительно к ADO.NET-объектам оказывается не столь 142 Microsoft ADO.NET эффективной, какой могла бы быть. В чем же причина такого довольно странного поведения?

ADO.NET-объекты, сериализуемые с помощью форматирующих объектов (только классы DataTable и DataSet), корректно реализуют интерфейс ISerializable, а значит, именно они отвечают за предоставление сериализу емых данных. Этот интерфейс состоит из единственного метода Get ObjectData, результат работы которого принимает форматирующий объ ект и сбрасывает в выходной поток.

Классы DataTable и DataSet спроектированы так, что сообщают о себе форматирующим объектам на основе XML-данных. В частности, они ис пользуют документ DifrGram, т. е. простой и многословный XML. Дво ичный форматирующий объект принимает эту достаточно длинную стро ку и добавляет ее в поток. Таким образом, эти объекты всегда передаются между доменами в виде XML, и это здорово. Увы, если вам нужно более компактное представление сохраненных таблиц, обычная сериализация периода выполнения, поддерживаемая.NET Framework, для ADO.NET объектов не годится (по крайней мере, без модификации).

Следующий псевдокод иллюстрирует сериалшацию этих объектов форма тирующим объектом.NET;

void GetObjectData(SerializationInfo Info, StreamingContext context) { info.AddValue("XrnlScfiema", this.GetXmlSchemaO);

this.WriteXmKstrWriter, XmlWriteMode.DiffGram);

info.AddValueC'XmlDiffGram", strWriter.ToStringO);

:

Класс передает свои данные форматирующему объекту, добавляя элемен ты в объект Serializationlnfo методом AddValue. Например, объект DataSet сериализует себя, добавляя несколько элементов для схемы и данных.

Объект DataTable делает то же самое, но использует временный объект DataSet, чтобы получить свою схему и данные, выраженные в виде XML строк, Информация, записанная в Serializationlnfo, затем сбрасывается в двоичный или SOAP-поток в зависимости от типа форматирующего объекта.

Нестандартная двоичная сериализация ADO.NET-объектов Есть только один способ оптимизировать двоичное представление объек та DataTable (или DataSet) Ч сопоставить класс промежуточному объек ту, сериализациеи которого вы можете управлять. Теоретически этого Двоичная сериализация ADO. NET-объектов можно добиться несколькими методами. Например, создать пользователь ские классы Ч оболочки функциональности классов DataTable и DataSet, Ч реализующие свой алгоритм сериализации в период выполнения. Или ис пользовать.NET-pecypc, созданный специально для решения таких про блем, Ч суррогатные типы (surrogate types). Суррогатный тип Ч это класс, берущий на себя работу по сериализации и десериализации данного типа независимо от того, сериализуем он или нет. Суррогатный тип для конк ретного класса регистрируется в выбранном форматирующем объекте, а далее обрабатывается, как обычно. Форматирующий объект распознает суррогатный тип и сериализует его вместо исходного переопределенного типа. Но по причинам, связанным с архитектурой, суррогаты не работают с объектами DataTable (я поясню это позже).

Нестандартная двоичная сериализация ADO.NET-объектов требует от вас создать пользовательский класс, поддерживающий нужный тип сериали зации, предоставить класс с сериализуемыми данными и, наконец, выпол нить сам процесс сериализации или десериализации.

Если вы пишете пользовательский класс, полностью управляющий сери ализацией DataTable (или DataSet), вам придется создать либо своего рода теневой сериализующий класс (ghost serializer>

Создание теневого сериализующего класса Если вам нужно сохранять только строки и столбцы объекта DataTable, создание теневого класса не займет много времени. Вот пример структу ры такого класса:

[Serlalizable] public>

protected ArrayList colTypes;

protected ArrayList dataRows;

public GhostDataTableSerializerO {... } public void Load(DataTable dt) {...} Microsoft ADO.NET public DataTable SaveC) {... } !

Полностью исходный код этого класса приведен на рис. 4. Ghost DataTable Serializer содержит три объекта ArrayList, хранящие имена столбцов, их типы и строки данных. Заметьте, что класс ArrayList Ч сериалиэуемый. В приложении объект DataTable сериализуется с использованием сервисов теневого сериализующего класса:

void BinarySerialize(DataTable dt, string outputFile) { BinaryFormatter bin = new BinaryFonnatter();

StreamWriter dat = new StreamWriter(outputFile);

// Создать экземпляр теневого сериализатора // и заполнить его данными GhostDataTable ghost = new GhostDataTablef);

ghost.Load(dt);

// Сериализовать объект bin.Serialize(dat.BaseStream, ghost);

dat.CloseC);

Рис. 4. Класс GhostDataTabteSettalizer using System;

using Systeffl.Collections;

using System.Data;

// Рабочий класс, выполняющий двоичную сериализацию [SerlalizableJ public>

colTypes = new Arraytistf);

dataRows = new ArrayListO;

// Свойства protected ArrayList eolNantes;

protected ArrayList colTypes;

protected ArrayList dataRows;

см. след. стр.

Двоичная сериализация AOO.NET-объектой Рис. 4. Класс GhostDataTableSeriatizer // Заполнить пользовательский объект таблицы для сариализации public void Uad(DataTable dt) { // Вставить информацию о столбцах (имена и типы) foreachCDataColumn col in dt. Columns) colTypes. Add(col, DataType. FullHanie);

\ // Вставить информацию о строках foreachtOataflow row in dt.Ftows) dataflows. Add(row.ItemAr ray);

// Вернуть объект DataTable, заполненный данными public fiatsTable SaveC) i DataTatle dt = new OataTableO;

// Добавить столбцы foKirvt 1=0;

i

1-й-) { OataColumn col = new DataColufflrr(colHaiBesti3.ToString(), Tyoe.6etType(colTypes[l].ToString() ));

dt,Columns.,Add

// Добавить отроки for(int 1=0;

KdataRows, Count;

DataRow row = dt.NewRowO:

row. IteraArray = (ob}ect[]) dataRowstl];

dt, Rows. Add{ row);

dt, AcceptChangesC } ;

return dt;

} 146 Microsoft ADO.NET Реализация JSerializable Другой, более элегантный и эффективный способ сериализащш объек та DataTable в истинно двоичный поток заключается в создании про изводного класса и реализации в нем интерфейса ISerializable. Этот способ позволяет еериализуемому классу контролировать свою сери ализацию. В чем его преимущества? Применяя ISemlizable, вы обра щаетесь к данным только раз. В противоположность этому теневой сериализующий класс сначала копирует данные из DataTable в своя свойства и только оттуда они действительно сернализуются в поток, Как видите, в конечном итоге данные перемешаются дважды. Кроме того, решение на основе интерфейса ISerializable будет в большей мере совместимым со следующими версиями ADO.NET.

Класс BinDataTable (пример его структуры показан ниже) может ис пользовать те же приемы для решения своей задачи, что и теневой се риализующий класс, ESerializatele] риШе>

QetObjectDataCSerializatlonlnfo si, StreafltingContext ctx) Метод GetObjectData копирует информацию, важную для DataTable, в ;

потоковый контекст. Защищенный конструктор сериализующего клас са делает обратное: извлекает данные из потока и инициализирует но венький объект BinDataTable. Полный исходный код, демонстрирую щий данный подход, можно скачать с сайта MSDN Magazine по уже.

приводившейся ссылке.

Самое важное, на что здесь следует обратить внимание: как объект Data Table сопоставляется с классом GhostDataTableSerializer. Сопоставление осуществляется в методе Load.

В нем рабочие массивы (colNames и colTypes) заполняются именами и ти пами столбцов, Внутренний массив DataRow содержит массив объектов, соответствующих всем значениям в строке.

Двоичная сериализация ADO.NET-объектов public void Load(DataTable dt) { foreach(DataColunm col in dt.Columns) { colNames.Add(col.ColumnName);

colTypes.Add(col.Datatype,FullName);

foreach(DataRow row in dt.Rows) dataRows.Add{row.ItemArray);

Свойство ItemArray объекта DataRow Ч массив объектов. Оно особенно полезно, так как позволяет обращаться к содержимому всей строки как к единому, монолитному блоку данных. Внутренний аксессор чтения (get accessor) этого свойства реализован в виде простого цикла, извлекающего и сохраняющего один столбец за другим. Аксессор записи (set accessor) еще ценнее, так как он автоматически группирует все изменения в вызо вы пары BeginEdit/EndEdit и при необходимости генерирует события, связанные с модификацией столбцов.

Заметьте, что вызов AcceptChanges (рис. 4) сбрасывает состояние всех строк в Unchanged, препятствуя достижению одной из основных задач се риализации Ч сохранению состояния объекта. Если вы прибегнете к ка ким-нибудь уловкам и откажетесь от вызова этой функции, все строки будут помечены как Added. Другими словами, состояние строк не сохра нится, и тогда, чтобы добиться нужной цели, теневому сериализатору при дется возиться еще и с информацией о состоянии.

Рис. 5 иллюстрирует работу приложения-примера. Как видите, объект DataTable, сериализованный с помощью теневого класса, на 80% меньше, чем такой же объект, сериализованный обычным способом с использова нием того же двоичного сериализатора. Возьмем, к примеру, объект Data Table, получаемый в результате выполнения запроса:

SELECT * FROM [Order Details] ilsiiio a Ghost berlaiazef ties*.

Рис. 5. Приложение-пример в действии 148 Microsoft ADO.NEt Таблица содержит пять столбцов и более 2000 записей. Если ее сериали зовать двоичным форматирующим объектом как объект DataTable, она займет полмегабайта. А при использовании промежуточного теневого се риализатора конечный файл будет примерно на 83% меньше.

Конечно, такие впечатляющие результаты получаются не всегда. Чем больше та часть таблицы, в которой содержатся числа, тем больше места вы экономите. И наоборот, чем больше полей типа BLOB, тем меньше вы года. Попробуйте выдать следующий запрос к базе данных Northwind в SQL Server, где BLOB-поле содержит фотографию работника:

SELECT photo FROM employees Здесь экономия составит всего 25%, и это максимум, чего я добился в сво их тестах. Причина, думаю, очевидна. BLOB-поля сами содержат двоичные данные, поэтому экономия невелика.

Десериализация данных После того как двоичные данные десериализованы, вы должны преобразо вать экземпляр теневого класса обратно в объект DataTable. Посмотрим, как это делается в приложении-примере:

DataTable BinaryDeserialize(string sourceFile) { BinaryFormatter bin = new BinaryFormatterO;

StreamReader reader = new StreamReader(sourceFile);

GhostDataTableSerializer ghost;

ghost = (GhostDataTableSerializer) of.Deserialize(reader.BaseStream);

sr.Closef);

return ghost.Savef);

} Метод Save пользуется информацией, сохраненной в массивах теневого класса, для добавления строк и столбцов к только что созданному объек ту DataTable (рис. 6).

А как насчет суррогатных типов?

Сериализация объекта DataTable с помощью суррогатного класса проста, эффективна и не создает никаких проблем. Однако из-за того, что проис ходит при десериализации, суррогаты непрактичны для ADO.NET-объек тов. Ниже показан фрагмент кода, который вы, возможно, захотите напи сать для сериализации объекта DataTable через суррогатный класс Data TableSurrogate:

Двоичная сериализацмя ADQ.NET-объектов Рис. 6. Добавлениеетйок и столбцов public DataTable Save() { DataTable dt - new DataTableO;

// Добавить столбцы for(int 1=0;

KcolNames. Count;

BataColuim col = new OataColumn(colNames[i].ToStrlng(), Type.GetType(eolTypes[i].ToString()));

dt.Coiueins.Add(col);

// Добавить строки forfint i=0;

Kdataflows.Count;

i-н-) { OataRow row = dt.NewRowO;

row.IteieArray = (objectU) dataRows[i];

dt.Rows.Add(row);

dt.AceeptChanges();

return dt;

* SurrogateSelector ss = new SurrogateSelector();

DataTableSurrogate dts = new DataTableSurrogate();

ss.AddSurrogate(typeof(DataTable), new StreamingContext(StreamingContextStates.All), dts);

formatter.SurrogateSelector = ss;

formatter.Serialize(dat,BaseStream, dt);

Сначала вы создаете селектор суррогата (surrogate selector) и добавляете суррогатный тип в цепочку суррогатов. Затем связываете селектор с фор матирующим объектом. Суррогатный класс реализует интерфейс ISerializa tion Surrogate, состоящий из двух методов: GetObjectData и Set Object Data.

Первый из них добавляет элементы в набор Serializationlnfo, как показа но на рис. 7. На рис. 8 приведена возможная реализация метода, имитиру ющего поведение теневого сериализующего класса, рассмотренного ранее.

Что касается быстродействия, то результаты практически одинаковы.

Как я уже говорил, причины, по которым суррогаты не годятся для сериа лизации ADO.NET-объектов, связаны с архитектурой этих объектов. Они крайне сложны, и их конструкторы играют очень важную роль, Чтобы объект DataSet или DataTable работал корректно, вы должны вызвать один из его предопределенных конструкторов. А исполняющая среда.NET при 150 Microsoft ADO.NET обработке суррогатов не пользуется конструкторами для создания экзем пляров объектов. Вместо этого она обращается к статическому методу GetUninitializedObject класса FormatterServices, и новый экземпляр объек та инициализируется нулями. Кроме того, поскольку конструкторы не за пускаются, экземпляр может оказаться в рассогласованном состоянии, и вроде бы безопасные операции могут вызывать неожиданные сбои.

Рис. 7. Суррогатный класс public>

AddSerializationInfo(

public object SetObjectData(object obj, Serializationlnfo info, StreamingContext context, JSurrogateSelector selector) DataTable dt * (DataTable) obj;

ReadSerializationInfo(dt, info);

return null;

Pлc. 8. Имитация теневого сериализатора protected void Flll(BataTable dt. Serializationlnfo info) { ArrayList colNames - new ArrayLlstO;

Array-list colTypes = new ArrayListO;

ArrayList dataflows = new ArraylistC);

// Вставить информацию о столбцах (имена и типы) // в рабочие массивы foreacnCDataColumfl col in dt.Columns) colNames.Add

colTypes.Add(col.DataType.FullName);

// Вставить информацию о строках в рабочие массивы f0reacft(DataRow row in dt.Roлs) dataflows. Ш( row. IteaArray);

см. след. стр.

Двоичная сериализация АОО.ЫЕТ-объектов Рис. 8. Имитация теневого сериализатора (окончание) // Добавить массивы в структуру с информацией, // необходимой для сериализации info,AddValae{"ColNaies", colNames);

info.AddValue("ColTypes", colTypes};

info.AddValue<"DataRows", dataflows);

Например, конструктор объекта DataTable по умолчанию выполняет длин ный список операций по инициализации членов. И не все внутренние чле ны этого объекта инициализируются значениями null. На самом деле этот объект опирается на множество внутренних массивов и других вспомога тельных объектов, инициализируемых конструктором. Метод GetUninitia HzedObject очищает блок памяти и не инициализирует ни один из таких объектов. С другой стороны, суррогатный класс вряд ли посвящен в тон кости внутренней реализации замещаемого им класса. То есть, чтобы точ но знать, что нужно для корректной инициализации объекта DataTable, вы должны сначала изучить его исходный код, Теперь допустим, вы сделали это (с помощью.NET-декомпилятора). Тогда вам придется вовсю пользо ваться механизмом отражения для доступа к внутренним и закрытым чле нам объекта DataTable, Короче говоря, теневой сериализатор дает практичес ки ту же экономию дискового пространства, но гораздо проще в создании.

Заключение Под занавес хочу еще раз отметить, что объективные трудности с исполь зованием суррогатных типов в процессе ADO.NET-сериализации возника ют по двум причинам. Первая Ч упомянутые объекты крайне сложны и связаны с другими объектами. Однако, согласно Джеффри Рихтеру, есть и другая причина, которая, надеюсь, скоро будет устранена, Ч ошибка в коде форматирующего объекта. Она приводит к тому, что SetObjectData не воз вращает совершенно новые типы. После того как ошибку исправят, вы сможете легко избегать использования системой метода Get Uninitialized, вызывая конструктор объекта из SetObjectData и возвращая созданный объект форматирующему объекту. Подробнее об этой ошибке см. статью в рубрике л.NET в сентябрьском выпуске журнала за 2002 г. ( msdn.microsoft.com/msdnmag/issues/02/09/net/default.aspx).

Дино Эспозито (Dino Esposito) Ч консультант и преподаватель из Рима.

Автор книг Building Web Solutions with ASP.NET and ADO.NET и Applied XML Programming for.NET (обе выпущены издательством Microsoft Press в 2002 г.). С ним можно связаться по адресу dinoe@wintellect.com.

Прийя Дхаван Разработка распределенных приложений в.NET В этой статье описывается, как выполнять локальные и распределенные транзакции в приложениях Microsoft.NET.

Введение Транзакция Ч это последовательность операций, выполняемых как единое целое. Благодаря объединению взаимосвязанных операций в транзакцию гарантируется согласованность и целостность данных в системе, несмотря на любые ошибки, которые могли возникнуть в ходе транзакции. Для ус пешного выполнения транзакции необходимо успешное завершение всех ее операций.

У транзакции есть начало и конец, определяющие ее границы (transaction boundaries), внутри которых транзакция может охватывать различные про цессы и компьютеры. Все ресурсы, используемые в ходе данной транзак ции, считаются участвующими в этой транзакции. Для поддержания цело стности используемых ресурсен транзакция должна обладать свойствами ACID: Atomicity (атомарность). Consistency (целостность), Isolation (изо ляция) и Durability (отказоустойчивость). Подробнее об основах обработ ки транзакций см. Processing Transactions ( Priya Dhawan Transaction Control. Building Distributed Applications with.NET//MSDN Library. 2001. November. Ч Прим. изд.

Разработка распределенных приложений в.NET J. library/en-us/cpguide/html/cpconprocessingtransactions.asp) в Microsoft.NET Framework SDK и в Microsoft Platform SDK.

В этой статье показывается, как выполнять локальные и распределенные транзакции в приложениях Microsoft.NET.

Локальные и распределенные транзакции Локальной называется транзакция, областью действия которой является один ресурс, поддерживающий транзакции, Ч база данных Microsoft SQL Server, очередь сообщений MSMQ и др. Например, отдельно взятая СУБД может вводить в действие правила ACID, когда в ее распоряжении имеют ся все данные, участвующие в транзакции. В SQL Server предусмотрен внутренний диспетчер транзакций (transaction manager), предоставляю щий функциональность для фиксации (commit) и отката (rollback) тран закций.

Распределенные транзакции могут использовать гетерогенные ресурсы, поддерживающие транзакции, включать самые разнообразные операции, например выборку информации из базы данных SQL Server, считывание сообщений Message Queue Server и запись в другие базы данных. Програм мирование распределенных приложений упрощается программным обес печением, способным координировать фиксацию и откат, а также восста новление данных, хранящихся в различных ресурсах. Одной из таких тех нологий является Microsoft DTC (Distributed Transaction Coordinator).

DTC реализует протокол двухфазной фиксации (two-phase commit proto col), гарантирующий непротиворечивость результатов транзакции во всех ресурсах, участвующих в этой транзакции. DTC поддерживает только при ложения, в которых реализуются совместимые с ним интерфейсы управ ления транзакциями. Эти приложения называются диспетчерами ресурсов (Resource Managers). [Дополнительную информацию по этой теме см. в Distributed Transactions ( de/htnil/cpcondistributedtransactions.asp) в.NET Framework Developer's Guide.] В настоящее время существует довольно много таких приложений MSMQ, Microsoft SQL Server, Oracle, Sybase и др, Транзакции баз данных Вызов хранимой процедуры (stored procedure), которая заключает необхо димые операции в операторы BEGIN TRANSACTION и COMMIT/ROLL BACK TRANSACTION, дает наилучшую производительность, позволяя выполнить транзакцию с разовым обменом данными е сервером (single round-trip). Кроме того, транзакции баз данных могут быть вложенными, т. е. внутри активной транзакции можно начать выполнение новой тран закции, Microsoft ADO.NET В следующем фрагменте кода оператор BEGIN TRANSACTION начинает новую транзакцию. Транзакцию можно завершить двумя способами: либо фиксацией изменений в базе данных оператором COMMIT TRANSAC TION, либо (при возникновении какой-либо ошибки) отменой всех изме нений оператором ROLLBACK TRANSACTION.

CREATE PROCEDURE Prod AS -- Начинаем транзакцию BEGIN TRANSACTION -- Выполняем операции транзакции Ч Проверяем наличие ошибок If @@Error О О - Откатываем транзакцию ROLLBACK TRANSACTION - Фиксируем транзакцию COMMIT TRANSACTION Показанная ниже хранимая процедура принимает в качестве входного па раметра XML-представление информации о заказе (order). Для выполне ния соответствующих вставок в таблицы Orders и OrderDetails хранимая процедура загружает и анализирует XML с помощью системной хранимой процедуры sp_xrnlpreparedocument. Как видно из исходного кода, все опе рации хранимой процедуры включены в явно выполняемую транзакцию, поэтому при неудачном завершении любой операции все внесенные изме нения отменяются (откатываются).

Заметьте, что процедура устанавливает флаг XACT_ABORT в ON, указы вая, что SQL Server должен автоматически откатить транзакцию, если выполнить какой-нибудь оператор не удастся.

CREATE PROCEDURE InsertOrder @0rder NVARCHAR(4000) = NULL, eOrderld int Output AS SET NOCOUNT ON DECLARE @hDoc INT DECLARE @PKId INT -- Указываем, что SQL Server должен автоматически откатывать текущую -- транзакцию, если оператор Transact-SQL генерирует ошибку периода Ч выполнения (run-time error) SET XACT_ABORT ON - Начинаем транзакцию BEGIN TRANSACTION - Загружаем и анализируем содержимое входного XML-представления Разработка распределенных приложений в.NET -- информации о заказе, а затем помещаем его в XMLDocument EXEC sp_xml_preparedocument @hDoc OUTPUT. @0rder -- Выбираем заголовок заказа из XMLDocument-уэла Orders -- и вставляем его в таблицу Orders INSERT Orders(CustomerId, OrderDate, ShipToName, ShipToAddressId, OrderStatus) SELECT Customerld, CONVERT(DateTime,OrderDate), ShipToName, ShipToAddressId, OrderStatus FROM OPENXHL(@hDoc, '/NewDataSet/Orders') WITH ( Customerld int 'Customerld1, OrderDate nvarchar(23) 'OrderDate', ShipToName nvarchar(40) 'ShipToName', ShipToAddressId int 'ShipToAddressId', OrderStatus int 'OrderStatus') -- Выбираем Orderld заказа, только что вставленного в таблицу Orders Ч для использования при вставке позиций заказа (order details) SELECT SPKId = ййIDENTITY - Выбираем позиции заказа из XHLDocument-узла Details -- и вставляем их в таблицу QrderDetails INSERT OrderDetails (Orderld, Itemld, UnitPrice, Quantity) SELECT оPKId as Orderld, Itenld, UnitPrice, Quantity FROM OPENXML(@hDoc, '/NewDataSet/Details') WITH (Itemld int 'Itemld', UnitPrice money 'UnitPrice', Quantity int 'Quantity') - Присваиваем значение выходному параметру Select @0rderld = QPKId - Фиксируем транзакцию COMMIT TRANSACTION EXEC sp_xml_removedocument @hDoc RETURN GO Хотя такой подход обеспечивает хорошую производительность, при его использовании приходится программировать на Transact SQL, а это слож нее, чем на языке, совместимом с.NET.

Транзакции вручную Транзакции, выполняемые вручную (manual transactions), позволяют явно управлять границами транзакции с помощью команд начала и конца тран закции. В этой модели также поддерживаются вложенные транзакции, т. е.

вы можете начинать новую транзакцию в рамках активной транзакции.

156 Microsoft ADO.NET Расплата за возможность такого управления заключается в том, что на вас ложится бремя включения в транзакцию нужных ресурсов данных и их координация. Встроенной поддержки распределенных транзакций нет, поэтому управление распределенной транзакцией вручную потребует взять на себя массу обязанностей. Вам придется управлять каждым под ключением к ресурсу и его использованием, а также реализовать поддер жку свойств ACID в транзакции.

Транзакции ADO.NET, выполняемые вручную Транзакции вручную поддерживают оба провайдера данных Microsoft ADO.NET, которые предоставляют набор объектов, позволяющих созда вать соединение с хранилищем данных, начинать транзакцию, фиксиро вать или откатывать ее и, наконец, закрывать соединение. В примерах мы будем использовать управляемый ADO.NET-провайдер SQL (ADO.NET SQL managed provider).

Для выполнения операций в рамках единой транзакции нужно создать объект SQLTransaction, начать транзакцию с помощью объекта SQLCon nection, добиться, чтобы все операции над базой данных проходили в этой транзакции, и зафиксировать или отменить транзакцию. Объект SQLTran saction предоставляет целый ряд свойств и методов для управления тран закцией. При успешном выполнении всех операций транзакции вы може те зафиксировать изменения в базе данных методом Commit. Для отката изменений применяется метод Rollback объекта SQLTransaction.

Примечание Для выполнения SQL-команды в транзакции свойство Transaction объекта Command необходимо установить на уже начатую транзакцию.

Visual Basic.NET Dim conn as SQLConnection Dim cmd as SQLComrcand Dim txn As SQLTransaction conn = New SQLConnectionC'ConnString") cmd = New SQLCommand Открываем соединение conn.OpenO Начинаем транзакцию txn - conn.BeginTransaction() Настраиваем свойство Transaction на транзакцию, где выполняется ' SQL-команда cmd.Transaction - Txn Разработка распределенных приложений в.NET Visual C#.NET SQLConnection Conn = New SQLConnectlon(TTConnString");

SQLCommand Cmd = Kew SQLCommand;

// Открываем соединение Conn.OpenO;

// Начинаем транзакцию 3QLTransaction Txn ~ Conn.Begin!ransaction();

// Настраиваем свойство Transaction на транзакцию, где выполняется // SQL-команда Crnd.Transaction - Txn;

В примере, представленном ниже, мы выполняем в рамках транзакции две SQL-команды. Первая вставляет заголовок заказа (order header) в табли цу Orders и возвращает Orderld только что созданного заказа. Этот Order Id используется во второй команде, которая вставляет позиции этого за каза в таблицу OrderDetails. Транзакция отменяется, если хотя бы одна из двух команд терпит неудачу;

при этом строки в базу данных не добавля ются.

Visual Basic.NET Dim conn As SqlConnection Dim cmd As SqlComntand Dim tran As SqlTransaction Создаем новое соединение conn = New SqlConnection("ConnString") ' Открываем соединение conn.OpenO ' Создаем объект Command cmd = New SqlCommandO Создаем транзакцию tran = conn.BeginTransaction ' Настраиваем свойство Transaction на транзакцию, где выполняется SQL-команда cmd.Transaction = tran Try ' Вставляем заголовок заказа. Настраиваем свойства Command.

With cmd.CommandType = CommandType.StoredProcedure,CommandText = "InsertOrderHeader".Connection = conn ' Добавляем входные и выходные параметры.Parameters.Add("@Customerid", SqlDbType.Int).Parameters("@Customerid").Direction = ParameterDirection.Input Устанавливаем значения параметров.Parameters("зCustomerId").Value = Microsoft ADO.NEt ' Выполняем команду. ExecuteNonQueryO ' Получаем Qrderld добавленного заголовка заказа Qrderld =.Pararneters("@0rderld").Value ' Очищаем параметры для следующей команды.Parameters.clearf) End With ' Вставляем позиции заказа. Настраиваем свойства Command.

With cmd.CommandType = CommandType.StoredProcedure,CommandText = "InsertOrderDetail".Connection = conn ' Добавляем параметры.Parameters.Add("0rderld", SqlDbType.Int). Parameters("@|OrderId"}.SourceColumn = "Orderld".Parameters("@0rderld").Direction = ParameterDirection,Input ' Устанавливаем значения параметров.Parameters*"@0rderld").Value = Orderld.Parameters("@ltemld").Value = ' Выполняем команду, ExecuteNonQueryO ' Повторяем показанные выше строки для каждой позиции заказа End With ' Фиксируем транзакцию tran.Commit() Catch Откатываем транзакцию tran.Rollback() Finally Код очистки. Закрываем соединение.

conn.Close() End Try Как видите, две команды выполняются как часть одной транзакции. Если одна из них терпит неудачу, транзакция отменяется, и любые изменения в базе данных откатываются. Заключив код в блок try /catch/finally, вы га рантируете корректное выполнение транзакции: она фиксируется в самом конце блока try после успешного выполнения обеих SQL-команд. Любое исключение перехватывается в блоке catch, где транзакция отменяется и изменения, внесенные в ходе этой транзакции, откатываются.

Управление транзакциями через объекты ADO.NET приводит к менее эффективному блокированию, чем при использовании явных транзакций в хранимых процедурах. Причина в том, что при транзакциях ADO.NET, Разработка распределенных приложений в.NET 15Э выполняемых вручную, треоуется как минимум столько же двусторонних обменов данными с СУБД, сколько операций выполняется в транзакции плюс два обмена в ее начале и конце. Блокировки удерживаются в течение всего времени передачи вызовов из кода ADO.NET на сервер базы данных и обратно.

Транзакции MSMQ, выполняемые вручную.NET Framework предусматривает два вида поддержки транзакций MSMQ:

внутреннюю (для транзакций вручную) и внешнюю (для автоматических транзакций). В первом случае в рамках транзакции возможен прием или передача нескольких сообщений. Во втором Ч сообщения участвуют в транзакциях DTC (Distributed Transaction Coordinator).

Транзакции MSMQ, выполняемые вручную, поддерживаются классом MessageQueueTransaction и обрабатываются исключительно ядром MSMQ.

Подробности см. в статье Дункана Мак-Кензи (Duncan Mackenzie) Reli able Messaging with MSMQ and.NET ( en-us/dnbda,/html/bdadotnetasync2.asp).

Автоматические транзакции Поддержка автоматических транзакций в.NET Framework опирается на службы MTS/COM+. СОМ+ использует DTC в качестве диспетчера и координатора транзакций в распределенной среде. Это позволяет прило жениям.NET выполнять транзакции, охватывающие разнообразные опе рации над множеством ресурсов, например вставку заказа в базу данных SQL Server, запись сообщения в очередь MSMQ (Microsoft Message Queue), отправку сообщения электронной почты и считывание информации из базы данных Oracle.

Предоставляя модель программирования на основе декларативных тран закций (declarative transactions), COM+ резко упрощает выполнение тран закций, в которых участвуют гетерогенные ресурсы. Но учтите, что за это приходится расплачиваться снижением производительности, связанным с издержками взаимодействия DTC и СОМ;

кроме того, поддержка вложен ных транзакций отсутствует.

Страницы ASP.NET, методы Web-сервисов и.NET-классы можно помечать как транзакционные, присваивая им атрибут декларативной транзакции (declarative transaction attribute).

ASP.NET <@ Page Transaction="Required"> Microsoft ADO.NET Web-сервис ASP.NET Public> Public Function Methodic) Для участия в автоматических транзакциях.NET-класс должен наследо вать от System.EnterpriseServices.ServicedComponent, который обеспечива ет выполнение класса в СОМ+. Если вы сделаете именно так, СОМ+, вза имодействуя с DTC, создаст распределенную транзакцию и подключит к ней все необходимые ресурсы без вашего участия. Кроме того, вам нужно присвоить классу атрибут декларативной транзакции, чтобы определить его поведение при выполнении транзакции.

Visual Basic.NET Public>

Х Disabled. Указывает, что объект никогда не создается в транзакции СОМ+. Для поддержки транзакций объект может обращаться к DTC напрямую.

Х NotSupported. Указывает, что объект никогда не создается в транзак ции.

Х Supported. Указывает, что объект выполняется в контексте транзакции своего создателя. Если объект сам является корневым или если его созда тель не выполняется в транзакции, объект создается вне транзакции.

Х Required. Указывает, что объект выполняется в контексте транзак ции своего создателя. Если объект сам является корневым или если его создатель не выполняется в транзакции, при создании такого объекта создается новая транзакция.

Разработка распределенных приложений в,NET * RequiresNew. Указывает, что объекту нужна транзакция и что при его создании создается новая транзакция.

В следующем коде содержится.NET-класс, настроенный на выполнение в СОМ+. Кроме того, атрибутам сборки присваиваются значения, необходи мые для конфигурирования свойств СОМ+-приложения.

Visual Basic.NET Imports System Imports System.Runtime.CompilerServices Imports System.EnterpriseServices Imports System.Reflection Детали регистрации.

Имя COM---приложения в том виде, в каком оно присутствует ' в каталоге СОМ+ ' Строгое имя (strong name) для сборки (assembly) Public>

using System,Runtime.CompilerServices;

using System.EnterpriseServices;

using System.Reflection;

// Детали регистрации, // Имя СОН+-приложения в том виде, в каком оно присутствует // в каталоге СОМ+ [Assembly;

ApplicationName("Classl")] // Строгое имя для сборки [Assembly: AssemblyKeyFileAttribute("class!.snk")] [Assembly: ApplicationActivation(ActivationOption.Server)] [Transaction(TransactionOption,Required)] public> ApplicationName(лClassl)> указывает имя СОМ+-приложе ния, в которое устанавливаются компоненты сборки, a определяет, является ли это при ложение сервером или библиотекой. Когда вы указываете Application Acti vation(ActivationOption.Server), сборку необходимо установить в GAC (global assembly cache) с помощью утилиты командной строки gacutil (GacUtil.exe).

Для преобразования сборки в библиотеку типов, регистрации библиотеки типов и ее установки в заданное СОМ+-приложение можно использовать утилиту командной строки Regsvcs.exe. Кроме того, эта утилита настраи вает свойства, добавленные в сборку программным способом. Например, если в сборке указано Application Activation( Activation Option. Server), ути лита создаст серверное приложение. Если вызванная сборка еще не уста новлена в СОМ+, исполняющая среда создаст и зарегистрирует библиоте ку типов, а затем установит ее в СОМ+. СОМ+-приложение, созданное для сборки, можно просмотреть и настроить в оснастке Component Ser vices.

Процесс создания, регистрации и использования обслуживаемых компо нентов (serviced components) подробно рассматривается в разделе Wri ting Serviced Components ( de/html/cpconwritmgservicedcomponents.asp) в.NET Framework Develo per's Guide.

В следующем коде показывается транзакционный класс, сконфигуриро ванный для запуска под управлением СОМ+, в котором в рамках транзак ции выполняются две SQL-команды. Первая вставляет заголовок заказа в таблицу заказов и возвращает Orderld добавленного заказа. Этот Orderld используется второй командой при вставке позиций заказа в таблицу OrderDetails. Транзакция отменяется, если не удалось выполнить хотя бы одну из двух команд;

при этом записи в базу данных не добавляются.

Visual Basic.NET Public>

With cmdl.CommandType = CommandType.StoredProcedure.CommandText = "InsertQrderHeader".Connection = conn ' Добавляем входные и выходные параметры.Parameters.Add("@CustomerId", SqlDbType.Int), ExecuteNonQuervO Очмцаем параметры для следующей команды.Parameters.clear() End With ' Вставляем позиции заказа, Настраиваем свойства Command, With cmd.CommandType = CommandType.StoredProcedure.CommandText = "InsertOrderDetail".Connection = conn Добавляем параметры.Parameters.Add("йOrderld", SqlDbType.Int) Выполняем команду. ExecuteNonQueryO Повторяем эти строки для каждой позиции заказа End With ' Фиксируем транзакцию Contextlltil. SetComplete() Catch ' Откатываем транзакцию ContextUtil.SetAbortO Finally ' Код очистки End Try End Sub Используя класс System. Enterprise Services. Context Util, можно получить информацию о контексте СОМ+-объекта. Этот класс предоставляет мето ды SetComplete и SetAbort, позволяющие явным образом фиксировать и откатывать транзакцию. Легко догадаться, что метод ContextUtil.Set Complete вызывается в самом конце блока try, когда все операции выпол нены успешно и нужно зафиксировать транзакцию. Все исключения пере 164 Microsoft ADO.NET хватывается в блоке catch, где транзакция отменяется с помощью Context Util.SeiAbort.

Кроме того, с помощью класса -атрибута (attribute>

явный вызов ContextUtil.SetAbort не нужен.

Чтобы воспользоваться этой возможностью, вставьте атрибут перед методом класса:

Visual Basic.NET Public>

Inherits ServicedComponent Public Sub Example1{) End Sub End>

В системах, где нужно выполнять транзакции, использующие MSMQ, и другие ресурсы, единственно возможный выбор Ч применение транзакций DTC или СОМ+. DTC координирует все диспетчеры ресурсов, участвую щие в распределенной транзакции, а также управляет деятельностью, свя Разработка распределенных приложений в.NET занной с транзакциями. Пример распределенной транзакции MSMQ и SQL Server см. в статье Дункана Мак-Кензи Reliable Messaging with MSMQ and.NET ( bdadotnetasync2.asp).

Заключение При использовании каждой из технологий работы с транзакциями прихо дится идти на компромисс между производительностью приложения и удобством сопровождения кода. Запуск реализованной в хранимой проце дуре транзакции базы данных обеспечивает наилучшую производитель ность, так как требуется лишь один двусторонний обмен информацией с базой данных. Кроме того, обеспечивается гибкость управления транзак циями, поскольку явно указываются начало и завершение транзакции. Но, хотя этот способ дает хорошую производительность и гибкость, приходит ся программировать на Transact SQL. что не так легко, как па языках.NET.

Транзакции, выполняемые вручную с помощью транзакционных объектов ADO.NET, просты в программировании. Благодаря явному применению инструкций начала и завершения транзакции, возможно гибкое управле ние границами транзакции. Платой за эту легкость и гибкость является снижение производительности за счет дополнительных двусторонних об менов информацией с базой данных при выполнении транзакции.

Автоматическая транзакция Ч единственно возможный выбор, когда тран закция использует несколько диспетчеров ресурсов, поддерживающих транзакции, например базы данных SQL Server, очереди сообщений MSMQ и т. д. Они значительно упрощают разработку приложений и предъявля ют более низкие требования к квалификации программиста. Однако из-за того, что всю работу по координации выполняет служба СОМ+, возмож ны дополнительные издержки.

альманах программиста Джонни Папа Пять способов подстегнуть производительность SQL* Здесь показывается, как увеличить либо производительность, либо масшта бируемость приложений на основе SQL Server. Вы узнаете о тонкостях использования SQL-операторов IDENTITY, LEFT JOIN и CROSS JOIN. Также рассматриваются ситуации, где замена HAVING на WHERE и GROUP BY на DISTINCT дает ощутимый выигрыш в производительности.

Иногда, чтобы заставить приложение работать гораздо быстрее, достаточ но подкрутить пару гаек. Только надо знать Ч где! Рано или поздно сталкиваешься с ситуацией, когда какой-нибудь SQL-запрос в приложе нии действует вовсе не так, как было задумано: или не возвращает нужные данные, или выполняется слишком долго. Кому понравится, если ваше корпоративное приложение будет задерживать результаты запросов? По мните, как ваши родители и слушать не хотели объяснений, почему вы явились домой так поздно? Пользователей тоже не волнует, почему их запросы выполняются столько времени. (Прости, мама. В моем запросе было слишком много операторов LEFT JOIN.) Они хотят, чтобы прило жения отвечали быстро, а аналитические данные в отчетах появлялись моментально. Меня самого раздражает, если какая-нибудь Интернет-стра ница грузится дольше десяти секунд. (Ну хорошо, дольше пяти.) Для решения этих проблем важно разобраться в их причинах. С чего на чать? Главная причина обычно связана со структурой базы данных и зап росов. Сегодня я продемонстрирую четыре способа, позволяющих увели чить либо производительность, либо масштабируемость приложений на Публиковалось в MSDN Magazine/Русская Редакция. 2002. №1 (июль). Ч Прим. изд.

Microsoft SQL Server основе SQL Server. Я рассмотрю применение операторов LEFT JOIN и CROSS JOIN, а также получение значения IDENTITY. Но учтите, что уни версальных решений нет. Настройка базы данных и запросов требует вре мени, анализа и тестирования. Хотя все предлагаемые мной способы дока зали свою эффективность, в вашем приложении какие-то из них окажут ся лучше, чем другие.

Получение IDENTITY из запроса INSERT Начну, пожалуй, с задачи, по которой мне задают массу вопросов: как по лучить значение IDENTITY после выполнения SQL-оператора INSERT?

Зачастую проблема не в том, как написать соответствующий запрос, а в том, где и когда его выполнять. В SQL Server, чтобы получить значение IDENTITY, созданное в результате выполнения последнего SQL-операто ра применительно к активному соединению с базой данных, используйте;

SELECT @@IDENTITY Это совсем простой SQL-оператор, но важно иметь в виду вот что: если последним был не оператор INSERT или если он был выполнен на другом соединении, ожидаемого результата вы не получите. Этот код надо выпол нять сразу после INSERT и на том же соединении, например так:

INSERT INTO Products (ProductName) VALUES ('Chalk') SELECT ^IDENTITY Выдав такой запрос по единственному соединению с базой данных North wind, вы получите значение IDENTITY для нового товара Ч Chalk (мел).

Например, в приложении на Visual Basic, использующем ADO, вы могли бы написать:

Set ofis = oCn.ExecuterSET NOCOUNT ON;

INSERT INTO Products (ProductName) VALUES ('Chalk');

SELECT IDENTITY") IProductID = oRs(O) Этот код указывает SQL Server не возвращать число строк, выполнить оператор INSERT и вернуть значение IDENTITY, созданное для новой строки, Оператор SET NOCOUNT ON приводит к тому, что возвращае мый объект Recordset содержит одну строку и один столбец со значением IDENTITY. Без этого оператора вы получили бы сначала пустой объект Recordset (так как INSERT не возвращает никаких данных), а потом еще один объект Recordset, на этот раз со значением IDENTITY. Тут можно запутаться Ч особенно если не знаешь, что оператор INSERT тоже возвра щает Recordset. Дело в том, что SQL Server следит за количеством строк Пять способов подстегнуть производительность SQL и, если оно меняется из-за вставки строки, интерпретирует новую строку как объект Recordset. А реальные данные помещаются во второй Recordset.

Хотя вы могли бы добраться до второго объекта Recordset ADO-методом Next Recordset, гораздо проще (и эффективнее) делать так, чтобы возвра щался только один Recordset.

Только что показанный способ решает задачу, но требует дополнительно го кода в SQL-выражении. Другой способ добиться того же результата Ч ввести SET NOCOUNT ON перед INSERT, а в триггер FOR INSERT для таблицы поместить SELECT ййIDENTITY, как показано ниже. Тогда любой оператор INSERT, адресованный данной таблице, автоматически вернет значение IDENTITY.

CREATE TRIGGER trProducts_Insert ON Products FOR INSERT AS SELECT @@IDENTITY GO Триггер срабатывает, только когда INSERT выполняется применительно к таблице Products, поэтому он всегда возвращает IDENTITY при успешном завершении запроса. Этот способ позволяет унифицировать получение значений IDENTITY в рамках всего приложения.

Встраиваемые представления и временные таблицы Иногда запросы должны объединять данные с другими данными, которые можно получить только через GROUP BY с последующей выдачей стан дартного запроса. Например, если вам нужна информация о последних пяти заказах, вы сначала выясняете, что они собой представляют. Для это го выдается SQL-запрос, возвращающий идентификаторы заказов. Полу ченные данные помещаются во временную таблицу, после чего она объе диняется с таблицей Products, и вы можете узнать, какие товары и в каком количестве проданы по этим заказам:

CREATE TABLE Tempi (OrderlD INT NOT NULL, OrderDate DATETIME NOT NULL) INSERT INTO Tempi (OrderlD, OrderDate} SELECT TOP 5 o.OrderlD, o.OrderOate FROM Orders о ORDER BY o.OrderDate DESC SELECT p.ProductName, SUM(od.Quantity) AS ProductQuantity FROM Tempi t INNER JOIN [Order Details] od ON t.OrderlD = od.OrderlD INNER JOIN Products p ON od.ProductID = p.ProductID GROUP BY p.ProductName ORDER BY p.ProductName DflOP TABLE #Temp Microsoft SQL Server Здесь создается временная таблица, которая заполняется данными, объе диняется с другой таблицей и. наконец, удаляется. Этот запрос вызывает массу операций ввода-вывода, однако его можно переписать так, чтобы вместо временной таблицы использовалось встраиваемое представление (inline view) Ч запрос, выполняющий объединение в блоке FROM. При этом вы получаете тот же результат, но без интенсивного ввода-вывода:

SELECT p.ProductName, SUH(od.Quantity) AS ProductQuantity FROM ( SELECT TOP 5 o.OrderlD, o.OrderDate FROM Orders о ORDER BY o.OrderDate DESC )t INNER JOIN [Order Details] od ON t.OrderlD = od.OrderlD INNER JOIN Products p ON od.ProductID = p.ProductID GROUP BY p.ProductName ORDER BY p.ProductName Этот запрос не только эффективнее предыдущего, но и короче. Кроме того, временные таблицы расходуют уйму ресурсов. Если данные нужны лишь для объединения, встраиваемые представления предпочтительнее.

Старайтесь не использовать LEFT JOIN и NULL Конечно, бывают ситуации, когда без LEFT JOIN и значений NULL не обойтись. Но их применение далеко не всегда оправданно. Изменив струк туру SQL-запросов, можно добиться того, что запрос, который выполнял ся за несколько минут, будет давать результат за несколько секунд. Иног да приходится изменять формат данных в запросе так, как это нужно ва шему приложению. Кроме применения типа данных TABLE, снижающего потребление ресурсов, есть много других возможностей оптимизировать запрос. В частности, в SQL широко используется такой удобный оператор, как LEFT JOIN. Он позволяет извлекать все строки из первой таблицы и строки из второй таблицы, совпадающие и не совпадающие с первой. На пример, если вам надо получить список всех клиентов и их заказов, то LEFT JOIN способен показать перечень клиентов, как сделавших заказы, так и не сделавших их.

Этим инструментом нередко злоупотребляют. LEFT JOIN приводит к се рьезным издержкам, потому что сравнивает данные с NULL (несуществу ющими данными). В каких-то случаях без LEFT JOIN не обойтись, но цена его использования может быть очень высока. LEFT JOIN требует го раздо больше ресурсов, чем INNER JOIN, и, если вам удастся так перепи сать запрос, чтобы избавиться от всех LEFT JOIN, вы получите колоссаль ный выигрыш (рис. 1).

Пять способов подстегнуть производительность SQL Возвращаются в JOIN Рис. 1. Запрос Один из способов ускорить выполнение запроса с LEFT JOIN Ч создать тип данных TABLE и вставить все строки из первой таблицы (находящей ся по левую сторону LEFT JOIN), а затем добавить в TABLE значения из второй таблицы. Хотя этот процесс проходит в два этапа, он может сэко номить массу времени по сравнению со стандартным LEFT JOIN. Но луч ше всего пробовать разные запросы и измерять время их выполнения так вы найдете, какой запрос в ваших условиях обрабатывается быстрее остальных.

Проверяя быстродействие запросов, важно проводить измерения по не сколько раз и в дальнейшем учитывать усредненные показатели. Запрос (или хранимая процедура) могут помещаться в соответствующий кэш SQL Server и при первом запуске выполняться дольше, чем при последующих.

Кроме того, параллельно с вашим запросом могут обрабатываться другие запросы, выданные к тем же таблицам. Ваш запрос может быть приоста новлен, пока другие запросы блокируют нужные таблицы.

Чтобы избавиться от задержек, вызываемых LEFT JOIN, надо соответ ствующим образом проектировать базу данных. Допустим, какие-то това ры относятся к определенным категориям, какие-то Ч нет. Если в табли це товаров хранится идентификатор категории, а конкретный товар ни под какую категорию не подпадает, то в соответствующее поле можно было бы записать значение NULL. Тогда, чтобы получить список всех товаров и их категорий, вам пришлось бы выполнить LEFT JOIN. Но можно создать категорию со значением No Category и тем самым указать отношение внешнего ключа (foreign key relationship), запрещающее значения NULL.

Теперь вы сможете получать те же результаты через INNER JOIN. Хотя 174 Microsoft SQL Server такой подход связан с добавлением лишних данных, он позволяет избе жать вызовов ресурсоемких LEFT JOIN. Распространив эту концепцию на всю базу данных, вы существенно ускорите обработку запросов. Помните:

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

Декартовы произведения Здесь я дам совет, идущий в разрез с общепризнанным мнением: в опре деленных ситуациях декартово произведение (Cartesian product) очень полезно. Почему-то декартовы произведения (CROSS JOIN) пользуются дурной славой, и разработчиков зачастую призывают вообще отказаться от них. Во многих случаях CROSS JOIN действительно приводят к неэффек тивному использованию ресурсов. Но их надо применять с умом Ч как и любой другой инструмент SQL. Например, декартово произведение очень удобно в запросе, который возвращает сводку по всем клиентам за каждый месяц, в том числе ничего не заказавшим в каком-либо месяце.

Рассмотрим SQL-код на рис. 2. Заметьте: если бы выполнялось стандарт ное внутреннее объединение (INNER JOIN) таблиц Customers и Orders с группированием по месяцам и суммированием продаж, то возвращались бы данные только по тем месяцам, в которые клиент делал заказы. То есть вы не получили бы нулевое значение для тех месяцев, когда клиент ниче го не заказывал. Если вам нужно построить график, отражающий объем продаж по каждому клиенту, то для наглядности на этом графике должны быть нулевые значения по тем месяцам, когда продаж не было. SQL-код на рис. 2 пропускает месяцы, в которые объем продаж равен нулю, так как в таблице Orders нет соответствующих строк (предполагается, что вы не храните того, чего не было).

Код на рис. 3 длиннее, зато возвращает данные и по тем месяцам, когда никаких продаж не было. Сначала мы получаем список всех месяцев про шедшего года и помещаем их в первую таблицу с типом данных TABLE (@tblMonths). Затем получаем список всех компаний, что-либо закупав ших в течение этого периода, и помещаем его во вторую таблицу с тем же типом данных (@tblCustomers). В этих таблицах хранятся все данные, не обходимые для создания набора результатов (resultset), за исключением реальных цифр, отражающих объемы продаж.

Все месяцы перечислены в первой таблице (12 строк), а все клиенты, что либо закупившие в этот период. Ч во второй (в моем случае Ч 81 компа ния). Не все клиенты делали покупки в каждом из 12 месяцев, поэтому INNER JOIN или LEFT JOIN пропускал бы тех клиентов, которые в дан ном месяце ничего не покупали.

Пять способов подстегнуть производительность SQL Рис. 2. Выборка по всем клиентам и объемам продаж set nocount on DECLARE йdtStartDate DATETIME, 9dtERU&ate DATETINE, SUtDats ОАШШ SET SdtEndDate = '5/5/1997' SET dtEntfBate * BATEABBCQD, -1. CAST(CASTлMONTH(зtftEndDate) + 1) AS VABCHAR(2)} + '/01/' + CAST(YEAR(@dtEndDate) AS VARCHftR<4 23;

59:59' AS DATETIME SET $dtStartDate * DATEAEJD(HM, -1 * 12, edtEndDate) SELECT CAST(YEAR(O.OrderOate) AS VAflCлAR<4 + *-' + Х CASE WHEN MOmiCo.QrderDate) < W THEN 000 + CAST(KQ8TH{o.Qr ES& AS sMonth, c.CustomerlD, с.СотрапуНате, c.ContactHame, SyM

'Х c.CoapanyNarae, G,CorttactNaffle OfiDEft BY c.CompanyNaroe, sKonth Декартово произведение возвращает всех клиентов и по всем месяцам.

Фактически первая таблица умножается на вторую, а результатом являет ся набор строк, число которых равно произведению количеств строк в пер вой и второй таблицах. Таким образом, я получаю таблицу @tblFinal с строками. Далее я обновляю таблицу @tblFinal данными о ежемесячных 176 Microsoft SQL Server объемах продаж по каждому клиенту в течение выбранного периода и про извожу выборку конечного набора строк.

Рис. 3. Декартово произведение на практике DECLARE etblMonths TABLE (sHonth VARCHAR(7 DECLARE etblGustomers TABLE CustomerlD CHAR(10), CompanyName VARCHAR(50), ContactNaroe VARCNAR(SO)) DECLARE йtblFinal TABLE { sMonth VARCHARC7), CustomerlD CHAR(1Q), CompanyName VARCHAR(50), ContactKame VARCHAR(SO), mSales KQNEY) DECLARE @dtStartDate МТШИЕ.

@dtEndDate DATETIME, зdtDate DATETIME, 91 INTEQER SET йdtEndDate = '5/5/1997' SET SdtEfidDate = DATEADD

INSERT INTO @tbIMortths SELECT CAST(YEAR(dtDate) AS VAfiCHAR(4 + CASE WHEN MONTH(edtOate) < THEN '0' + CAST(HONTH(@dtDate) AS VARCWAR(2)) ELSE CAST(MONTH(&dtOate) AS VARCHAR{2)> END AS sMonth SET &i = 91 + EHD Ч включить асех клиентов, что-либо закупавших в этот период, в таблицу 'у' INSERT INTO зtblCustomers SELECT DISTINCT с. Customer-ID, c.CompanyNaroe, c.ContactName FROM Customers с INNER JOIN Orders о ON c.CustomerlD * o.CustofflerlD. WHERE o.OfderDate BETWEEN eeftStartDate.AND fcJtEndDate см. след. стр.

Пять способов подстегнуть производительность SQL Рис, 3, Декартово произведение на практике (окончание) IHSE8T INTO SELECT m.sHonth, c.CustofflerIC, c.CompanyName, c.ContactNaffle, FHOM @tblHonthS n CROSS JOIN йtblCustGlsers С UPDATE йtblFinal SET mSales - mydata.fflSales оtblFinal f INKER JOIN SELECT c.CustoraerlD, CAST(YEAR(o.QrderDate> AS VAfiCHAR(4)) Х CASE WKEN MQNTH(o.QrderDate) < THEN '0' + CAST(MONTHo.QrderDate) AS VARCHARC2)) ELSE CASTCHONTHCo.QrtlerDate) AS VARCHAR(2)} END AS SHonth, SUH( od. Quantity * od.UnitPrice) AS raSales FROM Customers с ШЕЙ JOIN Orders о ON c.CustoroerlD = o.CustomerlO.

INKER JOIN tQrder Details} od ON o.OrderlD = od.OrderlD WHERE o.OrderDate BETWEEN 9dtStartDate AND йdtEndSata GROUP BY c.CustojaerlD, CAST(YAR

+ CASE WHEN HOHTHCo.OrderDate) < THEN '0' + CAST{MQNTH

Всякая всячина Вот еще несколько приемов, увеличивающих производительность SQL запросов. Допустим, вы хотите сгруппировать всех продавцов по регионам и суммировать объемы их продаж, причем вам нужны только продавцы, помеченные в базе данных как активные. Вы могли бы группировать про давцов по регионам и использовать HAVING для исключения неактивных продавцов, но то же самое можно сделать с помощью WHERE. Блок WHERE уменьшает число строк, подлежащих группированию, и поэтому он эффективнее, чем HAVING. Фильтрация строк по какому-либо крите рию в HAVING приводит к группированию и тех строк, которые были бы исключены в WHERE.

Другой прием, направленный на увеличение производительности, Ч при менение ключевого слова DISTINCT вместо блока GROUP BY в тех слу чаях, когда нужно найти список различающихся строк (distinct list of data rows). Тогда запросы с ключевым словом DISTINCT будут эффективнее.

GROUP BY следует использовать при вычислении функции суммирова ния вроде SUM, COUNT, МАХ и т. д. Кроме того, ключевое слово DIS TINCT нежелательно, когда точно известно, что данный запрос будет все гда возвращать уникальные строки. В этой ситуации применение DISTINCT просто излишне.

Как видите, для оптимизации запросов и реализации специфических биз нес-правил можно использовать самые разные способы. Весь фокус в том, чтобы проверять их в своих условиях и отбирать подходящие. Самое глав ное: тестировать, тестировать и еще раз тестировать. В рамках этой рубри ки я намерен продолжить исследование концепций SQL Server, проекти рования баз данных, способов эффективной индексации и парадигм защи ты в SQL Server.

Джонни Папа (Johnny Papa) Ч вице-президент компании MJM Investigations по информационным технологиям (Роли, штат Северная Каролина). Автор книги Professional ADO 2.5 RDS Programming with ASP 3.0 (Wrox, 2000).

Часто выступает на различных конференциях. С ним можно связаться по адресу datapoints@lancelotweb.com.

Алок Мехта и Дэниел Уильяме Сценарии в SQL Преобразование данных и предоставление отчетов SQL Server 2000 через VBScript-интерфейсы* Провайдеры программных сервисов (application service providers, ASP) часто отправляют информацию клиентам автоматически, а не по запросу Ч например, какой-то компании нужны ежедневные сводки об объемах продаж ее продукции в розничной сети. SQL Server идеально подходит для ведения баз данных такого типа, но, чтобы получать данные в формате, удобном для клиентов, вы должны писать сценарии (scripts). В этой статье вы увидите, как с помощью DTS (Data Transformation Services) Ч набора мощных средств SQL Server Ч автоматизировать получение и форматирование данных SQL Server 2000 и упростить их доставку вашим пользователям.

Технология доставки информации (push), также называемая Web-вещани ем (Web casting), автоматизирует поиск и получение данных. Эти опера ции инициируются не пользователями, а Web-сервером или сервером базы данных. Исходя из заданных критериев, соответствующее приложение доставки (push application) автоматически выполняет поиск в базе данных и самостоятельно определяет время и место доставки информации Ч за частую прямо на рабочий стол пользователя по электронной почте. Это не только удобный способ получения важной информации, которую не так то легко получить каким-то иным образом, но и средство, существенно влияющее на повседневную работу.

Публиковалось в MSDN Magazine/Русская Редакция. 2002. №2 (июль). Ч Прим. изд.

18G Microsoft SQL Server Приложения доставки бывают двух видов. Один из видов, часто предла гаемых крупными сайтами телеконференций и баз данных, Ч электронная почта, списки рассылки и службы Прямой доставки. Приложения достав ки второго вида разрешают пользователям и разработчикам создавать про фили и сохранять свои предпочтения, благодаря чему можно получать информацию, соответствующую критериям поиска, из многих источников, Программа-пример, которая рассматривается в нашей статье, относится к первому виду приложений доставки. Но она достаточно универсальна, и в нее можно добавить поддержку создания профилей.

Организации, которым нужна централизация и консолидация данных, могут использовать средства DTS (Data Transformation Services), постав ляемые с SQL Server 2000. Они позволяют выбирать и преобразовывать данные из разных источников и передавать заданным адресатам. С их по мощью можно не только выполнять просто разовую передачу данных, но и создавать сложные пакеты, управляемые рабочим процессом (workflow driven). Кроме того, средства DTS предоставляют пользовательский гра фический интерфейс и относительно простую в программировании объек тную модель.

DTS-пакет (DTS package) Ч это комбинация соединений, заданий, преоб разований и ограничений, определенных для рабочего процесса. Каждый пакет может включать одну или несколько операций (steps) или заданий (tasks), выполняемых последовательно или параллельно. В процессе сво его выполнения пакет подключается к источникам данных, копирует ин формацию и объекты баз данных, преобразует данные и оповещает о со бытиях других пользователей или другие процессы. Пакеты можно редак тировать, защищать паролем, выполнять по расписанию и выбирать в зависимости от версии. Пакеты легко создаются с помощью DTS Designer, о котором мы еще поговорим.

VBScript или JScript позволяет создать задание, которое выполняет фун кции, недоступные в других DTS-заданиях. Например, вы можете:

Х создавать и использовать ADO-соединения, команды, наборы записей (recordsets) и другие объекты для доступа к данным и управления ими;

Х форматировать и преобразовывать данные с помощью функций, про цедур и СОМ-объектов;

Х создавать, изменять и использовать значения, хранящиеся в глобаль ных переменных DTS и в константах ActiveX-сценариев;

Х включать другие задания и рабочие процессы DTS.

Преобразование данных и предоставление отчетов SQL Server 2000...

Приложение-пример Наш пример показывает, как использовать VBScript-задания для выпол нения функций, недоступных в других заданиях DTS, и каким образом сделать так, чтобы VBScript-сценарий работал в качестве задания, выпол няемого по расписанию (scheduled task). Мы не будем рассматривать гло бальные переменные DTS, рабочие процессы или интеграцию с другими заданиями, так как наша цель Ч продемонстрировать мощь VBScript в SQL Server 2000.

Данные {в базе данных SQL Server) Т Создание локального DTS-пакега s SQLServer Создание VBScript-задания в локальном DTS-пакете Т Написание на VBScript функций выборки.

преобразования и рассылки данных (по электронной почте) i Конфигурирование OTS-пакета как задания, выполняемого по расписанию Рис. 1. Создание и конфигурирование DTS-приложения Рис. 1 иллюстрирует схему, которой мы придерживались при разработке приложения-примера DTS. VBScript-задание B'DTS-пакете периодически рассылает авторам по электронной почте информацию по продажам их книг. Данные форматируются в отчет. В качестве источника данных мы взяли базу данных Pubs, поставляемую с SQL Server 2000, но наша мето дика носит универсальный характер и применима к любой модели данных.

182 Microsoft SQL Server Первый шаг Ч заполнение поля адреса электронной почты соответствую щим адресом автора. Как показано на рис. 2, для этого мы выбираем базу данных Pubs и расширяем таблицу authors, добавляя в нее поле au_email типа varChar размером 20 символов*. Мы исходим из того, что в реальном приложении это поле будет заполняться через отдельный GUI-интерфейс или процесс, но в демонстрационных целях адреса можно вводить и вручную.

(щ 3;

OesJtjn ТаЫл authfirt, in 'put)*' on *. .

'$ Э aujd id (varchar) aujname varchar aujnams yardw phone char " address varchar 40 V city varchar 3J ! V -_ Фstate chat 2 V char "" 5 V contract bit !l г aujsmaill that ' 75 V Рис. 2. Добавление поля адреса электронной почты Далее мы создаем новый локальный DTS-пакет в SQL Server 2000. Как показано на рис. 3;

, чтобы вызвать DTS Designer для создания пакета, нуж но запустить Enterprise Manager в SQL Server, щелкнуть дерево консоли Data Transformation Services правой кнопкой мыши и выбрать команду New Package. С помощью GUI-интерфейса DTS Designer можно создавать и конфигурировать пакеты, перетаскивая мышью нужные методы и запол няя страницы свойств различных DTS-объектов, образующих пакет.

Третий этап (рис. 4) Ч добавление VBScript-задания перетаскиванием ActiveX Script Task с панели инструментов Task в рабочую область дизай на иллюстрации хорошо видно, что это поле имеет тип char и длину 75 символов. Ч Прим. сост.

Преобразование данных и предоставление отчетов SQL Server 2000...

нера. Вы можете изменить имя задания с ActiveX Script Task;

undefined на что-нибудь более подходящее вам.

IM I rronsole Root\Microsolt- SQL ServersXStH Server tiЩup\DWlU JAMS (Windows ПЭ, J-.*SJ :.1 V :>....,_, Vо i Ш -r т ! С-жэ >^'. -- 11>ч:,;

^CwscJaPcw 1 nscwsefr S'5L 3лv*n | В l J SQL Server бгоир |. | DWUIAMS (Windows NT) j k Loiil Packs Meta D-ifs Mil* Data Х '. r'j Security :

li--Й Mлl* Did SrviMi pp lh DWIlL!AM'DWMJdM5_SER'/лR Windows HT Х IbSQlDEVDEFERRALlWifKjowsNT) Рис. З. Создание пакета. ActiveX Script Task Properties Package Objec Visual Eass veX bcri 'B Tasks Cons Author AiokMehtaandDi ale: 1Л/ aiy Sales Repent using CDOI 'ou should have 5QL Seivw i Change ihe *Q losing thiee cc SeeyowDBAcyNetwokAd MallTransfeiPitiocol | Соги(5МТР_ЗЕПУЕЯ-"м!с d(ireл and Text foi Ihel Const SENDER_EMAiL - Ю^ ADO Connection Strino to the ц itDB^CONNECTSTRINCl Рис. 4. Свойства ActiveX-задания 184 Microsoft SQL Server Теперь надо написать на VBScript функции для выборки, преобразования и отправки данных по электронной почте. Чтобы облегчить изучение при ложения-примера, просто скопируйте эти функции из исходного кода (см.

ссылку в конце статьи). Убедитесь, что в окне свойств ActiveX Script Task выбран язык VBScript. Язык указывается на вкладке Language (рис. 4).

Затем скопируйте наш исходный код в дизайнер, удалив код функции main, предлагаемый по умолчанию.

Кроме того, чтобы наше приложение заработало, надо изменить констан ты в SQL Server, так как у них могут быть различные значения (об этом Ч позже).

После выполнения всех операций сохраните пакет, как показано на рис. 5.

Мы присвоили ему имя BookSales.

Рис. 5. Сохранение пакета Запустить приложение-пример можно несколькими способами, Сначала щелкните кнопку Parse, чтобы убедиться в отсутствии синтаксических ошибок. Эта кнопка находится в окне свойств ActiveX Script Task. Затем щелкните кнопку Go* на панели инструментов в верхней части окна DTS Designer. Еще один вариант Ч щелкнуть нужное задание правой кнопкой мыши и выбрать из контекстного меню команду Execute step. Кроме того, пакет BookSales можно выполнить в Enterprise Manager (рис. 6). При ус пешном выполнении задания авторы будут получать электронную почту в соответствии с расписанием (рис. 7).

Эта кнопка называется Execute. Ч Пром. сост.

Преобразование данных и предоставление отчетов SQL Server 2000...

tn SQL Server ЕгАегичи" Manager - { '- ' l Pal*TrinsfonriatLai Semites il m*>i D-Ki Sen/net Mat, fsis Рис. 6. Запуск пакета BookSales ХХ-,/. й-.d Рис. 7. Расписание передачи по электронной почте 186 Microsoft SQL Server Чтобы пакет BookSales автоматически запускался по расписанию, выбери те из контекстного меню команду Schedule Package (рис. 6) и укажите в появившемся окне нужные параметры.

Пакет BookSales будет запускаться ежедневно в 11:00 вечера, начиная с 01.01.2002, и предоставлять отчеты о номерах заказов, количестве пози ций, виде платежа и названиях книг. Обратите внимание: необходимо за пустить SQL Server Agent и настроить его на запуск пакетов DTS по рас писанию. Вы увидите пакет BookSales как задание в Enterprise Manager в узле дерева консоли Management SQL Server Agent j Jobs.

Код на VBScript Исходный код нашего примера находится в файле SourceCode.txt. После того как мы поясним константы, которые нужно задать, и кратко рассмот рим функцию main, мы расскажем, как этот код обрабатывает данные по продажам, обращается к базе данных, форматирует набор записей и от правляет отчеты.

Следующим трем константам нужно присвоить значения, соответствую щие параметрам вашей системы и сети:

Const SHTP_SERVER *= "excfiange.afs-link.com" Const SENOER_E-MAIL = Book Sales Reporting Service"" " Const DB_CONNECT_STRING = "Provider=SQLOLEDB.1;

Data Source=(local);

" & _ "Initial Catalog=Pubs;

user id = ' s a ' ;

p a s s w o r d = ' ' Первая константа, Const SMTP_SERVER = "exchange.afs-link.com", сооб щает DNS-имя вашего SMTP-сервера (почтового сервера). Эта информа ция нужна для отправки электронной почты;

ее можно получить у вашего сетевого администратора.

Const SENDER_E-MAIL = ""Book Sales Reporting Service" " Ч электронный адрес отправителя. Он обычно имеет вид sys 1ет@шш_домена.сот. Первая часть этой константы (Book Sales Repor ting) может содержать любой текст (обычно там указывается название отдела или службы, высылающей ежедневный отчет по продажам. В угло вых скобках должен быть задан реальный электронный адрес. И в этом случае сетевой администратор подскажет вам, что здесь надо ввести.

Последняя константа Ч строка подключения (connection string) к базе данных, содержащая всю информацию, необходимую для соединения с базой данных. Строка подключения в ADO выглядит примерно так:

Преобразование данных и предоставление отчетов SQL Server 2000... Const DB_CONNECT_STRING = "Provider=SQLOLEDB.1;

Data " & _ "Source=(local);

Initial Catalog=Pubs;

user id = " & " ' s a ' ;

p a s s w o r d * ' ' " Мы исходим из того, что читатель знаком с основами ADO, и поэтому лишь кратко описываем каждую часть строки подключения.

Элемент Provider=SQLOLEDB.l;

указывает драйвер базы данных. В нем определяется тип ODBC-драйвера, нужного для подключения к базе данных. Мы используем SQL Server. Если ваше приложение работает с ба зами данных другого типа, информацию об ODBC-драйвере можно найти по ссылке coverview.asp.

Элемент Data Source задает местонахождение сервера. Мы указали в нем local, так как у нас SQL Server находится на локальном компьютере. В ином случае может потребоваться IP-адрес или имя компьютера, на кото ром размещена база данных.

Initial Catalog содержит имя базы данных;

в нашем примере это Pubs, ко торая поставляется с SQL Server. Наконец, user id определяет имя пользо вателя, a password Ч его пароль.

По умолчанию точкой входа в ActiveX-задание является функция main, но может быть и любая другая. Функция main состоит всего из двух строк.

Сначала она вызывает процедуру Process_Daily_Sales, а затем возвращает константу, используемую в ActiveX-сценариях, Ч DTSTaskExecResult^Suc cess. В Package Object Browser можно посмотреть все константы проекта и глобальные переменные (рис. 8).

Эти константы и глобальные переменные позволяют управлять выполне нием операций в DTS-пакете. Мы не будем подробно описывать эти кон станты и переменные. Важно, что функция main возвращает константу DTSTaskExecResult_Success, поскольку в нашем примере VBScript-зада ние выполняет всего одну операцию.

Обработка данных по продажам Функция Process_Daily_Sales Ч главная часть нашего приложения. Она извлекает набор записей со списком авторов, книги которых продавались в течение сегодняшнего дня. Затем она форматирует этот набор записей как HTML-таблицу и отправляет ее соответствующим авторам.

Функция содержит три локальных переменных (два набора записей ADO и одну дату):

Microsoft SQL Server 4.1ирвмиш Й ]S т.*rs Con^anl V^.iB^.Adiv^-.rn Da^a Punp Corijlani AulHiot: Atok Meht3 and Dad S В TSTiansformStaLOK Oale 1 Л / IJaijji Sales Report using CDG.J| DTSTiansrojmSialJnlo You should have SQL Serves | i s 'Chaige ihe lol^^ing three ей!

!Х Я DTSTransiufmSlaLSkpFelch l SeeyoutDBAcjfNetv*iokAdnJ Ш DTSTfans-tormStaLSkiplnsert IB DTSTransformSlaLDeslDalaNoSet i'iunpte Mail Transfer Protocol - Ш D"STramformStat..Eiror [Const 5MTP_SERVER = '-:-Х : S! DTSTfanstofmStat_EriotSkpFlcw jtmail Address and Тзч( for theT Я DISTransiomiS(aL^ceptionRow Consl SENDER_EMAIL = J 33 DTSTtansiQriTi)fll_AbcrtPump SI DTSTrar-islorniStaLNoMoreRom 'ADO Conn&etkin Stnna to thel ^ala Driver. Query Constant Coml D8_mNNECT_STRINl ! rfTCTrs^,?fjMl^Ck-> ' r-'-j-'llj.^r,r В DTSriwisloimSfaUJpditeQuery -jSl DTSTransrermStaLDfcleteQuefy Name: Main :

- JSJ DTSTFertsforrffiaUJserQuwji Aur.hoi: Atak Mehta and Oal ] (-..-Щ Active* Script Cnrtstant 'Dale' 1 Л / Purpose. Calbihe PioceM_Di 51 DTSTa;

kFse-?Result_SuccsES !

-[Я DTSTaskEKseRefull Failure \3 У Steps Coreienl --X DTSSiepEKecS(at_Compteted :

MictbnK)ain{l jf DTSSteptHecSsatjnashve Call P[ocess_Daily_Sa| У OTSSiepExecSiatJnProgrew !

End Function JT Х.. *e,ik.^..c5rai_ /a. ng ' ^ jf OTSStepSctiplResuH^ExeculeTask 'Name: Fundicin Serid^iii.J" 'Authoi: A!ck Mehta and С з, :

V l~.-ih !V 'яН A LitoM Уанаом* 'Daie 1/1.^ 'Puipoje IJsssCDD to spni 1 5глч1Св! J Return Noihing Рис. 8. Константы проекта Dim rstAuthors Dim rstSales Dim Todays_Date Так как не все базы данных Pubs содержат данные по продажам за сегод няшний день, следующие две строки кода генерируют отчет за 14.09.1994.

Отчет за эту дату можно получить почти в любой базе данных Pubs. Мы просто убрали признак комментария из строки кода для даты 14.09.1994 и закомментировали строку с сегодняшней датой:

"Todays_Date = & DateQ & Todays_Date = "'9/14/1994'" Преобразование данных и предоставление отчетов SQL Server 2000... Для выборки авторов, книги которых были проданы сегодня, использует ся несложный запрос, объединяющий несколько таблиц:

strAu_Sales = "Select Distinct Authors.* from " & Where " &.

"Authors,Sales, TitleAuthor "TitleAuthor.au_id = Authors,au_id and " & "TitleAuthor.Title_id = Sales.Title.id and " & "Sales.ord_date = " & Todays_Date Далее получаем набор записей ADO, соответствующий этому оператору SQL:

Set rstAuthors = ExecuteSQL(strAu^Sales) Затем, прежде чем проходить по набору записей rstAuthors и оповещать авторов, убеждаемся, что этот набор записей не пуст:

If Not (rstAuthors.Eof and rstAuthors.Bof ) Then While Not rstAuthors.Eof Мы извлекаем и другую информацию: магазин, принявший заказ, номер заказа, количество позиций, вид платежа и названия книг. Эта информа ция берется из таблиц Stores, Sales, TitleAuthors и Titles. Как видите, и здесь выполняется простое объединение таблиц* :

strAu_Sales = "SELECT distinct stores.stor_name as [Store Name], " & "sales.ord^num as [Order Number], sales.qty as [Quantity], " & "sales.payterms as [Pay Terms], Titles.Title FROH Stores, Sales, " & _ "TitleAuthor, Titles " & "Where TitleAuthor.au_id = '" & _ rstAuthors("au_id") & "' and Sales.ord_date = " & Todays_Date " & "and Sales.Title^id = Titles.Title_id and sales.stor_id = " & _ "stores.stor^id " И в этом случае мы получаем набор записей ADO, выполняя оператор Execute SQL:

Set rstSales=ExecuteSQL(strAu_Sales) Теперь формируем тело сообщения с именем и адресом автора. Для этого используется HTML-тэг
, так как нам нужен формат HTML:

strTable = rstAuthors("au_fname") & " " rstAuthors{"au_lname") & "
" & rstAuthorsC'Address") & "
" & rstAuthors("city") & ", " & rstAuthors("state") & " " & rstAuthors("Zip") Этот запрос исправлен в соответствии с исходным кодом, мятым из файла SourceCode.txt. Ч Прим. сост.

190 Microsoft SQL Server Тело сообщения готово. Теперь встраиваем в него HTML-таблицу, содер жащую ранее полученные данные по продажам. Функция Format Recordset преобразует набор записей rstSales в HTML-таблицу:

strTable = strTable & FormatRecorcfset(rstSales) Далее вызываем функцию отправки электронной почты, параметрами ко торой являются тема сообщения, электронный адрес автора и тело сообще ния в формате HTML:

Call send_e-mail("Book Sales Report For: " & Todays_Date, rstAuthors("au_email"), strTable) Затем переходим к следующему автору:

rstAuthors.movenext Mend Функция ExecuteSQL подключается непосредственно к базе данных. В этой функции мы обращаемся к базе данных, выполняем передаваемую при вызове строку SQL и возвращаем результаты (если они есть). Снача ла создаем ADO-соединение:

Set myConn = CreateObject("ADODB,Connection") Затем создаем набор записей ADO:

set myRecordset = CreateObject("ADODB.Recordset") На следующем этапе открываем соединение, используя константу DB_CONNECT_STRING:

myConn.Open = DB_CONNECT_STRING Открыв соединение, открываем набор записей:

myRecordset.Open mySQLCmdText, myConn И, наконец, возвращаем результат:

Set ExecuteSQL = myHecordset Если у вас есть опыт работы с ADO, все эти действия будут для вас очень просты.

Функция FormatRecordset получает к качестве параметра набор записей ADO и возвращает HTML-таблицу (в виде строковой переменной). В ней три цикла, причем третий цикл вложен во второй. В первом цикле пере бирается список полей и формируется заголовок таблицы. Затем таблица Преобразование данных и предоставление отчетов SQL Server 2000... заполняется данными;

при этом внешний цикл проходит по записям, внут ренний Ч по полям. В итоге набор записей преобразуется в HTML-таблицу.

HTML-таблица создается как строка. Эта простая таблица определяется так:

strTable = л

Затем переходим на первую запись и создаем строку таблицы, используя тэг :

rstTable.MoveFirst strTable = strTable & "

" Затем проходим по полям и добавляем в таблицу имена полей с помощью тэга " Next Закрываем тэг и таким образом завершаем формирование заголовка таблицы:

strTable = strTable & "

" Теперь, чтобы заполнить HTML-таблицу информацией, обрабатываем данные, хранящиеся в наборе записей. Проходим по всем записям и запол няем HTML-таблицу тэгами и " For Index = 0 To rstTable.Fields.Count - strTable = strTable & "" Next strTable = strTable & "" rstTable.MoveNext Wend Наконец таблица заполнена. Осталось вернуть значение функции Ч пере менную строкового типа:

Microsoft SQL Server strTable = strTable & "

, выделяя их цветом:

For Index = 0 То rstTable.Fields.Count - strTable = strTable & "

" & "" strTable = strTable & rstTable.Fields.Item(Index).Name strTable = strTable & "
:

While (Not rstTable.EOF) strTable = strTable & "

" strTable = strTable & rstTableCrstTable.Fields.Item(Index).Name).Value strTable = strTable & "
" strTable = strTable & "
" FonnatRecordset = strTable Отправка отчетов Для отправки отчетов авторам служит функция Send_Email. Заметьте, в ней используются константы SMTP_SERVER и SENDER_EMAIL, а так же Microsoft Collaboration Data Objects (CDO 2.0) из CDOSYS.DLL. CDO предоставляет объектную модель для разработки коммуникационных при ложений в Windows 2000, базируется на стандартах SMTP и NNTP и дос тупен как системный компонент при установке Windows 2000 Server. Это стандартный API для создания приложений массовой рассылки и переда чи почтовых сообщений через Web, Такие приложения работают под уп равлением Windows 2000 Server.

У функции Send _Email три параметра: subject, rcpt и msgHTML. Первый Ч это тема отправляемого сообщения, второй Ч электронный адрес получа теля, а третий Ч тело сообщения в формате HTML.

В Send_Email также используются две другие константы: cdoSendUsing Pickup (указывает, что сообщение должно быть отправлено ч:ерез локаль ный каталог службы SMTP) и cdoSendUsingPort (указывает, что сообще ние должно быть отправлено по сети).

Если на локальном компьютере установлена служба SMTP, по умолчанию берется константа cdoSendUsingPickup. В ином случае (если установлен Outlook Express) Ч константа cdoSendUsingPort и параметры учетной за писи по умолчанию (default account). Мы используем cdoSendUsing Pickup.

Далее в Send_Email создаются СОМ-объекты Message и Configuration:

set iMsg = CreateObjectC'CDO.Message") set iConf = CreateObjectC'CDO.Configuration") У объекта Configuration несколько полей. Перед присвоением значений эти поля связываются с объектом конфигурации:

Set Fids = iConf.Fields Большинство полей, используемых для задания конфигурации CDO объектов, определяется в пространстве имен cdo/configuration/. Мы присваиваем значения трем полям (SendUsing, SMTP_SERVER и TimeOut) объекта Configuration:

With Fids.Item(" = Преобразование данных и предоставление отчетов SQL Server 2000... cdoSendUsingPickup. Item( " г") = SHTP.SERVER,Item(" " & _ "smtpconnectiontimeout") =.Update End With Наконец, указываем, что сообщение будет использовать только что опре деленную нами конфигурацию, заполняем остальные поля и отправляем сообщение:

With iMsg Set.Configuration = iConf.To = rcpt.From = SENDER_EMAIL.Subject = subject.HTMLBody = msgHTML.Send End With Заключение В этой статье мы рассказали, как реализовать технологию доставки инфор мации на основе SQL Server 2000, VBScript, ADO, CDO и DTS-пакетов.

Наш пример прост, но функционален. Чтобы расширить его, вы могли бы использовать более сложную модель данных и внешние компоненты, по зволяющие отправлять пользователям более полные отчеты. Кроме того, вы могли бы интегрировать в VBScript-задание другие DTS-задания. Ме тодика, описанная в этой статье, применима и к рассылке самой разнооб разной информации: регулярных финансовых отчетов, сведений о состоя нии заказов и любых других данных.

Исходный код для этой статьи можно скачать по ссылке: load.microsoft.com/download/msdnmagazine/code/Aug02/WXP/EN-US/ VBScriptAndSQLServer2000.exe.

Алок Мехта (Alok Mehta) Ч старший вице-президент и директор по техноло гиям в American Financial Systems Inc. (AFS) (Уэстон, штат Миннесота). С ним можно связаться по адресу amehta@afs-link.com.

Дэниел Уильяме (Daniel Williams) Ч заместитель вице-президента по технологиям в Deferral.com (компании в составе AFS), а также инженер ПО, участвующий в проекте Deferral.com. С ним можно связаться по адресу dwilliams@afs-link.com.

7- Франческо Балена Автоматизация выполнения административных задач в SQL Server* SQL Server можно программно администрировать на основе системных хранимых процедур, но зачастую удобнее использовать современную объектно-ориентированную технологию DM0 {Distributed Management Objects). В статье описывается SQL-DMO в SQL Server 7.0 и SQL Server 2000, а также рассматривается объектная модель SQL-DMO. Основное внимание уделяется поддеревьям Databases и JobServer дерева этой модели. Примеры кода демонстрируют, как с помощью разнообразных объектов вроде Registry, Configuration и Database автоматизировать выполнение типичных задач администрирования, в том числе программное считывание параметров конфигурации, создание баз данных, запуск сценариев (scripts) T-SQL и резервное копирование данных по расписанию.

Вместе с Microsoft SQL Server поставляется набор хранимых процедур для автоматизации административных операций (создания баз данных и таб лиц, резервного копирования и г. д.), Однако раньше вам бы понадобился опыт программирования на T-SQL, чтобы в полной мере задействовать эти возможности. С появлением DMO (Distributed Management Objects) си туация изменилась. Теперь любой программист может решать такие зада чи, используя тот язык, который ему больше нравится.

SQL-DMO Ч это набор из примерно 150 объектов, отражающих практи чески все аспекты управления SQL Server 7.0 и SQL Server 2000. Эта объектная модель полностью поддерживает двойственные интерфейсы Публиковалось в MSDN Magazine. 2001. №5 (май). Ч Прим. изд.

Автоматизация выполнения административных задач в SQL Server (dual interfaces), поэтому она применима почти в любом языке, в том чис ле в Visual Basic, C++, VBScript, JScript, Windows Script Host (WSH) и ASP-сценариях. В статье приводится исходный код, показывающий, как программно считывать параметры конфигурации, создавать базы данных, запускать сценарии T-SQL, создавать задания на резервное копирование и указывать расписание их выполнения. Основная часть кода написана на Visual Basic, но концепции, о которых рассказывается в статье, легко реа лизовать на любом другом языке программирования.

Обзор SQL-DMO С физической точки зрения, объектная модель DMO реализована в SQL DMO.DLL. Вспомогательный файл SQLDMO.RLL содержит все локали зуемые ресурсы. В SQL Server 7.0 этот RLL-файл находится в каталоге \Mssql7\Binn\Resources'vrxYA- (если при установке выбран путь, предлага емый по умолчанию), где хххх Ч десятичное значение идентификатора языка (например, 1033 для U.S. English), В SQL Server 2000 по умолчанию используется каталог C:\Program Files\Microsoft SQL Server\80\Tools\ Binn\Resources\;

u:xr. Упомянутые файлы копируются на диск стандарт ной программой установки SQL Server, так что для активизации DMO дополнительных действий не требуется. Кроме того, программа установ ки запускает сценарий SQLDMO.SQL (написанный на T-SQL) для уста новки нескольких системных процедур, необходимых DMO. Вы можете самостоятельно запустить его из каталога \Mssql7\lnstall (для SQL Server 7.0) или Microsoft SQL Server\MSSQL$w.w,a cepeepa\lnsta\\ (для SQL Server 2000), если полагаете, что эти процедуры были удалены или изме нены. DMO также устанавливается в составе инструментальных средств, развертываемых на клиентских рабочих станциях. В файле REDIST.TXT в корневом каталоге установочного компакт-диска SQL Server содержит ся информация по установке и редистрибуции SQL-DMO.

Компонент SQLDMO.DLL обращается к SQLSVC.DLL (модулю абстрак ции базы данных), а тот Ч к ODBC32.DLL, которая в свою очередь вызы вает ODBC-драйвер SQL Server. ODBC-драйвер должен быть не ниже версии 3.70 (эта версия поставляется с SQL Server 7.0). Если определен псевдоним и вы указываете его в качестве имени сервера, то и SQL-DMO будет использовать этот псевдоним при поиске источника данных ODBC.

SQLDMO.DLL работает с протоколом по умолчанию, заданным через Client Network Utility, и не может самостоятельно перейти на другой се тевой протокол.

Объекты SQL-DMO верхних уровней Несмотря на большое количество объектов в DMO, эта модель удивитель но проста в использовании;

ее структура напоминает иерархию объектов 196 Microsoft SQL Server SQL Server Enterprise Manager. Корнем DMO-дерева является объект Application;

под ним находится объект ServerGroup, далее идет объект SQLServer. Все объекты верхних уровней модели показаны на рис. 1. У объекта SQLServer три важных поддерева: Databases (включает все объек ты, нужные для создания и управления базами данных, таблицами, индек сами и т. д.), JobServer (содержит все объекты, предназначенные для опре деления заданий и расписаний их выполнения) и Replication (позволяет реплицировать базы данных, определять издателей и подписчиков и т. д.).

В этой статье основное внимание уделяется поддеревьям Database и JobServer.

BackupDeuices Languages RemoteServers Re in ale Logins Server Roles Рис. 1. Объект Application Автоматизация выполнения административных задач в SQL Server Работа с объектами SQL-DMO Все объекты в иерархии SQL-DMO можно разбить на три большие груп пы: индивидуальные объекты (например, Database, Table и Column), набо ры (например, Databases, Tables и Columns) и списки. Объекты-списки аналогичны наборам, но не имеют встроенной поддержки для добавления и удаления элементов. Обычно списки Ч это значения, возвращаемые ме тодами или свойствами. Так, в следующем коде поле со списком (combo box) заполняется именами серверов:

Dim sqlApp As New SQLDMO.Application Dim NL As SQLDMO.NameList Set NL = sqlApp.ListAvailableSQLServers For index = 1 To NL.Count cboServers.Addltem NL.Item(index) Next Для SQL Server 7.0 этот код выведет список серверов, которые работают под управлением Windows NT, используют протокол Named Pipes и нахо дятся в одном домене сети. (Так как при поиске серверов применяется широковещание по NetBIOS, список серверов может стать еще короче в зависимости от инфраструктуры маршрутизации. И еще одно: серверы SQL Server под управлением Windows 9.r не показываются, поскольку они не прослушивают пакеты по протоколу Named Pipes.) В SQL Server при поиске серверов наряду с широковещанием по NetBIOS применяется широковещание по UDP, поэтому в список попадут и те серверы, которые используют стек протоколов TCP/IP. Кроме того, если на локальном ком пьютере работает сервер, он тоже добавляется в список.

Далее я покажу, как использовать объект SQLServer для выполнения не которых типичных задач администрирования.

Подключение и отключение Для подключения к заданному экземпляру SQL Server нужно создать объект SQLServer, при необходимости присвоить подходящее значение полю LoginTimeout и вызвать метод Connect:

Dim SQLServer As New SQLDMO.SQLServer SQLServer.LoginTimeout = If UseNTAuthentication Then Аутентификация средствами Windows NT SQLServer.LoginSecure = True SQLServer.Connect strServer Else 98 Microsoft SQL Server Аутентификация средствами SQL Server SQLServer.Connect strServer, strlogin, strPassword End If Как именно осуществляется подключение, зависит от вида аутентифика ции Ч через Windows NT или SQL Server.

В предыдущем фрагменте кода strServer Ч имя сервера, a strLogin и strPassword Ч имя и пароль при аутентификации через SQL Server. Учти те, что при неудачной попытке входа генерируется ошибка периода выпол нения, поэтому в реальных приложениях эти операторы надо защищать обработчиком ошибок.

В отличие от ADO-объекта Connection объект SQLServer при выходе из диапазона действия не закрывает соединение автоматически, поэтому, если вы сами его не закроете, оно остается открытым, пока не истечет вре мя ожидания. Весьма важно по завершении работы с SQL Server отклю читься от него (вызовом SQLServer.Disconnect), поскольку формируемый SQL-DMO кэш метаданных не уничтожается до отсоединения (хотя SQL Server 2000 по возможности пытается побыстрее освободить объекты кэша метаданных). Поэтому, чтобы DMO не удерживала лишние ресурсы, унич тожайте все объекты явно:

set oDatabase = Nothing Кэш метаданных поддерживается индивидуально для каждого процесса, использующего SQL-DMO (другими словами, кэш метаданных локален для процесса, в чьем адресном пространстве он размещается, и не разделя ется процессами, выполняемыми на одном компьютере).

Кроме того, с помощью объекта SQLServer можно в один прием запустить службу Microsoft SQL Server и подключиться к ней либо приостановить, возобновить или остановить ее выполнение (рис. 2). Пример на Visual Basic (stoplite), демонстрирующий, как реализовать эти операции, можно скачать по ссылке в разде ле за май. Получить текущее состояние службы позволяет свойство SQL Server. Status.

Объект SQLServer напрямую управляет службой MSSQLServer. Для за пуска и останова службы SQLServerAgent, управляющей выполнением заданий по расписанию, нужно использовать объект JobServer:

SQLSe rver.JobSe rve г.Start SQLSe rver.JobSe rve r.Stop Для настройки служб SQL Server на автоматический запуск при загрузке операционной системы установите свойства объекта Registry:

Автоматизация выполнения айминистратианых задач в SQL Server SQLServer.Registry.AutostartServer = True SQLServer.Registry.AutostartDTC = True Рис. 2. Приостановка, возобновление и остановка выполнения службы Public Sub SQLServiceManager{NewState as Int) Dim oSQLServer As SQLDMO.SQLServer Set oSQLServer = New SQLServer oSQLServer.Name = "(local)" On Error GoTo errHandler Select Case Index Case 1 ' запуск If oSQLServer.Status = SQLDHQSvc_Stopped Then oSQLServer. Start False Else HsgBox "Cannot start server, server is either" 1 "running or paused' vbOKOnly, Д "SQL Server Service Manager" End If Case 2 ' приоетановка/возобнаалени-е If oSQLServer.Status = SQLDMQSvc.Paused Then oSQLServer.Continue Elself oSQLServer.Status = SQLDMOSvc_Bunnjng Then oSQLServer,Pause Else HsgBox "Cannot pause a server which is running", & vbOKOnly, "SQL Server Service Manager" End If Case 3 ' остановка If oSQLServer.Status = SQLDMOSvc_Running Then oSQLServer.Stop ' oSQLServer.Shutdown выдает TSQL-конанду "shutdown" ' и не использует диспетчер управления службами (SCM) Else HsgBox "Cannot stop a server which is not running", vbOKOnly, "SQt Server Service Manager" End If End Select Exit Sub errHandler:

HsgBox Err.Number & " " & Err.Description & " <" & _ Err.Source & " " & Err.LastDUError & ")", _ vbQKOnly, "SQL Server Service Manager" Иnd Sub 200 Microsoft SQL Server Заметьте, что служба SQLAgent не работает в Windows 98, Windows 98 SE и Windows Me, так как в отличие от Windows NT в этих операционных системах нет диспетчера управления службами (Service Control Manager, SCM). (SQL Server 2000 больше не поддерживает Windows 95.) Соответ ственно в Windows 9x нет и учета взаимозависимостей служб, Объекты Registry и Configuration Объект Registry обеспечивает доступ ко многим важным параметрам, оп ределяющим каталог установки, чувствительность к регистру букв, путь к базе данных master и имя зарегистрированного владельца. Не удивитель но, что не все эти параметры можно изменить без переустановки SQL Server.

Объект Configuration открывает доступ к набору элементов ConfigValue.

Каждый элемент соответствует одному из примерно 40 параметров конфи гурации, значения которых можно просматривать и изменять с помощью хранимой процедуры sp_configure. Но проще работать с этими значения ми, используя объектно-ориентированный подход. Так, следующий код выводит список значений основных и дополнительных параметров конфи гурации:

Dim cv As SOLDMO.ConfigValue With SQLServer.Configuration.ShowAdvancedOptions = True For Each cv In.ConfigValues Print cv.Name, cv.RunningValue, cv.CurrentValue Next End With Вы можете изменить параметр конфигурации и зафиксировать изменения, вызвав метод ReconfigureCurrentValues или ReconfigureWithOverride:

' Разрешаем изменение системных таблиц With SQLServer.Configuration.ConfigValuesC'allow updates").CurrentValue =.ReconfigureWithOverride End With Извлечение данных из объекта QueryResults Не все параметры конфигурации так легко прочитать. Объект SQLServer содержит 10 методов Епигплхг, возвращающих информацию об учетной записи, атрибутах сервера, группах в домене Windows NT и такие важные динамические данные, как сведения о выполняемых процессах и текущих блокировках. Все методы Enunuxr возвращают объекты QueryResults.

Автоматизация выполнения административных задач в SQL Server Объект Query Results можно рассматривать как контейнер, хранящий один или несколько доступных только для чтения двухмерных массивов, из которых информация извлекается методом GetColumnString, GetColumn Long или Get Column Double. Применение этих методов довольно утоми тельно, потому что сначала нужно определить тип каждого столбца по свойству ColumnType и только потом выбрать соответствующий метод GetColummxt.

Однако есть более простой и быстрый способ извлечения данных из объ екта QueryResults, основанный на использовании метода GetRangeString.

Этот метод возвращает текст, который выводился бы в окне Query Analy zer при запросе той же информации через соответствующую хранимую процедуру (например, sp_\vho при получении списка пользователей и про цессов или sp_locks при получении информации о текущих блокировках).

Полученную строку можно быстро обработать для извлечения нужных значений вызовом функции Split языка Visual Basic или VBScript.

На рис. 3 показана универсальная процедура, которая преобразовывает объект QueryResults в ADO-объект Recordset. Я решил использовать Recordset вместо, например, обычного двухмерного массива строк, так как это позволяет сразу же показать результаты, связав с Recordset элемент управления DataGrid (рис. 4).

Рис- 3. Преобразование QueryResults в АОСЬобъект Recordset Function QueryResultToRecordset(qres As SQLDHQ.QueryResylts) _ A3 ADQDS.Recordset. Dim qresString As String DliB rs As New AOODB. Recordset Dim rowsO, As String, cols() As String Dim rowlndex As Long, callndex As Long * Получаем все результаты в виде одной длинной строки со столбцами ' и записями, разделенными специальными Символами;

в данном ' случав используются необычные разделители столбцов/записей, " так как некоторые значения иогут содержать CRLF qresString = qres.GetRangeString(t,,, СЬг(2), Chr<1)) ' Первая запись длинной строки содержит заголовки столбцов.

row&Q = SplitCqresString, Dhr<2)} cols() * Split(rQws(0), Ghr(1 Создаем поля Recordset For collnitex = 0 To UBoijnd(cols) rs.Fields.Append RTrim$(cols(colIndex)), adVarCtiar, Д cfres.ColurflnMaxLength(colIndex + 1) Next см. след. стр.

Microsoft SQL Server Рис. З. Преобразование QuervResults в ADO-объект... (окончание) Добавляем записи в Recordset rs.Open Вторая запись, rows(1), состоит из символов -, отделяющих заголовки For rowlntiex = 2 То UBound(rows) ' Получаем отдельные столбцы colsC) = Split(rows(rowlndex), Chr(1)) ' Добавляем запись rs.AddNew For collndex = 0 To UBound{cols) rs.Fields(collndex) - RTrim$(cols(colIndex)) Next rs.Update Next " Возвращен Recordset вызывающему Set QueryResultToflecordset = rs End Function Рис. 4. Отображение QueryResults Объект SQLServer Ч не единственный член иерархии, возвращающий объект QueryResults. Я насчитал в объектной модели SQL-DMO более методов Enumjcxu:, позволяющих получать информацию практически любого вида, в том числе зависимости между объектами базы данных, под писки репликации (replication subscriptions), таблицы связанных серверов и др. Конечно, эти методы позволяют не только показывать результаты пользователю, но и решать более важные задачи. Например, вы можете написать изощренные утилиты, помогающие оптимизировать работу сис темы за счет мониторинга блокировок и процессов.

Объект Database Это, пожалуй, самый интересный с точки зрения разработчика объект в иерархии DMO-SQL. Получив на него ссылку, вы можете создавать и уда Автоматизация выполнения административных задач в SQL Server лять все таблицы, представления, хранимые процедуры, пользователей и группы базы данных вплоть до отдельных столбцов, индексов и триггеров, перебирать эти объекты и изменять их поведение (рис. 5).

DataBaseRoles FullTeitCattlogs StoredProcedures SystemDatalypes UserDefinedDataTypes FileGroups Рис. 5. Объект Database Поскольку все зависимые объекты группируюгся в наборы, перебирать объекты очень легко. Так, код на рис. 6 перечисляет все таблицы (и их столбцы) базы данных Pubs, поставляемой с SQL Server.

У большинства объектов базы данных есть свойство SystemObject, возвра щающее True (если объект сгенерирован системой) или False (если объект определен пользователем), поэтому нетрудно показывать при выводе только те объекты, которые вас интересуют:

Перечисляем все несистемные хранимые процедуры ' в базе данных Pubs и их определения на языке T-SQL Dim sp As SQLDMO.StoredProcedure For Each sp In db.StoredProcedures Microsoft SQL Server If Not sp.SystemObject Then Print "- PROCEDURE " & sp.Name Print sp.Text End If Next Рис. 6. Список таблиц и их столбцов в базе данных Pubs ' Предполагается, что объект SQLServer подключен к серверу Dim db As SQLDMO.Database Dins tbl As SQLDMO. Table Dim col As SQLOMQ.Column Set db - SQLServer.Databases("pubs") For Each tbl In db.Tables PriRt "TA8L " 4 tbl.Nase For Each col In tbl.Columns Print " " 8 col.Name fiext Next Создание и выполнение сценариев Т-SQL Многие объекты SQL-DMO, в том числе Database, Table, Index, Key, Job, Alert, Trigger, User, Rule, Check и большинство объектов репликации, со держат метод Script, который возвращает сценарий Т-SQL, генерирующий этот объект. Но учтите, что данный сценарий не создает внутренние объек ты. Например, следующий код создает сценарий, содержащий только оператор CREATE DATABASE и группу вызовов хранимой процедуры sp_option, ' Получаем сценарий на Т-SQL для базы данных Pubs Dim pubsDB As SQLDMO.Database Set pubsDB = SQLServer.Databases("pubs") Print pubsDB.Script Чтобы воссоздать всю структуру базы данных, необходимо вызвать метод Script для всех внутренних объектов иерархии Database, в частности для всех таблиц, представлений и хранимых процедур. Однако метод Script объекта-таблицы облегчает генерацию сценариев, создающих базы дан ных. Он принимает аргумент с набором битовых флагов, указывающих, надо ли также генерировать сценарии для создания индексов, ограничений (constraints), триггеров и внешних ключей (рис. 7).

SQL-DMO выполняет один пакет (batch) единовременно и при анализе SQL-сценариев не проверяет наличие разделителей пакетов. Поэтому в Visual Basic сгенерированный сценарий нельзя выполнить с помощью ме Автоматизация выполнения административных задач в SQL Server года Execute Immediate или любого другого метода Ехеси1еЛда;

, не интер претируя сценарий самостоятельно. При программной генерации сценари ев T-SQL очень удобно, что их можно обрабатывать как строки. Например, можно взять таблицу и создать новую таблицу с такой же структурой и другим именем (рис, 8).

Рис, 7. Генераций сценария T-SQL для создания таблиц ' Генерируем сценарий T-SQL для создания всех таблиц ' в базе данных Pubs и их внутренних объектов Dim tbl As SQLDHQ.Table Dim allParams As Long, щ! As String ' Зти константы определены в библиотеке типов ОНО allParams = SOLDMQScript_Default Or SQLDMGScript.lndexes Or SQLOHOScript_DRI_AllConstraints Or _ SGLDMOSerlptJYiQgers Or SQLDMOScript_DflI_ForeignKeys For Each tbl In pubsDB.Tables sql * sql & tbl.Script(allParaiBS) Next Рис. 8., Создание таблицы с той же структурой, что и у существующей ' Чтобы создать сценарий для таблицы о другим именем, ' используйте параметр NewName метода Script, как,показано ниже Din oSQLServer As SOLDHO.SQLServer Set oSOLServer * Hew SQLDMO.SQLServer.

oSQLServer.LoginSecure * True,oSQLServer.Connect "(local)" Dim oTabla As SQLDMO.Table Set oTable = oSQLServer.Databases("pubs"}.Table8C"sales") Dim sqlText As String Dim ScriptOptionsI As SQLDMO.SQLDMQ_SCRIPT_TYPE Dim ScriptDptions2 As SQLDMQ.SQLBM(LSCRIPT2J"YPИ ScriptOptionsI = SQLDMOScript.Default * SQLDHOScriptJJwnerQualify ScriptOptions2 = SULOMOScript2_Default sqlText = oTa61e.Script(ScriptQptiotis1,, "New Sales", ScriptOptions2) Метод надо ввполнять в контексте базы данных Pubs, чтобы разрешать " (resolve) используемые в таблице sales пользовательские типы данных, aw. след. стр.

Microsoft SQL Server Рис. 8. Создание таблицы с той же структурой... (окончание) ' а также из-за отсутствия в sqlText оператора "use pubs" oSQLServer.Databases("pubs").Exeeutelmmediate sqlText ' Для выполнения метода в контексте сервера удалите ссылки " н а базу данных, так как здесь это будет приводить к ошибке ' из-за отсутствия oSQLServer,Exeeutelmmediate sqlText oSQLServer.Disconnect Set oSQLServer = Nothing Создание заданий и расписаний их выполнения Управление заданиями (jobs) и их выполнение по расписанию Ч та об ласть, где в полной мере проявляется полезность технологии SQL-DMO.

И действительно вам, как правило, нужно привести поведение SQL Server в соответствие с потребностями пользователей, а эти потребности часто меняются. Объекты поддерева JobServer иерархии DMO (рис. 9) позволя ют разрабатывать клиентские приложения, которые дают возможность пользователям с помощью простого и удобного CI создавать и выполнять по расписанию комплексные задания, связанные с обслуживанием базы данных.

В рамках одной статьи нельзя детально рассказать о каждом из дочерних объектов объекта JobServer, поэтому я сосредоточусь на самых важных и полезных из них;

объекте Job и зависимых от него объектах JobStep и JobSchedule. Если вы знакомы с созданием заданий и настройкой распи сания их выполнения в SQL Server Enterprise Manager, вам будет неслож но применять эти объекты.

Посмотрим, как создать задание, проверяющее целостность базы данных и выполняющее ее резервное копирование, если ошибок не обнаружено.

Создадим объект Job. присвоим значения его свойствам Name и Descrip tion, а затем добавим этот объект в набор Jobs объекта JobServer:

Создаем и инициализируем объект Job Dim SQLJob As New SQLDMO.Job SQLJob.Name = "Northwind Backup" SQLJob.Description = "Check and Backup Northwind" Добавляем его в набор Jobs SQLServer.JobServer.Jobs.Add SQLJob Каждая операция задания (job step) в Enterprise Manager соответствует объекту JobStep иерархии SQL-DMO. Операции задания бывают трех ти Автоматизация выполнения административны* задач в SQL Server нов: команды T-SQL, команды Windows Script и команды операционной системы. Тип операции задается присвоением свойству Subsystem соот ветствующего строкового значения, AlerlCategories JabCategories JabSchedules JobSteps QperatorCategories Operators TargetSermGroups MemberServers TargetServers Рис. 9. Поддерево JobServer Вот как, например, создать операцию задания, выполняющую команду DBCC CHECKDB применительно к базе данных Northwind:

Dim aJobStep As SQLDMO.JobStep Set aJobStep = New SQLDHO.JobStep aJobStep.Name = "Step 1: Check Northwind" aJobStep.StepId = aJobStep.Subsystem = "TSQL" aJobStep.DatabaseName = "Northwind" aJobStep.Command = "DBCC CHECKDB ('Northwind', REPAIR.FAST)" aJobStep.OutputFileName = "c:\temp\job1.log" Microsoft SQL Server Свойство OutputFileName служит для задания имени и пути файла, в ко торый выводятся данные при выполнении данной операции задания. При использовании этого свойства возникает проблема: для каждой операции задания содержимое файла перезаписывается (тогда как Enterprise Mana ger позволяет дописывать информацию в конец файла). Это одна из не многих ситуаций, где модель SQL-DMO менее гибка, чем Enterprise Mana ger. Такое ограничение можно обойти, используя для каждой операции задания свой временный файл, а потом объединяя их содержимое в файл реального вывода.

Чтобы указать, чем закончилось выполнение операции задания Ч успехом или ошибкой, вы присваиваете значения свойствам OnSuccessAction и ОII Failure Action соответственно- Эти свойства принимают одно из четы рех значений перечислимого типа: SQLDMOJobStepAction_QuitWith Success, SQLDMOJobStepAction_QuitWithFailure, SQLDMOJobStepAc tion_GotoNextStep или SQLDMOJobStepAction_GotoStep.

Допустим, если команда DBCC CHECKDB выполнена успешно, нужно перейти к следующей (второй) операции, а при неудаче задание требует ся завершить:

aJobStep.OnSuccessAction = SQLDMOJobStepAction_GotoNextStep aJobStep.OnFailureAction = SQLDMOJobStepAction_QuitWithFailure Можно перейти и к любой другой операции, но для этого надо присвоить значение свойства Stepld этой операции свойству OnSuccessStep или OnFailureStep:

Если DBCC CHECKDB потерпит неудачу, переходим к 3-й операции aJobStep.OnFailureStep = aJobStep.OnFailureAction = SQLDKOJobStepAction_GotoStep Закончив определение текущей операции задания, добавьте объект Job Step в набор JobSteps родительского объекта Job:

SQLJob.JobSteps.Add aJobStep Теперь можно определить остальные операции задания. Например, на рис. 10, если команда DBCC CHECKDB завершилась успешно, вторая операция выполняет резервное копирование базы данных, а третья Ч за пускает ActiveX-сценарий, объединяющий вывод предыдущих операций в файл журнала приложения. Для запуска ActiveX-сценария свойству Sub System объекта JobStep присваивается значение ActiveScripting, свой ству DatabaseName Ч имя языка сценариев (VBScript или JScript), а свой ству Command Ч код сценария. (Можно также выполнить команду опера ционной системы, присвоив свойству Subsystem строку CmdExec. a свойству Command Ч текст команды операционной системы. Скажем, для Автоматизация выполнения административных задач в SQL Server запуска служб, отвечающих за работу с Интернетом, свойству Command присваивается значение IISRESET START.) Рис. 10. Проверка базы данных, ее резервное копирование и генерация файла журнала * - Создаем к инициализируем объект Job Dim SQLJob As New SQLDMO.Job SQUob.Naaie - "Korthwind Saekup" SQLJob.Description = "Check and Backup Northwind" ' Добавляем объект в набор Jobs SQLServer.JobServer. Jobs. Add SQUob ' - Первая операция: проверка базы данных Din aJobStep As SQLDMO.JobStep Set aJobStep = New SQLDMO.JobStep aJobStep.Naiae = "Step 1: Check NorthwiiKl" aJobStep.StepId = aJobStep,Subsystem = "TSQi" aJobStep,BatabaseName = "Northwind" aJobStep.Comiiarwt = "BBCC CHECKOB {'Northwlnd', REPAIR_FAST)" aJobStep.OutpytFileHaHie *= "c:\temp\job1.tinp" ' Если DBCC CttECKDB терпит неудачу, переходим к 3-й операций aJobStep.OnFailureStep = aJotjStep.OnFailureAetion = SQLDMQjobStepAction_QotoStep SQLJob.JobSteps.Add aJobStep ' - вторая операция: резервное копирование базы данных.

йожно повторно использовать объект JobStep Set aJobStep = New SQLDHQ.JobStep Указываем, где размещать резервные копии bakdir = SQLServer.flegistry.SQLDataPath & "\backup" aJohStep,Kerne * "Step 2: Backup Northwlnd" aJobStep.StepId * aJooStep.Subsystem = "TSQI" aJobStep.DatabaseHaiue - "Northwind" aJobStep.Command = "BACKUP DATABASE [Northwind] TO DISK = '" _ bakdir & "\Korthwlnd.bak1 " ' Залетьте;

испопьзуетсв другой файл журнала, ' чтобы не перезаписывать вывод 1-й операции.aJobStep.GutpytFlleNaroe ^ "c:\terBp\job2.tmp" ' В любом случае переходик к следующей операции ajobStep.

210 Microsoft SQL Server Рис. 10. Проверка базы данных, ее резервное.., aJobStep.Name - "Gather output and delete tap files" JobStep.StepID JobStep.Subsystem = "AetiveScrtpting" ' При использовании сценария свойству DatabaseName ' присваивается название языка сценариев avIobStep.DatabasefiaBe = "VBScript" ' Создаем сценарий Ш!в Script As String Script = "Did fso* stepld, file, text, filename" & vbCrlf & _ "Set fso - CreateObject(""Scrlptlne-PibSystemObjecf'")" _ 4 vbCrlf & _ "* gather text front all temporary files and delete them '" _ & vbCftf 4 _ "For stepIO = 1 To 2" & vbCrLf & Д filemne - ""c:\temp\Job"" & CStr(stepID) & "".trap""" Д 4 vbCrlf & _ Set file = fsQ.OpenTextFileCfilename, 1, True, True)" Д & vbCrlf & _ text * text 4 file.ReadAll" & vbGrLf & _ file.Close" 4 vbCrlf & _ fso.DeleteFiie filenaffle" & vbCrtf & _ "Next" & vbGrtf S _ "Set file * fsD.CreateTextFUeC""" & txtFile & " " True)" _ ", & VbCrLf ' Завершаем сценарий - обратите внимание, что сценарии объекта Jo ' ДОЛЖНЫ явно присваивать всем объектам значение Nothing Script = Script 4 Д "file.WriteLine ""Output from '" & SQLOot.Nflffle Д 4 "' job at "" S №ж)" 4 vbCrLf & _ "file.Wrltellra StrlRf(40, ""--")" & vbGrlf & _ "file.Write text" 4 vbCrLf 4 _ "file,Close" 4 vbGrlf & vbCrtf & Д "Set fso * Nothing" 4 vbCrLf 4 _ "Set file Nothing" 4 vbCrLf * Присваиваен созданный сценарий свойству Command объекта JobStep aJebStep.Coeraand ;

= Script ' В любом случае выполнение задания завершается aJobStep.GflSuccessActlon = SQiOMOJobStepActioR_QuitWithSuccess adobStep.OnFailActien * SQlD№)JobStepAction_QuitWithFailMre SQUeb.JebSteps.Add aJobStep '- Применяем задание.

' Указываем операции, с которой начинается выполнение SQUofe.StartStepIO л ' Применяем к локальному SQL-сервзру SQLJob.ApplyloTargetServer "(local)" Автоматизация выполнения административных задач в SQL Server После создания объект Job применяется к указанному объекту SQLServer и запускается методом Invoke или Start. Эти методы делают одно и то же, но Start позволяет указать операцию, с которой начинается выполнение задания.

Последний штрих Ч подготовка одного или нескольких расписаний вы полнения задания. Для этого вы создаете объект JobSchedule, инициали зируете его свойства и добавляете этот объект в набор JobSchedules со зданного объекта Job. В свойствах поля Schedule объекта JobSchedule указываются время начала и окончания действия расписания, а также пе риодичность выполнения задания (рис. 11). Вы можете установить ежед невное, еженедельное или ежемесячное выполнение и выбрать, в какие дни недели будет выполняться задание. Заметьте, что свойства Active StartDate и ActiveEndDate объекта Schedule Ч значения типа Long в фор мате ггггммдд, а свойства ActiveStartTime и ActiveEndTime Ч значения типа Long в формате ччммсс.

Рис. 1. Расписание выполнения задания ' Получаем ссылку ма задание, для которого формируется расписание Dim SQLJob As SQLDMO.Jotj Set SQLJob = SQLServer.JobServer.Jobs"Horthwind Backup") ';

Объект JobSchedule будет задавать ежедневное резервное копированию Dim SQLSchedule As New SQLBHQ.JobSchedule SQLSchedule.Name = "Daily &ackup" SQLSchedule.Schedule.FrequeneyType = SGieKOFreo_Dally SQLSchedulS.Schedule.Frequencylnterval = * Начало действия - 18 октября 2000 г, в 23: SQLSchedule.Schedule,ActiveStartDate = SOLSchedyle.Schedule.ActiveStartTiffleOfBay 2355ФО ' Расписание никогда не прекращает действовать ' (можно было бы не указывать дату и время прекращения действия) SQLSehedule.Schedule.ActiveEndDate SQLDMO.NOEHDDATE SQtSchedule. Schedule, Active En dTimeOf Day *= SQLDMOJIQENOTIHE Х Добавляем расписание в объект Job SQLJob.BeginAlter.

SSLJob. JobSchedules.Add SQLSchedule SQUob.DoAlter Заключение В примерах кода к статье содержится демонстрационное приложение, по зволяющее выбрать одну или несколько баз данных и либо сразу же вы полнить их резервное копирование, либо указать расписание: каждые п Microsoft SQL Server дней, начиная с заданных даты и времени (рис. 12). Если щелкнуть кноп ку (Ж, программа динамически создаст объект Job, состоящий из несколь ких операций, и инициализирует его дочерний объект JobScheduIe в соот ветствии с параметрами, заданными пользователем. Это пример простого, но надежного UI, который по достоинству оценят большинство пользова телей.

Рис. 12. Выбор расписания резервного копирования баз данных В своей статье я лишь прошелся по верхам объектной модели SQL-DMO, тем не менее она позволяет ознакомиться с этой моделью. Большинство объектов в иерархии DMO работает весьма предсказуемым образом, так что освоить их несложно. Конечно, на это уйдет определенное время, но информация и примеры кода в моей статье помогут вам быстро добиться своих целей.

Франческо Балена (Francesco Balena) Ч главный редактор Visual Basic Journal (Италия), редактор и ведущий постоянной рубрики в Visual Basic Programmer's Journal, автор книги Programming Visual Basic 6.0 (Microsoft Press, 1999). Часто выступает на конференциях разработчиков. С ним можно связаться через сайт Дэйв Грундгейгер, Энсон Голдэйд и Вэрон Фугман Вызов хранимых процедур и получение их результатов через Web* Разработчикам клиентских интерфейсов часто приходится расширять функциональность презентационного уровня в многоуровневых приложениях, но даже простое получение данных и их отображение требует внесения изменений во все уровни. Этот процесс можно упростить и сделать более гибким, используя хранимые процедуры SQL Server для автоматизации передачи данных в формате XML из базы данных в клиентские компоненты.

В статье представлен компонент, в котором хранимые процедуры вызывают ся с помощью XML-строк. Данные возвращаются также в XML-формате, над ними выполняется XSL-преобразование, и они предоставляются клиенту в виде HTML. Эта методика позволяет быстро вносить изменения в приложе ние, не отказываясь от его многоуровневой архитектуры. Описанный в статье подход применим в работе с SQL Server 7.0 или SQL Server 2000.

Как и многие другие разработчики, мы считаем, что в n-уровневых бизнес приложениях традиционные Windows-клиенты эволюционируют в уни версальные клиенты на основе браузера. В таких системах для доставки Web-страниц применяются Microsoft IIS (Internet Information Services) и ASP, для реализации бизнес-объектов промежуточного уровня (middle tier) Ч компоненты, написанные на Visual Basic, а для хранения данных Ч Microsoft SQL Server.

Это замечательные средства, обеспечивающие высокую гибкость, но за гибкость приходится расплачиваться. Когда пользователь требует доба * Публиковалось в MSDN Magazine. 2001. №8 (август). Ч Прим. изд.

214 Microsoft SQL Server вить в приложение новые функции, разработчикам приходится тратить много времени, чтобы реализовать их поддержку на каждом уровне. Это верно даже для простых изменений вроде добавления поля адреса в окно заказа товара. При этом приходится определять новые методы (а иногда и классы) и заново развертывать компоненты.

Разработчикам клиентских интерфейсов настолько часто требуется реали зовать ввод какой-либо дополнительной информации, что мы решили най ти способ, который позволил бы ускорить внесение нужных изменений в многоуровневое приложение. В созданной нами архитектуре XML исполь зуется клиентской частью приложения для вызова специально написан ных нами хранимых процедур SQL Server. Результаты, возвращаемые эти ми процедурами, передаются клиенту тоже в XML-формате. Поскольку клиент может вызывать только определенные хранимые процедуры и по скольку XML перехватывается и обрабатывается на промежуточном уров не, мы сохранили все преимущества многоуровневой архитектуры и вновь получили возможность использовать средства RAD (Rapid Application Development) при работе над многоуровневой системой. И еще одно при ятное преимущество. Так как данные передаются в XML-формате, разра ботчики клиентских интерфейсов могут легко создавать красиво оформ ленное визуальное представление данных. В этой статье рассказывается об архитектуре, которую мы разработали, чтобы реализовать такое взаимо действие компонентов.

Исходный код и скомпилированные компоненты доступны на сайте MSDN Magazine по ссылке msdnmag/codeOl.asp в раз деле за август.

Первая попытка Мы с самого начала понимали, что данные должны передаваться на пре зентационный уровень в XML-формате, так как это позволило бы нам ис пользовать XSL для преобразования XML в HTML (поддержка наиболее распространенных браузеров была для нас очень важна, равно как и воз можность изменять визуальное представление без перекомпиляции при ложения). Однако было неясно, как создавать XML-данные и передавать их на презентационный уровень. Для надежности мы хотели инкапсули ровать создание и синтаксический анализ XML. Инкапсуляция позволи ла бы скрыть детали использования XML от разработчиков, незнакомых с синтаксическим анализатором (parser) MSXML.

Мы добились этих целей, создавая оболочки для XML-схем в виде объек тов Visual Basic. Для каждого набора данных, показываемого на презента ционном уровне, определяется своя схема. Например, для поддержки окна заказа на приобретение товара мы определили соответствующую XML Вызов хранимых процедур и получение их результатов через Web схему. Как оказалось, определив нужную XML-схему и разработав на Visual Basic класс-шаблон, создавать классы, свойства которых соответ ствуют элементам схемы, несложно. Надо просто задать свойства объекта, а затем считать свойство XML объекта, чтобы получить соответствующие X ML-данные.

Объекты работали наоборот. То есть вы могли бы присвоить XML-строку свойству XML объекта, что заставило бы объект выполнить разбор XML (с помощью синтаксического анализатора MSXML) и соответствующим образом настроить свои открытые свойства. Двое из нашей группы разра ботчиков, Дэйв Грундгейгер и Патрик Эскарсега (Patrick Escarcega) рас сказали об этой методологии в статье XML Wrapper Template: Transform XML Documents into Visual Basic>

Наши усилия увенчались успехом. Мы получили унифицированный спо соб генерации XML-данных и их передачи между уровнями приложения.

Это ускорило разработку многоуровневых приложений, поскольку мы использовали простой в модификации класс-шаблон. Однако мы чувство вали, что разработчики клиентской части, реализуя отображение дополни тельной информации, по-прежнему тратили слишком много усилий. При внесении таких изменений приходилось изменять XML-схему, чтобы учесть вывод новой информации, модифицировать класс Ч оболочку схе мы и уровень хранения данных, который должен предоставлять новые данные. Кроме того, это требовало изменений в бизнес-уровне (уровне прикладной логики) для передачи новых данных на презентационный уро вень и в самом презентационном уровне, чтобы он мог использовать эти данные. Значит, нам нужно более эффективное решение.

Решение А что если вместо определения уникальной XML-схемы для каждого на бора данных, поступающего на презентационный уровень, сделать так, чтобы сами данные определяли формат XML? И почему бы, вместо того чтобы поддерживать уникальные классы для каждого набора данных, не разработать универсальный код, который будет считывать данные и созда вать XML? Тогда мы смогли бы экономить по два-три дня на введении поддержки каждого нового набора данных! Именно это и было целью раз работки нашей архитектуры. Она показана на рис. 1. Вот что представля ют собой ее компоненты.

ASP-страницы В нашей архитектуре ASP-страницы служат лишь для создания экземпляров СОМ-объектов, выполняющих реальную работу.

Microsoft SQL Server УРОВЕНЬ s KpSH.BHI aotryni [ роиещри Ш$*ИШ1С iSKSener Рис. 1. Архитектура доступа данным с использованием XML Объекты презентационного уровня Этот компонент обращается к биз нес-уровню, чтобы получить данные в XML-формате. Затем он преобразо вывает XML в HTML и записывает HTML в US-объект Response.

Объекты бизнес-уровня Этот компонент предоставляет объекты и мето ды, используемые презентационным уровнем. Какие объекты и методы до ступны на этом уровне, зависит от требований бизнеса;

объектам бизнес уровня не известен механизм чтения данных (об этом Ч потом).

Адаптер доступа к данным и компонент доступа к данным Эти два ком понента используются совместно, чтобы с помощью XML вызывать храни мые процедуры и возвращать результаты в формате XML.

Хранимые процедуры Неотъемлемая часть архитектуры. Мы не хотели, чтобы универсальный механизм доступа к базе данных опирался на XML, так как это привело бы к размытию границ между уровнями. Вместо это го он обращается только к хранимым процедурам, написанным в опреде ленном формате. То есть лишь хранимым процедурам известна структура базы данных.

В следующих разделах мы рассмотрим, как работает каждый из уровней, и вы увидите, как в нашей архитектуре обслуживаются запросы на полу чение информации. Центральное место в статье занимают разделы, где описываются адаптер доступа к данным, компонент доступа к данным и хранимые процедуры. Но сначала мы расскажем о том, как вызываются базовые компоненты и как используются возвращаемые значения.

Пример Web-приложения В код, который можно скачать к этой статье, мы включили пример Web приложения, использующего нашу технологию. Оно предоставляет про стой Ш к базе данных Northwind, поставляемой с SQL Server. Как развер тывать компоненты, мы поясним позже.

После установки компонентов Web-приложение можно запустить, открыв его страницу login.asp (рис. 2). Введите Employee ID (1-9) и щелкните кнопку Log In. Появится страница Employee Detail (сведения о сотрудни Вызов хранимых процедур и получение их результатов через Web ке), где показывается информация о вошедшем в систему сотруднике и заказах, за обработку которых он отвечает (рис. 3).

Microsoft Internet KMptoner Histor Address'! http;

//rnyserver/lagin, asp Enter Your Employee ID To Log In (fD's 1 Through 9 are Valid) Рис. 2. Страница для входа сотрудника - Edit Employee Information:

Ms. Nancy Davolio Sales ^Representative Create New Order Here is a list of the orderer? for which you are the sales representative.

Pages:     | 1 | 2 | 3 | 4 | 5 |   ...   | 6 |    Книги, научные публикации