Как рассчитать / сохранить Top 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 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;

SQL Fiddle Demo .

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

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

Тарын
источник

Ответы:

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 лучших ParentID и показатель для каждого месяца. просто замените «TableInTabular» на имя вашей табличной таблицы, в которой у вас есть данные, и «DatesTableName» на имя таблицы дат.

Пожалуйста, дайте мне знать, если я неправильно понял ваш вопрос и надеюсь, что это поможет ...

Алехандро Пельц
источник
1
Спасибо за ответ, есть еще некоторые проблемы с этим. Во-первых, я могу использовать это внутри SSMS, но это внедряется в нашу табличную модель, чтобы наши пользователи могли получить к ней доступ через PowerView - они не будут писать никаких запросов - это просто необходимо сделать доступным. Во-вторых, если только я не делаю что-то не так, в табличной модели с помощью Visual Studio не разрешается никакая оценка или порядок - нет возможности для этого как функции. В-третьих, этот запрос возвращает только первые 10, мне также нужны свернутые данные или какой-то способ их получить. Я буду продолжать играть с этим, хотя.
Тарын