Power BI

Lenguaje M de Power Query: introducción práctica

Introducción práctica al lenguaje M de Power Query: estructura let/in, each, funciones clave, columnas condicionales, manejo de errores y funciones.

Por Equipo editorial de DashCore HubActualizado 7 min de lectura

M es el lenguaje de fórmulas que está detrás de cada paso que haces con clics en Power Query, tanto en Power BI como en Excel. Puedes hacer la mayor parte de la limpieza de datos sin escribir M, pero aprender a leerlo y editarlo un poco te permite arreglar pasos rotos, reutilizar lógica y resolver problemas que los botones no resuelven. Esta introducción práctica cubre la estructura de una consulta, la sintaxis básica y el puñado de funciones que vas a usar más.

Dónde vive M (y por qué ya lo estás usando)

Cada vez que quitas una columna, cambias un tipo de dato o filtras filas en el Editor de Power Query, este escribe una línea de M por ti. Para verla, abre Transformar datos y elige Inicio > Editor avanzado. También puedes activar la barra de fórmulas (Vista > Barra de fórmulas) para ver el M del paso seleccionado en la lista de Pasos aplicados.

Si Power Query es nuevo para ti, empieza con nuestra guía de Power Query en Excel; todo lo que verás allí aplica también en Power BI.

Anatomía de una consulta: let ... in

Casi todas las consultas tienen la misma forma. Un bloque let define pasos con nombre, e in indica cuál es el resultado final:

let
    Source = Csv.Document(
        File.Contents("C:\Data\sales.csv"),
        [Delimiter = ",", Encoding = 65001, QuoteStyle = QuoteStyle.Csv]
    ),
    PromotedHeaders = Table.PromoteHeaders(Source, [PromoteAllScalars = true]),
    ChangedTypes = Table.TransformColumnTypes(
        PromotedHeaders,
        {{"OrderDate", type date}, {"Region", type text}, {"Amount", type number}}
    ),
    FilteredRows = Table.SelectRows(ChangedTypes, each [Amount] > 0)
in
    FilteredRows

Lo más importante que debes notar:

  • Cada paso tiene la forma Nombre = expresión y se separa con comas. El último paso antes de in no lleva coma.
  • Cada paso normalmente recibe el paso anterior como primer argumento. Así se construye la cadena de transformaciones.
  • Los nombres de pasos con espacios se escriben como #"Tipo cambiado". Renombrarlos sin espacios (por ejemplo, ChangedTypes) hace el código más fácil de leer.
  • M distingue mayúsculas y minúsculas. Table.SelectRows funciona; table.selectrows no. Los nombres de columna también distinguen mayúsculas.
Consejo: Renombrar los pasos en el panel de Pasos aplicados (clic derecho > Cambiar nombre) es la forma más fácil de que tu M sea legible para ti mismo en el futuro. Una consulta con pasos llamados "Columnas quitadas1" y "Tipo cambiado3" es difícil de mantener.

Los bloques básicos: valores y tipos

M trabaja con un grupo pequeño de tipos de valores. Reconocerlos hace que los mensajes de error sean mucho más fáciles de entender:

"Hello"                       // text
42                            // number
#date(2026, 10, 25)           // date
true                          // logical
{1, 2, 3}                     // list
[Name = "Ana", Sales = 1200]  // record
#table({"A", "B"}, {{1, 2}})  // table with one row

Las llaves { } crean listas, los corchetes [ ] crean registros (y sirven para acceder a sus campos), y las tablas son, en esencia, listas de registros con las mismas columnas. Los comentarios se escriben con // para una línea o /* ... */ para varias.

Los tipos importan. Definir el tipo de cada columna (type date, type number, Int64.Type, type text) afecta cómo se cargan los datos al modelo, cómo se comprimen y si los pasos siguientes funcionan. Una causa muy común de errores es una columna de texto que debería ser numérica.

La palabra clave "each" y el guion bajo

Vas a ver each por todas partes. Es una forma corta de escribir una pequeña función que se ejecuta una vez por fila (o por elemento). Dentro de ella, [Amount] significa "el campo Amount de la fila actual", y _ significa "el elemento actual completo":

// These two lines do exactly the same thing
Table.SelectRows(Sales, each [Amount] > 0)
Table.SelectRows(Sales, (row) => row[Amount] > 0)

Cuando entiendes esto, casi todo el M generado se vuelve legible: "selecciona las filas de Sales donde, para cada fila, Amount es mayor que cero".

Las funciones que más vas a usar

TareaFunciónEjemplo
Filtrar filasTable.SelectRowsTable.SelectRows(T, each [Region] = "East")
Agregar una columnaTable.AddColumnTable.AddColumn(T, "Net", each [Amount] - [Cost])
Cambiar tiposTable.TransformColumnTypes{{"Amount", type number}}
Transformar valores en su lugarTable.TransformColumns{{"Name", Text.Trim}}
Conservar o quitar columnasTable.SelectColumns / Table.RemoveColumnsTable.RemoveColumns(T, {"Notes"})
Agrupar y resumirTable.GroupTotales por Region con List.Sum
Combinar dos tablasTable.NestedJoinCombinar Sales con Product por ProductID
Limpiar textoText.Trim, Text.Proper, Text.UpperText.Proper("ana lópez")
Trabajar con fechasDate.Year, Date.Month, Date.StartOfMonthDate.Year([OrderDate])

Ejemplo práctico: limpiar y enriquecer una tabla de ventas

Esta es una consulta realista que limpia una columna de texto, agrega una columna condicional, extrae el año y calcula un margen de forma segura:

let
    Source = Sales,
    CleanRegion = Table.TransformColumns(
        Source,
        {{"Region", each Text.Proper(Text.Trim(_)), type text}}
    ),
    AddSize = Table.AddColumn(
        CleanRegion,
        "Order Size",
        each if [Amount] >= 1000 then "Large"
             else if [Amount] >= 250 then "Medium"
             else "Small",
        type text
    ),
    AddYear = Table.AddColumn(AddSize, "Year", each Date.Year([OrderDate]), Int64.Type),
    SafeMargin = Table.AddColumn(
        AddYear,
        "Margin %",
        each try ([Amount] - [Cost]) / [Amount] otherwise null,
        type number
    )
in
    SafeMargin

Hay varios detalles que vale la pena aprender de este ejemplo:

  1. Text.Proper(Text.Trim(_)) se aplica a cada valor de Region: quita los espacios de más y corrige las mayúsculas, así " east" y "EAST" terminan como "East".
  2. if ... then ... else es la forma de escribir condiciones en M. A diferencia de Excel, no es una función, y el else es obligatorio.
  3. El último argumento de Table.AddColumn define el tipo de la columna, así que no necesitas un paso extra de "Tipo cambiado".
  4. try ... otherwise atrapa errores, como una división entre cero o un valor faltante, y devuelve un valor alternativo en lugar de romper la actualización.

Cómo escribir tus propias funciones

Cuando repites la misma lógica en varias consultas, conviértela en una función. Crea una Consulta en blanco, abre el Editor avanzado y escribe algo como esto:

// Query named fnAddTax
(amount as number, rate as number) as number =>
    amount * (1 + rate)

// Using it in another query
Table.AddColumn(Sales, "Amount With Tax", each fnAddTax([Amount], 0.07), type number)

La flecha => separa los parámetros del cuerpo de la función. Las funciones también son lo que Power Query crea por detrás cuando combinas todos los archivos de una carpeta.

Atajo con IA: Los asistentes de IA son muy útiles para explicar y escribir M. Pega el código del Editor avanzado y pide: "Explica esta consulta de Power Query M paso a paso en español sencillo, luego reescríbela con nombres de pasos claros y agrega una columna llamada Order Size que sea Large si Amount es al menos 1000, Medium si es al menos 250 y Small en los demás casos." Pega el resultado, haz clic en Listo y revisa cada paso antes de cargar.

Plegado de consultas: por qué importa el orden de los pasos

Cuando tu origen es una base de datos, Power Query intenta traducir tus pasos a una consulta nativa (normalmente SQL) para que la base de datos haga el trabajo. A esto se le llama plegado de consultas (query folding). Los filtros y la eliminación de columnas colocados al principio suelen plegarse; algunos pasos personalizados, como agregar un índice o ciertas funciones propias, pueden detener el plegado de todos los pasos que vienen después. En Power BI Desktop puedes hacer clic derecho en un paso y revisar Ver consulta nativa, o fijarte en los indicadores de plegado de Power Query, para saber qué se está plegando. La documentación de Microsoft Learn indica qué conectores y pasos lo admiten. Si sabes SQL, nuestra guía de SQL para Power BI explica cuándo conviene llevar la lógica al origen.

Próximos pasos

Abre una de tus consultas actuales en el Editor avanzado y simplemente léela, paso a paso, usando los patrones de este artículo. Después cambia el nombre de los pasos, agrega una columna editando el M directamente y confirma que todavía se actualiza sin errores. Cuando tus datos estén limpios, modélalos bien con nuestra guía de esquema estrella.

Mira: lecciones en video

¿Cómo utilizar LENGUAJE M de POWER QUERY?por Power Bi Training Perú · Ver en YouTube ↗
Learn Power Query's M Language in 2024por Goodly · Ver en YouTube ↗Video en inglés — activa los subtítulos traducidos en YouTube si los necesitas.

Preguntas frecuentes

¿Necesito aprender M para usar Power Query?

No. La mayoría de las transformaciones se hacen con los botones de la cinta. Saber leer y editar un poco de M te ayuda cuando necesitas corregir errores, reutilizar lógica o hacer algo que la interfaz no ofrece.

¿El lenguaje M es lo mismo que DAX?

No. M prepara y transforma los datos antes de cargarlos al modelo, mientras que DAX calcula medidas y columnas dentro del modelo. Tienen sintaxis y propósitos distintos.

¿M distingue mayúsculas y minúsculas?

Sí. Los nombres de funciones, de columnas y las comparaciones de texto distinguen mayúsculas en M, y es una causa común de errores para principiantes.

¿Qué significa 'each' en Power Query?

Es una forma corta de escribir una función de un argumento que se ejecuta una vez por fila o elemento. Dentro de ella, [Columna] se refiere a un campo de la fila actual y _ al elemento actual.

¿Aprendes con la Academia?
Marca esta lección como completada para seguir tu progreso.
Hacer el quiz