Нещодавно ми створили табличну модель 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
І, включити все, що не належить до Топ-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-запиту, але потім не мав поняття, як включити це в нашу табличну модель. Запит 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
таблицю, щоб включити новий стовпець для позначення елементів у Топ-10 та зведеному значенні.
alter table factStats
add Top10ParentID INT NOT NULL
constraint DF_factStats default (0);
Обмеження за замовчуванням посилається на наше значення "Згорнутий" для топ-10.
Спроба №1: Я створив нову таблицю Топ-10, щоб зберігати ParentID, ім'я та Ранг:
create table dimTop10Parent
(
Top10ParentID INT NOT NULL PRIMARY KEY,
ParentName varchar(100) NOT NULL,
Parent_Rank INT NOT NULL
);
Потім ця таблиця буде заповнена щоразу, коли ми оновлюємо нашу модель новими Топ-10 батьків на основі загальної кількості активних елементів. Потім Parent_Rank
стовпець ховається в нашій табличній моделі та використовується виключно для сортування. Це чудово працює, за винятком того, що ми не маємо можливості історично дістати Топ-10, оскільки вона не базується на місяці.
Спроба №2: Створіть нову таблицю для зберігання перших 10, але ПЕРШИЙ КЛЮЧ буде включати як Top10ParentID, так і BillingDateTimeID.
create table dimTop10Parent
(
Top10ParentID INT NOT NULL,
ParentName varchar(100) NOT NULL,
Parent_Rank INT NOT NULL,
BillDateTimeID BIGINT NOT NULL
);
Проблема в цьому полягає в тому, що ми не можемо створити зв’язок між фактичним станом одиночного FK до двох частин ПК у dimTop10Parent у табличній моделі.
Спроба №3: Створіть нову таблицю, але використовуйте ідентичність як ПК.
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. Наприклад, ви можете сортувати [Місто] за [регіон], оскільки для кожного міста є лише один регіон, але ви не можете сортувати [регіон] за [місто], оскільки для кожного регіону існує кілька міст.
Використовуючи вибіркові дані, кінцевий результат повинен бути аналогічним (цей показник показує Топ-2 з 3-м згортанням):
| 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 за будь-який місяць і все ще мати змогу скласти дані, як це потрібно в нашій табличній моделі?