XML в MS SQL Server 2000 и технологиях доступа к данным - FOR XML на стороне клиента

ОГЛАВЛЕНИЕ

FOR XML на стороне клиента

Провайдер 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.