Добавить в избранное   Сделать стартовой   Главная   E-mail   Форум   Мой блог 
   
Cертификации

Errors

ETL

FAQ (по темам)

GIS

Web

wiki

Администрирование

Безопасность

Книги
Oracle, ...

Новости

ОС

Программирование

Проектирование БД

Производительность

Скачать

Советы

Тестирование

Установка

FAQ - по базам данных
FAQ - по базам данных
Установка СУБД
Oracle
Sybase
MySQL
PostgreSQL
MS SQL Server
Interbase, Firebird
Другие DB
Администрирование
Oracle
MySQL
Sybase
PostgreSQL
MS SQL Server
Interbase, Firebird
IBM DB2
Другие DB
Проектирование БД
Статьи
ETL
Теория БД
ErWin
Designer 2000
PowerDesigner
Хранилища данных
CASE средства
OLAP
Бизнес - анализ (BI)
Производительность
Oracle
MSSQL
Interbase, Firebird
IBM DB2
MySQL
PostgreSQL
SYBASE
Безопасность БД
Oracle
MS SQL Server
Инъекция SQL
Программирование
Transact-SQL
PL/SQL
C++
XML
SQL
PostgreSQL
MDX
Java
VBA Excel
Книги по базам
Oracle
Заказ книг
ОС
Установка и настройка
UBUNTU
ОС
Установка и настройка
UBUNTU
FAQ
FAQ - по базам данных
Главная arrow MDX arrow Подсчёт отличающихся значений в языке MDX

Подсчёт отличающихся значений в языке MDX

Печать E-mail

 Сколько элементов набора данных имеют отличающиеся значения? Это кажется простым вопросом. Ясно, что эта проблема давно известна в Excel и для нее есть классическое решение. В Интернете есть много сайтов (например здесь и здесь), которые дают следующее решение:

=SUM(1/COUNTIF(A1:A6,A1:A6)))

(обратите внимание, что эту формулу необходимо вводить с помощью Ctrl-Shift-Enter, для указания того, что это матричная формула, в противном случае результат будет неправильным – это одна из хитростей в Excel).

Когда кто-то показал мне это решение, я посмотрел на него и не понял, как это работает. Только переписав это в MDX я понял в чем дело. Давайте возьмем следующий пример на основе Adventure Works – мы хотим сосчитать, со скольки различных букв начинаются названия продуктов (просто для того, чтобы подобрать что-то, для чего в измерении Продукт нет специального атрибута, потому что в противном случае это было бы слишком просто). MDX, создающий тот же алгоритм, что и вышеуказанная формула в Excel в данных условиях будет выглядеть следующим образом:

 

with
member measures.
CountDistinctLetters as
Sum([Product].[Product].[Product].MEMBERS AS AllProducts,
1/Filter([Product].[Product].[Product].MEMBERS,
VBA!Left(AllProducts.Current.Name,1) =
VBA!Left([Product].[Product].CurrentMember.Name,1)
).
Count)

select CountDistinctLetters on 0
from [Adventure Works]

Это более подробно, но вероятно не проще для понимания обычного разработчика, чем формула в Excel. Вкратце идея состоит в следующем: для каждого элемента в наборе данных мы подсчитываем сколько других элементов производят то же значение (функции COUNTIF в Excel и Filter(…).Count в MDX), а затем путем деления 1 на это число мы получаем дробное число текущего элемента.
(К сожалению, нет простого способа отладить такие формулы. MDX-отладчик в MDX Studio не в состоянии рассмотреть такие выражения. Я провел довольно много времени размышляя над тем, как построить соответствующее окружение для отладки MDX и после нескольких неудачных попыток я, кажется, нашел прорывную идею. Ранние эксперименты были очень обнадеживающими и если это работает, я скоро напишу об этом в блоге. Эта идея довольно сильно отличается от прочих попыток отладки в MDX.)

Ясно, что выполнить такое решение очень сложно. Каждый элемент в наборе данных мы снова и снова сравниваем со всеми прочими элементами для определения того, сколько элементов имеют одно и то же значение. Алгоритмическая сложность данного метода вложенного цикла выражается как O(n^2). Фактически вышеуказанный запрос осуществляется в течение 12 секунд. Можно несколько ускорить его специально запоминая результаты VBA!Left

with
member
Prefix as VBA!Left([Product].[Product].CurrentMember.Name,1)
member measures.CountDistinctLetters as
Sum([Product].[Product].[Product].MEMBERS AS AllProducts,
1/Filter([Product].[Product].[Product].MEMBERS,
(
AllProducts.Current,Prefix) = Prefix
).Count)

select CountDistinctLetters on 0
from [Adventure Works]

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

Другой вариант – вместо создания алгоритма вложенных циклов отсортировать элементы набора данных, а затем еще раз просмотреть отсортированный набор данных, подсчитывая количество отличающихся один от одного элементов. Единственной технической сложностью в этом случае при просмотре отсортированного набора данных является необходимость сравнивать текущий элемент с предыдущим. Получить текущий элемент легко, но как получить предыдущий? В MDX нет функции «предыдущий», есть только функция PrevMember, но она влияет только на иерархическое упорядочивание уровня и здесь мы имеем произвольный набор данных, отсортированных по произвольным критериям. Решением является использование функции CurrentOrdinal, которая возвращает индекс n-строки, повторяющийся в наборе данных, а затем, используя функцию CurrentOrdinal-1 в качестве нового индекса, посылает его в функцию Item. В языке MDX это выражается следующим образом:

with
member
Prefix as VBA!Left([Product].[Product].CurrentMember.Name,1)
set ProductsSortedByPrefix as
order([Product].[Product].[Product].MEMBERS, Prefix, BASC)
member measures.CountDistinctLetters as
Sum(ProductsSortedByPrefix as y,
iif(y.CurrentOrdinal = 1 OR Prefix <>
(
Prefix, y.Item(y.CurrentOrdinal-2)), 1, 0))

select CountDistinctLetters on 0
from [Adventure Works]

Этот запрос выполняется гораздо быстрее, всего за 0,234 секунды. Алгоритмическая сложность объясняется как O(n*log(n)). Однако, это не идеальное решение, так как его можно найти за одно сканирование исходного множества, то есть, со сложностью O(n). Для того чтобы сделать это мы должны написать хранимую процедуру.

public int CountDistinctValues(Set set, Expression exp)
{
System.Collections.Hashtable ht = new System.Collections.Hashtable();
foreach (Tuple t in set.Tuples)
{
string v = exp.Calculate(t).ToString();
if ( !ht.ContainsKey(v) )
ht.Add(v, null);
}

return ht.Count;
}

Теперь, если мы используем эту хранимую процедуру в запросе как указано ниже

with member CountDistinctLetters as 
ASSP.ASStoredProcs.Util.CountDistinctValues(
[Product].[Product].[Product].MEMBERS,
VBA!Left([Product].[Product].CurrentMember.Name,1))
select CountDistinctLetters on 0
from [Adventure Works]
 
 
« Пред.   След. »
Взаимосвязанные статьи
     

Последние добавленные статьи
Поиск
Ссылки
Главная
Скачать
Курсы
Роль АБД (SYSDBA)
Карта сайта
Автостекла
Контакты
Войти на сайт
Популярные статьи
Online - тесты
1Z0-042
Rambler's Top100 МЕТА - Украина. Рейтинг сайтов хостинг от freehost.com.ua

Все права защищены.SYSDBA 2010 | Если у Вас есть хороший материал пришлите его нам.