📆 Publié le
5 min de lecture
mack par Macktireh

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 :

parameters

  • ENVIRONMENT : choix entre LOCAL et PRODUCTION
  • URL_CSV_FILE : lien vers le fichier CSV
  • MySQL_HOSTNAME : nom d’hôte MySQL
  • MySQL_DATABASE_NAME : nom de la base de données
  • MySQL_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 :

is_prod

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.

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]),
SourceMySQL = MySQL.Database(
MySQL_HOSTNAME,
MySQL_DATABASE_NAME,
[
ReturnSingleDatabase = true,
Query = MySQLQuery,
CreateNavigationProperties = false
]
),
Result = if IS_PRODUCTION then SourceMySQL else CsvPromotedHeaders
in
Result
in
GetData

Cette fonction réalise trois opérations essentielles :

  1. 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
  2. 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
  3. 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”.

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
Result
in
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” .