Нещодавно ми створили табличну модель 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 за будь-який місяць і все ще мати змогу скласти дані, як це потрібно в нашій табличній моделі?