diff --git a/content/kb/bpa-avoid-invalid-characters-descriptions.md b/content/kb/bpa-avoid-invalid-characters-descriptions.md new file mode 100644 index 00000000..abd0bd25 --- /dev/null +++ b/content/kb/bpa-avoid-invalid-characters-descriptions.md @@ -0,0 +1,123 @@ +--- +uid: kb.bpa-avoid-invalid-characters-descriptions +title: Avoid Invalid Characters in Descriptions +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule preventing display and deployment issues by identifying control characters in object descriptions. +--- + +# Avoid Invalid Characters in Descriptions + +## Overview + +This best practice rule identifies objects whose descriptions contain invalid control characters (non-printable characters excluding standard whitespace). These characters can cause display problems, metadata corruption, and deployment failures. + +- Category: Error Prevention +- Severity: High (3) + +## Applies To + +- Tables +- Measures +- Hierarchies +- Levels +- Perspectives +- Partitions +- Data Columns +- Calculated Columns +- Calculated Table Columns +- KPIs +- Model Roles +- Calculation Groups +- Calculation Items + +## Why This Matters + +Control characters in descriptions cause various issues: + +- **Display corruption**: Tooltips and documentation panels may show garbled text +- **Metadata problems**: TMSL/XMLA export may produce invalid XML +- **Deployment failures**: Power BI Service or Analysis Services may reject the model +- **Documentation issues**: Generated documentation may break formatting +- **Encoding errors**: Cross-platform synchronization problems +- **User confusion**: Invisible characters create confusing or corrupted descriptions + +Standard whitespace (spaces, newlines, tabs) is acceptable, but non-printable control characters should be removed. + +## When This Rule Triggers + +The rule triggers when an object's description contains control characters that are not standard whitespace: + +```csharp +Description.ToCharArray().Any(char.IsControl(it) and !char.IsWhiteSpace(it)) +``` + +This detects problematic characters while allowing legitimate whitespace formatting. + +## How to Fix + +### Automatic Fix + +This rule includes an automatic fix that replaces invalid characters with spaces: + +```csharp +Description = string.Concat( + it.Description.ToCharArray().Select( + c => (char.IsControl(c) && !char.IsWhiteSpace(c)) ? ' ' : c + ) +) +``` + +To apply: +1. In the **Best Practice Analyzer** select flagged objects +3. Click **Apply Fix** + +### Manual Fix + +1. In **TOM Explorer**, select the object +2. In **Properties** pane, locate the **Description** field +3. Edit the description to remove invalid characters +4. Save changes + +## Common Causes + +### Cause 1: Copy/Paste from Rich Text + +Copying descriptions from Word documents, web pages, or emails can introduce hidden formatting characters. + +### Cause 2: Automated Documentation Generation + +Scripts generating descriptions may include control characters from source systems. + +### Cause 3: Data Import from External Sources + +Importing metadata that contains encoding artifacts or control codes. + +## Example + +### Before Fix + +``` +Measure: [Total Revenue] +Description: "Calculates\x00total\x0Brevenue" (contains NULL and vertical tab) +``` + +Tooltip displays: "Calculates□total□revenue" (with visible corruption) + +### After Fix + +``` +Measure: [Total Revenue] +Description: "Calculates total revenue" (control characters replaced with spaces) +``` + +Tooltip displays correctly: "Calculates total revenue" + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +## Related Rules + +- [Avoid Invalid Characters in Names](xref:kb.bpa-avoid-invalid-characters-names) - Similar validation for object names +- [Visible Objects Should Have Descriptions](xref:kb.bpa-visible-objects-no-description) - Ensuring descriptions exist diff --git a/content/kb/bpa-avoid-invalid-characters-names.md b/content/kb/bpa-avoid-invalid-characters-names.md new file mode 100644 index 00000000..be43497c --- /dev/null +++ b/content/kb/bpa-avoid-invalid-characters-names.md @@ -0,0 +1,125 @@ +--- +uid: kb.bpa-avoid-invalid-characters-names +title: Avoid Invalid Characters in Object Names +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule preventing deployment errors by identifying control characters in object names. +--- + +# Avoid Invalid Characters in Object Names + +## Overview + +This best practice rule identifies objects whose names contain invalid control characters (non-printable characters excluding standard whitespace). These characters can cause deployment failures, rendering issues, and data corruption. + +- Category: Error Prevention +- Severity: High (3) + +## Applies To + +- Tables +- Measures +- Hierarchies +- Levels +- Perspectives +- Partitions +- Data Columns +- Calculated Columns +- Calculated Table Columns +- KPIs +- Model Roles +- Calculation Groups +- Calculation Items + +## Why This Matters + +Control characters in object names cause serious issues: + +- **Deployment failures**: Power BI Service and Analysis Services may reject models with invalid characters +- **Rendering problems**: Client tools may display garbled or invisible names +- **DAX parsing errors**: Invalid characters can break DAX expressions referencing the object +- **XML corruption**: Model metadata (TMSL/XMLA) may become malformed +- **Copy/paste issues**: Names may not transfer correctly between applications +- **Encoding problems**: Cross-platform compatibility issues + +Standard whitespace (spaces, newlines, carriage returns) is allowed, but control characters should be removed. + +## When This Rule Triggers + +The rule triggers when an object's name contains control characters that are not standard whitespace: + +```csharp +Name.ToCharArray().Any(char.IsControl(it) and !char.IsWhiteSpace(it)) +``` + +This detects problematic characters while allowing legitimate whitespace formatting. + +## How to Fix + +### Automatic Fix + +This rule includes an automatic fix that replaces invalid characters with spaces: + +```csharp +Name = string.Concat( + it.Name.ToCharArray().Select( + c => (char.IsControl(c) && !char.IsWhiteSpace(c)) ? ' ' : c + ) +) +``` + +To apply: +1. In the **Best Practice Analyzer** select flagged objects +2. Click **Apply Fix** + +### Manual Fix + +1. In **TOM Explorer**, select the object +2. In **Properties** pane, locate the **Name** field +3. Edit the name to remove invalid characters +4. Save changes + +## Common Causes + +### Cause 1: Copy/Paste from Rich Text + +Copying names from Word documents, web pages, or emails can introduce hidden formatting characters. + +### Cause 2: Automated Name Generation + +Scripts generating names may include control characters from source systems. + +### Cause 3: Data Import from External Sources + +Importing metadata that contains encoding artifacts or control codes. + +## Example + +### Before Fix + +``` +Measure Name: "Total\x00Sales" (contains NULL character) +``` + +Deployment fails with "Invalid character in object name" + +### After Fix + +``` +Measure Name: "Total Sales" (NULL replaced with space) +``` + +Deploys successfully and displays correctly in all tools. + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +## Related Rules + +- [Avoid Invalid Characters in Descriptions](xref:kb.bpa-avoid-invalid-characters-descriptions) - Similar validation for description properties +- [Trim Object Names](xref:kb.bpa-trim-object-names) - Removing leading/trailing spaces + +## Learn More + +- [DAX Naming Rules](https://learn.microsoft.com/dax/dax-syntax-reference) diff --git a/content/kb/bpa-avoid-provider-partitions-structured.md b/content/kb/bpa-avoid-provider-partitions-structured.md new file mode 100644 index 00000000..c0f3a440 --- /dev/null +++ b/content/kb/bpa-avoid-provider-partitions-structured.md @@ -0,0 +1,96 @@ +--- +uid: kb.bpa-avoid-provider-partitions-structured +title: Avoid Provider Partitions with Structured Data Sources +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule preventing deployment errors by identifying legacy provider partitions used with structured data sources in Power BI. +--- + +# Avoid Provider Partitions with Structured Data Sources + +## Overview + +This best practice rule identifies partitions that use legacy provider-based queries (SourceType = Query) with structured data sources in Power BI models. This combination is not supported in Power BI Service and will cause deployment failures. + +- Category: Error Prevention + +- Severity: Medium (2) + +## Applies To + +- Partitions + +## Why This Matters + +Power BI Service requires structured data sources to use Power Query (M) partitions rather than legacy provider partitions. Using provider partitions with structured data sources causes: + +- **Deployment failures**: Models fail to publish to Power BI Service +- **Refresh errors**: Scheduled refresh operations fail in the service +- **Compatibility issues**: The model cannot be shared or deployed properly +- **Migration blockers**: Prevents moving from Analysis Services to Power BI + +## When This Rule Triggers + +The rule triggers when a partition meets all these conditions: + +1. `SourceType = "Query"` (legacy provider partition) +2. `DataSource.Type = "Structured"` (Power Query/M data source) +3. `Model.Database.CompatibilityMode != "AnalysisServices"` (Power BI or Azure AS) + +This combination indicates a structural mismatch that Power BI cannot process. + +## How to Fix + +### Manual Fix + +1. In **TOM Explorer**, select the affected partition +2. In **Properties** pane, note the existing query +3. Create a new **Power Query** partition with M expression +4. Delete the old provider partition after verifying the new one works + +## Common Causes + +### Cause 1: Migration from Analysis Services + +Models migrated from SQL Server Analysis Services retain legacy provider partitions. + +### Cause 2: Mixed Partition Types + +Mixing partition types during model development creates incompatible configurations. + +## Example + +### Before Fix + +``` +Partition: Sales_Partition + SourceType: Query + Query: SELECT * FROM Sales + DataSource: PowerQuerySource (Type: Structured) +``` + +**Error**: Deployment fails to Power BI Service + +### After Fix + +``` +Partition: Sales_Partition + SourceType: M + Expression: + let + Source = Sql.Database("server", "database"), + Sales = Source{[Schema="dbo",Item="Sales"]}[Data] + in + Sales + DataSource: PowerQuerySource (Type: Structured) +``` + +**Result**: Deploys successfully to Power BI Service + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher when deployed to Power BI or Azure Analysis Services. + +## Related Rules + +- [Data Column Must Have Source](xref:kb.bpa-data-column-source) - Ensuring column source mappings diff --git a/content/kb/bpa-calculation-groups-no-items.md b/content/kb/bpa-calculation-groups-no-items.md new file mode 100644 index 00000000..486293ab --- /dev/null +++ b/content/kb/bpa-calculation-groups-no-items.md @@ -0,0 +1,102 @@ +--- +uid: kb.bpa-calculation-groups-no-items +title: Calculation Groups Should Contain Items +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule identifying calculation groups without calculation items that should be populated or removed. +--- + +# Calculation Groups Should Contain Items + +## Overview + +This best practice rule identifies calculation groups that contain no calculation items. Empty calculation groups serve no purpose and should be populated or removed. + +- Category: Maintenance +- Severity: Medium (2) + +## Applies To + +- Calculation Groups + +## Why This Matters + +- **Deployment errors**: Empty groups may fail validation in Power BI Service +- **Model errors**: Can cause unexpected behavior in DAX calculations +- **Developer confusion**: Team members waste time investigating incomplete structures +- **Performance overhead**: Engine processes unnecessary metadata + +## When This Rule Triggers + +The rule triggers when a calculation group has zero calculation items: + +```csharp +CalculationItems.Count == 0 +``` + +## How to Fix + +### Option 1: Add Calculation Items + +If the calculation group has a valid business purpose: + +1. In **TOM Explorer**, expand the calculation group table +2. Expand the **Calculation Group** column +3. Right-click and select **Add Calculation Item** +4. Define the calculation item expression + +### Option 2: Delete the Calculation Group + +If no longer needed: + +1. In **TOM Explorer**, locate the calculation group table +2. Right-click the table +3. Select **Delete** + +## Common Causes + +### Cause 1: Incomplete Development + +Calculation group created during planning but not yet implemented. + +### Cause 2: Migration from Other Models + +Calculation group structure copied without items. + +### Cause 3: Refactoring + +All calculation items moved to a different calculation group. + +## Example + +### Before Fix + +``` +Calculation Group: Time Intelligence + Items: (none) ← Problem +``` + +### After Fix + +``` +Calculation Group: Time Intelligence + Items: + - Current Period: SELECTEDMEASURE() + - Year-to-Date: CALCULATE(SELECTEDMEASURE(), DATESYTD('Date'[Date])) + - Prior Year: CALCULATE(SELECTEDMEASURE(), SAMEPERIODLASTYEAR('Date'[Date])) +``` + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +## Related Rules + +- [Perspectives Should Contain Objects](xref:kb.bpa-perspectives-no-objects) - Similar rule for empty perspectives +- [Expression Required](xref:kb.bpa-expression-required) - Ensuring calculation items have expressions + +## Learn More + +- [Calculation Groups in Tabular Models](https://learn.microsoft.com/analysis-services/tabular-models/calculation-groups) +- [Creating Calculation Groups](https://www.sqlbi.com/articles/introducing-calculation-groups/) +- [Calculation Group Patterns](https://www.sqlbi.com/calculation-groups/) diff --git a/content/kb/bpa-data-column-source.md b/content/kb/bpa-data-column-source.md new file mode 100644 index 00000000..9bc9e55b --- /dev/null +++ b/content/kb/bpa-data-column-source.md @@ -0,0 +1,93 @@ +--- +uid: kb.bpa-data-column-source +title: Data Column Must Have Source +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule ensuring data columns have a valid source column mapping to prevent refresh errors. +--- + +# Data Column Must Have Source + +## Overview + +This best practice rule identifies data columns that lack a valid `SourceColumn` property. Every data column must reference a source column from the underlying data source to function correctly during refresh. + +- Category: Error Prevention +- Severity: High (3) + +## Applies To + +- Data Columns + +## Why This Matters + +- **Refresh failures**: Data refresh operations fail with column not found errors +- **Deployment issues**: Model validation fails in Power BI Service or Analysis Services +- **Data integrity**: Column remains empty or contains stale data +- **Broken dependencies**: Measures and relationships produce incorrect results + +## When This Rule Triggers + +The rule triggers when a data column has: + +```csharp +string.IsNullOrWhitespace(SourceColumn) +``` + +## How to Fix + +### Manual Fix + +1. In **TOM Explorer**, locate the flagged data column +2. In **Properties** pane, find the `Source Column` property +3. Enter the correct source column name from your data source query +4. Verify the mapping matches the partition query + +The source column name must exactly match: +- For Power Query: Column name in M expression output +- For SQL: Column name or alias in SELECT statement +- For Direct Lake: Column name in Delta Lake table + +## Common Causes + +### Cause 1: Renamed Source Column + +Source query was modified and column renamed. + +### Cause 2: Manual Column Creation + +Column created manually without specifying source. + +### Cause 3: Copy/Paste Corruption + +Columns copied from another table without preserving metadata. + +## Example + +### Before Fix + +``` +Table: Sales +Column: ProductName (DataColumn) + SourceColumn: [empty] +``` + +Result: Refresh fails with "Column 'ProductName' not found in source query" + +### After Fix + +``` +Table: Sales +Column: ProductName (DataColumn) + SourceColumn: ProductName +``` + +Result: Column populates correctly during refresh + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +## Related Rules + +- [Expression Required for Calculated Objects](xref:kb.bpa-expression-required) - Ensuring calculated columns have expressions diff --git a/content/kb/bpa-date-table-exists.md b/content/kb/bpa-date-table-exists.md new file mode 100644 index 00000000..97931246 --- /dev/null +++ b/content/kb/bpa-date-table-exists.md @@ -0,0 +1,113 @@ +--- +uid: kb.bpa-date-table-exists +title: Date Table Should Exist +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule ensuring your model includes a dedicated date table for proper time intelligence functionality. +--- + +# Date Table Should Exist + +## Overview + +This best practice rule verifies that your tabular model contains at least one properly configured date table. Date tables are essential for time intelligence calculations and ensuring consistent date-based filtering across your model. + +- Category: Performance + +- Severity: Medium (2) + +## Applies To + +- Model + +## Why This Matters + +A dedicated date table is essential because it: + +- **Enables time intelligence**: Functions like `DATESYTD`, `SAMEPERIODLASTYEAR`, and `TOTALYTD` require a date table +- **Ensures consistent filtering**: Provides single source of truth for date attributes +- **Improves performance**: Establishes proper calendar relationships +- **Supports custom calendars**: Enables fiscal year calculations and custom hierarchies + +Without a properly marked date table, many DAX time intelligence functions will fail or produce incorrect results. + +## When This Rule Triggers + +The rule triggers when **all** tables in your model meet the following conditions: + +1. No table has any calendars defined (`Calendars.Count = 0`) +2. No table contains a column marked as a key with `DataType = DateTime` +3. No table has `DataCategory = "Time"` + +This indicates that the model lacks a proper date dimension. + +## How to Fix + +### Option 1: Create a Date Table Using DAX + +Add a calculated table with a complete date range: + +```dax +DateTable = +ADDCOLUMNS ( + CALENDAR (DATE(2020, 1, 1), DATE(2030, 12, 31)), + "Year", YEAR([Date]), + "Quarter", "Q" & FORMAT([Date], "Q"), + "Month", FORMAT([Date], "MMMM"), + "MonthNumber", MONTH([Date]), + "Day", DAY([Date]), + "WeekDay", FORMAT([Date], "dddd") +) +``` + +### Option 2: Import from Data Source + +Create a date dimension table in your data warehouse or data source and import it into the model. + +### Mark as Date Table + +After creating the table: + +1. Select the date table in the **TOM Explorer** +2. Right-click and choose **Mark as Date Table** +3. Select the date column as the key column +4. Create relationships between the date table and your fact tables + +### Set Calendar Metadata + +Alternatively, configure the calendar metadata: + +1. Select the date table +2. In the **Properties** pane, expand **Calendars** +3. Add a new calendar and configure the date column reference + +## Example + +A typical date table structure: + +| Date | Year | Quarter | Month | MonthNumber | Day | +|------|------|---------|-------|-------------|-----| +| 2025-01-01 | 2025 | Q1 | January | 1 | 1 | +| 2025-01-02 | 2025 | Q1 | January | 1 | 2 | +| ... | ... | ... | ... | ... | ... | + +Once created, establish relationships: + +``` +'DateTable'[Date] (1) -> (*) 'Sales'[OrderDate] +'DateTable'[Date] (1) -> (*) 'Orders'[ShipDate] +``` + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +## Related Rules + +- [Remove Auto Date Table](xref:kb.bpa-remove-auto-date-table) - Removing automatic date tables that duplicate functionality + +## Learn More + +- [Create Date Tables in Power BI](https://learn.microsoft.com/power-bi/guidance/model-date-tables) +- [Time Intelligence Functions in DAX](https://learn.microsoft.com/dax/time-intelligence-functions-dax) +- [Mark as Date Table](https://learn.microsoft.com/power-bi/transform-model/desktop-date-tables) diff --git a/content/kb/bpa-do-not-summarize-numeric.md b/content/kb/bpa-do-not-summarize-numeric.md new file mode 100644 index 00000000..f29ab822 --- /dev/null +++ b/content/kb/bpa-do-not-summarize-numeric.md @@ -0,0 +1,119 @@ +--- +uid: kb.bpa-do-not-summarize-numeric +title: Set SummarizeBy to None for Numeric Columns +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule preventing incorrect default aggregations on numeric columns that should not be summed. +--- + +# Set SummarizeBy to None for Numeric Columns + +## Overview + +This best practice rule identifies visible numeric columns (Int64, Decimal, Double) that have a default aggregation behavior (`SummarizeBy`) other than `None`. Most numeric columns should not be automatically aggregated, as summing values like IDs, quantities in non-additive contexts, or codes produces meaningless results. + +- Category: Formatting + +- Severity: High (3) + +## Applies To + +- Data Columns +- Calculated Columns +- Calculated Table Columns + +## Why This Matters + +Default aggregation on inappropriate columns causes serious issues: + +- **Incorrect analysis**: Users get meaningless totals (sum of CustomerIDs, etc.) +- **Misleading dashboards**: Visualizations show wrong numbers by default +- **User confusion**: Users must manually change aggregation for every visual +- **Wrong decisions**: Business decisions based on incorrect automatic aggregations +- **Data credibility**: Users lose trust in the model and data + +Common columns that should NOT be aggregated include IDs, keys, codes, ratios, percentages, and non-additive quantities. + +## When This Rule Triggers + +The rule triggers when a column meets ALL these conditions: + +```csharp +(DataType = "Int64" or DataType="Decimal" or DataType="Double") +and +SummarizeBy <> "None" +and not (IsHidden or Table.IsHidden) +``` + +In other words: visible numeric columns that have a summarization behavior other than "None". + +## How to Fix + +### Automatic Fix + +This rule includes an automatic fix: + +```csharp +SummarizeBy = AggregateFunction.None +``` + +To apply: +1. In the **Best Practice Analyzer** select flagged objects +3. Click **Apply Fix** + +### Manual Fix + +1. In **TOM Explorer**, locate the column +2. In **Properties** pane, find **Summarize By** +3. Change from **Sum**, **Average**, **Min**, **Max**, **Count**, or **DistinctCount** to **None** +4. Save changes + +## Common Causes + +### Cause 1: Default Import Behavior + +Numeric columns default to Sum aggregation during import. + +### Cause 2: Lack of Column Review + +Models deployed without reviewing column aggregation settings. + +### Cause 3: ID Columns Not Hidden + +Numeric ID columns remain visible with default Sum aggregation. + +## Example + +### Before Fix + +``` +Column: CustomerID + DataType: Int64 + SummarizeBy: Sum +``` + +**Result**: Visual shows "Sum of CustomerID: 12,456,789" (meaningless number) + +### After Fix + +``` +Column: CustomerID + DataType: Int64 + SummarizeBy: None +``` + +**Result**: Visual requires explicit aggregation or shows individual Customer IDs + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +## Related Rules + +- [Hide Foreign Keys](xref:kb.bpa-hide-foreign-keys) - Related column hygiene rule +- [Format String for Columns](xref:kb.bpa-format-string-columns) - Column formatting + +## Learn More + +- [Column Properties](https://learn.microsoft.com/analysis-services/tabular-models/column-properties-ssas-tabular) +- [When to Use Measures vs. Calculated Columns](https://learn.microsoft.com/power-bi/transform-model/desktop-tutorial-create-measures) diff --git a/content/kb/bpa-expression-required.md b/content/kb/bpa-expression-required.md new file mode 100644 index 00000000..5dd370ef --- /dev/null +++ b/content/kb/bpa-expression-required.md @@ -0,0 +1,98 @@ +--- +uid: kb.bpa-expression-required +title: Expression Required for Calculated Objects +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule ensuring measures, calculated columns, and calculation items have valid DAX expressions. +--- + +# Expression Required for Calculated Objects + +## Overview + +This best practice rule identifies measures, calculated columns, and calculation items that lack a DAX expression. All calculated objects must have a valid, non-empty expression to function correctly and prevent errors during model deployment and query execution. + +- Category: Error Prevention + +- Severity: High (3) + +## Applies To + +- Measures +- Calculated Columns +- Calculation Items + +## Why This Matters + +Calculated objects without expressions will cause critical failures: + +- **Model validation errors**: The model will fail validation when saved or deployed +- **Query failures**: Attempts to use the object in queries will generate errors +- **Broken dependencies**: Other measures or calculations referencing the object will fail +- **Deployment blockers**: Power BI Service and Analysis Services will reject models with empty expressions +- **Unexpected behavior**: The object may appear in field lists but produce no results + +Empty expressions typically result from incomplete object creation, copy/paste operations, or programmatic model generation errors. + +## When This Rule Triggers + +The rule triggers when any of the following objects have an empty or whitespace-only expression: + +```csharp +string.IsNullOrWhiteSpace(Expression) +``` + +This applies to: +- **Measures**: Should contain a DAX aggregation or calculation +- **Calculated Columns**: Should contain a row-context DAX expression +- **Calculation Items**: Should contain a DAX expression modifying the base measure + +## How to Fix + +### Manual Fix + +1. In **TOM Explorer**, locate the measure, calculated column, or calculation item +2. Double-click to open the **DAX Editor** +3. Enter a valid DAX expression +4. Validate the syntax and save + +## Common Causes + +### Cause 1: Incomplete Creation + +Object was created intending to define it later but was forgotten. + +### Cause 2: Template-Based Creation + +Scripts or templates created objects without expressions. + +### Cause 3: Failed Copy Operation + +Copied an object but the expression didn't transfer. + +## Example + +### Before Fix + +``` +Measure: [Total Revenue] + Expression: [empty] + FormatString: $#,0.00 +``` + +**Error when queried**: "The expression for measure '[Total Revenue]' is not valid." + +### After Fix + +``` +Measure: [Total Revenue] + Expression: SUM('Sales'[Revenue]) + FormatString: $#,0.00 +``` + +**Result**: Measure functions correctly and returns aggregated revenue. + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + diff --git a/content/kb/bpa-format-string-columns.md b/content/kb/bpa-format-string-columns.md new file mode 100644 index 00000000..69428333 --- /dev/null +++ b/content/kb/bpa-format-string-columns.md @@ -0,0 +1,84 @@ +--- +uid: kb.bpa-format-string-columns +title: Provide Format String for Numeric and Date Columns +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule ensuring visible numeric and date columns have appropriate format strings for consistent display. +--- + +# Provide Format String for Numeric and Date Columns + +## Overview + +This best practice rule identifies visible columns with numeric or date data types that lack format strings. Format strings ensure consistent, professional data display across all client tools. + +- Category: Formatting + +- Severity: Medium (2) + +## Applies To + +- Data Columns +- Calculated Columns +- Calculated Table Columns + +## Why This Matters + +Columns without format strings display inconsistently: + +- **Unprofessional appearance**: Raw numbers like 1234567.89 instead of $1,234,567.89 +- **User confusion**: Users can't tell if values are currency, percentages, or plain numbers +- **Inconsistent formatting**: Different visuals may show different formats +- **Manual formatting burden**: Users must format every visual individually +- **Date ambiguity**: Dates show timestamps when only dates are needed + +## When This Rule Triggers + +```csharp +IsVisible +and string.IsNullOrWhitespace(FormatString) +and (DataType = "Int64" or DataType = "DateTime" or DataType = "Double" or DataType = "Decimal") +``` + +## How to Fix + +### Manual Fix + +1. In **TOM Explorer**, select the column +2. In **Properties** pane, locate the **Format String** field +3. Choose from standard formats or enter custom format +4. Save changes + +## Common Causes + +### Cause 1: Missing Format Definition + +Columns do not have a format string when imported. + +## Example + +### Before Fix + +``` +Column: SalesAmount +Format String: (empty) +``` + +**Display**: 1234567.89 (hard to read, no currency symbol) + +### After Fix + +``` +Column: SalesAmount +Format String: "$#,0.00" +``` + +**Display**: $1,234,567.89 (clear, professional formatting) + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +## Related Rules + +- [Provide Format String for Measures](xref:kb.bpa-format-string-measures) - Similar validation for measures diff --git a/content/kb/bpa-format-string-measures.md b/content/kb/bpa-format-string-measures.md new file mode 100644 index 00000000..7b4cf80f --- /dev/null +++ b/content/kb/bpa-format-string-measures.md @@ -0,0 +1,105 @@ +--- +uid: kb.bpa-format-string-measures +title: Provide Format String for Measures +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule ensuring visible measures have appropriate format strings for consistent display. +--- + +# Provide Format String for Measures + +## Overview + +This best practice rule identifies visible measures with numeric or date data types that lack format strings. All measures should have explicit format strings for professional, consistent display. + +- Category: Formatting + +- Severity: Medium (2) + +## Applies To + +- Measures + +## Why This Matters + +Measures without format strings display raw values, causing user confusion and inconsistent reporting. Format strings ensure: + +- **Professional appearance**: Values display with appropriate currency, percentage, or number formatting +- **Consistency**: All reports show values in the same format +- **User confidence**: Properly formatted numbers are easier to read and interpret +- **Business alignment**: Formatting matches corporate standards + +## When This Rule Triggers + +```csharp +IsVisible +and string.IsNullOrWhitespace(FormatString) +and (DataType = "Int64" or DataType = "DateTime" or DataType = "Double" or DataType = "Decimal") +``` + +## How to Fix + +### Manual Fix + +1. In **TOM Explorer**, select the measure +2. In **Properties** pane, locate the **Format String** field +3. Enter an appropriate format string based on what the measure calculates +4. Save changes + +### Common Format Patterns + +```dax +Total Revenue = +SUM('Sales'[Amount]) +// Format String: "$#,0" + +Average Price = +AVERAGE('Sales'[UnitPrice]) +// Format String: "$#,0.00" + +YoY Growth = +DIVIDE([This Year] - [Last Year], [Last Year], 0) +// Format String: "0.0%" + +Order Count = +COUNTROWS('Orders') +// Format String: "#,0" +``` + +## Common Causes + +### Cause 1: Missing Format Definition + +When creating a new measure the defualt state is to not have any format string set. + +### Cause 2: Copy/Paste from Calculated Columns + +Copying measures from columns that don't require format strings. + +## Example + +### Before Fix + +```dax +Total Revenue = SUM('Sales'[Amount]) +// No Format String +``` + +**Display**: 1234567.89 (hard to read, no currency symbol) + +### After Fix + +```dax +Total Revenue = SUM('Sales'[Amount]) +// Format String: "$#,0" +``` + +**Display**: $1,234,568 (clear, professional formatting) + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +## Related Rules + +- [Format String for Columns](xref:kb.bpa-format-string-columns) - Similar validation for columns diff --git a/content/kb/bpa-hide-foreign-keys.md b/content/kb/bpa-hide-foreign-keys.md new file mode 100644 index 00000000..62717d39 --- /dev/null +++ b/content/kb/bpa-hide-foreign-keys.md @@ -0,0 +1,118 @@ +--- +uid: kb.bpa-hide-foreign-keys +title: Hide Foreign Key Columns +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule for hiding foreign key columns to simplify the model for end users. +--- + +# Hide Foreign Key Columns + +## Overview + +This best practice rule identifies foreign key columns (many-side of relationships) that are visible to end users. Foreign keys should be hidden because they serve only as relationship connectors and provide no analytical value when displayed. + +- Category: Formatting + +- Severity: Medium (2) + +## Applies To + +- Data Columns +- Calculated Columns +- Calculated Table Columns + +## Why This Matters + +Visible foreign key columns create unnecessary clutter: + +- **User confusion**: Foreign keys look like useful data but duplicate dimension attributes +- **Redundant fields**: Users see both the key and the related dimension attributes +- **Larger field lists**: More objects to scroll through finding relevant fields +- **Incorrect usage**: Users may group by keys instead of proper dimension attributes +- **Poor visualizations**: Charts showing key values instead of descriptive names + +Foreign keys exist only to create relationships between tables. Once relationships are established, users should work with dimension attributes, not the foreign keys themselves. + +## When This Rule Triggers + +The rule triggers when a column is: + +1. Used as the "from" column in a relationship (many-side) +2. The relationship has many cardinality on the from-side +3. The column is visible (`IsHidden = false`) + +```csharp +UsedInRelationships.Any(FromColumn.Name == current.Name and FromCardinality == "Many") +and +IsHidden == false +``` + +## How to Fix + +### Automatic Fix + +This rule includes an automatic fix: + +```csharp +IsHidden = true +``` + +To apply: +1. In the **Best Practice Analyzer** select flagged foreign key columns +2. Click **Apply Fix** + +### Manual Fix + +1. In **TOM Explorer**, locate the foreign key column +2. In **Properties** pane, set **IsHidden** to **true** +3. Save changes + +## Common Causes + +### Cause 1: Incomplete Model Setup + +Foreign keys remain visible after creating relationships. + +### Cause 2: Bulk Import + +Tables imported without post-processing to hide foreign keys. + +### Cause 3: Legacy Models + +Older models where foreign key hiding wasn't enforced. + +## Example + +### Before Fix + +``` +Sales Table Fields (visible): + - OrderDate + - CustomerKey ← Foreign key (should be hidden) + - ProductKey ← Foreign key (should be hidden) + - SalesAmount + - Quantity +``` + +**User experience**: Field list is cluttered. Users might mistakenly use `Sales[CustomerKey]` instead of `Customer[CustomerName]`. + +### After Fix + +``` +Sales Table Fields (visible): + - OrderDate + - SalesAmount + - Quantity +``` + +**User experience**: Clean field list. Users naturally use dimension attributes, relationship filtering works automatically. + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +## Related Rules + +- [Set SummarizeBy to None for Numeric Columns](xref:kb.bpa-do-not-summarize-numeric) - Related column configuration +- [Format String for Columns](xref:kb.bpa-format-string-columns) - Column display settings diff --git a/content/kb/bpa-many-to-many-single-direction.md b/content/kb/bpa-many-to-many-single-direction.md new file mode 100644 index 00000000..fdcadb21 --- /dev/null +++ b/content/kb/bpa-many-to-many-single-direction.md @@ -0,0 +1,115 @@ +--- +uid: kb.bpa-many-to-many-single-direction +title: Many-to-Many Relationships Should Use Single Direction +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule to avoid performance issues by using single-direction filtering on many-to-many relationships. +--- + +# Many-to-Many Relationships Should Use Single Direction + +## Overview + +This best practice rule identifies many-to-many relationships that use bidirectional cross-filtering. Many-to-many relationships with both-directions filtering cause significant performance degradation. + +- Category: Performance +- Severity: Medium (2) + +## Applies To + +- Relationships + +## Why This Matters + +- **Severe performance impact**: Engine must evaluate filters in both directions +- **Memory consumption**: Additional filter contexts maintained +- **Ambiguous filter paths**: Multiple routes produce unexpected results +- **Complex DAX logic**: Debugging filter context becomes difficult +- **Risk circular dependencies**: Can lead to infinite evaluation loops + +## When This Rule Triggers + +The rule triggers when a relationship meets all conditions: + +1. `FromCardinality = "Many"` +2. `ToCardinality = "Many"` +3. `CrossFilteringBehavior = "BothDirections"` + +## How to Fix + +### Manual Fix + +1. In **TOM Explorer**, locate the flagged relationship +2. In **Properties** pane, find `Cross Filter Direction` +3. Change from **Both** to **Single** + +Choose direction based on typical filter flow: +- From dimension to fact +- From lookup to data table + +When opposite-direction filtering is needed, handle explicitly in measures: + +```dax +SalesWithCrossFilter = +CALCULATE( + SUM('Sales'[Amount]), + CROSSFILTER('BridgeTable'[Key], 'DimensionTable'[Key], Both) +) +``` + +## Common Causes + +### Cause 1: Default Both-Direction Setting + +Model designer applied bidirectional filtering by default. + +### Cause 2: Misunderstood Requirements + +Believed both-direction filtering was necessary for all scenarios. + +### Cause 3: Quick Fix Approach + +Used both-direction filtering to solve a specific problem without considering performance. + +## Example + +### Before Fix + +``` +'Sales' (Many) <--> (Many) 'ProductBridge' +Cross Filter Direction: Both ← Problem +``` + +### After Fix + +``` +'Sales' (Many) --> (Many) 'ProductBridge' +Cross Filter Direction: Single +``` + +When Products need to filter Sales, use DAX: + +```dax +SalesForSelectedProducts = +VAR SelectedProducts = VALUES('Products'[ProductKey]) +RETURN +CALCULATE( + SUM('Sales'[Amount]), + TREATAS(SelectedProducts, 'ProductBridge'[ProductKey]) +) +``` + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +## Related Rules + +- [Relationship Data Types Must Match](xref:kb.bpa-relationship-same-datatype) - Ensuring relationship integrity + +## Learn More + +- [Many-to-Many Relationships in Power BI](https://learn.microsoft.com/power-bi/transform-model/desktop-many-to-many-relationships) +- [Relationship Cross-Filtering](https://learn.microsoft.com/power-bi/transform-model/desktop-relationships-understand) +- [DAX CROSSFILTER Function](https://dax.guide/crossfilter/) +- [DAX TREATAS Function](https://dax.guide/treatas) diff --git a/content/kb/bpa-perspectives-no-objects.md b/content/kb/bpa-perspectives-no-objects.md new file mode 100644 index 00000000..13772524 --- /dev/null +++ b/content/kb/bpa-perspectives-no-objects.md @@ -0,0 +1,89 @@ +--- +uid: kb.bpa-perspectives-no-objects +title: Perspectives Should Contain Objects +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule for removing empty perspectives that contain no visible objects. +--- + +# Perspectives Should Contain Objects + +## Overview + +This best practice rule identifies perspectives that don't contain any visible tables. Empty perspectives serve no purpose and should be removed. + +- Category: Maintenance +- Severity: Low (1) + +## Applies To + +- Perspectives + +## Why This Matters + +- **User confusion**: Empty perspectives appear in client tools but show no data + +## When This Rule Triggers + +The rule triggers when a perspective has no visible tables: + +```csharp +Model.Tables.Any(InPerspective[current.Name]) == false +``` + +## How to Fix + +### Automatic Fix + +This rule includes an automatic fix that deletes the empty perspective: + +```csharp +Delete() +``` + +To apply: +1. Run the **Best Practice Analyzer** +2. Select the empty perspectives +3. Click **Apply Fix** + +### Manual Fix + +1. In **TOM Explorer**, expand the **Perspectives** node +2. Right-click the empty perspective +3. Select **Delete** + +## Common Causes + +### Cause 1: Removed All Tables + +All tables removed from perspective without deleting it. + +### Cause 2: Incomplete Configuration + +Perspective created during design but never populated. + +## Example + +### Before Fix + +``` +Perspectives: + - Sales (contains: Sales, Customer, Product tables) ✓ + - Marketing (contains: NO TABLES) ✗ +``` + +### After Fix + +``` +Perspectives: + - Sales (contains: Sales, Customer, Product tables) ✓ +``` + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +## Learn More + +- [Perspectives in Tabular Models](https://learn.microsoft.com/analysis-services/tabular-models/perspectives-ssas-tabular) + diff --git a/content/kb/bpa-powerbi-latest-compatibility.md b/content/kb/bpa-powerbi-latest-compatibility.md new file mode 100644 index 00000000..bb97ccb7 --- /dev/null +++ b/content/kb/bpa-powerbi-latest-compatibility.md @@ -0,0 +1,86 @@ +--- +uid: kb.bpa-powerbi-latest-compatibility +title: Use Latest Compatibility Level for Power BI Models +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule ensuring Power BI models use the latest compatibility level for optimal features and performance. +--- + +# Use Latest Compatibility Level for Power BI Models + +## Overview + +This rule identifies Power BI models not using the latest available compatibility level. Using the latest level ensures access to newest features, performance optimizations, and bug fixes. + +- Category: Governance +- Severity: High (3) + +## Applies To + +- Model (Power BI semantic models only) + +## Why This Matters + +- **Missing features**: New DAX functions and model capabilities unavailable +- **Future compatibility**: Easier upgrades when using recent levels + +## When This Rule Triggers + +For Power BI models, triggers when compatibility level is below current maximum: + +```csharp +Model.Database.CompatibilityMode=="PowerBI" +and Model.Database.CompatibilityLevel<>[CurrentMaxLevel] +``` + +## How to Fix + +### Automatic Fix + +The best practice rule includes an automatic fix that sets the Compatability Level to the highest avaliable that exist on the current installation of Tabular Editor 3. If you have an older version of Tabular Editor 3 installed you should update your installation. + +```csharp +Model.Database.CompatibilityLevel = [PowerBIMaxCompatibilityLevel] +``` + +### Manual Fix + +1. In Tabular Editor, go to **Model** properties +2. Set **Compatibility Level** to the latest version +3. Test all DAX expressions and features +4. Deploy to Power BI Service + +## Common Causes + +### Cause 1: Model Created in Power BI Desktop + +Model created with in Power BI Desktop does not necesarily have the latest Compatability Level. + +### Cause 2: Model Created at Lower Level + +Model created with older version of Power BI Desktop. + +### Cause 3: Conservative Approach + +Team policy to delay upgrades. + +## Example + +### Before Fix + +``` +Model Compatibility Level: 1500 +Current Maximum Level: 1700 +``` + +### After Fix + +``` +Model Compatibility Level: 1700 (Latest) +``` + +Access to new features like enhanced calculation groups and field parameters. + +## Compatibility Level + +This rule applies to Power BI models at all compatibility levels. \ No newline at end of file diff --git a/content/kb/bpa-relationship-same-datatype.md b/content/kb/bpa-relationship-same-datatype.md new file mode 100644 index 00000000..fb3263fd --- /dev/null +++ b/content/kb/bpa-relationship-same-datatype.md @@ -0,0 +1,92 @@ +--- +uid: kb.bpa-relationship-same-datatype +title: Relationship Columns Must Have Same Data Type +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule ensuring relationships connect columns with matching data types to prevent errors and performance issues. +--- + +# Relationship Columns Must Have Same Data Type + +## Overview + +This best practice rule identifies relationships where the connected columns have mismatched data types. Both columns in a relationship must share the same data type to ensure proper filtering, prevent errors, and maintain optimal query performance. + +- Category: Error Prevention + +- Severity: High (3) + +## Applies To + +- Relationships + +## Why This Matters + +Relationships with mismatched data types cause serious problems: + +- **Model validation errors**: The model may fail to save or deploy +- **Relationship creation failures**: Power BI and Analysis Services may reject the relationship +- **Implicit conversions**: Expensive data type conversions on every query +- **Incorrect results**: Type coercion leads to unexpected filtering behavior +- **Performance degradation**: Converting data types during queries slows execution +- **Memory overhead**: Additional memory required for conversion buffers + +## When This Rule Triggers + +The rule triggers when: + +```csharp +FromColumn.DataType != ToColumn.DataType +``` + +This detects relationships connecting columns with different data types. + +## How to Fix + +### Manual Fix + +1. Identify which column should change data type +2. Change the data type in **Power Query**, in the underlying data source or in the model +3. Delete the existing relationship +4. Create a new relationship between the corrected columns +5. Verify filtering works correctly + +## Common Causes + +### Cause 1: Inconsistent Data Type Choices + +Different data types chosen for the same logical key during import or table creation. + +### Cause 2: Source System Differences + +Foreign keys imported from different source systems with different type conventions. + +### Cause 3: DateTime vs Date Mismatch + +Fact tables using DateTime columns while date dimensions use Date type. + +## Example + +### Before Fix + +``` +Relationship: Sales[CustomerID] (Int64) → Customers[CustomerID] (String) +``` + +**Error**: Relationship fails validation or creates performance issues with implicit conversion + +### After Fix + +``` +Relationship: Sales[CustomerID] (Int64) → Customers[CustomerID] (Int64) +``` + +**Result**: Relationship works efficiently with no type conversion overhead + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +## Related Rules + +- [Many-to-Many Relationships Should Use Single Direction](xref:kb.bpa-many-to-many-single-direction) - Relationship performance optimization diff --git a/content/kb/bpa-remove-auto-date-table.md b/content/kb/bpa-remove-auto-date-table.md new file mode 100644 index 00000000..91451c32 --- /dev/null +++ b/content/kb/bpa-remove-auto-date-table.md @@ -0,0 +1,106 @@ +--- +uid: kb.bpa-remove-auto-date-table +title: Remove Auto Date Tables +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule to identify and remove automatically generated date tables that increase model size and reduce performance. +--- + +# Remove Auto Date Tables + +## Overview + +This best practice rule identifies automatically generated date tables created by Power BI Desktop. These auto-generated tables (`DateTableTemplate_` and `LocalDateTable_`) should be removed in favor of a single, explicit date table to optimize model size and performance. + +- Category: Performance + +- Severity: Medium (2) + +## Applies To + +- Tables +- Calculated Tables + +## Why This Matters + +Power BI automatically creates hidden date tables for every date/datetime column when "Auto Date/Time" is enabled. This causes issues: + +- **Increased model size**: Each auto-generated table adds unnecessary data +- **Memory overhead**: Multiple date tables consume more memory than one shared table +- **Slower refresh**: Additional tables increase refresh duration + +A single, well-designed date table is far more efficient and maintainable. + +## When This Rule Triggers + +The rule triggers when it finds calculated tables with names that: + +- Start with `"DateTableTemplate_"`, or +- Start with `"LocalDateTable_"` + +These prefixes indicate Power BI's automatically generated date tables. + +## How to Fix + +### Manual Fix + +1. Disable **Auto Date/Time** in Power BI Desktop (**File** > **Options** > **Data Load**) +2. Create a dedicated date table. +3. Mark it as a date table and create relationships to fact tables +4. In **TOM Explorer**, delete tables starting with `DateTableTemplate_` or `LocalDateTable_` +5. Verify custom date table relationships work correctly + +## Common Causes + +### Cause 1: Auto Date/Time Feature Enabled + +Power BI Desktop's "Auto Date/Time" feature automatically creates these tables. + +### Cause 2: Migrated Models + +Models created with auto tables enabled and never cleaned up. + +### Cause 3: Default Settings + +New models use default settings which enable auto date tables. + +## Example + +### Before Fix + +``` +Tables: + - Sales + - LocalDateTable_OrderDate (hidden, auto-generated) + - LocalDateTable_ShipDate (hidden, auto-generated) + - Products + - LocalDateTable_ReleaseDate (hidden, auto-generated) +``` + +**Result**: Multiple hidden tables inflate model size + +### After Fix + +``` +Tables: + - Sales + - Products + - DateTable (explicit, marked as date table) + -> Relationships to Sales[OrderDate], Sales[ShipDate], Products[ReleaseDate] +``` + +**Result**: Single efficient date table serves all date relationships + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +## Related Rules + +- [Date Table Should Exist](xref:kb.bpa-date-table-exists) - Ensuring a proper date table is present + +## Learn More + +- [Disable Auto Date/Time in Power BI](https://learn.microsoft.com/power-bi/guidance/auto-date-time) +- [Create Date Tables](https://learn.microsoft.com/power-bi/guidance/model-date-tables) +- [Date Table Best Practices](https://www.sqlbi.com/articles/creating-a-simple-date-table-in-dax/) diff --git a/content/kb/bpa-remove-unused-data-sources.md b/content/kb/bpa-remove-unused-data-sources.md new file mode 100644 index 00000000..d5b82ed4 --- /dev/null +++ b/content/kb/bpa-remove-unused-data-sources.md @@ -0,0 +1,117 @@ +--- +uid: kb.bpa-remove-unused-data-sources +title: Remove Unused Data Sources +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule for removing orphaned data sources to reduce model complexity and improve maintainability. +--- + +# Remove Unused Data Sources + +## Overview + +This best practice rule identifies data sources that are not referenced by any partitions or table expressions. Removing unused data sources reduces model complexity, improves maintainability, and prevents confusion. + +- Category: Maintenance +- Severity: Low (1) + +## Applies To + +- Provider Data Sources +- Structured Data Sources + +## Why This Matters + +Unused data sources create unnecessary overhead: + +- **Maintenance burden**: Credentials and connection strings must be maintained for unused connections +- **Security concerns**: Unnecessary connection strings may expose sensitive information +- **Model complexity**: Extra objects clutter the data source list +- **Confusion**: Developers may mistakenly use obsolete data sources +- **Deployment issues**: Unused data sources may reference systems that no longer exist +- **Documentation overhead**: Extra objects require explanation in model documentation + +Unused data sources typically result from: +- Refactoring partitions to use different sources +- Consolidating multiple sources into one +- Removing tables without cleaning up their data sources +- Testing alternative connection methods + +## When This Rule Triggers + +The rule triggers when a data source meets all these conditions: + +```csharp +UsedByPartitions.Count() == 0 +and not Model.Tables.Any(SourceExpression.Contains(OuterIt.Name)) +and not Model.AllPartitions.Any(Query.Contains(OuterIt.Name)) +``` + +In other words: +1. No partitions directly reference the data source +2. No table source expressions (M queries) reference the data source by name +3. No partition queries contain the data source name + +## How to Fix + +### Automatic Fix + +This rule includes an automatic fix that deletes the unused data source: + +```csharp +Delete() +``` + +To apply: +1. In the **Best Practice Analyzer** select flagged objects +3. Click **Apply Fix** + +### Manual Fix + +1. In **TOM Explorer**, expand the **Data Sources** node +2. Right-click the unused data source +3. Select **Delete** +4. Confirm the deletion + +### Before Deleting + +Verify the data source is truly unused: +- Check all partitions in all tables +- Search M expressions for references to the data source name +- Review custom expressions and calculated tables +- Ensure no documentation references the connection + +## Example + +### Before Fix + +``` +Data Sources: + - SQLServer_Production (Provider, used by Sales partition) + - SQLServer_Staging (Provider, NOT USED) ← Remove + - AzureSQL_Archive (Structured, NOT USED) ← Remove + - PowerQuery_Web (Structured, used by Product partition) +``` + +### After Fix + +``` +Data Sources: + - SQLServer_Production (Provider, used by Sales partition) + - PowerQuery_Web (Structured, used by Product partition) +``` + +**Result**: Simpler model with only necessary data sources + +## False Positives + +The rule may flag data sources that are: +- Referenced through dynamic M expressions using variables +- Used in commented-out partition queries +- Referenced by name in annotations or descriptions + +**Solution**: Manually verify before deleting; add comments or annotations if the data source should be kept for documentation purposes. + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. \ No newline at end of file diff --git a/content/kb/bpa-set-isavailableinmdx-false.md b/content/kb/bpa-set-isavailableinmdx-false.md new file mode 100644 index 00000000..80f4c327 --- /dev/null +++ b/content/kb/bpa-set-isavailableinmdx-false.md @@ -0,0 +1,91 @@ +--- +uid: kb.bpa-set-isavailableinmdx-false +title: Set IsAvailableInMDX to False +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule to optimize performance by disabling MDX access for hidden columns that are not used in relationships or hierarchies. +--- + +# Set IsAvailableInMDX to False + +## Overview + +This best practice rule identifies hidden columns that have the `IsAvailableInMDX` property set to `true` but don't need to be accessible through MDX queries. Setting this property to `false` for unused hidden columns can improve query performance and reduce memory overhead. + +- Category: Performance +- Severity: Medium (2) + +## Applies To + +- Data Columns +- Calculated Columns +- Calculated Table Columns + +## Why This Matters + +When a column has `IsAvailableInMDX` set to `true`, the Analysis Services engine maintains additional metadata and structures to support MDX queries against that column. For hidden columns that aren't used in relationships, hierarchies, variations, calendars, or as sort-by columns, this overhead is unnecessary and can: + +- Increase memory consumption +- Slow down query processing +- Add complexity to the model metadata + +By explicitly setting `IsAvailableInMDX` to `false` for these columns, you optimize the model for DAX-only scenarios, which is the primary query language for Power BI and modern Analysis Services models. + +## When This Rule Triggers + +The rule triggers when all of the following conditions are met: + +1. The column has `IsAvailableInMDX = true` +2. The column is hidden (or its table is hidden) +3. The column is NOT used in any `SortBy` relationships +4. The column is NOT used in any hierarchies +5. The column is NOT used in any variations +6. The column is NOT used in any calendars +7. The column is NOT serving as a `SortByColumn` for another column + +## How to Fix + +### Automatic Fix + +This rule includes an automatic fix expression. When you apply the fix in the Best Practice Analyzer: + +```csharp +IsAvailableInMDX = false +``` +To apply: +1. In the **Best Practice Analyzer** select flagged objects +3. Click **Apply Fix** + +### Manual Fix + +1. In the **TOM Explorer**, locate the flagged column +2. In the **Properties** pane, find the `IsAvailableInMDX` property +3. Set the value to `false` +4. Save your changes + +## Example + +Consider a hidden calculated column used only for intermediate calculations: + +```dax +_TempCalculation = +CALCULATE( + SUM('Sales'[Amount]), + ALLEXCEPT('Sales', 'Sales'[ProductKey]) +) +``` + +If this column is: +- Hidden from client tools +- Not used in any hierarchies or relationships +- Not referenced by sort operations + +Then setting `IsAvailableInMDX = false` is recommended for optimal performance. + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +## Related Rules + +- [Set IsAvailableInMDX to True When Necessary](xref:kb.bpa-set-isavailableinmdx-true-necessary) - The complementary rule ensuring columns that need MDX access have it enabled diff --git a/content/kb/bpa-set-isavailableinmdx-true-necessary.md b/content/kb/bpa-set-isavailableinmdx-true-necessary.md new file mode 100644 index 00000000..5c3062cc --- /dev/null +++ b/content/kb/bpa-set-isavailableinmdx-true-necessary.md @@ -0,0 +1,144 @@ +--- +uid: kb.bpa-set-isavailableinmdx-true-necessary +title: Set IsAvailableInMDX to True When Necessary +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule preventing query errors by ensuring columns used in hierarchies and relationships have MDX availability enabled. +--- + +# Set IsAvailableInMDX to True When Necessary + +## Overview + +This best practice rule identifies columns that have `IsAvailableInMDX` set to `false` but are actually used in scenarios requiring MDX access. These columns must have MDX availability enabled to function correctly in hierarchies, relationships, and sort operations. + +- Category: Error Prevention +- Severity: High (3) + +## Applies To + +- Data Columns +- Calculated Columns +- Calculated Table Columns + +## Why This Matters + +When a column is used in certain model structures, the Analysis Services engine requires MDX access to that column. Disabling MDX access for columns that need it causes: + +- **Query failures**: Hierarchies and sort operations fail with errors +- **Broken visualizations**: Charts and tables using affected hierarchies display errors +- **Relationship problems**: MDX queries against relationships may fail +- **Calendar/variation errors**: Time intelligence features break +- **Unpredictable behavior**: Some queries work while others fail depending on client tool + +Columns need `IsAvailableInMDX = true` when they are: +- Used in hierarchies as levels +- Referenced as sort-by columns +- Used in variations (alternate hierarchies) +- Part of calendar definitions +- Serving as sort-by targets for other columns + +## When This Rule Triggers + +The rule triggers when a column has `IsAvailableInMDX = false` AND any of these conditions are true: + +```csharp +IsAvailableInMDX = false +and +( + UsedInSortBy.Any() + or + UsedInHierarchies.Any() + or + UsedInVariations.Any() + or + UsedInCalendars.Any() + or + SortByColumn != null +) +``` + +The rule checks these dependency collections: + +| Property | Description | Example Usage | +|----------|-------------|---------------| +| `UsedInHierarchies` | Hierarchies where column is a level | Product hierarchy levels | +| `UsedInSortBy` | Columns using this as sort key | Month names sorted by month number | +| `UsedInVariations` | Alternate hierarchies using column | Product variations | +| `UsedInCalendars` | Calendar metadata references | Date table calendar definitions | +| `SortByColumn` | Column sorts by another column | This column has a sort-by reference | + +## How to Fix + +### Automatic Fix + +This rule includes an automatic fix: + +```csharp +IsAvailableInMDX = true +``` + +To apply: +1. In the **Best Practice Analyzer** select flagged objects +3. Click **Apply Fix** + +### Manual Fix + +1. In **TOM Explorer**, locate the flagged column +2. In **Properties** pane, find `IsAvailableInMDX` +3. Set the value to `true` +4. Save and test affected hierarchies/sorts + +## Common Scenarios + +### Scenario 1: Hierarchy Level Column + +**Problem**: A column used as a hierarchy level has MDX disabled + +```dax +Hierarchy: Geography + Levels: + - Country + - State (IsAvailableInMDX = false) ← Problem + - City +``` + +**Error**: "The hierarchy 'Geography' cannot be used because one of its levels is not available in MDX." + +**Solution**: Set `State[IsAvailableInMDX] = true` + +### Scenario 2: Sort-By Column + +**Problem**: A column serving as a sort-by target has MDX disabled + +``` +Month Name column: + - SortByColumn = MonthNumber + - MonthNumber.IsAvailableInMDX = false ← Problem +``` + +**Error**: Months display in alphabetical order instead of calendar order + +**Solution**: Set `MonthNumber[IsAvailableInMDX] = true` + +### Scenario 3: Calendar Definition + +**Problem**: A date column used in calendar metadata has MDX disabled + +``` +DateTable: + - Calendar uses DateKey column + - DateKey.IsAvailableInMDX = false ← Problem +``` + +**Error**: Time intelligence functions fail + +**Solution**: Set `DateKey[IsAvailableInMDX] = true` + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +## Related Rules + +- [Set IsAvailableInMDX to False](xref:kb.bpa-set-isavailableinmdx-false) - The complementary optimization rule diff --git a/content/kb/bpa-specify-application-name.md b/content/kb/bpa-specify-application-name.md new file mode 100644 index 00000000..50dbf82b --- /dev/null +++ b/content/kb/bpa-specify-application-name.md @@ -0,0 +1,70 @@ +--- +uid: kb.bpa-specify-application-name +title: Specify Application Name in Connection Strings +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule for including application name in SQL Server connection strings to enable monitoring and troubleshooting. +--- + +# Specify Application Name in Connection Strings + +## Overview + +This rule identifies SQL Server provider data sources that lack an Application Name parameter in their connection strings. Including the application name enables better monitoring and troubleshooting. + +- Category: Performance +- Severity: Low (1) + +## Applies To + +- Provider Data Sources + +## Why This Matters + +- **Query tracking**: DBAs can identify which application generated queries +- **Performance monitoring**: Isolate tabular model queries for analysis +- **Troubleshooting**: Quickly identify source of problem queries +- **Auditing**: Track data access by application + +## When This Rule Triggers + +This rule triggers when a data source meets both of these conditions: + +1. The connection string uses a SQL Server provider (contains `SQLNCLI`, `SQLOLEDB`, or `MSOLEDBSQL`) +2. The connection string does NOT include an `Application Name` parameter + +In other words, the rule identifies SQL Server connections that are missing the application name identifier. + +## How to Fix + +### Manual Fix + +Add Application Name to your connection string: + +``` +Provider=MSOLEDBSQL;Data Source=ServerName;Initial Catalog=DatabaseName;Application Name=Tabular Editor;Integrated Security=SSPI; +``` + +## Example + +### Before Fix + +``` +Provider=MSOLEDBSQL;Data Source=localhost;Initial Catalog=AdventureWorks; +``` + +### After Fix + +``` +Provider=MSOLEDBSQL;Data Source=localhost;Initial Catalog=AdventureWorks;Application Name=Sales Model; +``` + +Result: Queries now identifiable in SQL Server monitoring tools. + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +## Related Rules + +- [Remove Unused Data Sources](xref:kb.bpa-remove-unused-data-sources) - Data source maintenance diff --git a/content/kb/bpa-translate-descriptions.md b/content/kb/bpa-translate-descriptions.md new file mode 100644 index 00000000..24a01fb4 --- /dev/null +++ b/content/kb/bpa-translate-descriptions.md @@ -0,0 +1,99 @@ +--- +uid: kb.bpa-translate-descriptions +title: Translate Descriptions for All Cultures +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule ensuring object descriptions are translated for all defined cultures. +--- + +# Translate Descriptions for All Cultures + +## Overview + +This rule identifies objects with descriptions that lack translations for one or more cultures. + +- Category: Model Layout +- Severity: Low (1) + +## Applies To + +- Model +- Tables +- Measures +- Hierarchies +- Levels +- Perspectives +- Data Columns +- Calculated Columns +- Calculated Tables +- Calculated Table Columns + +## Why This Matters + +- **Incomplete localization**: Descriptions display in default language only +- **Inconsistent help text**: Users see mix of languages +- **User confusion**: Documentation appears incomplete +- **Professional appearance**: Missing translations reduce model quality + +## When This Rule Triggers + +```csharp +not string.IsNullOrEmpty(Description) +and Model.Cultures.Any(string.IsNullOrEmpty(outerIt.TranslatedDescriptions[it])) +``` +This rule triggers when an object meets both of these conditions: + +1. The object has a description (not empty) +2. At least one culture in the model is missing a translation for that description + +In other words, if you have descriptions and multiple cultures defined, all descriptions should be translated for all cultures. + + +## How to Fix + +### Manual Fix + +1. In **TOM Explorer**, select the object +2. In **Properties** pane, expand **Translated Descriptions** +3. Enter translation for each culture + +## Common Causes + +### Cause 1: New Descriptions Added + +Descriptions created without translations. + +### Cause 2: Culture Added Later + +Culture added after descriptions were written. + +### Cause 3: Incomplete Translation + +Translation process didn't cover descriptions. + +## Example + +### Before Fix + +``` +Measure: [Total Revenue] +Description (English): "Sum of all revenue" +Description (Spanish): (missing) +``` + +### After Fix + +``` +Measure: [Total Revenue] +Description (English): "Sum of all revenue" +Description (Spanish): "Suma de todos los ingresos" +``` + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +## Related Rules + +- [Translate Visible Names](xref:kb.bpa-translate-visible-names) - Translating object names +- [Translate Display Folders](xref:kb.bpa-translate-display-folders) - Translating display folders diff --git a/content/kb/bpa-translate-display-folders.md b/content/kb/bpa-translate-display-folders.md new file mode 100644 index 00000000..a8b7bb38 --- /dev/null +++ b/content/kb/bpa-translate-display-folders.md @@ -0,0 +1,104 @@ +--- +uid: kb.bpa-translate-display-folders +title: Translate Display Folders for All Cultures +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule ensuring display folders are translated for all defined cultures. +--- + +# Translate Display Folders for All Cultures + +## Overview + +This rule identifies visible objects with display folders that lack translations for one or more cultures. + +- Category: Model Layout +- Severity: Low (1) + +## Applies To + +- Measures +- Hierarchies +- Data Columns +- Calculated Columns +- Calculated Table Columns + +## Why This Matters + +- **Incomplete localization**: Display folders show in default language only +- **Inconsistent navigation**: Partially translated folder structure +- **User confusion**: Organization appears incomplete +- **Professional appearance**: Missing translations reduce model quality + +## When This Rule Triggers + +This rule triggers when an object meets all three of these conditions: + +1. The object is **visible** to end users (not hidden) +2. The object has a **display folder** assigned (organizing it into a folder structure) +3. At least one culture in the model is **missing a translation** for that display folder + +In plain language: visible objects that are organized in display folders should have those folder names translated for all cultures in your model. + +```csharp +IsVisible +and not string.IsNullOrEmpty(DisplayFolder) +and Model.Cultures.Any(string.IsNullOrEmpty(outerIt.TranslatedDisplayFolders[it])) +``` + +## How to Fix + +### Automatic Fix + +```csharp +TranslatedDisplayFolders.Reset() +``` + +Resets translations to use the default display folder. + +### Manual Fix + +1. Select object in **TOM Explorer** +2. Expand **Translated Display Folders** in properties +3. Enter translation for each culture + +## Common Causes + +### Cause 1: New Display Folders Added + +Display folders created without translations. + +### Cause 2: Culture Added Later + +Culture added after display folders were defined. + +### Cause 3: Incomplete Translation + +Translation workflow didn't cover display folders. + +## Example + +### Before Fix + +``` +Measure: [Total Sales] +Display Folder (English): "Sales Metrics" +Display Folder (French): (missing) +``` + +### After Fix + +``` +Measure: [Total Sales] +Display Folder (English): "Sales Metrics" +Display Folder (French): "Métriques de Vente" +``` + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +## Related Rules + +- [Translate Visible Names](xref:kb.bpa-translate-visible-names) - Translating object names +- [Translate Descriptions](xref:kb.bpa-translate-descriptions) - Translating descriptions diff --git a/content/kb/bpa-translate-hierarchy-levels.md b/content/kb/bpa-translate-hierarchy-levels.md new file mode 100644 index 00000000..63265dc2 --- /dev/null +++ b/content/kb/bpa-translate-hierarchy-levels.md @@ -0,0 +1,92 @@ +--- +uid: kb.bpa-translate-hierarchy-levels +title: Translate Hierarchy Level Names for All Cultures +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule ensuring hierarchy level names are translated for all defined cultures. +--- + +# Translate Hierarchy Level Names for All Cultures + +## Overview + +This rule identifies hierarchy levels in visible hierarchies that lack name translations for one or more cultures. + +- Category: Model Layout +- Severity: Low (1) + +## Applies To + +- Levels (within hierarchies) + +## Why This Matters + +- **Incomplete localization**: Level names display in default language only +- **Inconsistent experience**: Partially translated hierarchies +- **User confusion**: Navigation appears incomplete +- **Professional appearance**: Missing translations reduce quality + +## When This Rule Triggers + +This rule triggers when a hierarchy level meets both of these conditions: + +1. The hierarchy containing the level is **visible** to end users +2. At least one culture in the model is **missing a translation** for the level name + +That is if you have visible hierarchies with multiple cultures, all the level names within those hierarchies should be translated for each culture. + +```csharp +Hierarchy.IsVisible +and Model.Cultures.Any(string.IsNullOrEmpty(outerIt.TranslatedNames[it])) +``` + +## How to Fix + +### Manual Fix + +1. In **TOM Explorer**, select the level +2. In **Properties** pane, expand **Translated Names** +3. Enter translation for each culture + +## Common Causes + +### Cause 1: New Levels Added + +Levels created without translations. + +### Cause 2: Culture Added Later + +Culture added after hierarchy was created. + +### Cause 3: Incomplete Translation + +Translation process didn't cover all hierarchy levels. + +## Example + +### Before Fix + +``` +Hierarchy: Geography + Level: Country + English: "Country" + Spanish: (missing) +``` + +### After Fix + +``` +Hierarchy: Geography + Level: Country + English: "Country" + Spanish: "País" +``` + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +## Related Rules + +- [Translate Visible Names](xref:kb.bpa-translate-visible-names) - Translating object names +- [Translate Perspectives](xref:kb.bpa-translate-perspectives) - Translating perspective names diff --git a/content/kb/bpa-translate-perspectives.md b/content/kb/bpa-translate-perspectives.md new file mode 100644 index 00000000..8a14a954 --- /dev/null +++ b/content/kb/bpa-translate-perspectives.md @@ -0,0 +1,87 @@ +--- +uid: kb.bpa-translate-perspectives +title: Translate Perspective Names for All Cultures +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule ensuring perspective names are translated for all defined cultures. +--- + +# Translate Perspective Names for All Cultures + +## Overview + +This rule identifies model perspectives that lack name translations for one or more cultures. + +- Category: Model Layout +- Severity: Low (1) + +## Applies To + +- Model +- Perspectives + +## Why This Matters + +- **Incomplete localization**: Perspectives display in default language only +- **Inconsistent experience**: Mix of translated and untranslated perspective names +- **User confusion**: Expected language support not available +- **Professional appearance**: Incomplete translations reduce model quality + +## When This Rule Triggers + +This rule triggers when a perspective has: + +- At least one culture in the model that is **missing a translation** for the perspective name + +```csharp +Model.Cultures.Any(string.IsNullOrEmpty(outerIt.TranslatedNames[it])) +``` + +## How to Fix + +### Manual Fix + +1. In **TOM Explorer**, select the perspective +2. In **Properties** pane, expand **Translated Names** +3. Enter translation for each culture + +## Common Causes + +### Cause 1: New Perspectives Added + +Perspectives created without translations. + +### Cause 2: Culture Added Later + +Culture added after perspectives were defined. + +### Cause 3: Incomplete Translation + +Translation workflow didn't cover perspectives. + +## Example + +### Before Fix + +``` +Perspective: "Sales Analysis" +English: "Sales Analysis" +German: (missing) +``` + +### After Fix + +``` +Perspective: "Sales Analysis" +English: "Sales Analysis" +German: "Vertriebsanalyse" +``` + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +## Related Rules + +- [Translate Visible Names](xref:kb.bpa-translate-visible-names) - Translating object names +- [Translate Descriptions](xref:kb.bpa-translate-descriptions) - Translating descriptions diff --git a/content/kb/bpa-translate-visible-names.md b/content/kb/bpa-translate-visible-names.md new file mode 100644 index 00000000..db2d51f6 --- /dev/null +++ b/content/kb/bpa-translate-visible-names.md @@ -0,0 +1,99 @@ +--- +uid: kb.bpa-translate-visible-names +title: Translate Visible Object Names for All Cultures +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule ensuring visible object names are translated for all defined cultures. +--- + +# Translate Visible Object Names for All Cultures + +## Overview + +This rule identifies visible objects whose names lack translations for one or more cultures defined in the model. + +- Category: Model Layout +- Severity: Low (1) + +## Applies To + +- Tables +- Measures +- Hierarchies +- Data Columns +- Calculated Columns +- Calculated Tables +- Calculated Table Columns + +## Why This Matters + +- **Incomplete localization**: Users in different cultures see untranslated names +- **Inconsistent experience**: Mix of translated and untranslated content +- **User confusion**: Expected language support not provided +- **Professional appearance**: Incomplete translations appear unpolished + +## When This Rule Triggers + +This rule triggers when an object meets both of these conditions: + +1. The object is **visible** to end users (not hidden) +2. At least one culture in the model is **missing a translation** for the object name + +In other words visible objects with multiple cultures defined should have their names translated for each culture. + +```csharp +IsVisible +and Model.Cultures.Any(string.IsNullOrEmpty(outerIt.TranslatedNames[it])) +``` + +## How to Fix + +### Manual Fix + +1. In **TOM Explorer**, select the object +2. In **Properties** pane, expand **Translated Names** +3. Enter translation for each culture +4. Save changes + +## Common Causes + +### Cause 1: New Objects Added + +New objects created without translations. + +### Cause 2: Culture Added Later + +Culture added to model after objects were created. + +### Cause 3: Incomplete Translation Process + +Translation workflow didn't cover all objects. + +## Example + +### Before Fix + +``` +Measure: [Total Sales] +English: "Total Sales" +Spanish: (missing) +French: (missing) +``` + +### After Fix + +``` +Measure: [Total Sales] +English: "Total Sales" +Spanish: "Total de Ventas" +French: "Total des Ventes" +``` + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +## Related Rules + +- [Translate Perspectives](xref:kb.bpa-translate-perspectives) - Translating perspective names +- [Translate Descriptions](xref:kb.bpa-translate-descriptions) - Translating descriptions diff --git a/content/kb/bpa-trim-object-names.md b/content/kb/bpa-trim-object-names.md new file mode 100644 index 00000000..9318b2e1 --- /dev/null +++ b/content/kb/bpa-trim-object-names.md @@ -0,0 +1,103 @@ +--- +uid: kb.bpa-trim-object-names +title: Trim Leading and Trailing Spaces from Object Names +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule for removing leading and trailing spaces from object names to prevent confusion and referencing issues. +--- + +# Trim Leading and Trailing Spaces from Object Names + +## Overview + +This best practice rule identifies objects whose names contain leading or trailing spaces. These unnecessary spaces cause DAX referencing issues, display problems, and general confusion. + +- Category: **Naming Conventions** +- Severity: Low (1) + +## Applies To + +- Model +- Tables +- Measures +- Hierarchies +- Levels +- Perspectives +- Partitions +- Provider Data Sources +- Data Columns +- Calculated Columns +- Calculated Tables +- Calculated Table Columns +- Structured Data Sources +- Named Expressions +- Model Roles +- Calculation Groups +- Calculation Items + +## Why This Matters + +- **DAX syntax problems**: Extra spaces require careful bracket notation +- **Display inconsistency**: Objects appear misaligned in field lists +- **Search difficulties**: Users may not find objects when searching +- **Maintenance confusion**: Developers may create duplicates not noticing spaces + +## When This Rule Triggers + +The rule triggers when an object name starts or ends with a space: + +```csharp +Name.StartsWith(" ") or Name.EndsWith(" ") +``` + +## How to Fix + +### Manual Fix + +1. In **TOM Explorer**, locate the object +2. Right-click and select **Rename** (or press F2) +3. Remove leading/trailing spaces +4. Press Enter to confirm + +## Common Causes + +### Cause 1: Accidental Spacebar Presses + +Accidental spacebar presses during naming. + +### Cause 2: Copy/Paste from External Sources + +Copy/paste from documents with formatting. + +### Cause 3: Dublicating objects + +When dublicating objects the name with have an added " copy" post-fixed. It is easy to miss deleting the space before "copy" + +## Example + +### Before Fix + +``` +Measures: + - Total Sales + - Total Sales (with spaces - appears different!) +``` + +DAX: `[ Total Sales]` - Which one? + +### After Fix + +``` +Measures: + - Total Sales (single consistent measure) +``` + +DAX: `[Total Sales]` - Unambiguous + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +## Related Rules + +- [Avoid Invalid Characters in Names](xref:kb.bpa-avoid-invalid-characters-names) - Related naming hygiene rule diff --git a/content/kb/bpa-visible-objects-no-description.md b/content/kb/bpa-visible-objects-no-description.md new file mode 100644 index 00000000..7685d2b2 --- /dev/null +++ b/content/kb/bpa-visible-objects-no-description.md @@ -0,0 +1,106 @@ +--- +uid: kb.bpa-visible-objects-no-description +title: Visible Objects Should Have Descriptions +author: Morten Lønskov +updated: 2026-01-09 +description: Best practice rule ensuring visible model objects have descriptions to improve discoverability and user experience. +--- + +# Visible Objects Should Have Descriptions + +## Overview + +This best practice rule identifies visible tables, columns, measures, calculation groups, and user-defined functions that lack descriptions. Adding descriptions improves model usability, documentation quality, and user experience. + +- Category: **Maintenance** + +- Severity: Low (1) + +## Applies To + +- Tables +- Calculated Tables +- Data Columns +- Calculated Columns +- Calculated Table Columns +- Measures +- Calculation Groups +- User-Defined Functions (Compatibility Level 1702+) + +## Why This Matters + +Descriptions provide critical context for model users: + +- **Improved discoverability**: Users understand field purpose before using them +- **Better self-service BI**: Business users can work independently with clear guidance +- **Reduced support burden**: Fewer questions about field definitions +- **Enhanced tooltips**: Power BI and Excel show descriptions in hover tooltips +- **Documentation foundation**: Descriptions form the basis for automated documentation +- **Governance and compliance**: Descriptions can include data lineage and business definitions +- **Useage by AI**: AI Agents can better infer the purpose of an object if it has a description. +Without descriptions, users guess at field meanings, leading to incorrect analysis and increased support requests. + +## When This Rule Triggers + +The rule triggers when an object is **visible** AND has an empty or whitespace-only description: + +```csharp +string.IsNullOrWhitespace(Description) +and +IsHidden == false +``` + +**Note**: Hidden objects are excluded because they are not meant for end-user consumption. + +## How to Fix + +### Manual Fix + +1. In **TOM Explorer**, select the object +2. In **Properties** pane, locate the **Description** field +3. Enter a clear, concise description +4. Save changes + +## Common Causes + +### Cause 1: Missing Documentation During Development + +Objects created without adding descriptions. + +### Cause 2: Rapid Prototyping + +Models built quickly without proper documentation. + +### Cause 3: Legacy Models + +Older models created before description standards were established. + +## Example + +### Before Fix + +``` +Measure: [Total Revenue] +Description: (empty) +``` + +**User experience**: Tooltip shows no information, users must guess measure purpose. + +### After Fix + +``` +Measure: [Total Revenue] +Description: "Total revenue excluding taxes and discounts. Calculated as SUM(Sales[UnitPrice] * Sales[Quantity]). Use for financial reporting." +``` + +**User experience**: Clear tooltip helps users understand and correctly use the measure. + +## Compatibility Level + +This rule applies to models with compatibility level **1200** and higher. + +User-Defined Function descriptions are validated at compatibility level **1702** and higher. + +## Related Rules + +- [Avoid Invalid Characters in Descriptions](xref:kb.bpa-avoid-invalid-characters-descriptions) - Ensuring description quality