Rules / Formatting

Mark primary keys

info MARK_PRIMARY_KEYS · ported · model layer · scope: Column, CalculatedColumn, CalculatedTableColumn

What it checks

Columns on the one side of a relationship, outside date tables, that are not marked as the table's key.

Each finding names the column, as 'Product'[Product ID]. The relationship that puts it on the one side is not in the line, so look at the model view to see which fact table points at it.

Example

Fires the rule
table Sales
	column 'Product Key'
		dataType: int64
		isHidden
		summarizeBy: none
		sourceColumn: Product Key

table Product
	column 'Product ID'
		dataType: int64
		summarizeBy: none
		sourceColumn: Product ID

relationship Sales_Product
	fromColumn: Sales.'Product Key'
	toColumn: Product.'Product ID'
After the fix
table Sales
	column 'Product Key'
		dataType: int64
		isHidden
		summarizeBy: none
		sourceColumn: Product Key

table Product
	column 'Product ID'
		dataType: int64
		isKey
		summarizeBy: none
		sourceColumn: Product ID

relationship Sales_Product
	fromColumn: Sales.'Product Key'
	toColumn: Product.'Product ID'

Why it matters

The key flag declares that the column is unique, which lets the engine and client tools treat it as the identifier of the row rather than one more attribute to aggregate, and on an import model the engine enforces the uniqueness at refresh, so a duplicate key fails loudly instead of quietly doubling a total. It also documents intent: the next person reading the model can see at a glance which column defines the grain of the dimension, without tracing every relationship to work it out. Tables marked as date tables are skipped, because marking a table as a date table already sets the key on its date column.

How to fix it

Add isKey under the column in the TMDL file. Power BI Desktop has no box for the property on an ordinary table, and it keeps the value once it is in the file; the one place Desktop sets a key itself is Mark as date table, which sets it on the date column of the table you mark and takes that table out of this rule at the same time. Check that the column really is unique before you set it, because the engine takes the property at its word. Tabular Editor exposes the property in its property grid, which is a quicker way to set it across the dimensions of a large model than editing each file.

When to ignore it

A dimension you cannot yet guarantee is unique is the case to leave alone. If the table is loaded from a feed that occasionally repeats a row, the key property turns a silent double-count into a loud failure, which is the right trade once the source is under control and the wrong one while you are still chasing it. Check the distinct count against the row count before you set the property, not after. A calendar table is the other case: mark it as a date table instead, which sets the key on its date column and takes the table out of the rule.

To ignore this rule on one object, add annotation pbiplint.ignore = MARK_PRIMARY_KEYS under the object in its TMDL file. Power BI Desktop keeps the annotation. To turn the rule off for a whole project, set "MARK_PRIMARY_KEYS": "off" under rules in pbiplint.config.json.

Quirks

Ported from Microsoft's Best Practice Analyzer ruleset (opens in a new tab).

Check a model for this Improve this page (opens in a new tab)