Viendo productos sin venta en los último X meses con MDX

Posted by Miguel Egea | Posted in Analisys Services, MDX | Posted on 18-07-2010

1

Haca unos dias, un amigo, y alumno de un curso de MDX que impartí lanzaba esa pregunta en los post, ¿como saber los artículos que no han tenido venta en los últimos tres meses. Para eso hace falta saber que dia es hoy, aunque en realidad, me parece que es mejor olvidarse de que dia es hoy y hacerlo genérico. Vamos a ver como podriamos hacer eso.

Bueno, vamos a ir por partes.. lo primero que vamos a hacer es ver como calcular el día de hoy.

with member hoy
as
 now()
select hoy on 0
from [Adventure Works]

despues vamos a ver como sacar el conjunto de articulos con ventas en un mes.

select  exists([Product].[Product].product*[Date].[Calendar].[Month].members,,
		  "Internet Sales")
  on 1,
        {Measures.[Internet Sales Amount]} on 0
from  [Adventure Works];

el conjunto que forman los meses y  los productos que han tenido ventas es lo que nos devuelve la clasula de arriba.

Ahora vamos a calcular los que no han tenido ventas en absoluto, para ello simplemente hacemos una resta de conjuntos, es decir, todos los productos menos los que han tenido ventas

select  [Product].[Product].product*[Date].[Calendar].[Month].members-
exists([Product].[Product].product*[Date].[Calendar].[Month].members,,
		  "Internet Sales")
  on 1,
        {Measures.[Internet Sales Amount]} on 0
from  [Adventure Works];
 

De esta forma tenemos los articulos sin ventas en un mes concreto, porque son aquellos que no tienen datos en el grupo de medidas Internet Sales, para el mes en cuestion. El problema viene porque ahora necesitamos saber los que no tienen ventas en los últimos tres meses. Para poder usar Exist, necesitariamos tener un grupo especial que tuviese en el mes actual las ventas de los últimos tres meses para cada producto y comprobar su existencia. El resultado sería rápido, pero parece que quedaría bastante engorroso de hacer, además implica cambios en el ETL y eso siempre quiere decir, mucho tiempo, muchas pruebas. Así pues dejamos esta via solamente para el caso de que el rendimiento no se corresponda con el necesario. En otro caso pasamos al plan B y usamos el método filter.

	select
	filter ( [Product].[Product].product*
				[Date].[Calendar].[Month].members  ,
				 sum(([Date].[Calendar].currentmember.lag(3):[Date].[Calendar].currentmember),Measures.[Internet Sales Amount])>0) on 1,
				Measures.[Internet Sales Amount] on 0
	from  [Adventure Works];

si nos fijamos he usado la cláusula Sum para el filter, esto penalizará rendimiento, sin duda,.. considerad hacer esta medida física aun si es en el mismo grupo de medidas para mejorar el rendimiento. Aún este ejemplo nos está mostrando los que si tienen ventas en los últimos 3 meses.. veamos los que no… creo que se vé facil, si has llegado hasta aquí :)

	select [Product].[Product].product*[Date].[Calendar].[Month].members -
	filter ( [Product].[Product].product*
				[Date].[Calendar].[Month].members  ,
				 sum(([Date].[Calendar].currentmember.lag(3):[Date].[Calendar].currentmember),Measures.[Internet Sales Amount])>0) on 1,
				Measures.[Internet Sales Amount] on 0
	from  [Adventure Works]

Esta consulta tardó casi 15 segundos (una barvaridad) en mi portatil contra adventure works, si la personalizamos a un único mes.. tardará mucho menos, claro.

	select [Product].[Product].product-
	filter ( [Product].[Product].product
				,
				 sum(([Date].[Calendar].currentmember.lag(3):[Date].[Calendar].currentmember),Measures.[Internet Sales Amount])>0) on 1,
				Measures.[Internet Sales Amount] on 0
	from  [Adventure Works]
	 where
	[Date].[Calendar].[Month].&[2004]&[7]

y si ahora lo enlazamos con el now que veiamos al principio.. (ojo , no funcionará a menos que le pongas el reloj al server a un valor que de un miembro válido) Además ojo con usar STRTOMEMBER, que puede dar también problemas de rendimiento, no en este ejemplo.. pero si en general

with member ahora as
    "[Date].[Calendar].[Month].&[" +cstr(year(now()))+ "]&["+cstr(month(now()))+"]"
	select [Product].[Product].product-
	filter ( [Product].[Product].product
				,
				 sum(([Date].[Calendar].currentmember.lag(3):[Date].[Calendar].currentmember),Measures.[Internet Sales Amount])>0) on 1,
				Measures.[Internet Sales Amount] on 0
	from  [Adventure Works]
	 where
	strtomember(ahora)