Провайдер SQLXMLOLEDB является типичным сервисным
провайдером, как уже упоминавшиеся ранее MSDataShape и
MSPersist, в том плане, что он получает данные от другого
провайдера, а не непосредственно из источника. SQLXMLOLEDB
позволяет преобразовывать recordset в XML непосредственно на
клиенте. Таким образом, провайдер SQLOLEDB, стоящий между
SQLXMLOLEDB и SQL Server, получает от сервера не XML-поток, а
обычный recordset и передает его SQLXMLOLEDB, который уже
занимается превращением реляционной информации в XML. Скрипт 5
демонстрирует FOR XML на стороне клиента.
public static
void
Execute_FORXMLQuery_OnClient()
{
...
cmd.CommandText
= "SELECT c.ContactName, COUNT(o.OrderDate) as 'Кол-во
заказов' FROM Customers c INNER JOIN Orders o ON c.CustomerID
= o.CustomerID WHERE o.OrderDate between ? and ? GROUP BY
c.ContactName FOR XML
NESTED";
cmd.CreateParameter().Value
=
"19970101";
cmd.CreateParameter().Value
= "19971231
23:59:59";
cmd.ClientSideXml
= true;
cmd.OutputEncoding
= "UTF-8"; cmd.RootTag =
"Корень";
Stream str =
cmd.ExecuteStream();
StreamReader
sr =
new
StreamReader(str);
FileInfo
f =
new
FileInfo("..\\Results\\FORXMLQueryResults.xml");
StreamWriter
sw =
new StreamWriter(f.FullName,
false,
System.Text.Encoding.UTF8);
sw.WriteLine("<!--Этот
XML сделан на стороне
клиента-->");
str.Position
= 0;
sw.Write(sr.ReadToEnd());
sr.Close();
sw.Close();
...
}
Скрипт 5
При помощи SQL Profiler можно оттрассировать и сравнить
запросы, которые в действительности обрабатываются сервером
при выполнении Скриптов 4 и 5.
Скрипт 4:
exec sp_executesql N'SELECT
'''' SELECT c.ContactName, c.ContactTitle, o.OrderDate
FROM Customers c INNER JOIN Orders o ON c.CustomerID =
o.CustomerID WHERE c.ContactName = @P1 AND year(o.OrderDate) =
@P2 FOR XML AUTO SELECT ''''', N'@P1
nvarchar(30),@P2 int', N'Maria Larsson', 1997
Скрипт 5:
exec sp_executesql N'SELECT
c.ContactName, COUNT(o.OrderDate) as ''Кол-во заказов'' FROM
Customers c INNER JOIN Orders o ON c.CustomerID = o.CustomerID
WHERE o.OrderDate between @P1 and @P2 GROUP BY
c.ContactName ', N'@P1 nvarchar(8),@P2 nvarchar(17)',
N'19970101', N'19971231 23:59:59'
В Скрипте 5 я слегка изменил текст запроса, чтобы показать
использование GROUP BY (этот предикат не разрешается в
серверных FOR XML-запросах). Запрос считает количество
заказов, сделанных каждым клиентом за определенный период
времени. Обратите внимание на отсутствие предиката FOR XML во
втором случае. Это значит, что преобразование recordset'a в
XML действительно происходит в данном случае на
клиенте.
Несмотря на бОльшие издержки на стороне клиента
такая схема работы является более универсальной и позволяет
получать клиенту XML от тех источников данных, которые его не
поддерживают и не будут. Для этого будет достаточно в строке
соединения:
"Provider=
SQLXMLOLEDB;DataProvider=SQLOLEDB, …"
поставить вместо SQL Server соответствующего OLE
DB-провайдера. К сожалению, для этого придется подождать
следующего, 4-го, веб-релиза. Пока SQLXMLOLEDB умеет работать
только с SQL Server 2000.
При клиентском XML-форматировании
не поддерживается возврат в качестве результата одной команды
нескольких recordset'ов, зато допускается предикат GROUP BY с
агрегатными функциями. Режим FOR XML NESTED клиентского
форматирования примерно соответствует FOR XML AUTO серверного,
за исключением того, что вместо псевдонимов в имена элементов
ставятся настоящие названия таблиц. О других различиях можно
прочитать в документации на SQLXML 3.0.