power-query-m
Power Query M language for ETL, transformations, and connector authoring. PROACTIVELY activate for: (1) writing Power Query M code, (2) query folding and Native Query verification, (3) data transformations (merge, append, pivot, unpivot, split column), (4) custom connector development, (5) Power Query parameters and dynamic source patterns, (6) performance optimization (folding, query reduction, partition strategy), (7) Table.TransformColumns, List.Generate, Record.Field functions, (8) error handling in M (try/otherwise, error records), (9) mashup engine internals, (10) ETL pipelines from folder/SharePoint/database sources. Provides: M language reference, query-folding diagnosis, transformation recipes, dynamic source patterns, error-handling templates, and performance tuning techniques.
What this skill does
# Power Query (M Language)
## Overview
Power Query is the data transformation engine in Power BI, using the M functional language. It handles ETL (Extract, Transform, Load) from sources to the data model. Understanding query folding, step optimization, and M syntax is critical for performant data refresh.
## Query Folding
Query folding translates M steps into native source queries (SQL, OData, etc.), pushing computation to the source instead of the mashup engine.
**How to check folding:**
1. Right-click a step in Applied Steps > "View Native Query" -- if grayed out, folding broke
2. Use Query Diagnostics (Tools > Start Diagnostics) to see what queries are sent
**Steps that fold (common):**
| Operation | SQL Translation |
|-----------|----------------|
| Remove columns | SELECT (column list) |
| Filter rows | WHERE clause |
| Sort rows | ORDER BY |
| Group by | GROUP BY |
| Rename columns | Column aliases |
| Change type (basic) | CAST |
| Merge queries (database) | JOIN |
| Top N rows | TOP / LIMIT |
| Remove duplicates | DISTINCT |
**Steps that break folding:**
| Operation | Why |
|-----------|-----|
| Add custom column (complex) | M expression cannot translate to SQL |
| Pivot/Unpivot (sometimes) | Depends on source capability |
| Merge with non-foldable source | Cannot push cross-source joins |
| Table.Buffer | Explicitly materializes in memory |
| Reorder after custom step | Once broken, subsequent steps cannot fold |
| Date/time transforms (some) | Source-specific function differences |
**Golden rule:** Put foldable steps BEFORE non-foldable steps. Once folding breaks, all subsequent steps run in the mashup engine.
## M Language Essentials
### Let Expression (Query Structure)
Every Power Query query is a `let...in` expression:
```m
let
Source = Sql.Database("server", "database"),
Filtered = Table.SelectRows(Source, each [Status] = "Active"),
Renamed = Table.RenameColumns(Filtered, {{"OldName", "NewName"}}),
Typed = Table.TransformColumnTypes(Renamed, {{"Amount", type number}})
in
Typed
```
### Data Types
| M Type | Description |
|--------|-------------|
| `type text` | String/text |
| `type number` | Decimal number |
| `Int64.Type` | Whole number (64-bit integer) |
| `type date` | Date only |
| `type datetime` | Date and time |
| `type datetimezone` | Date, time, and timezone |
| `type duration` | Time duration |
| `type logical` | Boolean (true/false) |
| `type binary` | Binary data |
| `type null` | Null value |
| `Currency.Type` | Fixed decimal (4 places) |
| `Percentage.Type` | Percentage |
### Common Table Functions
```m
// Filter rows
Table.SelectRows(table, each [Column] > 100)
// Add column
Table.AddColumn(table, "NewCol", each [Col1] * [Col2], type number)
// Remove columns
Table.RemoveColumns(table, {"Col1", "Col2"})
// Select columns (keep only these)
Table.SelectColumns(table, {"Col1", "Col2", "Col3"})
// Rename columns
Table.RenameColumns(table, {{"Old1", "New1"}, {"Old2", "New2"}})
// Change types
Table.TransformColumnTypes(table, {{"Col1", type number}, {"Col2", type text}})
// Replace values
Table.ReplaceValue(table, "old", "new", Replacer.ReplaceText, {"Column"})
// Group by
Table.Group(table, {"GroupCol"}, {
{"Sum", each List.Sum([Amount]), type number},
{"Count", each Table.RowCount(_), Int64.Type}
})
// Merge (JOIN)
Table.NestedJoin(left, {"KeyCol"}, right, {"KeyCol"}, "Merged", JoinKind.LeftOuter)
// Expand merged columns
Table.ExpandTableColumn(merged, "Merged", {"Col1", "Col2"})
// Pivot
Table.Pivot(table, List.Distinct(table[PivotCol]), "PivotCol", "ValueCol")
// Unpivot
Table.UnpivotOtherColumns(table, {"KeepCol1", "KeepCol2"}, "Attribute", "Value")
// Sort
Table.Sort(table, {{"Col1", Order.Ascending}, {"Col2", Order.Descending}})
// Remove duplicates
Table.Distinct(table, {"KeyCol1", "KeyCol2"})
// Combine/Append tables
Table.Combine({table1, table2, table3})
// Buffer (force materialization)
Table.Buffer(table)
```
### List Functions
```m
// Generate a sequence
{1..100}
List.Numbers(1, 100)
List.Dates(#date(2024,1,1), 365, #duration(1,0,0,0))
// Transform
List.Transform({1,2,3}, each _ * 2)
// Filter
List.Select({1,2,3,4,5}, each _ > 3)
// Aggregate
List.Sum(list), List.Average(list), List.Min(list), List.Max(list)
// Generate with custom logic (pagination pattern)
List.Generate(
() => [Page = 0, Data = GetPage(0)],
each [Data] <> null,
each [Page = [Page] + 1, Data = GetPage([Page] + 1)],
each [Data]
)
```
## Parameters and Dynamic Sources
Create parameters for environment-specific connections:
```m
// Define parameter in Power Query UI or M:
// Name: ServerName, Type: Text, Current Value: "prod-server.database.windows.net"
// Use in query:
let
Source = Sql.Database(ServerName, DatabaseName),
...
```
**Dynamic source pattern:**
```m
let
BaseUrl = "https://api.example.com/v2/",
Endpoint = BaseUrl & "data?page=",
GetPage = (pageNum as number) =>
let
url = Endpoint & Number.ToText(pageNum),
response = Json.Document(Web.Contents(url))
in
response[results],
AllPages = List.Generate(
() => [i = 1, res = GetPage(1)],
each List.Count([res]) > 0,
each [i = [i] + 1, res = GetPage([i] + 1)],
each [res]
),
Combined = List.Combine(AllPages),
AsTable = Table.FromList(Combined, Record.FieldValues,
type table [id = Int64.Type, name = text, value = number])
in
AsTable
```
## Error Handling
```m
// Try/otherwise pattern
let
result = try SomeRiskyOperation() otherwise "default"
in
result
// Try with error record inspection
let
attempt = try Number.FromText("abc"),
output = if attempt[HasError]
then "Error: " & attempt[Error][Message]
else attempt[Value]
in
output
// Replace errors in a column
Table.ReplaceErrorValues(table, {{"Column1", null}, {"Column2", 0}})
// Remove error rows
Table.RemoveRowsWithErrors(table, {"Column1", "Column2"})
```
## Custom Connectors
Build custom Power Query connectors using the Power Query SDK:
1. Install Power Query SDK (VS Code extension)
2. Create a `.mproj` project with `DataConnector.pq` file
3. Implement the data source function with authentication
4. Package as `.mez` file
5. Deploy to `Documents\Power BI Desktop\Custom Connectors` or gateway
**Basic connector structure:**
```m
section MyConnector;
[DataSource.Kind="MyConnector", Publish="MyConnector.Publish"]
shared MyConnector.Contents = (url as text) =>
let
source = Web.Contents(url),
json = Json.Document(source)
in
json;
MyConnector = [
Authentication = [
Key = [],
OAuth = [...]
],
Label = "My Custom Connector"
];
MyConnector.Publish = [
Beta = true,
Category = "Other",
ButtonText = {"My Connector", "Connect to My Service"}
];
```
## Performance Optimization
| Technique | Impact |
|-----------|--------|
| Put foldable steps first | High -- pushes work to source |
| Remove unused columns early | High -- reduces data volume |
| Filter early, before joins | High -- reduces row count |
| Avoid Table.Buffer unless needed | Medium -- prevents unnecessary materialization |
| Use native queries when folding fails | High -- bypass mashup engine |
| Disable "Include in report refresh" for staging queries | Medium -- skips unnecessary refresh |
| Use Table.Partition for parallel loading | Medium -- parallelizes large tables |
| Set Privacy Levels correctly | Medium -- incorrect levels block folding |
## Additional Resources
### Reference Files
- **`references/m-patterns-cookbook.md`** -- Common M patterns: web API pagination, incremental load, JSON flattening, CSV handling, SharePoint folder combine
Related in Writing & Docs
jax-development
IncludedUse this skill when the user is writing, debugging, profiling, refactoring, reviewing, benchmarking, parallelising, exporting, or explaining JAX code, or when they mention JAX, jax.numpy, jit, grad, value_and_grad, vmap, scan, lax, random keys, pytrees, jax.Array, sharding, Mesh, PartitionSpec, NamedSharding, pmap, shard_map, Pallas, XLA, StableHLO, checkify, profiler, or the JAX repo. It helps turn NumPy or PyTorch-style code into pure functional JAX, fix tracer/control-flow/shape/PRNG bugs, remove recompiles and host-device syncs, choose transforms and sharding strategies, inspect jaxpr/lowering/IR, and benchmark compiled code correctly.
nature-article-writer
IncludedDrafts, rewrites, diagnostically critiques, and style-calibrates primary research manuscripts for Nature and Nature Portfolio journals. Use when the user wants a Nature-style title, summary paragraph or abstract, introduction, results, discussion, methods, figure legends, presubmission enquiry, cover letter, reviewer response, or when a scientific draft sounds generic, jargon-heavy, structurally weak, or AI-ish and needs precise, broad-reader-friendly prose without inventing data, analyses, or references. Best for primary research articles and letters rather than reviews or press releases unless explicitly adapting one.
deckrd
IncludedDocument-driven framework that derives requirements, specifications, implementation plans, and executable tasks from goals through structured AI dialogue. Use when user says "write requirements", "create spec", "plan implementation", "derive tasks", "structure this feature", "break down into tasks", or "document this module". Also use for reverse engineering existing code into docs (/deckrd rev). Do NOT use for direct code writing — use /deckrd-coder after tasks are generated. Do NOT use when the user only wants to run or fix existing code without planning.
clinical-decision-support
IncludedGenerate professional clinical decision support (CDS) documents for pharmaceutical and clinical research settings, including patient cohort analyses (biomarker-stratified with outcomes) and treatment recommendation reports (evidence-based guidelines with decision algorithms). Supports GRADE evidence grading, statistical analysis (hazard ratios, survival curves, waterfall plots), biomarker integration, and regulatory compliance. Outputs publication-ready LaTeX/PDF format optimized for drug development, clinical research, and evidence synthesis.
handling-sf-data
IncludedSalesforce data operations with 130-point scoring. Use this skill to create, update, delete, bulk import/export, generate test data, and clean up org records using sf CLI and anonymous Apex. TRIGGER when: user creates test data, performs bulk import/export, uses sf data CLI commands, needs data factory patterns for Apex tests, or needs to seed/clean records in a Salesforce org. DO NOT TRIGGER when: SOQL query writing only (use querying-soql), Apex test execution (use running-apex-tests), or metadata deployment (use deploying-metadata).
accelint-ac-to-playwright
IncludedConvert and validate acceptance criteria for Playwright test automation. Use when user asks to (1) review/evaluate/check if AC are ready for automation, (2) assess if AC can be converted as-is, (3) validate AC quality for Playwright, (4) turn AC into tests, (5) generate tests from acceptance criteria, (6) convert .md bullets or .feature Gherkin files to Playwright specs, (7) create test automation from requirements. Handles both bullet-style markdown and Gherkin syntax with JSON test plan generation and validation.