Як обчислити / зберегти Топ-10 у табличній моделі?


23

Нещодавно ми створили табличну модель 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;

SQL Скрипки Demo .

Я спробував декілька різних способів отримати результат, але кожен з них мав проблеми. Мої спроби нижче.

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

Відповіді:


1

У мене був аналогічний сценарій, і я використовував наступний запит DAX ...

По-перше, щоб зробити його простішим, я визначив міру використання всередині DAX, тому мені не потрібно повторювати формулу. Тоді я використав генерувати, щоб повторити формулу TOPN:

define measure TableInTabular[NameOfTheMeasure] = COUNTAX(FILTER('Stats', ISBLANK([DeactDate]) = TRUE), 1)
evaluate
 (
  addcolumns
   (  
    filter
     (  
      generate
        (  
         VALUES(DatesTableName[Month]),  
         TOPN (10, VALUES(TableInTabular[ParentID]),TableInTabular[NameOfTheMeasure],0)
        ),
        TableInTabular[NameOfTheMeasure]>0
      ),
      "ActiveCount (or how you want to call this Column)",
      TableInTabular[NameOfTheMeasure]  
    )  
 )  
order by DatesTableName[Month] asc, 
TableInTabular[NameOfTheMeasure] desc

З урахуванням вищезазначеного у вас має бути 10 найвищих батьків і міра за кожним місяцем. просто замініть "TableInTabular" на свою табличну назву таблиці, де у вас є дані, а "DatesTableName" на назву таблиці дат.

Будь ласка, дайте мені знати, якщо я неправильно зрозумів ваше запитання і сподіваюся, що це допоможе ...


1
Дякую за відповідь, є ще деякі проблеми з цим. По-перше, я можу використовувати це все в SSMS, але це розгорнуто у нашій табличній моделі, щоб наші користувачі могли отримати доступ до нього через PowerView - вони не записуватимуть жодних запитів - це просто має бути доступним. По-друге, якщо я щось неправильно не роблю, в табличній моделі через Visual Studio не допускається оцінювання чи наказ, не існує можливості для цієї функції. По-третє, цей запит повертає лише Топ-10, мені також потрібні зведені дані або якийсь спосіб їх отримання. Я продовжую грати з цим, хоча.
Тарін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.