Mostrando entradas con la etiqueta Qlik Sense. Mostrar todas las entradas
Mostrando entradas con la etiqueta Qlik Sense. Mostrar todas las entradas

jueves, 31 de agosto de 2023

Agregación en tablas simples, MTD, YTD

 A veces tenemos la necesidad de crear una columna que vaya sumando los resultados de las filas anteriores.

Qlik Sense tiene una opción que nos permite hacer esto sobre una columna, 

el problema es que en la primera fila escribe 0, y no el primer valor que debería acumular. además tampoco permite acumular en sentido descendente.

Imaginemos esta tabla con 2 columnas llamadas Fecha Contable y Sales a la que queremos añadir una columna MTD (Month To Date) que acumule mes a mes:


Lo primero que vemos es que está ordenada descendentemente por fecha. Y debemos tener en cuenta que cada mes el contador deberá ponerse a 0, o sea, no acumulará entre meses.

La expresión que debemos usar en la nueva columna es: 

AGGR(RANGESUM(BELOW(SUM(Sales), 0, DAY([Fecha Contable]))),([Fecha Contable],(NUMERIC, DESCENDING)))

Vamos a analizarla: 

BELOW(SUM(Sales), 0, DAY([Fecha Contable])) --> Suma todas las ventas (Sales) desde la fila actual (indicada por el parámetro 0) hasta n filas anteriores. Estas n filas anteriores vienen dadas por el parámetro DAY(Fecha Contable) de modo que si estamos en el día 17 de un mes, sumará desde el día 17 hasta 17 filas hacia abajo, o sea, hasta el día 1.

AGGR (RANGESUM (......,([Fecha Contable],(NUMERIC, DESCENDING))) --> Hace el cálculo para cada fecha de la tabla. El primer parámetro de AGGR es la función que queremos agregar, en este caso RANGESUM.... El segundo parámetro de AGGR indica el campo por el que se deben hacer los grupos, el tipo de este campo y cómo está ordenado. En nuestro caso la columna que usamos para agrupar es la propia Fecha Contable, que es de tipo numérico y la hemos ordenado descendentemente.

El resultado es el siguiente: 


Obsérvese cómo va acumulando los valores de cada fecha hasta que llega el día 1 del mes siguiente. entonces reinicia la suma para empezar a acumular de nuevo.


En caso de que la tabla estuviera ordenada de manera ascendente, o sea, como se ve aquí, 


La expresión hay que "darle la vuelta" porque ahora cada fila acumula las filas superiores, o sea, ahora la función es ABOVE(),  y también cambia el sentido de ordenación de AGGR de "DESCENDING" a "ASCENDING"

AGGR(RANGESUM(ABOVE(SUM(Sales), 0, DAY([Fecha Contable]))),([Fecha Contable],(NUMERIC, ASCENDING)))

Si en vez del campo "Fecha Contable" usamos cualquier otro, como por ejemplo "Mes" (suponiendo que tengamos un campo Mes asociado a cada fecha contable), sólo veríamos el cálculo el día 1 de cada mes.

En caso de que quisiéramos acumular año a año (YTD - Year To Date), basta con sustituir la función DAY() por DAYNUMBEROFYEAR(). En este ejemplo vemos la expresión usada cuando el orden es descendente.

aggr(rangesum(BELOW(SUM(Sales), 0, daynumberofyear([Fecha Contable]))),([Fecha Contable],(NUMERIC, DESCENDING)))

Atención: Todo lo anterior sólo sirve en caso de que que la primera fecha comience el día 1 del mes y no haya huecos entre fechas.






lunes, 28 de diciembre de 2020

Ordenar valores Hexadecimales

Me he encontrado un proyecto en el que el campo índice es un número en formato hexadecimal y los listados, gráficos, etc deben salir ordenados por ese campo.

 

Imaginemos la siguiente tabla:

LOAD * INLINE [
Campo_Indice
AA

2C

11
20
A3
1A
];

 

Una ordenación por defecto lee los valores y los interpreta como texto o número y los agrupa de forma que primero muestra los texto y después los números.

 


 Observese, en este objeto de lista, cómo los textos aparecen alineados a la izquierda y los números a la derecha.

Si intentamos solucionarlo marcando sólo la opción “TEXTO” en las opciones de ordenación del objeto, la cosa no mejora mucho más:



Una solución rápida consiste en ordenarlo convirtiendo el campo a nº hexadecimal :


 

Pero como se puede observar siguen apareciendo alineados a la izquierda o derecha porque el campo sigue teniendo valores con formato texto y número. No hemos transformado el campo en la carga, sino solo para ordenarlo. De hecho, lo que ocurre durante la ordenación es que transforma los valores del campo a formato numérico decimal y luego los ordena.

Podríamos tener la tentación de utiliar esta fórmula para hacer la transformación directamente en el script de carga. No es aconsejable a menos que este campo no necesite para representarse en ningún objeto de la aplicación, porque el resultado sería el siguiente: 

LOAD Num(Num#([Campo Indice],'(HEX)'))  AS "Campo Indice"
 
INLINE [
Campo Indice
AA
2C
11
20
A3
1A
]
;

 


 

Observamos también que si tuviéramos los números de la última lista (pero desordenados) y los quisiéramos convertir a Hexadecimal, bastaría con utilizar esta expresión en el script:  

 

LOAD Num(Num#([Campo Indice]),'(HEX)')  AS "Campo Indice"
 
INLINE [
Campo Indice
170
44
17
32
163
26
]
;
 

 


 

Obsérvese cómo en este caso todos aparecen por defecto perfectamente ordenados y alineados a la derecha, lo que indica que realmente son números. En este caso el ‘HEX’ se aplica a la visualización (función num) no a la conversión (función num#).

Por tanto, aplicando todo lo anterior, la mejor forma de hacerlo sería con una doble conversión en el script de carga: de hexadecimal a decimal y nuevamente a hexadecimal.

 

LOAD Num(Num#([Campo Indice],'(HEX)'),'(HEX)')  AS "Campo Indice"
 
INLINE [
Campo Indice
AA
2C
11
20
A3
1A
]
;
 

 


 

martes, 22 de octubre de 2019

► Carga de ficheros no estructurados

Vamos a analizar paso por paso cómo cargar un fichero no estructurado.
Este tipo de ficheros suelen ser informes generados por otras herramientas y exportados a ficheros de texto. Suelen tener cabeceras, pies de página, líneas de texto e información relevante que no siempre está encolumnada en las mismas prosiciones.
Ejemplos: facturas, informes financieros, listados.... etc.

El truco para poderlo cargar es buscar palabras claves que se repitan y que nos den un indicio de dónde empieza y termina la información relevante.

En este caso vamos a cargar el fichero WEB http://www.linux-usb.org/usb.ids.
Este fichero, entre otras cosas contiene una lista de nombres de fabricantes y de dispositivos USB. Está más o menos formateado ya que las líneas comienzan siempre igual: por un carácter en blanco, tabuladores o '#' para los comentarios.

Contiene varias secciones, pero nos vamos a quedar sólo con la parte de los vendedores y sus dispositivos.
La estrategia a seguir es similar en todos estos ficheros y comienza cargando todo el fichero en una tabla. Luego buscaremos registro a registro la información relevante y la almacenaremos en una tabla de hechos.
En este caso cada línea del fichero puede tener un vendedor, un dispositivo o un interface. O sea, sólo uno a la vez. en cambio nuestra tabla de hechos tendrá un registro por cada combinación de vendedor, sus dispositivos y los interface de cada dispositivo.

 Id_Vendedor  Vendedor  Id_Dispositivo  Dispositivo  Id_Interface  Interface 

Veamos los pasos detalladamente.

1.- El modo de lectura por defecto de Qlik quita los espacios en blanco y los tabuladores a la izquierda del primer carácter de cada campo leído. Nos interesa leer la línea del fichero sin que se pierda nada, por tanto ponemos:

       SET VERBATIM = 1;

Esto nos va a garantizar cargar las líneas bien tabuladas.

2.- Cargamos cada línea del fichero en un registro de una tabla temporal. Luego borraremos esta tabla.

       DISPOSITIVOS_TMP:
        LOAD recno() AS "Num Linea",
                  @1        AS Linea
        FROM [http://www.linux-usb.org/usb.ids] (txt, utf8, no labels, delimiter is ';');

3.- Volvemos a establecer el modo de carga por defecto

      SET VERBATIM = 0;

4.- Ya tenemos cargada cada línea del fichero original en el campo "Línea" de la tabla "DISPOSITIVOS_TMP". Vamos a recorrer cada registro de la tabla para buscar y obtener la información relevante. El registro leído lo metemos en una variable para poderlo manipular.

       FOR i = 0 to NoOfRows('DISPOSITIVOS_TMP')-1
               vL.Linea=peek('Linea',$(i),'DISPOSITIVOS_TMP');

5.- Sabemos que el bloque de vendedores comienza por una línea que contiene el texto '# vendor  vendor_name' y que el siguiente bloque de datos comienza en una línea que contiene el texto '# C class  class_name'. Por tanto estableceremos un FLAG que indique si estamos en el bloque de vendedores o no.

       IF substringcount(vL.Linea,'# vendor  vendor_name') > 0 THEN // Comienzo Lista de vendedores
              vL.Flag_Fabricantes=1;
       END IF
       IF substringcount(vL.Linea,'# C class  class_name') > 0 THEN // Fin Lista de vendedores
              vL.Flag_Fabricantes=0;
       END IF

6.-  Llega el momento de verificar la información de cada línea. Si la línea pertenece al bloque de los Vendedores, la analizaremos, si no, directamente la desechamos.

       IF vL.Flag_Fabricantes=1 THEN  

7.- Para analizarla sabemos:

  •  que las líneas de comentarios comienzan por '#'
  •  que las líneas que comienzan por ' ' (espacio blanco) son líneas en blanco a desechar
  •  que las líneas de los dispositivos comienzan por un caracter tabulador (CHR(9))
  •  que las líneas de los interfaces comienzan por 2 caracteres tabulador (CHR(9)&CHR(9))
Dependiendo del tipo de línea detectada, guardaremos su información relevante en variables diferentes. 

     IF MATCH(LEFT(vL.Linea,1),'#',CHR(09),' ') = 0 THEN //Si no hay tabs ni #, es una línea de fabricante
              vL.Id_Fabricante = SubField(vL.Linea,' ',1); 
              vL.Nombre_Fabricante = TRIM(MID(vL.Linea, index(vL.Linea,' ')));
          
              //Cada vez que cambiamos de fabricante reseteamos los valores de los dispositivos y de los interfaces
              vL.Id_Interface='';
              vL.Nombre_Interface='';
              vL.Id_Dispositivo='';
              vL.Nombre_Dispositivo='';
          
              vL.Grabar_Linea=1;
       
             ELSEIF LEFT(vL.Linea,2) = CHR(09)&CHR(09) THEN // Es un Interface
                  vL.Id_Interface = Subfield(REPLACE(vL.Linea,'\t',''),' ',1);  
                  vL.Nombre_Interface = TRIM(MID(vL.Linea, index(vL.Linea,' ')));
             
                  vL.Grabar_Linea=1;
          
                 ELSEIF LEFT(vL.Linea,1) = CHR(09) THEN // Es un Dispositivo
                       vL.Id_Dispositivo = Subfield(REPLACE(vL.Linea,'\t',''),' ',1);  
                       vL.Nombre_Dispositivo = TRIM(MID(vL.Linea, index(vL.Linea,' ')));
                
                       //Cada vez que cambiamos de dispositivo reseteamos los valores de los interfaces
                       vL.Id_Interface='';
                       vL.Nombre_Interface='';
                
                       vL.Grabar_Linea=1;                
       ENDIF    

      

8.-  Si en cualquier IF anterior se ha marcado la línea como "grabable", procedemos a meterla en la tabla de hechos



      IF vL.Grabar_Linea=1; //Con este IF no duplicamos la última línea válida antes de una inválida que comience por #
           DISPOSITIVOS:
            LOAD
                 '$(vL.Id_Fabricante)'             AS Id_Vendor,
                 '$(vL.Nombre_Fabricante)'   AS Vendor,
                 '$(vL.Id_Dispositivo)'            AS Id_Device,
                 '$(vL.Nombre_Dispositivo)'  AS Device,
                 '$(vL.Id_Interface)'               AS Id_Interface,
                 '$(vL.Nombre_Interface)'     AS Interface              
             AUTOGENERATE 1;
    
          vL.Grabar_Linea=0;   //Reseteo del Flag que nos indica que hay que grabar la línea
       ENDIF  



9.- Repetimos el proceso para una nueva línea

      ENDIF //Cierre del IF que preguntaba si estamos en el bloque de Vendedores
    
     NEXT i  //Fin del bucle que lee línea a línea de la tabla temporal

10.- Por último borramos la tabla auxiliar

         DROP TABLE DISPOSITIVOS_TMP;


Con esto ya tendríamos transformado el fichero en una tabla lista para ser usada en nuestro Dashboard.

La filosofía para cualquier fichero de texto es siempre la misma: Cargar todo el fichero, fila a fila, en un campo de una tabla y luego leer cada registros de la tabla buscando información relevante como palabras clave que se repitan o patrones de palabras. Para ello usaremos con asiduidad casi todas las funciones de cadena que permitan trocear y limpiar texto como SUBFIELD, TRIM, LEFT, MID, RIGHT, INDEX, SUBSTRINGCOUNT...
Tendremos que crear variables de control para cargar la línea en curso y saber qué debemos hacer con ella. Usaremos también bucles FOR-NEXT para repetir trabajo sobre líneas, controles IF-THEN-ELSE para verificar si tenemos que tratarla o desecharla y por último un bloque donde grabaremos registro a registro los datos extraídos previamente en variables.

martes, 21 de mayo de 2019

► Fichero con el listado de tablas del modelo

Esta entrada es una variación de la que hablaba de cómo exportar todas las tablas de un modelo.
Me encuentro ahora en la siguiente situación: Tengo un cuadro de mandos que hace un BINARY de otro, pero sólo necesito algunas tablas del CdM original, por lo que tengo que borrar las que me sobran.
Por alguna razón otro desarrollador ha modificado el CdM original y han desaparecido tablas, por lo que ahora mi cuadro de mandos me falla con el siguiente error:


En condiciones normales bastaría con mirar en el visor de tablas las tablas que tenemos e ir comparando con las que se borran en el script. Pero resulta que nuestro modelo tiene muuuuchas tablas que no caben en pantalla a no ser que se reduzca la resolución del visor de tablas, y en ese caso los nombres de las tablas dejan de ser visibles.

Por tanto necesitamos algún sistema que nos saque un listado de las tablas del modelo.

Existen 2 soluciones:

1º la más fácil:
Antes del punto que provoca el error interrumpimos el script con un exit script:
En cualquier pestaña de visualización creamos un cuadro de lista donde el campo elegido es el campo del sistema $Table.
Una vez que tengamos la lista la podemos exportar a excel.

2º Desde el script:
Antes del punto que provoca el error escribimos el siguiente código que recorre las tablas del sistema, mete sus nombres en una tabla temporal, exporta la tabla y luego la borra:


LET numTablas = NoOfTables();

FOR i = 0 to $(numTablas)-1
       LET Tabla= TRIM(TableName($(i)));
          
       Mis_Tablas:
       LOAD '[$(Tabla)]' as NombreTabla
       AUTOGENERATE 1;
      
      Store Mis_Tablas INTO Mis_Tablas.txt (txt);
NEXT i

Drop table Mis_Tablas;

exit script;

miércoles, 20 de marzo de 2019

► FIRST vs SAMPLE

¿Qué pasa cuando tenemos que probar un cambio mínimo en el script pero la recarga dura horas porque carga millones de registros?
Una situación así puede acabar con la paciencia de cualquiera. Cada cambio, por mínimo que sea nos obligará a esperar.... esperar... esperar....
¿Cómo evitarlo?

La respuesta es obvia. No cargando todas las filas del modelo. Sólo unas pocas. Las suficientes para probar si el script falla y que los objetos se visualicen correctamente.

Para cargar sólo unas cuantas filas existen 2 estrategias: usando FIRST y SAMPLE.

FIRST n carga las n primeras filas de la tabla
SAMPLE n (donde n está entre 0 y 1) carga n %  filas aleatorias., ej: 0,5 carga el 50% (una de cada 2). 0.0001 carga 1 de cada 10.000... etc)

Independientemente de si usamos una estrategia u otra, conviene siempre crear una variable que guarde el nº de filas a cargar. De esta forma nos evitamos comentar todas las sentencias FIRST y SAMPLE en el script cuando no las queramos usar, pudiéndolas dejar descomentadas.

LET vNumFilas= 1000000000; //Un número más grande que el nº máx de registros) en el caso de usar FIRST

LET vNumFilas= 1; // En caso de SAMPLE esto cargará todas las filas.

Basta con modificar sólo el valor de la variable para obtener el nº de filas deseadas en el modelo.

Estrategia con FIRST.

El inconveniente es que no toma un número de registros homogéneo, si no sólo los n primeros registros.
Imaginemos una tabla con 10 millones de facturas. Con toda probabilidad estará ordenada por fecha. Si tomamos sólo las 1.000 primeras facturas es casi seguro que todas corresponderán al mismo mes, por lo que cualquier objeto calendario no se visualizará correctamente porque sólo mostrará ese mes y ese año.


Estrategia con SAMPLE 

Nos permite obtener 1.000 registros de fechas variadas de la misma tabla de 10 Millones de facturas. basta con usar SAMPLE 0.0001 para quedarnos con una de cada 10.000.

ejemplo con SAMPLE.
Vamos a suponer que tenemos un modelo con varias tablas. tenemos una tabla de hechos con 15 millones de registros. Queremos cargar sólo 1 de cada 10.000. Del resto de tablas sólo nos interesan los registros que estén relacionados con la tabla de echos, por lo que usaremos un WHERE EXISTS en cada una de ellas.

Para no tener que comentar y descomentar los WHERE EXISTS y el SAMPLE cada vez que lo queramos usar, lo mejor es crear variables.

LET vNumFilasMuestra= 0.0001;

SAMPLE ($(vNumFilasMuestra))
Tabla_A:
LOAD %KeyB,  //Campo que une con la tabla B
     %KeyC,  //Campo que une con la tabla C
     campo1, campo2, campo3..... campoN
FROM A; //Sustituir A por una tabla, fichero qvd, excel... etc.

// establecemos una condición para cargar sólo las filas de la tabla B cargadas previamente en en la tabla_A
LET vCondicionWHERE IF($(vNumFilasMuestra)<1,'WHERE EXISTS(%KeyB)';

Tabla_B:
LOAD %KeyB,
     campo_b1, campo_b2, campo_b3..... campo_bN
FROM //Sustituir B por una tabla, fichero qvd, excel... etc.
$(vCondicionWHERE)
; //La sentencia termina aquí, no después del FROM.

// establecemos una condición para cargar sólo las filas de la tabla C cargadas previamente en en la tabla_A
LET vCondicionWHERE IF($(vNumFilasMuestra)<1,'WHERE EXISTS(%KeyC, IF(Zona=3, '& CHR(39)& 'Madrid' & CHR(39& ', Zona)& ' CHR(39& '-' CHR(39& ' & Provincia)');

Tabla_C:
LOAD IF(Zona=3, 'Madrid', Zona)&'-'& Provincia AS %KeyC,
     campo_c1, campo_c2, campo_c3..... campo_cN
FROM //Sustituir C por una tabla, fichero qvd, excel... etc.
$(vCondicionWHERE)
; //La sentencia termina aquí, no después del FROM.

Observad que el campo clave %KeyC en la tabla C es un campo compuesto por otros 2, Zona y Provincia. Pero además se usa una condición para formar el campo.
La correspondiente condición WHERE sería:

WHERE EXISTS (KeyC, IF(Zona=3, 'Madrid', Zona) & '-' & Provincia)

Pero eso no se puede asignar así, tal cual, en una variable porque las comillas simples se interpretarían como el final de la cadena de texto y provocaría un error.

La forma de conseguirlo es sutituir en la variable vCondicionWHERE todas las comillas simples por su correspondiente código ASCII y tener mucho cuidado de dividir toda la cadena WHERE en partes más pequeñas que se van concatenando con '&'. 













domingo, 16 de diciembre de 2018

► Cálculo de facturas vencidas

Es muy común tener que hacer listados en las que se muestren facturas vencidas a partir de unos rangos de vencimiento. Por ejemplo de 0-1 meses, 1 a 2 meses, 2 a 3 meses, 3 a 6 meses.... etc.

Y he visto todo tipo de soluciones para resolver el problema, desde crear expresiones con complejos Set Analysis hasta tablas de conversión de periodos de vencimiento pasando por complejos if anidados tanto en script como en expresiones.

Esta solución es una más y la que yo suelo usar.

Consiste en añadir un campo más con el periodo de vencimiento a cada registro de factura. Este periodo se calcula en el script y se actualiza cada vez que se recarga la aplicación, lo que mejora luego bastante su rendimiento porque no hará falta ningún set analysis ni ifs anidados en las expresiones.

Para no complicar mucho el tema, imaginemos que tenemos esta tabla de facturas:

FACTURAS:
LOAD * INLINE [
Num_factura, FechaFactura, FechaVencimiento
1 ,          13/02/2018,   13/03/2018
2,           13/02/2018,   25/08/2018
3,           25/07/2018,   25/11/2018
]

Y las queremos clasificar en los siguientes rangos de vencimiento de modo que el usuario seleccione uno de ellos y automáticamente nos aparezca un listado con las facturas vencidas de ese periodo.

0 a 1 meses, 1 a 2 meses, 2 a 3 meses, 3 a 6 meses, 6 a 12 meses. 12 a 24 meses, 24 a 36 meses y más de 36 meses.

Siguiendo la regla de oro de que todo lo que se pueda calcular en el script hay que hacerlo en el script mejor que en expresiones de objetos, calculamos el rango de vencimiento y lo añadimos como un nuevo campo.

LEFT JOIN(FACTURAS)
LOAD *,
Pick(ALT(
       
IF(Interval(today()-FechaVencimiento,'D') <30, 1),
       
IF(Interval(today()-FechaVencimiento,'D') <60, 2),
       
IF(Interval(today()-FechaVencimiento,'D') <90, 3),
       
IF(Interval(today()-FechaVencimiento,'D') <180, 4),
       
IF(Interval(today()-FechaVencimiento,'D') <365, 5),
       
IF(Interval(today()-FechaVencimiento,'D') <730, 6),
       
IF(Interval(today()-FechaVencimiento,'D') <1095, 7),
        8),
   '0 a 1', '1 a 2', '2 a 3', '3 a 6', '8 a 12', '12 a 24', '24 a 36',' más 36 meses') &' meses' 
AS RangoDeuda
RESIDENT   FACTURAS; 

Como vemos, el LEFT JOIN se hace leyendo la propia tabla  (LEF JOIN Y RESIDENT tienen como origen y destino la misma tabla), sin necesidad de usar una tabla auxiliar intermedia.

Utilizo ALT() por evitar los IF anidados, que siempre son más engorrosos, pero como ALT() sólo devuelve valores numéricos y no puede devolver directamente el nombre del rango, hay que recoger su valor en un PICK() .

El resultado final del ejemplo sería algo así:

Cuando el usuario elige un rango, la tabla se filtra automáticamente.


martes, 4 de diciembre de 2018

► GetObjectDimension y GetObjectExpression





Existen dos funciones en Qlik Sense que estuvieron documentadas en las primeras versiones y, aunque siguen funcionando (verificado hasta la versión de Noviembre de 2018), han desaparecido de la documentación sin dejar rastro.

Son GetObjectDimension(índice) y GetObjectExpresion(índice)

GetObjectDimension(0) Devuelve la etiqueta de la dimensión actualmente seleccionada en un grupo de dimensiones alternativas.

GetObjectExpresion(0) Devuelve la etiqueta de la expresión actualmente seleccionada en un grupo de expresiones alternativas.

Ojo, lo que devuelve es la etiqueta y quizá este sea el motivo de que hayan desaparecido de la documentación. Aunque siguen funcionando, es posible que discontinúen su funcionamiento. Quizá de problemas cuando la etiqueta de la expresión/dimensión es una variable.

Veamos un ejemplo.

Vamos a suponer que queremos un gráfico que acumule las ventas durante un periodo de tiempo. En Sense no existe el Tic de QlikView para acumular, por tanto lo tenemos que hacer con una fórmula.
Para agravar la situación, el gráfico tiene 3 dimensiones alternativas. Mes, Semana  y Día. Para permitir mostrar la información, necesitamos 3 expresiones distintas, una que acumule por Mes, otra por Semana y otra por Día y que cada una se ejecute dependiendo de la dimensión seleccionada.

El resultado debería ser este:




La configuración de datos y expresiones queda como en la siguiente imagen. Observad que hay 3 dimensiones alternativas y sólo una medida.



La expresión de la medida es esta:


ALT(
   
IF(GetObjectDimension(0)='Mes Venta',
             
SUM(aggr(rangesum(above(SUM({<[Year]={'$(vL.AñoActual)'}>} [Precio Neto]+[Iva Compra]),0,num([Month]))),([Month],(NUMERIC, ASCENDING))))),
   
IF(GetObjectDimension(0)='Dia del Año Venta',
             
SUM(aggr(rangesum(above(SUM({<[Year]={'$(vL.AñoActual)'}>} [Precio Neto]+[Iva Compra]),0,num([Dia del Año]))),([Dia del Año],(NUMERIC, ASCENDING))))),
    IF(
GetObjectDimension(0)='Semana Venta',
             
SUM(aggr(rangesum(above(SUM({<[Year]={'$(vL.AñoActual)'}>} [Precio Neto]+[Iva Compra]),0,num([Week]))),([Week],(NUMERIC, ASCENDING)))))
)


Vamos a diseccionarla:

  • ·         En lugar de utilizar IF anidados, usamos la función ALT, que es mucho más legible. Cada IF devuelve un valor numérico (si se cumple) o nulo, en cuyo caso el flujo del programa pasa al siguiente IF.
  • ·         El valor con el que se compara GetObjectDimension(0) en cada IF es el texto que hayamos puesto en la etiqueta de la dimensión (ver imagen superior).
  • ·         $(vL.AñoActual) Es una variable que contiene el año máximo de los seleccionados (=Max(Year)).
  • ·         SUM(aggr(rangesum(above(SUMEs la expresión que calcula toda la suma de ventas acumulada para cada unidad de tiempo (Month, Dia del Año o Week).
  • ·         (NUMERIC, ASCENDING) Son dos parámetros (exclusivos de Sense y de View a partir de la versión 12) de la función AGGR que permiten ordenarla. Sin estos parámetros AGGR se ordena según el orden de carga.