Недавно мы создали табличную модель SSAS, чтобы наши пользователи могли получить к ней доступ через PowerView. У нас есть мера в одной из наших таблиц фактов, чтобы получить TotalActiveItems
формулу:
TotalActive:=COUNTAX(FILTER('Stats', ISBLANK([DeactDate]) = TRUE), 1)
Это прекрасно работает по мере необходимости, но теперь у нас есть запрос, чтобы получить Топ 10 родителей за каждый месяц в TotalActive
.
Для справки, вот часть нашей модели:
create table factStats
(
StatsID INT IDENTITY NOT NULL PRIMARY KEY,
DevID INT NOT NULL,
DeactDate DATETIME NULL,
BillDateTimeID BIGINT NOT NULL,
CustID INT NOT NULL,
ParentID INT NOT NULL
);
create table dimCust
(
CustID INT NOT NULL PRIMARY KEY,
CustName varchar(150) NOT NULL
);
create table dimParent
(
ParentID INT NOT NULL PRIMARY KEY,
ParentName varchar(100) NOT NULL
);
create table dimDateTime
(
DateTimeID BIGINT NOT NULL PRIMARY KEY
);
SQL Fiddle с таблицами и образцами данных.
factStats
Таблица имеет FKs к DevID
, CustID
, BillDateTimeID
и ParentID
. Наш запрос состоит в том, чтобы либо рассчитать, либо сохранить Top 10 Parents
для каждого BillDateTimeID
на основе TotalActive
AND и включить все, что не входит в Топ-10 в свернутой категории, аналогично следующему:
+----------------+------------+------+
| BillDateTimeID | Parent | Rank |
+----------------+------------+------+
| 20140801 | Jim | 1 |
| 20140801 | Bob | 2 |
| 20140801 | All Others | 3 |
+----------------+------------+------+
Я легко могу сделать это в SQL, используя функции управления окнами, но попытаться воспроизвести это для SSAS было сложно. В SQL мы получили бы результат, используя:
;with Total as
(
select
ParentID,
BillDateTimeID,
sum(case when DeactDate is null then 1 else 0 end) TotalActive
from factStats
group by ParentID, BillDateTimeID
),
PRank as
(
select
ParentID,
BillDateTimeID,
TotalActive,
row_number() over(partition by BillDateTimeID
order by TotalActive desc) pr
from total
)
select
parentid,
BillDateTimeID,
TotalActive,
pr
from prank
where pr <= 2
union all
select
0,
BillDateTimeID,
sum(TotalActive) TotalActive,
3
from prank
where pr > 2
group by BillDateTimeID
order by BillDateTimeID desc, pr;
Я пробовал несколько разных способов получить результат, но у каждого была проблема. Мои попытки ниже.
Первоначально я смог получить данные с помощью запроса многомерных выражений, но потом понятия не имел, как включить это в нашу табличную модель. MDX-запрос для справки:
with
set [Top10Parent] AS
(
(TOPCOUNT({ORDER(({[Parent].[Parent Name].[Parent Name]}),
([Measures].[Total Count]), BDESC)}, 10))
)
MEMBER [Parent].[Parent Name].[Others] AS
(
AGGREGATE(EXCEPT([Parent].[Parent Name].[Parent Name], [Top10Parent]))
)
select
[Measures].[Total Count] on columns,
{[Top10Parent]}+ {[Parent].[Parent Name].[Others]} on Rows
from [OurModel]
where {[Date and Time].[Month and Year].[Month and Year].[Jul 2014]};
Конечно, это также дало мне результат только один месяц, а не каждый месяц.
Когда я понял, что запрос MDX не будет работать, я начал с того, что изменил нашу factStats
таблицу, добавив в нее новый столбец, чтобы пометить элементы в первой десятке и в свернутом значении.
alter table factStats
add Top10ParentID INT NOT NULL
constraint DF_factStats default (0);
Ограничение по умолчанию ссылается на наше значение «Свернуто» для Топ-10.
Попытка # 1: я создал новую таблицу Top 10 для хранения ParentID, имени и ранга:
create table dimTop10Parent
(
Top10ParentID INT NOT NULL PRIMARY KEY,
ParentName varchar(100) NOT NULL,
Parent_Rank INT NOT NULL
);
Эта таблица будет заполняться каждый раз, когда мы обновляем нашу модель новыми 10 лучшими родителями на основе имеющихся у них элементов Total Active. Parent_Rank
Колонка затем скрыта в нашей табличной модели и используется исключительно для сортировки. Это прекрасно работает, за исключением того, что у нас нет возможности исторически получить Топ-10, поскольку он не основан на месячной основе.
Попытка № 2: Создайте новую таблицу для хранения первых 10, но PRIMARY KEY будет включать в себя как Top10ParentID, так и BillingDateTimeID.
create table dimTop10Parent
(
Top10ParentID INT NOT NULL,
ParentName varchar(100) NOT NULL,
Parent_Rank INT NOT NULL,
BillDateTimeID BIGINT NOT NULL
);
Проблема в том, что мы не можем создать связь между одним FK factStats и двумя частями PK в dimTop10Parent в табличной модели.
Попытка № 3: Создайте новую таблицу, но используйте идентификатор в качестве PK.
create table dimTop10Parent
(
Top10ID INT IDENTITY NOT NULL PRIMARY KEY,
Top10ParentID INT NOT NULL,
ParentName varchar(100) NOT NULL,
Parent_Rank INT NOT NULL,
BillDateTimeID BIGINT NOT NULL
);
В factStats
таблице будет храниться Top10ID
значение, которое будет уникальным для каждой строки. Я думал, что это решит мою проблему, но это не так, потому что мы больше не можем сортировать по Parent_Rank
модели, это выдает ошибку:
Невозможно отсортировать ParentName по Parent_Rank, поскольку хотя бы одно значение в ParentName имеет несколько различных значений в Parent_Rank. Например, вы можете отсортировать [Город] по [Регион], потому что для каждого города есть только один регион, но вы не можете отсортировать [Регион] по [Город], потому что для каждого региона есть несколько городов.
Используя пример данных, конечный результат должен быть похож (это показывает Top 2 с третьим свернутым):
| PARENTNAME | BILLDATETIMEID | TOTALACTIVE | PR |
|------------|----------------|-------------|----|
| FDN | 201408010000 | 11 | 1 |
| FDO | 201408010000 | 3 | 2 |
| All Others | 201408010000 | 5 | 3 |
| FDN | 201407010000 | 12 | 1 |
| EVOD | 201407010000 | 2 | 2 |
| All Others | 201407010000 | 5 | 3 |
На данный момент, я в растерянности от того, как получить этот конечный результат. Я могу изменить таблицы по мере необходимости, чтобы получить его, я могу изменить модель, используя формулу, меру и т. Д. Я читал о ранжировании с помощью формул DAX 1 , 2 , 3, но я не могу обернуть голову вокруг их достаточно, чтобы иметь возможность точно получить результат.
Как я могу рассчитать / сохранить эту Топ-10 за любой месяц и при этом иметь возможность склеивать данные по мере необходимости в нашей табличной модели?