Усовершенствования в языке выражений

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

Сочетание данных из нескольких наборов данных

Чтобы отобразить данные из нескольких источников в таблице (или в области данных) следует создать набор данных, каким=либо образом сочетающий данные, поскольку область данных может быть привязана только к одному набору. Для такого набора можно создать запрос, соединяющий данные, если оба источника имеют реляционную структуру и доступны по одному и тому же способу проверки подлинности. Но что если данные поступают из разных реляционных платформ? Как действовать в случае, если одна часть данных приходит из SQL Server, а другая — из списка SharePoint? Даже если источники имеют реляционную структуру, что делать в случае, если доступны только хранимые процедуры, и создать запрос на соединение источников нельзя? Это лишь несколько примеров ситуация, когда могут пригодиться новые функции Lookup в языке выражений Reporting Services.

В общем и целом три новые функции, — Lookup, MultiLookup и LookupSet, — работают одинаково, используя значение из набора данных, привязанного к области данных (источника), и сопоставляя его со значением во втором наборе данных (конечном). Различие функций проявляется в том, являются ли вход и выход одиночными значениями или группами значений.

Функция Lookup используется при наличии связи «один к одному» между набором-источником и конечным набором. Функция Lookup сопоставляет одно значение из источника с одним конечным значением за раз, как показано на рис. 9-1.

Рисунок 9-1. Результатыфункции Lookup.

В примере результирующий отчет содержит таблицу данных продаж, сформированную по набору Dataset2, но вместо поля StateProvinceCode из этого же набора функция Lookup в первом столбце таблицы указывает службам Reporting Services, что следует сопоставить все значения этого поля набора Dataset2 с полем StProv в наборе Dataset1 и затем отобразить соответствующее значение StProvName. Выражение в первом столбце таблицы:

=Lookup(Fields!StateProvinceCode.Value, Fields!StProv.Value,
Fields!StProvName.Value, "Dataset1")

Функция MultiLookup также использует отношение «один к одному» между набором-источником и конечным набором, но она принимает на входе набор значений источника. Службы Reporting Services сопоставляют все эти значения с конечными значениями по одному, а затем возвращают совпавшие значения в виде массива. Затем можно использовать выражение, преобразующее массив в список с разделителями-запятыми, как показано на рис. 9-2.

Рисунок 9-2. Результаты функции MultiLookup.

Функция MultiLookup во втором столбце таблицы получает массив значений из набора данных, привязанного к таблице. Здесь это поле StateProvinceCode в Dataset2. Следует использовать функцию Split, чтобы преобразовать список значений с разделителями-запятыми из поля StateProvinceCode в массив. Службы Reporting Services обрабатывают каждый элемент массива, сопоставляя его с полем StProv в Dataset1, а затем комбинируют результаты в массиве, который можно преобразовать в список с помощью функции Join. ВотвыражениевстолбцеTerritory:

=Join(MultiLookup(Split(Fields!StateProvinceCode.Value, ","), Fields!StProv.Value,
Fields!StProvName.Value, "Dataset1 "), ", ")

Функция LookupSet используется при наличии связи «один ко многим» между набором-источником и конечным набором. Эта функция принимает одно значение из набора-источника и возвращает массив соответствующих значений из конечного набора. После этого можно использовать функцию Join для преобразования результата в строку с разделителями, как и в примере с функцией MultiLookup, или использовать другие функции для работы с массивами, например Count, как показано на рис. 9-3.

Рисунок 9-3. Результаты функции LookupSet.

В столбце Customer Count используется следующее выражение:

LookupSet(Fields!SalespersonCode.Value,Fields!SalesperonCode.Value,
Fields!CustomerName.Value,"Dataset2").Length

Агрегация

Функции агрегации, доступные в службах Reporting Services с первого их выпуска в составе платформы SQL Server 2000, обеспечивали всю необходимую функциональность, требовавшуюся большинству пользователей большую часть времени. Тем не менее, если требовалось использовать результат агрегации в качестве входных данных другой функции агрегации, но по каким-то причинам занести этот результат в куб SQL Server Analysis Services было нельзя, то единственный вариант действий сводился к предварительной обработке результатов в запросе к набору данных. Другими словами, начальную агрегацию следовало выполнять в запросе набора данных, а последующую — в выражении отчета. Службы Reporting Services в SQL Server 2008 R2 позволяют вложить функцию агрегации в другую функцию агрегации. Говоря по-другому, можно агрегировать агрегаты. В таблице на рис. 9-4 приведено вычисление средних ежемесячных продаж за выбранный год. В наборе данных содержится по строке на каждый продукт, которые в отчете группируются по годам и месяцам. Подробности при этом скрываются.

Рисунок 9-4. Агрегация агрегата.

Вот выражение для значения в строке Monthly Average:

=Avg(Sum(Fields!SalesAmount.Value,"EnglishMonthName"))

Выражения условного отображения

Язык выражений в службах Reporting Services в SQL Server 2008 R2 поддерживает новую глобальную переменную, позволяющую задавать значения свойств внешнего вида на основе формата отображения, используемого для отчета. Это значит, что все свойства, отвечающие за внешний вид (например, Color) и поведение (например, Hidden), могут использовать члены глобальной переменной RenderFormat в условных выражениях, что позволяет динамически менять значения свойств в зависимости от формата отображения.

Предположим, требуется упростить структуру отчета при экспорте в Microsoft Excel. Иногда наличие отдельных элементов отчета приводит к тому, что текстовое поле в области данных отображается как несколько объединенных ячеек, если выравнять все оптимальным образом не удается. Обычная причина экспорта в Excel — необходимость фильтрации и сортировки данных. Пользователей при этом мало интересуют сведения в посторонних элементах отчета. Вместо того чтобы менять структуру отчета для достижения оптимального расположения, можно использовать выражение в свойстве Hidden, чтобы соответствующие элементы отчета отображались во всех форматах, кроме Excel. Просто укажите расширение в форме, указанной в файле RSReportServer.config, в выражении, аналогичном следующему:

=iif(RenderFormat.Name="EXCEL", True, False)

Еще один вариант — использование глобальной переменной RenderFormat с членом Islnteractive для задания условий свойств. Предположим, есть отчет с суммами продаж, позволяющий переключить элемент отчета в режим отображения сопутствующих сведений. Вместо того чтобы экспортировать все сведения в ситуации, когда если формат отчета не поддерживает интерактивность, можно просто их пропустить при отображении, используя следующее выражение в свойстве Hidden группы строк, содержащей сведения:

=iif(RenderFormat.IsInteractive, False, True)

Нумерация страниц

Если говорить о глобальных переменных, то следует упомянуть новые глобальные переменные Globals!OverallPageNumber и Globals!OverallTotalPages, позволяющие отображать номер текущей страницы в отчете и общее число страниц. Эти глобальные переменные, также называемые встроенными полями, можно использовать только в верхних и нижних колонтитулах страниц. Как объясняется далее в разделе «Свойства деления на страницы», можно указать условия, при которых номер страницы сбрасывается до 1, а не увеличивается на единицу. Также доступны переменные Globals!PageNumber и Globals!TotalPages, как и в предыдущих версиях. Их можно использовать для отображения страничных сведений в текущем разделе отчета. На рис. 9-5 показан пример нижнего колонтитула, где используются все четыре переменные.

Рисунок 9-5. Глобальные страничные переменные.

Выражение, строящее этот заголовок, выглядит следующим образом:

="Section Page " + CStr(Globals!PageNumber) + " of " + CStr(Globals!TotalPages) +
" (Overall " + Cstr(Globals!OverallPageNumber) + " of " +
CStr(Globals!OverallTotalPages) +")"

Переменная отчета для чтения и записи

Еще одно расширение языка выражений — поддержка задания значения переменной запроса. Как и в предыдущих версиях Reporting Services, для реализации значения с зависимостью от времени выполнения можно использовать переменные отчета. Службы Reporting Services сохраняют значение во время выполнения отчета и используют его на протяжении работы отчета. Соответственно, по мере того как пользователь листает отчет, переменная не изменяется, даже если время отображения отдельных страниц изменяется.

По умолчанию переменная отчета доступна только для чтения (в предыдущих версиях ReportingServices это был единственный вариант). В SQL Server 2008 R2 теперь можно снять параметр «Только для чтения», как показано на рис. 9-6, если значение переменной отчета требуется менять в ходе выполнения отчета.

Рисунок 9-6. Изменение переменных отчета.

Чтобы записать значение в переменную отчета, используется метод SetValue. Предположим, в отчете настроена вставка разрыва страницы между экземплярами групп, и следует изменить время выполнения при смене группы. Добавьте в отчет переменную отчета, а затем добавьте в область данных с группой, используемой для создания разрыва страницы, скрытое текстовое поле. Далее поместите следующее выражение в текстовое поле, чтобы принудительно вычислять выражение для каждого экземпляра группы:

=Variables!MyVariable.SetValue(Now())

В предыдущих версиях Reporting Services тип переменной отчета был значением, как и у любого текстового поля в отчете. В SQL Server 2008 R2 переменная отчета также может иметь сериализуемый тип .NET. В начале сеанса отчета следует инициализировать и заполнить переменную отчета. После этого можно независимо добавлять и изменять значения переменной отчета на каждой странице отчета в текущем сеансе.