¿Cómo calcular / almacenar Top 10 en un modelo tabular?


23

Recientemente hemos creado un modelo tabular SSAS para que nuestros usuarios puedan acceder a él a través de PowerView. Tenemos una medida en una de nuestras tablas de hechos para TotalActiveItemsusar una fórmula:

TotalActive:=COUNTAX(FILTER('Stats', ISBLANK([DeactDate]) = TRUE), 1)

Esto funciona muy bien según sea necesario, pero ahora tenemos una solicitud para obtener los 10 mejores padres para cada mes en el TotalActive.

Como referencia, aquí es parte de nuestro modelo:

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 con tablas y datos de muestra.

La factStatstabla tiene al FKs DevID, CustID, BillDateTimeID, y ParentID. La solicitud que tenemos es calcular o almacenar Top 10 Parentscada uno en BillDateTimeIDfunción del TotalActive AND e incluir todo lo que no esté en el Top 10 en una categoría acumulada similar a la siguiente:

+----------------+------------+------+
| BillDateTimeID |   Parent   | Rank |
+----------------+------------+------+
|       20140801 | Jim        |    1 |
|       20140801 | Bob        |    2 |
|       20140801 | All Others |    3 |
+----------------+------------+------+

Puedo lograr esto fácilmente en SQL usando las funciones de ventanas, pero tratar de reproducir esto para SSAS ha sido difícil. En SQL, obtendríamos el resultado usando:

;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;

Demostración de violín SQL .

He intentado varias formas diferentes de obtener el resultado, pero cada una ha tenido un problema. Mis intentos están abajo.

Inicialmente, pude obtener los datos utilizando una consulta MDX, pero luego no tenía idea de cómo incorporar esto en nuestro modelo tabular. La consulta MDX para referencia es:

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]};

Por supuesto, esto también solo me dio el resultado para un solo mes, no todos los meses.

Cuando me di cuenta de que la consulta MDX no funcionaría, comencé alterando nuestra factStatstabla para incluir una nueva columna para marcar los elementos en el Top 10 y en el valor acumulado.

alter table factStats
    add Top10ParentID INT NOT NULL
    constraint DF_factStats default (0);

La restricción predeterminada hace referencia a nuestro valor "enrollado" para los 10 principales.

Intento n. ° 1: creé una nueva tabla Top 10 para almacenar el ParentID, el nombre y el Rango:

create table dimTop10Parent
(
    Top10ParentID INT NOT NULL PRIMARY KEY,
    ParentName varchar(100) NOT NULL,
    Parent_Rank INT NOT NULL
);

Esta tabla se completará cada vez que refresquemos nuestro modelo con los nuevos 10 padres principales en función de los elementos activos totales que tengan. La Parent_Rankcolumna se oculta en nuestro modelo tabular y se usa exclusivamente para ordenar. Esto funciona muy bien, excepto que históricamente no tenemos la capacidad de obtener el Top 10 ya que no se basa en una base mensual.

Intento n. ° 2: cree una nueva tabla para almacenar los 10 principales, pero la CLAVE PRIMARIA incluirá tanto el Top10ParentID como el BillingDateTimeID.

create table dimTop10Parent
(
    Top10ParentID INT NOT NULL,
    ParentName varchar(100) NOT NULL,
    Parent_Rank INT NOT NULL,
    BillDateTimeID BIGINT NOT NULL
);

El problema con esto es que no podemos crear una relación entre el factStats FK único con la PK de dos partes en dimTop10Parent en el modelo tabular.

Intento n. ° 3: cree la nueva tabla pero use una identidad como 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
);

La factStatstabla almacenará el Top10IDvalor que será único para cada fila. Pensé que esto resolvería mi problema, pero no lo hizo porque ya no podemos ordenar por Parent_Ranken el modelo, arroja un error:

No se puede ordenar ParentName por Parent_Rank porque al menos un valor en ParentName tiene varios valores distintos en Parent_Rank. Por ejemplo, puede ordenar [Ciudad] por [Región] porque solo hay una región para cada ciudad, pero no puede ordenar [Región] por [Ciudad] porque hay varias ciudades para cada región.

Usando los datos de la muestra, el resultado final debe ser similar a (esto muestra el Top 2 con un 3er enrollado):

| 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 |

En este punto, no sé cómo obtener este resultado final. Puedo alterar las tablas según sea necesario para obtenerlo, puedo alterar el modelo usando una fórmula, medida, etc. He leído sobre la clasificación usando las fórmulas DAX 1 , 2 , 3 pero parece que no puedo entenderlo. lo suficiente como para poder obtener el resultado con precisión.

¿Cómo puedo calcular / almacenar este Top 10 para cualquier mes y aún así poder empalmar los datos según sea necesario en nuestro modelo tabular?

Respuestas:


1

Tuve un escenario similar y utilicé la siguiente consulta DAX ...

Primero, para simplificar, definí una medida para usar dentro del DAX para no tener que repetir la fórmula. Luego usé el generate para iterar en la fórmula 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

Con lo anterior, debe tener un Top 10 ParentID y la Medida por mes. simplemente reemplace "TableInTabular" con el nombre de su tabla tabular donde tiene los datos y el "DatesTableName" con el nombre de la tabla de fechas.

Avíseme si no he entendido bien su pregunta y espero que ayude ...


1
Gracias por la respuesta, todavía hay algunos problemas con esto. Primero, puedo usar esto dentro de SSMS, pero esto se está implementando en nuestro modelo tabular para que nuestros usuarios puedan acceder a él a través de PowerView, no escribirán ninguna consulta, esto solo debe estar disponible. En segundo lugar, a menos que esté haciendo algo mal, no hay una evaluación u orden permitida en el modelo tabular a través de Visual Studio; no hay opción para esto como una función. En tercer lugar, esta consulta solo devuelve los 10 principales, también necesito los datos acumulados o alguna forma de obtenerlos. Aunque continuaré jugando con esto.
Taryn
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.