martes, 24 de agosto de 2021

Power Query: Table.SelectRows entre Tablas

Días atrás expliqué una alternativa al clásico Table.NestedJoin (i.e., a la acción de Combinar consultas).
Hoy veremos una nueva posibilidad empleando otra función M importante: Table.SelectRows:
Table.SelectRows(table as table, condition as function) as table
Es una función conocida por todos, ya que es la que sirve para aplicar filtros sobre una tabla, es decir, muestra solo las filas de una tabla que cumplen las condiciones dadas...
Si bien, lo habitual es trabajar sobre columnas y filas de la misma tabla...

Y ahí reside lo interesante del artículo de hoy. Veremos como con esta función podremos obtener datos de terceras tablas!!

Partiremos de las cinco tablas fuentes siguientes, situadas en nuestro libro de trabajo: 'TblDATOS','TblREGION','TblDESCUENTOS','TblPRECIOS' y 'TblCOMISION':
Power Query: Table.SelectRows entre Tablas


Comenzaremos cargando las cinco tablas, solo como conexión, en nuestro editor de consultas de Power Query.

En un primer ejercicio, con el objetivo de comparar tiempos de ejecución, montaremos una consulta donde emplearemos la herramienta 'Combinar consultas' (por tanto: Table.NestedJoin).
Empezando desde la 'TblDATOS' iremos combinando y expandiendo los distintos campos necesarios... El código quedaría:
let
    Origen = Excel.CurrentWorkbook(){[Name="TblDATOS"]}[Content],
    Tipo_cambiado = Table.TransformColumnTypes(Origen,{{"Fecha", type datetime}, {"País", type text}, {"Comercial", type text}, {"Producto", type text}, {"Unidades", Int64.Type}}),
    Consultas_combinadas = Table.NestedJoin(Tipo_cambiado, {"País"}, TblREGION, {"Country"}, "TblREGION", JoinKind.LeftOuter),
    Se_expandió_TblREGION = Table.ExpandTableColumn(Consultas_combinadas, "TblREGION", {"Region", "UE/No UE"}, {"Region", "UE/No UE"}),
    Consultas_combinadas1 = Table.NestedJoin(Se_expandió_TblREGION, {"Comercial"}, TblDESCUENTO, {"Comerciales"}, "TblDESCUENTO", JoinKind.LeftOuter),
    Se_expandió_TblDESCUENTO = Table.ExpandTableColumn(Consultas_combinadas1, "TblDESCUENTO", {"Dirección", "Descuento"}, {"Dirección", "Descuento"}),
    Consultas_combinadas2 = Table.NestedJoin(Se_expandió_TblDESCUENTO, {"Producto"}, TblPRECIOS, {"Artículo"}, "TblPRECIOS", JoinKind.LeftOuter),
    Se_expandió_TblPRECIOS = Table.ExpandTableColumn(Consultas_combinadas2, "TblPRECIOS", {"Precio"}, {"Precio"}),
    Consultas_combinadas3 = Table.NestedJoin(Se_expandió_TblPRECIOS, {"Producto", "Comercial"}, TblCOMISION, {"Cod", "Vendedor"}, "TblCOMISION", JoinKind.LeftOuter),
    Se_expandió_TblCOMISION = Table.ExpandTableColumn(Consultas_combinadas3, "TblCOMISION", {"Comisión"}, {"Comisión"})
in
    Se_expandió_TblCOMISION

Power Query: Table.SelectRows entre Tablas

Puesto que solo he seguido la mecánica tantas veces vista en el blog, no me detengo en esta parte... (Para más detalle accede a la categoría de Power Query).

Lo importante viene ahora, donde empleando la función Table.SelectRows replicaremos esas relaciones entre distintas consultas o tablas!!.

En una nueva consulta en blanco escribiremos:
let
    Origen = TblDATOS,
    //Agregamos una nueva columna con el Precio (de la TblPRECIOS) correspondiente
    //cuando coincidan los códigos de los productos ('Producto' = 'Artículo')
    AddCol_PRECIO=
                Table.AddColumn(
                Origen,
                "N_Precio", each 
                            let a=[Producto]
                            in Table.SelectRows(TblPRECIOS, each [Artículo] = a )[Precio]{0}),
    //Dividimos una columna en dos.
    //Columna que hemos agregado con información de la TblREGION, tras verificar la coincidencia del País
    //Al necesitar recuperar dos columnas de REGION,
    //he realizado una concatenación (Table.CombineColumns) de la info de dos columnas
    AddCol_REGION=
        Table.SplitColumn(
                Table.AddColumn(
                AddCol_PRECIO,
                "N_Región", each 
                            let p=[País]
                            in Table.CombineColumns(
                                Table.SelectRows(TblREGION, each [Country] = p ),
                                 {"Region","UE_Y_N"},Combiner.CombineTextByDelimiter("|"),"Para_extraer")[Para_extraer]{0}),
        "N_Región", Splitter.SplitTextByDelimiter("|", QuoteStyle.Csv), {"Region", "UE_Y_N"} ),
    //idem al anterior pero para la TblDESCUENTOS
    AddCol_DCTO=
        Table.SplitColumn(
                Table.AddColumn(
                AddCol_REGION,
                "N_Comercial", each 
                            let c=[Comercial]
                            in Table.CombineColumns(Table.TransformColumnTypes( 
                                Table.SelectRows(TblDESCUENTO, each [Comerciales] = c ),
                                {"Descuento", type text}),
                                 {"Dirección","Descuento"},Combiner.CombineTextByDelimiter("|"),"Para_extraer")[Para_extraer]{0}),
        "N_Comercial", Splitter.SplitTextByDelimiter("|", QuoteStyle.Csv), {"Dirección","Descuento"} ),
    //Agregamos el dato de la 'comisión' desde la TblCOMISION cuando se de la doble coincidencia de Comrcial y Artículo
    AddCol_COMISION=
                Table.AddColumn(
                AddCol_DCTO,
                "N_Comision", each 
                            let a=[Producto], c=[Comercial]
                            in Table.SelectRows(TblCOMISION, each [Cod] = a and [Vendedor]=c)[Comisión]{0})

in
    AddCol_COMISION

En esencia, la parte relevante es donde se emplea Table.SelectRows:
Table.SelectRows(TblPRECIOS, each [Artículo] = a )[Precio]{0}

A pesar de estar trabajando sobre la TblDATOS, podemos referirnos a una tercera tabla ('TblPRECIOS') para recuperar aquellas filas donde se de la condición de que en su columna [Artículo] (OJO!, columna de la tabla TblPRECIOS!!) sea igual al dato 'a' (variable cargada previamente con el dato de la misma fila y la columna [Producto] de la tabla TblDATOS.

La información resultante la incorporamos en una nueva columna de la TblDATOS... lo que se consigue con la función Table.AddColumn:
Table.AddColumn(
                Origen,
                "N_Precio", each 
                            let a=[Producto]
                            in Table.SelectRows(TblPRECIOS, each [Artículo] = a )[Precio]{0})

De cada tabla resultante para cada fila, nos interesa recuperar el dato de la columna [Precio] y el primer y único valor de la fila 1 ([Precio]{0})

Si el cruce de información debe responder a un doble criterio, podemos añadirlo en el argumento de condición de Table.SelectRows:
Table.AddColumn(
                AddCol_DCTO,
                "N_Comision", each 
                            let a=[Producto], c=[Comercial]
                            in Table.SelectRows(TblCOMISION, each [Cod] = a and [Vendedor]=c)[Comisión]{0})

La doble condición, en este caso, se aplica con un criterio lógico 'and':
each [Cod] = a and [Vendedor]=c
donde 'a' y 'c' son variables cargadas previamente con los valores de la TblDATOS de las columnas [Producto] y [Comercial].

Otro caso diferente es cuando queremos recuperar información de más de una columna....
Existen diferentes métodos (los más eficientes suelen ser trabajar con 'Records'), pero en este caso expondré uno distinto empleando concatenaciones (Table.CombineColumns) y divisiones de columnas (Table.SplitColumn).
Table.SplitColumn(
                Table.AddColumn(
                AddCol_REGION,
                "N_Comercial", each 
                            let c=[Comercial]
                            in Table.CombineColumns(Table.TransformColumnTypes( 
                                Table.SelectRows(TblDESCUENTO, each [Comerciales] = c ),
                                {"Descuento", type text}),
                                 {"Dirección","Descuento"},Combiner.CombineTextByDelimiter("|"),"Para_extraer")[Para_extraer]{0}),
        "N_Comercial", Splitter.SplitTextByDelimiter("|", QuoteStyle.Csv), {"Dirección","Descuento"} )

Algo largo... pero fácil de entender.
Desde lo más 'profundo' de la fórmula hacia afuera...
Primero tenemos la selección de filas de la TblDESCUENTO que cumplen la condición de tener igual 'Comerciales' (con Table.SelectRows).
Segundo. Para poder concatenar las columnas deseadas, les cambiamos el tipo de datos a text, con la función Table.TransformColumnTypes
Ese concatenado resultante de las columnas 'Dirección' y 'Descuento' de la TblDESCUENTOS, lo agregamos en una nueva columna en la TblDATOS... usando Table.Addcolumns
Finalmente, aplicamos una división de esta columna recién agregada con Table.SplitColumn

Algo 'retorcidillo'... pero interesante por el uso de distintas funciones M frecuentes en otros contextos...
Desde luego el resultado es idéntico a la primera consulta creada con la herramienta de Combinar consultas.

Algo muy importante que debemos tener presente al usar este sistema es la optimización y/o la agilidad y rapidez de la carga de datos...
Este sistema es sustancialmente más lento que el clásico de 'combinar consultas'!!... pero por contra ofrece un infinito mundo de posibilidades para cruzar distintas tablas, y no únicamente el básico 1-1, 2-2,..., n-n de Table.NestedJoin.

Veamos un ejemplo al respecto...
Supongamos una nueva tabla con unas tarifas determinadas por un rango de fechas (Desde - Hasta) por cada comercial y que únicamente deben aplicar a ES, IT y FR:
Power Query: Table.SelectRows entre Tablas

Como siempre cargaremos esta nueva tabla ('TblTARIFAS') solo como conexión. Y a continuación desde una consulta en blanco escribiremos:
let
    Origen = TblDATOS,

    AddCol_TARIFAS=
                Table.AddColumn(
                Origen,
                "N_Tarifa", each 
                            let f=[Fecha], c=[Comercial]
                            in if ([País]="ES" or [País]="FR" or [País]="IT") then 
                                    Table.SelectRows(TblTARIFAS, each ([Desde] <= f and [Hasta]>=f) and [Comercial]=c)[Tarifa]{0} 
                                else null)
in
    AddCol_TARIFAS

Tras definir y cargar las variable 'f' y 'c' con los datos de la 'TblDATOS', procedemos a aplicar la comparativa (mayor o igual que, menor o igual que) con las filas de la TblTARIFAS dentro del contexto de la función Table.SelectRows (como hemos visto más arriba...).
Condiciones solo aplicadas cuando se cumpla las condiciones lógicas del 'or' indicada con un if... then... else....
Obteniendo:
Power Query: Table.SelectRows entre Tablas


Todas estas opciones y posibles condiciones a incorporar a nuestras consultas resultan muy cómodas de aplicar con este método... y no tanto con otros alternativos, por lo que nuevamente debemos sopesar pros y contras en cada caso particular antes de decidirnos por un método a aplicar ;-)

No hay comentarios:

Publicar un comentario

Nota: solo los miembros de este blog pueden publicar comentarios.