Gestion flexible des sources de données dans Power BI avec MySQL
Table des matières
Introduction
Dans le cadre d’un projet client sur lequel je travaille actuellement, nous avons été confrontés à un défi passionnant : concevoir un tableau de bord Power BI connecté à une base de données MySQL contenant des millions de lignes. Cette expérience nous a permis de développer une solution efficace pour adapter les sources de données en fonction de l’environnement de travail. Dans cet article, je vous expliquerai les étapes de mise en place de cette solution, en abordant les problématiques rencontrées et les solutions mises en place.
Problématiques rencontrées
1. Problèmes de connectivité
Après l’installation du driver MySQL pour Power Query, nous avons rencontré des difficultés de connexion liées à la configuration du pare-feu sur le poste professionnel du client. Fait intéressant, la connexion fonctionnait parfaitement depuis mon ordinateur personnel et lors de l’actualisation des rapports dans le service Power BI.
2. Performance et volume de données
La base de données contenait plusieurs millions de lignes, rendant le chargement local des données particulièrement chronophage. Cette situation nécessitait une approche alternative pour optimiser les performances en environnement de développement.
Solution mise en place
1. Architecture de la solution
Pour résoudre ces problèmes, nous avons développé une approche hybride :
- En environnement local (Power BI Desktop) : utilisation de fichiers CSV stockés sur SharePoint
- En production (Power BI Service) : connexion directe à la base de données MySQL
2. Préparation des données
Pour simplifier le développement en local, nous avons d’abord rédigé la requête SQL permettant d’extraire les données nécessaires. Ensuite, nous avons conçu un script Python qui se connecte à la base de données, exécute la requête, exporte les résultats au format CSV, puis nous avons déposé les fichiers sur un site SharePoint.
3. Configuration des paramètres
Nous avons mis en place cinq paramètres permettant de gérer dynamiquement les sources de données :
ENVIRONMENT
: choix entre LOCAL et PRODUCTIONURL_CSV_FILE
: lien vers le fichier CSVMySQL_HOSTNAME
: nom d’hôte MySQLMySQL_DATABASE_NAME
: nom de la base de donnéesMySQL_QUERY
: requête SQL préconstruite
Implémentation technique
1. Variable dynamique de détection d’environnement
Nous avons créé une variable dynamique booléenne IS_PRODUCTION
qui simplifie la détection de l’environnement :
let Source = Text.Contains(Text.Upper(ENVIRONMENT), "PROD")in Source
Cette variable retourne automatiquement :
True
si le paramètre ENVIRONMENT contient “PROD”False
dans tous les autres cas
Cette approche nous permet d’utiliser simplement IS_PRODUCTION
dans nos conditions plutôt que de réécrire la formule complète Text.Contains(Text.Upper(ENVIRONMENT), "PROD")
à chaque fois.
2. Fonction GetData
Nous avons créé une fonction Power Query nommée GetData
, permettant de récupérer les données depuis MySQL en production et depuis un fichier CSV en local.
let GetData = (NumberColumnsCSV as number, MySQLQuery as text) => let CsvSource = Csv.Document( Web.Contents(URL_CSV_FILE), [ Delimiter = ",", Columns = NumberColumnsCSV, Encoding = 1252, QuoteStyle = QuoteStyle.None ] ), CsvPromotedHeaders = Table.PromoteHeaders(CsvSource, [PromoteAllScalars = true]),
SourceMySQL = MySQL.Database( MySQL_HOSTNAME, MySQL_DATABASE_NAME, [ ReturnSingleDatabase = true, Query = MySQLQuery, CreateNavigationProperties = false ] ), Result = if IS_PRODUCTION then SourceMySQL else CsvPromotedHeaders in Resultin GetData
Cette fonction réalise trois opérations essentielles :
-
Chargement des données locales :
- Récupère un fichier CSV depuis une URL
- Utilise un délimiteur de virgule
- Spécifie le nombre de colonnes
- Gère l’encodage et le style de citation
- Promeut automatiquement la première ligne comme en-têtes
-
Connexion à la base de données :
- Établit une connexion à MySQL en production
- Exécute une requête SQL personnalisée
- Récupère les données directement depuis la base
-
Sélection dynamique de la source :
- Bascule automatiquement entre CSV local et base MySQL
- Utilise la variable
IS_PRODUCTION
comme critère de sélection
3. Utilisation de la Fonction GetData
Pour utiliser la fonction GetData, il suffit de créer une nouvelle requête et de la renommer comme vous le souhaitez. Dans cet exemple, nous l’appelons “MyData”.
let Source = GetData(5, MySQL_QUERY)in Source
Cette approche permet de récupérer les données en spécifiant uniquement deux arguments :
- Le nombre de colonnes du fichier CSV (5 dans cet exemple)
- La requête MySQL à exécuter (via le paramètre MySQL_QUERY)
c’est simple, n’est-ce pas ? 🙂
4. Optimisation des Transformations des Données
Dans certains cas, nous devons appliquer des transformations spécifiques aux données CSV. Pour cela, nous avons identifié deux approches possibles :
— Modification de la Fonction GetData
Une première approche consiste à intégrer les transformations directement dans la fonction GetData :
let GetData = (NumberColumnsCSV as number, MySQLQuery as text) => let CsvSource = Csv.Document( Web.Contents(URL_CSV_FILE), [ Delimiter = ",", Columns = NumberColumnsCSV, Encoding = 1252, QuoteStyle = QuoteStyle.None ] ), CsvPromotedHeaders = Table.PromoteHeaders(CsvSource, [PromoteAllScalars = true]),
CsvReplacedValue = Table.ReplaceValue( CsvPromotedHeaders, ".", ",", Replacer.ReplaceText, {"my_column_numeric"} ),
SourceMySQL = MySQL.Database( MySQL_HOSTNAME, MySQL_DATABASE_NAME, [ ReturnSingleDatabase = true, Query = MySQLQuery, CreateNavigationProperties = false ] ), Result = if IS_PRODUCTION then SourceMySQL else CsvPromotedHeaders Result = if IS_PRODUCTION then SourceMySQL else CsvReplacedValue in Resultin GetData
Cependant, cette approche n’est pas recommandée car elle va à l’encontre du principe de responsabilité unique : une fonction devrait idéalement n’avoir qu’une seule responsabilité.
— Transformation dans la Requête MyData
Une approche plus élégante consiste à séparer la récupération des données de leur transformation. Mettons à jour notre requête “MyData” pour appliquer les transformations spécifiques aux données CSV :
let Source = GetData(5, MySQL_QUERY), ReplacedValue = Table.ReplaceValue( Source, ".", ",", Replacer.ReplaceText, {"my_column_numeric"} ), ChangedType = Table.TransformColumnTypes( if IS_PRODUCTION then Source else ReplacedValue, {{"my_column_numeric", type number}} )in ChangedType
Cette seconde approche offre plusieurs avantages :
- Séparation claire des responsabilités
- Meilleure maintenabilité du code
- Plus grande flexibilité pour modifier les transformations
- Possibilité d’appliquer des transformations conditionnelles selon l’environnement
Conclusion
Dans cet article, nous avons exploré une solution pour gérer efficacement les sources de données dans Power BI en mettant en place un système de basculement dynamique entre des fichiers CSV locaux et une base de données MySQL en production. L’approche que nous avons développée n’a pas seulement résolu nos défis immédiats de connectivité et de performance, mais a également fourni un cadre flexible qui peut être facilement adapté à d’autres environnements ou sources de données. Cette approche peut facilement être adaptée et étendue pour répondre à d’autres besoins par exemple ajout de nouveaux environnements (test, pré-production) ou support d’autres sources de données (PostgreSQL, Oracle, etc.). Sinon, si vous souhaitez importer et combiner plusieurs fichiers Excel/CSV de manière propre et optimisée, je vous invite à consulter mon article “Importer Plusieurs Fichiers Excel/CSV dans Power BI avec une Fonction Power Query Personnalisée” .