diff --git a/BestPracticeRules/Italian/BPARules.json b/BestPracticeRules/Italian/BPARules.json index 7f74d5f..198c1fb 100644 --- a/BestPracticeRules/Italian/BPARules.json +++ b/BestPracticeRules/Italian/BPARules.json @@ -75,7 +75,7 @@ "ID": "SNOWFLAKE_SCHEMA_ARCHITECTURE", "Name": "[Prestazioni] Considerare prima uno Star-Schema invece di un'architettura Snowflake", "Category": "Prestazioni", - "Description": "In generale, uno schema a stella è l'architettura ottimale per i modelli tabulari. Stando così le cose, ci sono casi validi per utilizzare un approccio a fiocco di neve. Controlla il tuo modello e considera il passaggio a un'architettura con schema a stella. Riferimento: https://docs.microsoft.com/power-bi/guidance/star-schema", + "Description": "In generale, uno schema a stella è l'architettura ottimale per i modelli tabulari. Stando così le cose, ci sono casi validi per utilizzare un approccio a fiocco di neve. Controlla il tuo modello e considera il passaggio a un'architettura con schema a stella. Riferimento: https://docs.microsoft.com/en-us/power-bi/guidance/star-schema", "Severity": 2, "Scope": "Table, CalculatedTable", "Expression": "UsedInRelationships.Any(current.Name == FromTable.Name)\r\nand\r\nUsedInRelationships.Any(current.Name == ToTable.Name)", @@ -95,7 +95,7 @@ "ID": "DATE/CALENDAR_TABLES_SHOULD_BE_MARKED_AS_A_DATE_TABLE", "Name": "[Prestazioni] Le tabelle di tipo data/calendario dovrebbero essere contrassegnate come Date Table", "Category": "Prestazioni", - "Description": "Questa regola cerca le tabelle che contengono le parole \"data\" o \"calendario\" poiché probabilmente dovrebbero essere contrassegnate come tabella di date. Riferimento: https://docs.microsoft.com/power-bi/transform-model/desktop-date-tables", + "Description": "Questa regola cerca le tabelle che contengono le parole \"data\" o \"calendario\" poiché probabilmente dovrebbero essere contrassegnate come tabella di date. Riferimento: https://docs.microsoft.com/en-us/power-bi/transform-model/desktop-date-tables", "Severity": 2, "Scope": "Table, CalculatedTable", "Expression": "(Name.ToUpper().Contains(\"DATE\") or Name.ToUpper().Contains(\"CALENDAR\"))\n\nand\n\n(\nDataCategory <> \"Time\"\n\nor\n\nColumns.Any(IsKey == true && DataType == \"DateTime\") == false\n)", @@ -135,7 +135,7 @@ "ID": "MODEL_USING_DIRECT_QUERY_AND_NO_AGGREGATIONS", "Name": "[Prestazioni] Considerare l'utilizzo di aggregazioni se si utilizza Direct Query in Power BI", "Category": "Prestazioni", - "Description": "Se usi Direct Query in Power BI Premium, potresti prendere in considerazione l'uso di aggregazioni per migliorare le prestazioni. Riferimento: https://docs.microsoft.com/power-bi/transform-model/desktop-aggregations", + "Description": "Se usi Direct Query in Power BI Premium, potresti prendere in considerazione l'uso di aggregazioni per migliorare le prestazioni. Riferimento: https://docs.microsoft.com/en-us/power-bi/transform-model/desktop-aggregations", "Severity": 1, "Scope": "Model", "Expression": "Tables.Any(ObjectTypeName == \"Table (DirectQuery)\")\r\nand\r\n\n\nAllColumns.Any(AlternateOf != null) == false\r\nand \r\nDefaultPowerBIDataSourceVersion.ToString() == \"PowerBI_V3\"", @@ -145,7 +145,7 @@ "ID": "MINIMIZE_POWER_QUERY_TRANSFORMATIONS", "Name": "[Prestazioni] Minimizza l'utilizzo delle trasformazioni di Power Query", "Category": "Prestazioni", - "Description": "Riduci al minimo le trasformazioni di Power Query per migliorare le prestazioni di elaborazione del modello. Se possibile, è consigliabile scaricare queste trasformazioni nel data warehouse. Inoltre, controlla se il raggruppamento delle query (Query Folding) si verifica all'interno del tuo modello. Fare riferimento all'articolo di seguito per ulteriori informazioni sul raggruppamento delle query. Riferimento: https://docs.microsoft.com/power-query/power-query-folding", + "Description": "Riduci al minimo le trasformazioni di Power Query per migliorare le prestazioni di elaborazione del modello. Se possibile, è consigliabile scaricare queste trasformazioni nel data warehouse. Inoltre, controlla se il raggruppamento delle query (Query Folding) si verifica all'interno del tuo modello. Fare riferimento all'articolo di seguito per ulteriori informazioni sul raggruppamento delle query. Riferimento: https://docs.microsoft.com/en-us/power-query/power-query-folding", "Severity": 2, "Scope": "Partition", "Expression": "\nSourceType.ToString() = \"M\"\r\nand\r\n(\r\nQuery.Contains(\"Table.Combine(\")\r\nor\r\n\nQuery.Contains(\"Table.Join(\")\r\nor\r\n\nQuery.Contains(\"Table.NestedJoin(\")\r\nor\r\nQuery.Contains(\"Table.AddColumn(\")\r\nor\r\nQuery.Contains(\"Table.Group(\")\r\nor\r\nQuery.Contains(\"Table.Sort(\")\r\nor\r\nQuery.Contains(\"Table.Pivot(\")\r\nor\r\nQuery.Contains(\"Table.Unpivot(\")\r\nor\r\nQuery.Contains(\"Table.UnpivotOtherColumns(\")\r\nor\r\nQuery.Contains(\"Table.Distinct(\")\r\nor\r\nQuery.Contains(\"[Query=\"\"SELECT\")\r\nor\r\nQuery.Contains(\"Value.NativeQuery\")\r\nor\r\nQuery.Contains(\"OleDb.Query\")\r\nor\r\nQuery.Contains(\"Odbc.Query\")\r\n)", @@ -225,7 +225,7 @@ "ID": "CHECK_IF_DYNAMIC_ROW_LEVEL_SECURITY_(RLS)_IS_NECESSARY", "Name": "[Prestazioni] Verifica se è necessaria la sicurezza dinamica sulle riga (Dynamic RLS)", "Category": "Prestazioni", - "Description": "L'utilizzo della sicurezza dinamica a livello di riga (RLS) può aggiungere memoria e sovraccarico delle prestazioni. Si prega di ricercare i pro/contro del suo utilizzo. Riferimento: https://docs.microsoft.com/power-bi/admin/service-admin-rls", + "Description": "L'utilizzo della sicurezza dinamica a livello di riga (RLS) può aggiungere memoria e sovraccarico delle prestazioni. Si prega di ricercare i pro/contro del suo utilizzo. Riferimento: https://docs.microsoft.com/en-us/power-bi/admin/service-admin-rls", "Severity": 1, "Scope": "TablePermission", "Expression": "RegEx.IsMatch(Expression,\"(?i)USERNAME\\(\")\r\nor\r\nRegEx.IsMatch(Expression,\"(?i)USERPRINCIPALNAME\\(\")", @@ -275,7 +275,7 @@ "ID": "USE_THE_DIVIDE_FUNCTION_FOR_DIVISION", "Name": "[Espressioni DAX] Usa la funzione DIVIDE per la divisione", "Category": "Espressioni DAX", - "Description": "Utilizzare la funzione DIVIDE invece di utilizzare \"/\". La funzione DIVIDE risolve i casi di divisione per zero. Pertanto, si consiglia di utilizzare per evitare errori. Riferimento: https://docs.microsoft.com/power-bi/guidance/dax-divide-function-operator", + "Description": "Utilizzare la funzione DIVIDE invece di utilizzare \"/\". La funzione DIVIDE risolve i casi di divisione per zero. Pertanto, si consiglia di utilizzare per evitare errori. Riferimento: https://docs.microsoft.com/en-us/power-bi/guidance/dax-divide-function-operator", "Severity": 2, "Scope": "Measure, CalculatedColumn, CalculationItem", "Expression": "RegEx.IsMatch(Expression,\"\\]\\s*\\/(?!\\/)(?!\\*)\")\r\nor\r\nRegEx.IsMatch(Expression,\"\\)\\s*\\/(?!\\/)(?!\\*)\")", @@ -335,7 +335,7 @@ "ID": "AVOID_USING_'1-(X/Y)'_SYNTAX", "Name": "[Espressioni DAX] Evita di usare la sintassi '1-(x/y)'", "Category": "Espressioni DAX", - "Description": "Invece di utilizzare la sintassi '1-(x/y)' per ottenere un calcolo percentuale, utilizzare le funzioni DAX di base (come mostrato di seguito). L'utilizzo della sintassi migliorata generalmente migliorerà le prestazioni. La sintassi '1-...' restituisce sempre un valore, mentre la soluzione senza '1-...' no (poiché il valore potrebbe essere 'vuoto'). Pertanto la sintassi '1-...' può restituire più righe/colonne che possono comportare una velocità di query più lenta. Chiariamo con un esempio: 1 - SUM ( 'Sales'[CostAmount] ) / SUM( 'Sales'[SalesAmount] ) Better: SUM ( 'Sales'[SalesAmount] ) - DIVIDE ( SUM ( 'Sales'[CostAmount] ), SUM ( 'Sales'[SalesAmount] ) ) Meglio: VAR x = SUM ( 'Sales'[SalesAmount] ) RETURN x - DIVIDE ( SUM ( 'Sales'[CostAmount] ), x )", + "Description": "Invece di utilizzare la sintassi '1-(x/y)' per ottenere un calcolo percentuale, utilizzare le funzioni DAX di base (come mostrato di seguito). L'utilizzo della sintassi migliorata generalmente migliorerà le prestazioni. La sintassi '1-...' restituisce sempre un valore, mentre la soluzione senza '1-...' no (poiché il valore potrebbe essere 'vuoto'). Pertanto la sintassi '1-...' può restituire più righe/colonne che possono comportare una velocità di query più lenta. Chiariamo con un esempio: 1 - SUM ( 'Sales'[CostAmount] ) / SUM( 'Sales'[SalesAmount] ) Better: DIVIDE ( SUM ( 'Sales'[SalesAmount] ) - SUM ( 'Sales'[CostAmount] ), SUM ( 'Sales'[SalesAmount] ) ) Meglio: VAR x = SUM ( 'Sales'[SalesAmount] ) RETURN DIVIDE ( x - SUM ( 'Sales'[CostAmount] ), x )", "Severity": 2, "Scope": "Measure, CalculatedColumn, CalculationItem", "Expression": "RegEx.IsMatch(Expression,\"[0-9]+\\s*[-+]\\s*[\\(]*\\s*(?i)SUM\\s*\\(\\s*\\'*[A-Za-z0-9 _]+\\'*\\s*\\[[A-Za-z0-9 _]+\\]\\s*\\)\\s*\\/\")\r\nor\r\nRegEx.IsMatch(Expression,\"[0-9]+\\s*[-+]\\s*(?i)DIVIDE\\s*\\(\")", @@ -394,13 +394,35 @@ { "ID": "RELATIONSHIP_COLUMNS_SAME_DATA_TYPE", "Name": "[Prevenzione errori] Le colonne usate nelle relazioni devono essere dello stesso tipo di dati", - "Category": "Error Prevention", + "Category": "[Prevenzione errori]", "Description": "Le colonne utilizzate in una relazione devono essere dello stesso tipo di dati. Idealmente, saranno di tipo di dati intero (consultare la regola correlata \"[Formattazione] Le colonne di relazione dovrebbero essere di tipo di dati intero\"). Avere colonne all'interno di una relazione che sono di tipi di dati diversi può portare a vari problemi.", "Severity": 3, "Scope": "Relationship", "Expression": "FromColumn.DataType != ToColumn.DataType", "CompatibilityLevel": 1200 }, + { + "ID": "AVOID_INVALID_NAME_CHARACTERS", + "Name": "[Prevenzione errori] Evita caratteri non validi nei nomi", + "Category": "[Prevenzione errori]", + "Description": "Questa regola identifica se un nome per un determinato oggetto nel tuo modello (ad esempio tabella/colonna/misura) contiene un carattere non valido. I caratteri non validi causano un errore durante il deploy del modello (e il fallimento del deploy). Questa regola ha un'espressione di correzione che converte il carattere non valido in uno spazio, risolvendo il problema.", + "Severity": 3, + "Scope": "Table, Measure, Hierarchy, Level, Perspective, Partition, DataColumn, CalculatedColumn, CalculatedTable, CalculatedTableColumn, KPI, ModelRole, CalculationGroup, CalculationItem", + "Expression": "Name.ToCharArray().Any(char.IsControl(it) and !char.IsWhiteSpace(it))", + "FixExpression": "Name = string.Concat( it.Name.ToCharArray().Select( c => (char.IsControl(c) && !char.IsWhiteSpace(c)) ? ' ': c ))", + "CompatibilityLevel": 1200 + }, + { + "ID": "AVOID_INVALID_DESCRIPTION_CHARACTERS", + "Name": "[Prevenzione errori] Evita caratteri non validi nelle descrizioni.", + "Category": "[Prevenzione errori]", + "Description": "Questa regola identifica se una descrizione per un determinato oggetto nel tuo modello (ad esempio tabella/colonna/misura) contiene un carattere non valido. I caratteri non validi causano un errore durante il deploy del modello (e il fallimento del deploy). Questa regola ha un'espressione di correzione che converte il carattere non valido in uno spazio, risolvendo il problema.", + "Severity": 3, + "Scope": "Table, Measure, Hierarchy, Level, Perspective, Partition, DataColumn, CalculatedColumn, CalculatedTable, CalculatedTableColumn, KPI, ModelRole, CalculationGroup, CalculationItem", + "Expression": "Description.ToCharArray().Any(char.IsControl(it) and !char.IsWhiteSpace(it))", + "FixExpression": "Description = string.Concat( it.Description.ToCharArray().Select( c => (char.IsControl(c) && !char.IsWhiteSpace(c)) ? ' ': c ))", + "CompatibilityLevel": 1200 + }, { "ID": "UNNECESSARY_COLUMNS", "Name": "[Manutenzione] Rimuovi colonne non necessarie", @@ -440,7 +462,7 @@ "Description": "Le origini dati a cui non fa riferimento alcuna partizione possono essere rimosse.", "Severity": 1, "Scope": "ProviderDataSource, StructuredDataSource", - "Expression": "UsedByPartitions.Count() == 0", + "Expression": "UsedByPartitions.Count() == 0\r\nand not Model.Tables.Any(SourceExpression.Contains(OuterIt.Name))\r\nand not Model.AllPartitions.Any(Query.Contains(OuterIt.Name))", "FixExpression": "Delete()", "CompatibilityLevel": 1200 }, @@ -517,6 +539,16 @@ "Expression": "Name.IndexOf(char(9)) > -1\r\nor\r\n\nName.IndexOf(char(10)) > -1 \r\nor\r\n\nName.IndexOf(char(13)) > -1", "CompatibilityLevel": 1200 }, + { + "ID": "TRIM_OBJECT_NAMES", + "Name": "[Naming Conventions] Accorpa i nomi degli oggetti.", + "Category": "Naming Conventions", + "Description": "Lasciare accidentalmente uno spazio in coda a un nome di oggetto è un'occorrenza comune quando si copiano/duplicano oggetti in Tabular Editor.", + "Severity": 1, + "Scope": "Model, Table, Measure, Hierarchy, Level, Perspective, Partition, ProviderDataSource, DataColumn, CalculatedColumn, CalculatedTable, CalculatedTableColumn, StructuredDataSource, NamedExpression, ModelRole, CalculationGroup, CalculationItem", + "Expression": "Name.StartsWith(\" \") or Name.EndsWith(\" \")", + "CompatibilityLevel": 1200 + }, { "ID": "FORMAT_FLAG_COLUMNS_AS_YES/NO_VALUE_STRINGS", "Name": "[Formattazione] Formatta le colonne flag come stringhe di valori Sì/No", @@ -616,7 +648,7 @@ "ID": "ADD_DATA_CATEGORY_FOR_COLUMNS", "Name": "[Formattazione] Aggiungi categoria di dati per colonne", "Category": "Formattazione", - "Description": "Aggiungi la proprietà Categoria dati per le colonne appropriate. Riferimento: https://docs.microsoft.com/power-bi/transform-model/desktop-data-categorization", + "Description": "Aggiungi la proprietà Categoria dati per le colonne appropriate. Riferimento: https://docs.microsoft.com/en-us/power-bi/transform-model/desktop-data-categorization", "Severity": 1, "Scope": "DataColumn, CalculatedColumn, CalculatedTableColumn", "Expression": "string.IsNullOrWhitespace(DataCategory)\r\nand\r\n(\r\n(\r\nName.ToLower().Contains(\"country\")\r\nor \r\n\nName.ToLower().Contains(\"continent\"\n)\r\nor\r\nName.ToLower().Contains(\"city\")\r\n)\r\nand DataType == \"String\"\r\n)\r\nor \r\n(\r\n(\nName.ToLower() == \"latitude\" \n or \nName.ToLower() == \"longitude\")\r\nand (DataType == DataType.Decimal or DataType == DataType.Double)\r\n)",