Numeriblog Google Workspace Google Apps Script, la puissance des outils Google à portée de scripts BigQuery, Google Apps Script, Google Sheets : visualiser des données volumineuses dans un tableur

BigQuery, Google Apps Script, Google Sheets : visualiser des données volumineuses dans un tableur

Vous disposez des données dans des tables BigQuery et vous voulez les exploiter dans Google Sheets ? Voici une solution qui utilise Google Apps Script et le service dédié BigQuery Prérequis : Posséder un projet […]

personnes ont consulté cet article

4 minutes

Rédigé par Thierry Vanoffe - il y a 2 ans

Ce que vous allez découvrir

  • Prérequis :
  • Sélection de la table BigQuery à afficher : côté client
  • Récupération et affichage des données de la table BigQuery : côté serveur
  • Pour aller plus loin, essayez de :
  • Code

BigQuery, Google Apps Script, Google Sheets : visualiser des données volumineuses dans un tableur

Vous disposez des données dans des tables BigQuery et vous voulez les exploiter dans Google Sheets ? Voici une solution qui utilise Google Apps Script et le service dédié BigQuery

Prérequis :

  • Posséder un projet Google Cloud Platform (GCP)
  • Disposer de données dans une table BigQuery

Contraintes :

  • L’exploitation des données dans Google Sheets est possible tant que le total des cellules renvoyées par vos requêtes ne dépasse pas les 5 millions.
  • Faire attention aux rafraîchissements des données qui définissent votre facture pour l’utilisation de BigQuery. Pas de panique, vous pouvez gratuitement stocker 10 Go de données ainsi qu’analyser (via les requêtes) jusqu’à 1 To par mois.

Le code Google Apps Script est divisé en deux parties : client (front end) et serveur (back end).

Le déroulement du script est le suivant :

  • Un utilisateur choisit parmi la liste automatique des ensembles de données et leurs tables respectives pour un projet GCP donné.
  • Les informations de la table sélectionnée ainsi que 10 lignes (petit échantillon pour vous montrer le fonctionnement) de la table sont ajoutées dans un nouvel onglet 

Sélection de la table BigQuery à afficher : côté client

Le fichier : index.html

La balise <form> contient le formulaire affiché côté utilisateur. Vous affichez automatiquement les ensembles de données BigQuery (DataSet) liés au projet GCP indiqué dans le code et les tables du DataSet sélectionné.

Une fois la table sélectionnée, ces 10 premières lignes sont affichées dans un nouvel onglet.

<form>
      <label for="dataset">Sélectionnez l'ensemble de données :</label>
      <select id="selectDatasets" name="dataset">
      </select>
      <button id="datasetsButton" onclick="getTablesList()">OK</button>
      <br><br>
      <label id="tablesLabel" style="display:none" for="table">Sélectionnez la table :</label>
      <select style="display:none" id="selectTables" name="table">
      </select>
      <button style="display:none" id="tablesButton" onclick="getTable()">OK</button>
</form>   

Récupération et affichage des données de la table BigQuery : côté serveur

Le reste de l’exécution du script se déroule désormais côté serveur.

  • Récupérez les informations de la table avec :
BigQuery.Tables.get(projectId, datasetId, tableId);
  • Obtenez les 10 premières lignes de la table avec :
const request = {
   query: `SELECT * FROM [${projectId}:${datasetId}.${tableId}] LIMIT 10;`,   
   useLegacySql: true,
 };
let queryResults = BigQuery.Jobs.query(request, projectId);
const jobId = queryResults.jobReference.jobId;
queryResults = BigQuery.Jobs.getQueryResults(projectId, jobId);

Il ne vous reste plus qu’à afficher le résultat dans un nouvel onglet

Pour aller plus loin, essayez de :

  • Afficher les données filtrées d’une table BigQuery avec des paramètres renseignés par un utilisateur
  • Ajouter un déclencheur automatique pour mettre à jour les données d’une table BigQuery selon une fréquence donnée

Code

Voici le code complet de ce cas d’usage, si vous avez besoin de conseils pour l’implémenter et/ou le modifier, n’hésitez pas à prendre contact avec nous !

index.html

<!DOCTYPE html>
<html>
  <head>    
    <base target="_top">
  </head>
  <body onload="getDatasets()">
    <h3>Afficher les données d'une table BigQuery</h3> 
    <br>
    <form>
      <label for="dataset">Sélectionnez l'ensemble de données :</label>
      <select id="selectDatasets" name="dataset">
      </select>
      <button id="datasetsButton" onclick="getTables()">OK</button>
      <br><br>
      <label id="tablesLabel" style="display:none" for="table">Sélectionnez la table :</label>
      <select style="display:none" id="selectTables" name="table">
      </select>
      <button style="display:none" id="tablesButton" onclick="getTable()">OK</button>
    </form>   
      <script>
        let toDisplay = [
          'tablesLabel',
          'selectTables',
          'tablesButton'
        ];
        let datasetId, tableId;
        function getDatasets(){
          google.script.run.withSuccessHandler(updateSelect).getDatasets();
        }
        function getTables(){
          const select = document.getElementById('selectDatasets');
          datasetId = select.options[select.selectedIndex].value;   
          google.script.run.withSuccessHandler(updateSelect).getTables(datasetId);
        }
        function getTable(){
          const select = document.getElementById('selectTables');
          tableId = select.options[select.selectedIndex].value;
          const params = [datasetId, tableId];
          google.script.run.getTable(params);
        }  
        function updateSelect([optionList, dest]){
          const select = document.getElementById(dest);
          optionList.forEach(option => {
            const newOption = document.createElement("option");
            newOption.textContent = option; 
            select.appendChild(newOption);
          })
          if (dest === "selectTables"){
            toDisplay.forEach(id => {
              document.getElementById(id).style.display = 'inline'
              if (id === 'selectTables') document.getElementById(id).style.width = '60%';
            });
          }
        }
      </script>
  </body>
</html>

server.gs

// Remplacer par l’ID de votre projet GCP
const projectId = 'XXXXX';
 
function showSidebarData() {
  var html = HtmlService.createHtmlOutputFromFile("index");
  SpreadsheetApp.getUi().showSidebar(html);
}
 
function getTable([datasetId, tableId]){
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  let sheet, headers, numRows;
 
  const setTableInfo = (tableInfo) => {
    headers = tableInfo.schema.fields.map(field => field.name);
    numRows = tableInfo.numRows;
    const totals = ["total cellules pour la table", headers.length * numRows, "num lignes", numRows];
    sheet = ss.getSheetByName(tableInfo.tableReference.tableId);
    if (sheet === null) sheet = ss.insertSheet(tableInfo.tableReference.tableId);
    setFlatValues(sheet, [[totals], [headers]]);
  };
  const setFlatValues = (sheet, arrayValues) => {
    sheet.getDataRange().clearContent();
    arrayValues.forEach((array,i) => {
      sheet.getRange(2 + i,1,1, array[0].length).setValues(array);
    })
  };
  
  //On récupère les infos de la table
  const tableInfo = BigQuery.Tables.get(projectId, datasetId, tableId);
  // On ajoute les infos de la table dans le fichier Google Sheets
  setTableInfo(tableInfo);
 
  //On récupère les données de la table BigQuery
  const request = {
    query: `SELECT * FROM [${projectId}:${datasetId}.${tableId}] LIMIT 10;`,    
    useLegacySql: true,
  };
  let queryResults = BigQuery.Jobs.query(request, projectId);
  const jobId = queryResults.jobReference.jobId;
 
  // On vérifie le statut du job BigQuery
  const sleepTimeMs = 500;
  while (!queryResults.jobComplete) {
    Utilities.sleep(sleepTimeMs);
    sleepTimeMs *= 2;
    queryResults = BigQuery.Jobs.getQueryResults(projectId, jobId);
  }
 
  // On récupère toutes les lignes du résultat de la requête
  const rows = queryResults.rows;
  if (rows) {
    // On ajoute ces lignes au fichier Google Sheets.
    const data = rows.map(row => row.f.map(el => el.v));
    sheet.getRange(sheet.getLastRow() + 1, 1, rows.length, headers.length).setValues(data);
  } else {
    Logger.log('Aucune ligne retournée.');
  }
  return ;
}
 
function getDatasets(){
  const datasetsList = [];  
  BigQuery.Datasets.list(projectId).datasets.forEach(dataset => {
    datasetsList.push(dataset.datasetReference.datasetId);
  });
  return [datasetsList, "selectDatasets"];
}
 
function getTables(datasetId){
  const tablesList = [];
  BigQuery.Tables.list(projectId, datasetId).tables.forEach(table => {
    tablesList.push(table.tableReference.tableId);
  });
  return [tablesList, "selectTables"];
}

Besoin d'un peu plus d'aide sur Google Forms ?

Des formateurs sont disponibles toute l'année pour vous accompagner et optimiser votre utilisation de Google Forms, que ce soit pour votre entreprise ou pour vos besoins personnels !

Découvrir nos formations Google Forms

Rédacteur

Photo de profil de l'auteur
Thierry Vanoffe

Thierry VANOFFE, consultant, formateur, coach Google Workspace CEO de Numericoach, leader de la formation Google Workspace en France. Passionné par Google, ce blog me permet de partager cette passion et distiller tutos, trucs, astuces, guides sur les outils Google. N'hésitez pas à me solliciter pour vos projets de formation.

S’abonner
Notification pour
2 Commentaires
Le plus ancien
Le plus récent Le plus populaire
Commentaires en ligne
Afficher tous les commentaires
Fabien
Fabien

Excellent, bravo ! Le style du code, en javascript moderne et optimisé est un pur bonheur pour les geeks 🙂
Et au demeurant le résultat est un outils très pratique.
Juste une petite correction:
var html = HtmlService.createHtmlOutputFromFile(« indexData »);
à remplacer par:
var html = HtmlService.createHtmlOutputFromFile(« index »);

Cet avis vous a été utile ?

Administrateur
Répondre à  Fabien
1 année il y a

Merci Fabien, si c’est pratique et ré-utilisé je suis heureux !
Merci aussi pour la coquille, c’est corrigé.

Cet avis vous a été utile ?