forked from TabularEditor/BestPracticeRules
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBPARules-standard-lax.json
217 lines (217 loc) · 12.9 KB
/
BPARules-standard-lax.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
[
{
"ID": "DAX_COLUMNS_FULLY_QUALIFIED",
"Name": "Column references should be fully qualified",
"Category": "DAX Expressions",
"Description": "Using fully qualified column references makes it easier to distinguish between column and measure references, and also helps avoid certain errors.",
"Severity": 2,
"Scope": "Measure, CalculatedColumn, CalculatedTable, KPI",
"Expression": "DependsOn.Any(Key.ObjectType = \"Column\" and Value.Any(not FullyQualified))",
"FixExpression": null,
"CompatibilityLevel": 1200,
"Source": "standard\\DAX Expressions"
},
{
"ID": "DAX_DIVISION_COLUMNS",
"Name": "Avoid division (use DIVIDE function instead)",
"Category": "DAX Expressions",
"Description": "Calculated Columns, Measures or Calculated Tables should not use the division symbol in their expressions (/). Instead, it is advised to always use the DIVIDE(\u003cnumerator\u003e,\u003cdenominator\u003e) function.",
"Severity": 2,
"Scope": "Measure, CalculatedColumn, CalculatedTable",
"Expression": "Expression.Contains(\"/\")",
"Remarks": "This rule may flag false positives, if a slash (/) is used in an object name or in a comment. To fix this, we need access to the tokens from lexing the expression.",
"Source": "standard\\DAX Expressions"
},
{
"ID": "DAX_MEASURES_UNQUALIFIED",
"Name": "Measure references should be unqualified",
"Category": "DAX Expressions",
"Description": "Using unqualified measure references makes it easier to distinguish between column and measure references, and also helps avoid certain errors.",
"Severity": 2,
"Scope": "Measure, CalculatedColumn, CalculatedTable, KPI",
"Expression": "DependsOn.Any(Key.ObjectType = \"Measure\" and Value.Any(FullyQualified))",
"FixExpression": null,
"CompatibilityLevel": 1200,
"Source": "standard\\DAX Expressions"
},
{
"ID": "DAX_TODO",
"Name": "Revisit TODO expressions",
"Category": "DAX Expressions",
"Description": "Objects with an expression containing the word \"TODO\" (typically as a comment), should most likely be revisited.",
"Severity": 1,
"Scope": "Measure, Partition, CalculatedColumn, CalculatedTable",
"Expression": "Expression.IndexOf(\"TODO\", StringComparison.OrdinalIgnoreCase) \u003e= 0",
"Source": "standard\\DAX Expressions"
},
{
"ID": "APPLY_FORMAT_STRING_MEASURES",
"Name": "Provide format string for all visible measures",
"Category": "Formatting",
"Description": "Visible measures should have their Format String property assigned",
"Severity": 2,
"Scope": "Measure",
"Expression": "IsVisible \nand string.IsNullOrWhitespace(FormatString)",
"FixExpression": null,
"CompatibilityLevel": 1200,
"Source": "standard\\Formatting"
},
{
"ID": "META_AVOID_FLOAT",
"Name": "Do not use floating point data types",
"Category": "Metadata",
"Description": "Floating point datatypes can cause unexpected results when evaluating values close to 0. Use Currency / Fixed Decimal Number (decimal) instead.",
"Severity": 3,
"Scope": "DataColumn, CalculatedColumn, CalculatedTableColumn",
"Expression": "DataType = \"Double\"",
"FixExpression": "DataType = DataType.Decimal",
"Source": "standard\\Metadata"
},
{
"ID": "META_SUMMARIZE_NONE",
"Name": "Don\u0027t summarize numeric columns",
"Category": "Metadata",
"Description": "Set the SummarizeBy property of all visible numeric columns to \"None\", to avoid unintentional summarization in client tools. Create measures for columns that are supposed to be summarized.",
"Severity": 1,
"Scope": "DataColumn, CalculatedColumn, CalculatedTableColumn",
"Expression": "IsVisible and SummarizeBy \u003c\u003e \"None\" and (DataType = \"Double\" or DataType = \"Decimal\" or DataType = \"Int64\")",
"FixExpression": "SummarizeBy = AggregateFunction.None",
"Source": "standard\\Metadata"
},
{
"ID": "LAYOUT_ADD_TO_PERSPECTIVES",
"Name": "Add objects to perspectives",
"Category": "Model Layout",
"Description": "Visible tables, columns, measures and hierarchies should be assigned to at least one perspective, if the Tabular Model uses perspectives. Otherwise, the objects will only be visible when connecting directly to the model.",
"Severity": 1,
"Scope": "Table, Measure, Hierarchy, DataColumn, CalculatedColumn, CalculatedTableColumn",
"Expression": "Model.Perspectives.Any() and IsVisible and not InPerspective.Any(it)",
"Source": "standard\\Model Layout"
},
{
"ID": "LAYOUT_COLUMNS_HIERARCHIES_DF",
"Name": "Organize columns and hierarchies in display folders",
"Category": "Model Layout",
"Description": "Tables with more than 10 visible columns and/or hierarchies should have them organized in display folders for improved usability.",
"Severity": 1,
"Scope": "Table",
"Expression": "IsVisible and \n (Columns.Count(IsVisible and string.IsNullOrEmpty(DisplayFolder)) +\n Hierarchies.Count(IsVisible and string.IsNullOrEmpty(DisplayFolder))\n) \u003e 10",
"Source": "standard\\Model Layout"
},
{
"ID": "LAYOUT_HIDE_FK_COLUMNS",
"Name": "Hide foreign key columns",
"Category": "Model Layout",
"Description": "Columns used on the Many side of a relationship should be hidden, as the related (dimension) table is likely the best place to apply a filter context.",
"Severity": 1,
"Scope": "DataColumn, CalculatedColumn, CalculatedTableColumn",
"Expression": "Model.Relationships.Any(FromColumn = outerIt) and IsVisible",
"FixExpression": "IsHidden = true",
"Source": "standard\\Model Layout"
},
{
"ID": "LAYOUT_LOCALIZE_DF",
"Name": "Translate Display Folders",
"Category": "Model Layout",
"Description": "Display Folder translations should be assigned for objects where the base DisplayFolder property has been assigned. Otherwise, users connecting to the model using a specific Culture will not see the Display Folder structure.",
"Severity": 1,
"Scope": "Measure, Hierarchy, DataColumn, CalculatedColumn, CalculatedTableColumn",
"Expression": "Model.Cultures.Any() and IsVisible and DisplayFolder \u003c\u003e \"\"",
"FixExpression": "TranslatedDisplayFolder.Reset()",
"Source": "standard\\Model Layout"
},
{
"ID": "LAYOUT_MEASURES_DF",
"Name": "Organize measures in display folders",
"Category": "Model Layout",
"Description": "Tables with more than 10 visible measures should have them organized in display folders for improved usability",
"Severity": 1,
"Scope": "Table",
"Expression": "IsVisible and Measures.Count(IsVisible and string.IsNullOrEmpty(DisplayFolder)) \u003e 10",
"Source": "standard\\Model Layout"
},
{
"ID": "NO_CAMELCASE_COLUMNS_HIERARCHIES",
"Name": "Avoid CamelCase on visible columns and hierarchies",
"Category": "Naming Conventions",
"Description": "Visible columns and hierarchies should not use CamelCase in their names, unless translations are applied",
"Severity": 2,
"Scope": "Hierarchy, DataColumn, CalculatedColumn, CalculatedTableColumn",
"Expression": "IsVisible \nand RegEx.IsMatch(Name, \"[A-Z]([A-Z0-9]*[a-z][a-z0-9]*[A-Z]|[a-z0-9]*[A-Z][A-Z0-9]*[a-z])[A-Za-z0-9]*\") \nand not Name.Contains(\" \") \nand (Model.Cultures.Count = 0 or TranslatedNames.Any(it = \"\" or it = outerIt.Name))",
"FixExpression": null,
"CompatibilityLevel": 1200,
"Source": "standard\\Naming"
},
{
"ID": "NO_CAMELCASE_MEASURES_TABLES",
"Name": "Avoid CamelCase on visible measures and tables",
"Category": "Naming Conventions",
"Description": "Visible measures and tables should not use CamelCase in their names, unless translations are applied",
"Severity": 2,
"Scope": "Measure, Table, CalculatedTable",
"Expression": "IsVisible \nand RegEx.IsMatch(Name, \"[A-Z]([A-Z0-9]*[a-z][a-z0-9]*[A-Z]|[a-z0-9]*[A-Z][A-Z0-9]*[a-z])[A-Za-z0-9]*\") \nand not Name.Contains(\" \") \nand (Model.Cultures.Count = 0 or TranslatedNames.Any(it = \"\" or it = outerIt.Name))",
"FixExpression": null,
"CompatibilityLevel": 1200,
"Source": "standard\\Naming"
},
{
"ID": "RELATIONSHIP_COLUMN_NAMES",
"Name": "Names of columns in relationships should be the same",
"Category": "Naming Conventions",
"Description": "When a single relationship exists between two tables, the columns on both sides of the relationship must have the same name. When multiple relationships exist between two tables, the name of the FromColumn must end with the name of the ToColumn (for example OrderDateKey, ShipDateKey, DueDateKey, etc.)",
"Severity": 2,
"Scope": "Relationship",
"Expression": "(Model.Relationships.Count(FromTable = OuterIt.FromTable and ToTable = OuterIt.ToTable) = 1 and FromColumn.Name \u003c\u003e ToColumn.Name) or\n(Model.Relationships.Count(FromTable = OuterIt.FromTable and ToTable = OuterIt.ToTable) \u003e 1 and not FromColumn.Name.EndsWith(ToColumn.Name))",
"FixExpression": null,
"CompatibilityLevel": 1200,
"Source": "standard\\Naming"
},
{
"ID": "UPPERCASE_FIRST_LETTER_COLUMNS_HIERARCHIES",
"Name": "Column and hierarchy names must start with uppercase letter",
"Category": "Naming Conventions",
"Description": "Avoid using prefixes and camelCasing. Use \"Sales\" instead of \"dimSales\" or \"mSales\".",
"Severity": 2,
"Scope": "DataColumn, CalculatedColumn, CalculatedTableColumn",
"Expression": "IsVisible and\nchar.IsLower(Name[0])\nand (Model.Cultures.Count = 0 or TranslatedNames.Any(it = \"\" or it = outerIt.Name))\n",
"FixExpression": null,
"CompatibilityLevel": 1200,
"Source": "standard\\Naming"
},
{
"ID": "UPPERCASE_FIRST_LETTER_MEASURES_TABLES",
"Name": "Measure and table names must start with uppercase letter",
"Category": "Naming Conventions",
"Description": "Avoid using prefixes and camelCasing. Use \"Sales\" instead of \"dimSales\" or \"mSales\".",
"Severity": 2,
"Scope": "Table, Measure, CalculatedTable",
"Expression": "IsVisible\nand char.IsLower(Name[0])\nand (Model.Cultures.Count = 0 or TranslatedNames.Any(it = \"\" or it = outerIt.Name))\n",
"FixExpression": null,
"CompatibilityLevel": 1200,
"Source": "standard\\Naming"
},
{
"ID": "PERF_UNUSED_COLUMNS",
"Name": "Remove unused columns",
"Category": "Performance",
"Description": "Hidden columns, which do not have any dependencies, are not used in any relationships, not used in any hierarchies and not used as the SortByColumn for other columns, will likely not be used by clients and thus take up unnecessary space. Consider removing the columns from the model to save space and improve processing time, if you are certain that no external DAX or MDX queries make use of the columns.",
"Severity": 2,
"Scope": "DataColumn, CalculatedColumn, CalculatedTableColumn",
"Expression": "(IsHidden or Table.IsHidden)\n\nand ReferencedBy.Count = 0 \n\nand (not UsedInRelationships.Any())\n\nand (not UsedInSortBy.Any())\n\nand (not UsedInHierarchies.Any())\n\nand (not Table.RowLevelSecurity.Any(\n it \u003c\u003e null and \n it.IndexOf(\"[\" + current.Name + \"]\", \"OrdinalIgnoreCase\") \u003e= 0\n))\n\nand (not Model.Roles.Any(RowLevelSecurity.Any(\n it \u003c\u003e null and \n (\n it.IndexOf(current.Table.Name + \"[\" + current.Name + \"]\", \"OrdinalIgnoreCase\") \u003e= 0 or\n it.IndexOf(\"\u0027\" + current.Table.Name + \"\u0027[\" + current.Name + \"]\", \"OrdinalIgnoreCase\") \u003e= 0\n )\n)))",
"FixExpression": "Delete()",
"CompatibilityLevel": 1200,
"Source": "standard\\Performance"
},
{
"ID": "PERF_UNUSED_MEASURES",
"Name": "Remove unused measures",
"Category": "Performance",
"Description": "Hidden measures, that are not referenced by any DAX expression, should be removed.",
"Severity": 1,
"Scope": "Measure",
"Expression": "(Table.IsHidden or IsHidden) and ReferencedBy.Count = 0",
"FixExpression": "Delete()",
"CompatibilityLevel": 1200,
"Source": "standard\\Performance"
}
]