1. Présentation
La plupart des bugs SQL qui survivent à la revue de code sont des bugs d'agrégation. Un nombre est correct dans le tableau où il se trouve, une jointure le copie et le total est incorrect sans que vous le sachiez.
BigQuery Graph vous permet de décrire les connexions entre vos tables une seule fois, sous la forme d'un graphique de propriétés, puis d'interroger ces connexions. Les mesures de graphique ajoutent une chose de plus à cette description : l'agrégation elle-même. Vous déclarez MEASURE(SUM(streams)) sur le nœud qui possède le nombre, et chaque requête qui lit le graphique obtient le bon total avec la bonne précision. Pas de tour de passe-passe GROUP BY, pas de CTE que quelqu'un doit mémoriser.
Dans cet atelier de programmation, vous allez créer un catalogue musical de cinq tables, y placer une mesure et constater que la même question renvoie deux réponses différentes : 12,6 milliards avec SUM et 5,1 milliards avec AGG. L'écart correspond à la forme du catalogue : un titre qui figure dans quatre playlists devient quatre lignes une fois le graphique aplati, et SUM ajoute les quatre. Vous apprendrez ensuite les règles qui rendent la deuxième phrase correcte.

Où saisir du texte
Cet atelier utilise exactement deux emplacements, dans cet ordre :
où | ce que vous y faites. | |
1 | Terminal Cloud Shell | quelques commandes, uniquement à l'étape Configuration |
2 | Console BigQuery | Tout le reste : coller des requêtes dans l'éditeur de requête, cliquer dans le modélisateur de graphiques, discuter avec un agent |
Chaque instruction est taguée pour que vous sachiez toujours de laquelle il s'agit :
- 👉💻 Cloud Shell : saisissez cette commande dans le terminal Cloud Shell.
- 👉📋 Éditeur de requête : collez cette requête dans l'éditeur de requête BigQuery, puis cliquez sur Exécuter.
- 👉🌐 Console : explorez la console BigQuery. Vous n'avez rien à saisir, sauf si l'étape l'indique.
- 👀 : regardez le résultat.
Points abordés
- Déclarer une mesure dans une table de nœuds (dans le modélisateur de graphiques visuels ou en SQL)
- Comment
GRAPH_EXPANDaplatit un graphique en un tableau large - Pourquoi
AGGest la seule fonction capable de lire une mesure et ce que répondent les autres tentatives - Les six agrégats qu'une mesure accepte et comment effectuer des opérations arithmétiques sur ces agrégats
- Comment une mesure répond à une question regroupée différemment sans aucune modification
- Facultatif : comment GQL et l'onglet "Graphique" répondent à une question à laquelle un total ne peut pas répondre, et comment un agent de données BigQuery utilise vos mesures sans que vous ayez à les lui indiquer
Prérequis
- Un compte Google pour lequel la facturation est disponible, ainsi qu'un navigateur. Vous n'avez pas besoin d'un projet Google Cloud au préalable, car la configuration en crée un pour vous.
- Pour l'étape de chat facultative uniquement : l'API Gemini Data Analytics est activée et le rôle Créateur d'agent de données des analyses de données Gemini est attribué.
Vous n'avez rien à installer sur votre ordinateur. Tout s'exécute dans Cloud Shell et la console BigQuery.
État et coût du produit
BigQuery Graph est désormais en disponibilité générale. Les mesures de graphiques, le modélisateur visuel de graphiques, la visualisation de graphiques et l'utilisation d'un graphique comme source de connaissances pour un agent de données sont en version Preview et sont couverts par les Conditions d'utilisation des offres antérieures à la disponibilité générale.
GRAPH_EXPAND et AGG sont facturés au tarif à la demande. Les requêtes GQL (étape de chemin d'accès facultative) nécessitent une réservation qui utilise l'édition Enterprise ou Enterprise Plus, conformément à la documentation. C'est pourquoi cette étape est facultative.
L'ensemble de données ne pèse que quelques kilo-octets. Avec la tarification à la demande, chaque requête est facturée au minimum de 10 Mo. L'ensemble de l'atelier de programmation ne coûte donc qu'une fraction de centime.
2. Configuration
Il s'agit de la seule étape qui utilise Cloud Shell. Il crée un projet, y associe la facturation et crée les tables brutes. Vous pourrez ensuite exécuter le graphique, la mesure et chaque requête dans la console BigQuery.
Il y a une chose que cet atelier ne peut pas vous fournir : un projet Google Cloud avec la facturation activée. L'étape 1 vous permet d'obtenir un compte qui peut payer, tandis que l'étape 4 transforme ce compte en projet. Effectuez-les dans cet ordre. Chaque étape après la quatrième lit le projet créé à l'étape 4.
1 · Obtenir un compte avec facturation (~1 min)
👉🌐 Faites-le avant toute autre chose. Connectez-vous avec un compte Google pour lequel la facturation est activée. N'importe quel compte de ce type fonctionne. Si ce n'est pas le cas, démarrez un essai sans frais. Vous obtiendrez ainsi un compte de facturation, ce qui est tout ce dont vous avez besoin pour l'étape 4.
2 · Ouvrez Cloud Shell (environ 1 min)
👉 🌐 Console : cliquez sur Activer Cloud Shell en haut de la console Google Cloud. Il s'agit de l'icône en forme de terminal dans l'en-tête. Un terminal s'ouvre en bas de la page.

👉🌐 Console : si vous êtes invité à autoriser l'accès à un moment donné aujourd'hui, cliquez sur Autoriser pour continuer.
👉💻 Cloud Shell : vérifiez le compte avec lequel vous êtes connecté à Cloud Shell :
gcloud auth list
👀 Attendez-vous à voir votre propre compte marqué d'un *. Il doit s'agir du même compte que celui avec lequel vous vous êtes connecté à l'étape 1. Si ce n'est pas le cas, exécutez gcloud auth login et sélectionnez le bon.
3) Obtenir l'atelier (environ 1 min)
👉💻 Cloud Shell : clonez le dépôt :
cd ~
rm -rf ~/bq-graph-measures-lab
git clone https://github.com/cuppibla/bq-graph-measures-lab.git
cd ~/bq-graph-measures-lab
4 · Créez votre projet (~2 min)
👉💻 Cloud Shell : un script trouve votre compte de facturation, y crée un projet et pointe Cloud Shell vers ce projet :
cd ~/bq-graph-measures-lab
./setup_project.sh
👀 Attendez-vous à un ✓ pour le compte de facturation sélectionné, un ✓ pour le projet créé et une fin comme celle-ci :
Project ready: bq-graph-4271 Next: ./setup.sh
bq-graph-4271 est l'ID de votre projet. Le vôtre aura des chiffres différents. Quatre choses se passent :
facturation | prend le compte | |
project | crée | |
sélectionne | écrit l'ID dans | |
waits | interroge jusqu'à ce que le nouveau projet soit réellement autorisé à diffuser des annonces, pendant deux minutes maximum. | |
🌐 Le même projet se trouve désormais dans la console. Son ID figure dans la fiche Informations sur le projet du tableau de bord :

5 · Créer l'ensemble de données (~3 min)
👉💻 Cloud Shell : un autre script crée les tables :
cd ~/bq-graph-measures-lab
./setup.sh
Le script affiche chaque étape à mesure qu'il s'exécute. Voici ce qu'il fait, dans l'ordre :
project | lit le projet à partir de | |
API | permet | |
dataset | crée l'ensemble de données | |
check | compte les lignes de chaque table et s'arrête si un nombre est incorrect. | |
👀 À savoir :
BigQuery Graph Measures · setup ✓ project: bq-graph-4271 ✓ BigQuery API enabled Creating dataset music (US) and five tables... ✓ music.Labels 2 rows ✓ music.Artists 3 rows ✓ music.Songs 5 rows ✓ music.Playlists 4 rows ✓ music.PlaylistEntries 10 rows 🎉 ready — open BigQuery Studio: https://console.cloud.google.com/bigquery?project=bq-graph-4271
Vous avez terminé avec Cloud Shell. Vous pouvez la fermer.
6 · Ouvrez BigQuery et un éditeur de requête.
👉 🌐 Console : cliquez sur le lien à la fin du résultat ou ouvrez BigQuery dans le menu de navigation. Dans le volet Explorateur à gauche, développez votre projet : un ensemble de données music avec cinq tables.

👉🌐 Console : en haut de BigQuery Studio, dans la ligne Créer, cliquez sur Requête SQL. Un onglet d'éditeur vide s'ouvre.

Conservez cet onglet. Chaque bloc 👉📋 de cet atelier doit être placé ici : sélectionnez le contenu de l'éditeur, collez le nouveau bloc par-dessus, puis cliquez sur Exécuter. Le résultat s'affiche dans le panneau ci-dessous.
Découvrir les tables
Deux éléments de ces tableaux sont importants pour la suite. Chaque table déclare sa clé primaire comme NOT ENFORCED. BigQuery ne la vérifie pas, mais le graphique de propriétés que vous allez créer a besoin d'une clé explicite pour savoir ce qui identifie une ligne. La dernière table, PlaylistEntries, est une table de jonction : un titre qui figure dans quatre playlists aura quatre lignes dans cette table. C'est dans ce tableau que tout se passe.
Les tables sont liées de la manière suivante : un label signe des artistes, un artiste sort des titres, un titre est placé dans des playlists via PlaylistEntries. Cymbal Records possède Ava Sterling et Mina Okafor, et donc quatre titres. Northwind Audio est propriétaire de Kai Nakamura et d'une chanson, Midnight Drive.
table | lignes | clé | ce qu'il contient ; |
Étiquettes | 2 |
| nom, budget marketing |
Artistes | 3 |
| nom, quelle étiquette |
Titres | 5 |
| titre, artiste, flux |
Playlists | 4 |
| name, followers |
PlaylistEntries | 10 |
| quelle chanson se trouve dans quelle playlist ; |
✅ Point de contrôle : 🎉 ready dans Cloud Shell, cinq tables sous music dans l'explorateur et un onglet d'éditeur de requête vide sont ouverts.
3. Créer le graphique : option A, le modeleur visuel
Un graphique de propriétés décrit la façon dont les tables sont connectées. Vous pouvez l'assembler dans le modélisateur de graphiques visuels (cette étape, en cliquant) ou l'écrire en SQL (l'étape suivante, en collant). Les deux produisent le même objet. Choisissez l'un ou l'autre, mais pas les deux.
1. Créer le shell du graphique
👉🌐 Console : dans BigQuery Studio, dans la ligne Créer, ouvrez le menu IA et connaissances, puis cliquez sur Graphique.
👉🌐 Console : saisissez MusicGraph comme nom du graphique et music comme destination du graphique dans l'ensemble de données. La région est renseignée à partir de l'ensemble de données (États-Unis).
👉🌐 Console : sous Sources de données, sélectionnez les cinq tables (Artistes, Labels, PlaylistEntries, Playlists, Songs), puis cliquez sur Créer.

👀 Le canevas s'ouvre avec cinq nœuds colorés et aucune ligne entre eux. Le panneau Détails à droite liste Nœuds (5) et Arêtes (0). Chaque nœud comporte déjà la clé primaire de sa table et toutes ses colonnes en tant que propriétés. Cliquez sur un nœud pour les afficher.

2. Dessiner les quatre bords
Une arête est une clé étrangère, dessinée. Chacune d'elles va de la table qui contient la clé à celle vers laquelle elle pointe.
👉🌐 Console : cliquez sur Nouvelle périphérie. Dans la boîte de dialogue Sélectionner une table, sélectionnez PlaylistEntries, puis cliquez sur Ajouter. Dans le panneau Ajouter un bord :
- Définissez Nom sur
EntryToSong. - Cliquez sur Ajouter une clé primaire et acceptez
entry_id. - Sous Source, définissez Nœud source sur "PlaylistEntries", puis utilisez Ajouter des colonnes référencées au nœud et Ajouter des colonnes référencées à l'arête pour sélectionner
entry_idpour les deux. - Sous Cible, définissez Nœud cible sur "Titres", puis sélectionnez
song_idpour Colonnes référencées dans le nœud et Colonnes référencées sur l'arête. - Cliquez sur OK.

👉🌐 Console : répétez trois fois de plus :
nom de l'arête | table | nœud source → nœud cible | la colonne à sélectionner des deux côtés. |
| PlaylistEntries | PlaylistEntries → Playlists | source : |
| Titres | Chansons → Artistes | source : |
| Artistes | Artistes → Labels | source : |
Laissez chaque arête sans propriétés supplémentaires.
👀 Le panneau "Détails" indique désormais Edges (4) (Arêtes (4)), et le canevas affiche la chaîne : Labels ← Artistes ← Titres ← PlaylistEntries → Playlists.

3. Ajouter la mesure
👉🌐 Console : cliquez sur le nœud Songs (Titres), puis sur l'icône Modifier (en forme de crayon) en haut du panneau "Détails". Sous Propriétés du libellé, cliquez sur Ajouter une propriété, puis sur Ajouter un champ.
👉🌐 Console : saisissez total_streams comme Nom du champ, puis saisissez ce qui suit dans la zone Expression :
MEASURE(SUM(streams))
👉 🌐 Console : cliquez sur Ajouter, puis sur OK.

4. Lire le code qu'il a écrit, puis le publier
👉🌐 Console : ouvrez l'onglet Code dans le panneau de droite. Le modélisateur a écrit l'instruction CREATE PROPERTY GRAPH pour vous. Faites défiler la page jusqu'au nœud "Songs" (Chansons) : sa liste de propriétés se termine désormais par
MEASURE(SUM(streams)) AS `total_streams`
Voilà toute la fonctionnalité, en une ligne. Tout ce qui suit cette étape concerne le fonctionnement de cette ligne.

👉🌐 Console : cliquez sur Publier.
✅ Point de contrôle : dans l'explorateur, music contient désormais un dossier Graphs (Graphiques) contenant MusicGraph. Cliquez dessus : cinq nœuds, quatre arêtes et la ligne MEASURE(SUM(streams)) dans l'onglet "Code". Ignorez l'étape suivante et passez à SUM et AGG, côte à côte.
4. Créer le graphique : option B (SQL)
Ignorez cette étape si vous avez créé le graphique dans le modélisateur.
👉📋 Éditeur de requête : collez la requête, puis cliquez sur Exécuter :
CREATE OR REPLACE PROPERTY GRAPH music.MusicGraph
NODE TABLES (
music.Labels KEY (label_id)
PROPERTIES (label_id, label_name, marketing_budget,
MEASURE(SUM(marketing_budget)) AS total_budget),
music.Artists KEY (artist_id)
PROPERTIES (artist_id, artist_name, label_id,
MEASURE(COUNT(artist_id)) AS artist_count),
music.Songs KEY (song_id)
PROPERTIES (song_id, title, artist_id, streams,
MEASURE(SUM(streams)) AS total_streams,
MEASURE(COUNT(song_id)) AS song_count),
music.Playlists KEY (playlist_id)
PROPERTIES (playlist_id, playlist_name, followers,
MEASURE(SUM(followers)) AS total_followers),
music.PlaylistEntries KEY (entry_id)
PROPERTIES (entry_id, playlist_id, song_id,
MEASURE(COUNT(entry_id)) AS placement_count)
)
EDGE TABLES (
music.PlaylistEntries AS EntryToSong
SOURCE KEY (entry_id) REFERENCES PlaylistEntries(entry_id)
DESTINATION KEY (song_id) REFERENCES Songs(song_id) NO PROPERTIES,
music.PlaylistEntries AS EntryToPlaylist
SOURCE KEY (entry_id) REFERENCES PlaylistEntries(entry_id)
DESTINATION KEY (playlist_id) REFERENCES Playlists(playlist_id) NO PROPERTIES,
music.Songs AS SongToArtist
SOURCE KEY (song_id) REFERENCES Songs(song_id)
DESTINATION KEY (artist_id) REFERENCES Artists(artist_id) NO PROPERTIES,
music.Artists AS ArtistToLabel
SOURCE KEY (artist_id) REFERENCES Artists(artist_id)
DESTINATION KEY (label_id) REFERENCES Labels(label_id) NO PROPERTIES
)
👀 Le panneau de résultats indique que le graphique de propriété a été créé.

Lire une fois
Le graphique comporte cinq tables de nœuds et quatre tables d'arêtes. Chaque tableau de nœuds nomme son KEY et liste ses PROPERTIES. Les lignes qui commencent par MEASURE sont la nouvelle partie : chacune d'elles encapsule l'un des agrégats compatibles autour d'une propriété et lui attribue un alias, ce qui est obligatoire.
Chaque table d'arête est une clé étrangère, qui s'écrit SOURCE KEY sur la table qui contient la clé et DESTINATION KEY sur la table vers laquelle elle pointe. NO PROPERTIES signifie que l'arête ne transporte rien en soi.
Notez que PlaylistEntries apparaît trois fois : une fois en tant que table de nœuds, et deux fois en tant que table physique derrière les arêtes EntryToSong et EntryToPlaylist. C'est volontaire, et nous vous expliquerons pourquoi plus tard.
Découvrez ce que vous avez créé
👉 🌐 Console : dans le volet "Explorateur", développez music, puis Graphiques et cliquez sur MusicGraph. La page du graphique s'ouvre sur un canevas avec cinq nœuds et quatre arêtes. Cliquez sur un nœud pour afficher ses propriétés. Les mesures sont listées avec leur agrégat. Ouvrez l'onglet Code pour voir l'instruction derrière l'image.

✅ Point de contrôle : music comporte un dossier Graphs contenant MusicGraph, et l'onglet "Code" affiche MEASURE(SUM(streams)) AS total_streams.
5. SUM et AGG, côte à côte
Il s'agit de l'ensemble de l'atelier de programmation en une seule requête. Les deux colonnes tentent de répondre à la même question (le nombre total de flux par maison de disques) à partir de la même table.
Il s'agit du tableau généré par GRAPH_EXPAND. Il aplatit le graphique : il commence par une table et suit chaque bordure vers l'extérieur sous la forme d'un LEFT JOIN, jusqu'à ce que chaque table connectée soit placée côte à côte dans un seul résultat large. Une ligne par emplacement, et non par titre, et chaque colonne doit être nommée TableLabel_property. Examinez-le avant de l'interroger.
👉📋 Éditeur de requête : collez la requête, puis cliquez sur Exécuter :
SELECT Labels_label_name, Songs_title, Songs_streams, Playlists_playlist_name
FROM GRAPH_EXPAND("music.MusicGraph")
ORDER BY Songs_title, Playlists_playlist_name
👀 Attendez-vous à 10 lignes, soit une pour chaque emplacement dans PlaylistEntries, ce qui correspond exactement à ce que Setup a chargé. "Neon Summer" en occupe quatre, un par playlist dans laquelle il figure, et Songs_streams indique 2 000 000 000 pour chacun d'eux. Il n'y a aucun problème avec ces lignes. Les playlists sont bien au nombre de quatre. Toutefois, une colonne appartenant à un titre est désormais répétée sur les lignes appartenant à des emplacements. C'est tout le problème que mesure la requête suivante.
Passons maintenant à la comparaison.
👉📋 Éditeur de requête : collez la requête, puis cliquez sur Exécuter :
SELECT
Labels_label_name,
SUM(Songs_streams) AS with_sum,
AGG(Songs_total_streams) AS with_agg
FROM GRAPH_EXPAND("music.MusicGraph")
GROUP BY 1 ORDER BY 1
👀 Résultat :
Labels_label_name | with_sum | with_agg |
Cymbal Records | 12 600 000 000 | 5 100 000 000 |
Northwind Audio | 9 000 000 000 | 4 500 000 000 |

Vérifiez-le manuellement. Les quatre titres de Cymbal ont été écoutés 2 milliards, 1,5 milliard, 1,2 milliard et 0,4 milliard de fois. Soit 5,1 milliards. AGG est correct. SUM est deux fois et demie trop grand, et la seule chanson de Northwind a été doublée.
Trois nouvelles syntaxes
GRAPH_EXPAND("music.MusicGraph")est une fonction de valeur de table qui transforme le graphique en un tableau large en suivant chaque arête comme unLEFT JOIN. Chaque colonne est nomméeTableLabel_property. Par conséquent,Songs_streamscorrespond à la colonne brute de la table "Songs" (Titres) etSongs_total_streamsà la mesure que vous avez déclarée.SUM(Songs_streams)est un SQL ordinaire sur cette table large et hérite de tous les doublons produits par l'aplatissement.AGG(Songs_total_streams)évalue la mesure telle qu'elle a été déclarée :SUM(streams)une fois parsong_id, quel que soit le nombre de lignes qu'un titre occupe dans le tableau large.
✅ Point de contrôle : deux libellés, deux colonnes et les nombres with_agg correspondent au nombre de mains.
6. Pourquoi la fonction SUM surestime-t-elle les valeurs ?
Prenez un titre et regardez ce que ces jointures lui font.
👉📋 Éditeur de requête : collez la requête, puis cliquez sur Exécuter :
SELECT 'BEFORE - the Songs table' AS step, title, streams, CAST(NULL AS STRING) AS playlist
FROM music.Songs WHERE title = 'Neon Summer'
UNION ALL
SELECT 'AFTER - joined to playlists', s.title, s.streams, pl.playlist_name
FROM music.Songs s
LEFT JOIN music.PlaylistEntries e ON e.song_id = s.song_id
LEFT JOIN music.Playlists pl ON pl.playlist_id = e.playlist_id
WHERE s.title = 'Neon Summer'
ORDER BY step DESC, playlist
👀 Résultat :
étape | titre | appareils IoT et flux d'application. | playlist |
AVANT : table "Songs" | Été fluo | 2 000 000 000 | null |
AFTER - joined to playlists | Été fluo | 2 000 000 000 | Gym Pump |
AFTER - joined to playlists | Été fluo | 2 000 000 000 | Tendances du moment |
AFTER - joined to playlists | Été fluo | 2 000 000 000 | Pop montante |
AFTER - joined to playlists | Été fluo | 2 000 000 000 | Road Trip |

Une ligne est devenue quatre. Le nombre de flux n'a pas changé, il a été copié. Additionnez les copies de Cymbal Records : Neon Summer 2.0 milliards sur quatre playlists, soit 8.0, Paper Crown 1.5 milliards sur deux, soit 3.0, Double Shot 1.2 sur une, Slow Arrow 0.4 sur une. Il s'agit de 12,6 milliards, le nombre SUM renvoyé. "Northwind's Midnight Drive" figure dans deux playlists. Le nombre de vues est donc passé de 4, 5 milliards à 9 milliards.
Il s'agit d'une distribution ramifiée, et non d'un bug dans la jointure. Les lignes de playlists sont réelles. L'erreur consiste à additionner un nombre appartenant à un titre sur des lignes appartenant à des emplacements.
La mesure corrige le problème, car elle a été déclarée sur le nœud "Songs" (Titres) avec KEY (song_id). La clé, c'est la précision. Lorsque AGG évalue total_streams, il agrège une fois par clé de chanson, puis regroupe les résultats en fonction de la dimension que vous avez choisie.
Qu'est-ce qui a changé ?
Vous avez écrit | Avec une mesure |
|
|
|
|
Trois |
|
CTE qui agrège avant la jointure | Rien. |
✅ Point de contrôle : cinq lignes (une AVANT et quatre APRÈS) à partir desquelles vous pouvez reconstruire 12, 6 milliards.
7. Règle : seul AGG lit une mesure
Les mesures ont leur propre type, MEASURE, et une seule fonction l'accepte. Les quatre requêtes ci-dessous échouent volontairement. Exécutez-les et lisez l'erreur en rouge. Ce sont les quatre façons que tout le monde essaie en premier. Savoir ce qu'elles disent vous fera gagner du temps par la suite.
👉📋 Éditeur de requête – AGG sur une colonne simple :
SELECT AGG(Songs_streams) FROM GRAPH_EXPAND("music.MusicGraph")
👀 No matching signature for aggregate function AGG ... Argument 1: expected MEASURE

👉📋 Éditeur de requête : colonne de mesure sans AGG :
SELECT Songs_total_streams FROM GRAPH_EXPAND("music.MusicGraph") LIMIT 1
👀 Returning expressions of type MEASURE is not allowed

👉📋 Éditeur de requête : mesure référencée dans GQL, qui ne voit jamais de mesures (il s'agit d'une requête GQL ; consultez la note d'édition dans la présentation) :
GRAPH music.MusicGraph
MATCH (s:Songs)
RETURN s.total_streams
LIMIT 1
👀 total_streams is not exposed by element type GRAPH_NODE(...)

👉📋 Éditeur de requête : SUM au-dessus d'une colonne de mesure, celle qui vous fait envie :
SELECT Labels_label_name, SUM(Songs_total_streams)
FROM GRAPH_EXPAND("music.MusicGraph")
GROUP BY 1
👀 MEASURE-typed arguments are only permitted in the AGG function

Le système de typographie applique donc la règle pour vous. Une mesure n'est visible que dans GRAPH_EXPAND et n'est lisible que dans AGG. Les 12,6 milliards comptabilisés en trop auparavant ne peuvent provenir que de la somme de la colonne brute, et jamais d'une mauvaise gestion de la mesure.
✅ Point de contrôle : quatre erreurs rouges, et vous pouvez indiquer la règle que chacune d'elles applique.
8. Chaque agrégat qu'une mesure accepte
MEASURE() encapsule l'un des six agrégats : SUM, AVG, MIN, MAX, COUNT et COUNT(DISTINCT). Recréez le graphique avec les six nœuds sur le nœud "Songs" (Chansons). Les arêtes restent NO PROPERTIES, ce qui est la bonne pratique documentée lorsqu'une arête partage sa table physique avec un nœud.
👉📋 Éditeur de requête : collez la requête, puis cliquez sur Exécuter :
CREATE OR REPLACE PROPERTY GRAPH music.MusicGraph
NODE TABLES (
music.Labels KEY (label_id)
PROPERTIES (label_id, label_name, marketing_budget,
MEASURE(SUM(marketing_budget)) AS total_budget),
music.Artists KEY (artist_id)
PROPERTIES (artist_id, artist_name, label_id,
MEASURE(COUNT(artist_id)) AS artist_count),
music.Songs KEY (song_id)
PROPERTIES (song_id, title, artist_id, streams,
MEASURE(SUM(streams)) AS total_streams,
MEASURE(AVG(streams)) AS avg_streams,
MEASURE(MIN(streams)) AS min_streams,
MEASURE(MAX(streams)) AS max_streams,
MEASURE(COUNT(song_id)) AS song_count,
MEASURE(COUNT(DISTINCT artist_id)) AS distinct_artists),
music.Playlists KEY (playlist_id)
PROPERTIES (playlist_id, playlist_name, followers,
MEASURE(SUM(followers)) AS total_followers),
music.PlaylistEntries KEY (entry_id)
PROPERTIES (entry_id, playlist_id, song_id,
MEASURE(COUNT(entry_id)) AS placement_count)
)
EDGE TABLES (
music.PlaylistEntries AS EntryToSong
SOURCE KEY (entry_id) REFERENCES PlaylistEntries(entry_id)
DESTINATION KEY (song_id) REFERENCES Songs(song_id) NO PROPERTIES,
music.PlaylistEntries AS EntryToPlaylist
SOURCE KEY (entry_id) REFERENCES PlaylistEntries(entry_id)
DESTINATION KEY (playlist_id) REFERENCES Playlists(playlist_id) NO PROPERTIES,
music.Songs AS SongToArtist
SOURCE KEY (song_id) REFERENCES Songs(song_id)
DESTINATION KEY (artist_id) REFERENCES Artists(artist_id) NO PROPERTIES,
music.Artists AS ArtistToLabel
SOURCE KEY (artist_id) REFERENCES Artists(artist_id)
DESTINATION KEY (label_id) REFERENCES Labels(label_id) NO PROPERTIES
)

Maintenant, relis-les tous. Chaque mesure passe par AGG. La dernière colonne montre que les résultats AGG sont des valeurs ordinaires avec lesquelles vous pouvez effectuer des calculs arithmétiques. Le graphique vient de changer. Commencez donc par décocher l'option Utiliser les résultats mis en cache (Plus → Paramètres de requête).
👉📋 Éditeur de requête : collez la requête, puis cliquez sur Exécuter :
SELECT
Labels_label_name,
AGG(Songs_total_streams) AS total_streams,
AGG(Songs_avg_streams) AS avg_streams,
AGG(Songs_min_streams) AS min_streams,
AGG(Songs_max_streams) AS max_streams,
AGG(Songs_song_count) AS songs,
AGG(Songs_distinct_artists) AS artists,
AGG(Songs_total_streams) / AGG(Songs_song_count) AS streams_per_song
FROM GRAPH_EXPAND("music.MusicGraph")
GROUP BY 1
ORDER BY 1
👀 Résultat :
Labels_label_name | total_streams | avg_streams | min_streams | max_streams | chansons | artistes | streams_per_song |
Cymbal Records | 5 100 000 000 | 1 275 000 000 | 400 000 000 | 2 000 000 000 | 4 | 2 | 1 275 000 000 |
Northwind Audio | 4 500 000 000 | 4 500 000 000 | 4 500 000 000 | 4 500 000 000 | 1 | 1 | 4 500 000 000 |

Chaque nombre est calculé sur quatre titres pour Cymbal et un pour Northwind, et non sur les dix lignes d'emplacements. AVG aurait été incorrect dans l'autre sens : la moyenne de la colonne brute sur les lignes d'emplacement donne 1,575 milliard pour Cymbal, car les 2 milliards de Neon Summer sont comptabilisés quatre fois.
"Remplacer SUM par AGG" est en fait une double décision. Lorsque vous déclarez la mesure, vous choisissez toujours l'un des six agrégats. Lorsque vous la lisez, la fonction est toujours AGG.
✅ Point de contrôle : une ligne par libellé, sept nombres chacun et streams_per_song égal à avg_streams.
9. Modifier le regroupement, pas la mesure
La même mesure répond à une question différente. Regroupez les titres par playlist plutôt que par label.
👉📋 Éditeur de requête : collez la requête, puis cliquez sur Exécuter :
SELECT
Playlists_playlist_name,
AGG(Songs_total_streams) AS streams,
AGG(Songs_song_count) AS songs
FROM GRAPH_EXPAND("music.MusicGraph")
GROUP BY 1
ORDER BY 2 DESC
👀 Résultat :
Playlists_playlist_name | appareils IoT et flux d'application. | chansons |
Road Trip | 6 900 000 000 | 3 |
Gym Pump | 6 500 000 000 | 2 |
Tendances du moment | 4 700 000 000 | 3 |
Pop montante | 3 500 000 000 | 2 |

Les deux lignes AGG sont identiques à celles de SUM et AGG, côte à côte. Seul GROUP BY a été déplacé. Road Trip contient Neon Summer, Midnight Drive et Slow Arrow, et chaque titre est comptabilisé une fois dans cette playlist. C'est exactement ce que signifie "streams reaching this playlist".
La personne qui a écrit la mesure a décidé de la précision une fois. Toutes les requêtes ultérieures, quel que soit leur auteur, l'héritent.
✅ Point de contrôle : Road Trip en tête avec 6,9 milliards.
10. Découvrez ce que GRAPH_EXPAND vous offre
Vous n'avez pas besoin d'exécuter GRAPH_EXPAND pour connaître sa forme. La procédure système BQ.SHOW_GRAPH_EXPAND_SCHEMA écrit la liste des colonnes dans une variable.
👉📋 Éditeur de requête : collez les trois lignes, puis cliquez sur Exécuter :
DECLARE schema STRING DEFAULT '';
CALL BQ.SHOW_GRAPH_EXPAND_SCHEMA('music.MusicGraph', schema);
SELECT schema;
👀 Le résultat est une chaîne JSON. Chaque champ possède un nom, un type et un mode, et chaque colonne de mesure comporte "is_measure": true. Pour le graphique que vous venez de créer, vous obtenez, entre autres, les éléments suivants :
nom | type | is_measure | |
Songs_title | STRING | ||
Songs_streams | INTEGER | ||
Songs_total_streams | INTEGER | true | |
Songs_avg_streams | FLOAT | true | |
Songs_distinct_artists | INTEGER | true | |
PlaylistEntries_placement_count | INTEGER | true | |

Toute description ou tout synonyme que vous définissez pour une propriété s'affiche également ici. L'étape de chat facultative indique qui lit ces messages.
✅ Point de contrôle : chaîne JSON avec is_measure dans chaque colonne que vous avez déclarée avec MEASURE.
11. Ce dont GRAPH_EXPAND a besoin de la forme d'un graphique
Vous n'avez rien à exécuter dans cette étape. Lisez simplement. GRAPH_EXPAND crée sa table large avec une chaîne de LEFT JOIN, en commençant par une table et en suivant les bords vers l'extérieur. Trois règles en découlent, et MusicGraph les respecte toutes.

Exactement une table de nœuds racine. La racine est la table de nœuds dont la clé n'apparaît dans aucune autre table (celle vers laquelle aucune arête ne pointe). Dans MusicGraph, il s'agit de PlaylistEntries : la source de deux arêtes et la destination d'aucune. Le tableau large comporte 10 lignes, une par emplacement.
Chaque arête est de type "plusieurs-à-un", de la source à la destination. Chaque entrée comporte un titre et une playlist, chaque titre comporte un artiste et chaque artiste comporte un label. C'est pourquoi le tableau large ne s'étend jamais au-delà des propres lignes de la racine. Les données auxquelles la racine ne peut pas accéder n'apparaissent pas : une chanson sans placement serait absente du tableau large.
Les relations de type plusieurs à plusieurs deviennent un nœud. Les titres et les playlists sont associés selon une relation plusieurs-à-plusieurs. Si vous avez déclaré la table de jonction comme une seule arête de "Songs" à "Playlists", GRAPH_EXPAND l'ignorerait, car elle est ambiguë. Sans arête utilisable, l'opération échouerait. La promotion de PlaylistEntries vers une table de nœuds avec trop d'arêtes de type "plusieurs à un" est le modèle documenté, et c'est ce que vous avez fait lorsque vous avez créé le graphique.
12. (FACULTATIF) Chemins avec GQL
Jusqu'à présent, chaque question avait une réponse numérique. Celui-ci ne l'est pas. Où les diffusions de Cymbal touchent-elles les auditeurs ? est une question sur les itinéraires dans le graphique, et un total ne peut pas y répondre, car une fois que vous avez fait la somme, l'itinéraire a disparu.
1. Demander où les flux sont diffusés
👉📋 Éditeur de requête : collez la requête, puis cliquez sur Exécuter :
GRAPH music.MusicGraph
MATCH (l:Labels)<-[:ArtistToLabel]-(a:Artists)<-[:SongToArtist]-(s:Songs)
<-[:EntryToSong]-(e:PlaylistEntries)-[:EntryToPlaylist]->(pl:Playlists)
WHERE l.label_name = 'Cymbal Records'
RETURN pl.playlist_name, pl.followers,
SUM(s.streams) AS streams_reaching_this_playlist,
STRING_AGG(s.title, ', ') AS songs
GROUP BY pl.playlist_name, pl.followers
ORDER BY streams_reaching_this_playlist DESC
👀 Quatre playlists seront disponibles :
pl.playlist_name | pl.followers | streams_reaching_this_playlist | chansons |
Tendances du moment | 35 000 000 | 4 700 000 000 | Double Shot, Neon Summer, Paper Crown |
Pop montante | 2 000 000 | 3 500 000 000 | Neon Summer, Paper Crown |
Road Trip | 5 000 000 | 2 400 000 000 | Neon Summer, Slow Arrow |
Gym Pump | 3 000 000 | 2 000 000 000 | Été fluo |

Lisez la ligne MATCH comme une phrase : commencez par un label, remontez jusqu'à ses artistes, puis jusqu'à leurs titres, puis jusqu'aux entrées de playlist dans lesquelles ces titres figurent, puis avancez jusqu'aux playlists. Les flèches indiquent le sens dans lequel les arêtes ont été déclarées. <- indique un déplacement vers l'arrière.
Additionnez maintenant la troisième colonne. 12,6 milliards : le nombre SUM que vous avez obtenu à l'étape 4, celui qui était incorrect.
C'est ici. À l'époque, la question était de savoir combien de fois Cymbal avait été écouté en streaming. Un titre était comptabilisé une seule fois, quelle que soit la playlist dans laquelle il figurait. Cette question concerne la portée de ces streams. Une chanson présente dans quatre playlists touche réellement quatre audiences. La duplication GRAPH_EXPAND est une erreur pour une question et la réponse à l'autre. AGG n'a jamais signifié que les doublons étaient mauvais, mais qu'il fallait faire correspondre l'arithmétique à la question.
2. Renvoyer les chemins eux-mêmes
Supprimez l'agrégation et demandez les itinéraires.
👉📋 Éditeur de requête : collez la requête, puis cliquez sur Exécuter :
GRAPH music.MusicGraph
MATCH p = (l:Labels)<-[:ArtistToLabel]-(a:Artists)<-[:SongToArtist]-(s:Songs)
<-[:EntryToSong]-(e:PlaylistEntries)-[:EntryToPlaylist]->(pl:Playlists)
WHERE l.label_name = 'Cymbal Records'
RETURN TO_JSON(p) AS path
p = nomme l'intégralité de la correspondance, et TO_JSON l'affiche.
👀 Attendez-vous à huit lignes et une colonne path. Chaque cellule est un tableau JSON des nœuds et des arêtes le long d'un itinéraire, chaque nœud portant ses labels et properties.

Huit lignes pour quatre titres, car il existe un itinéraire par emplacement, et non par titre.
3. Ouvrez l'onglet "Graphique".
👉🌐 Console : au-dessus des résultats, cliquez sur Graphique.
Cet onglet n'apparaît que lorsqu'un résultat contient des éléments graphiques. La requête précédente renvoyait des nombres et n'avait donc pas d'onglet "Graphique". Celle-ci renvoie des chemins et en a donc un.

Le coin du graphique de Cymbal est dessiné sous forme de mise en page forcée, avec un nombre de nœuds et d'arêtes en vue et un chip par libellé. Faites glisser un nœud pour séparer la mise en page, ou cliquez sur un nœud pour lire ses propriétés.
✅ Point de contrôle : la troisième colonne de la première requête est égale à 12, 6 milliards, la deuxième requête renvoie huit chemins et l'onglet "Graphique" les affiche.
13. (FACULTATIF) Discuter avec le graphique
Aucun code SQL n'est utilisé dans cette étape. Les agents de données BigQuery peuvent utiliser un graphique comme source de connaissances. La documentation indique qu'ils utilisent des mesures pour l'agrégation à plusieurs niveaux. Voyons si c'est vrai.
1. Créer l'agent
👉🌐 Console : dans le menu de navigation BigQuery à gauche, cliquez sur Agents, puis sur Catalogue d'agents, puis sur Créer un agent.
👉🌐 Console : saisissez Music graph agent dans Nom de l'agent, puis saisissez ceci dans Description de l'agent :
Ask questions about labels, artists, songs and playlists in the music property graph.
👉🌐 Console : sous Sources de connaissances, cliquez sur Ajouter une source. Dans la boîte de dialogue Sélectionner des sources, développez votre projet, music, Graphiques, cochez MusicGraph, puis cliquez sur Confirmer.
👀 Le bouton Ajouter une source est grisé. Un agent ne peut contenir qu'un seul graphique et ne peut pas mélanger des graphiques avec des tableaux.

👉🌐 Console : laissez le champ Région tel qu'il a été défini à partir de l'emplacement du graphique. Cliquez sur Enregistrer, puis sur Publier. Dans le panneau Canaux de publication, cliquez sur Publier l'agent, puis fermez la confirmation.
2. Poser la question
👉🌐 Console : cliquez sur Créer une conversation. Une page de chat s'ouvre. Saisissez la requête suivante dans la zone Poser une question en bas de l'écran, puis appuyez sur Entrée :
For each label, show total streams and how many playlists their songs are on
👀 L'agent renvoie un tableau et un résumé d'une phrase :
label_name | total_streams | playlist_placements |
Cymbal Records | 5 100 000 000 | 8 |
Northwind Audio | 4 500 000 000 | 2 |

5,1 milliards Personne n'a parlé à l'agent de la distribution ramifiée, et il n'y est pas tombé.
3. Lisez la requête qu'il a écrite.
👉 🌐 Console : dans le menu de navigation, cliquez sur Explorateur de jobs, puis sur le job le plus récent. Ses libellés incluent ca-bq-job: true et data-agent-id avec l'ID de votre agent, et sa requête est la suivante :
SELECT
Labels_label_name AS label_name,
AGG(Songs_total_streams) AS total_streams,
AGG(PlaylistEntries_placement_count) AS playlist_placements
FROM GRAPH_EXPAND("music.MusicGraph")
GROUP BY label_name
ORDER BY total_streams DESC

Il a utilisé GRAPH_EXPAND et AGG seuls, car les mesures figuraient dans le schéma qu'il a lu. Rien dans la description de l'agent ne les mentionne.
✅ Point de contrôle : 5, 1 milliards dans la réponse du chat et AGG(...) dans le code SQL du job.
14. Effectuer un nettoyage
👉💻 Cloud Shell : supprimez l'ensemble de données, ses tables et le graphique de propriétés :
cd ~/bq-graph-measures-lab
./cleanup.sh
👀 Attendez-vous à ✓ dropped YOUR_PROJECT_ID.music (tables and graph).
Si vous avez créé l'agent de données facultatif, supprimez-le dans la console sous Agents → Catalogue d'agents. Si vous avez ./setup_project.shcréé un projet spécifiquement pour cet atelier, la meilleure façon de tout nettoyer est d'arrêter l'ensemble du projet. L'ID se trouve dans ~/project_id.txt.
15. Félicitations
Vous avez déclaré une ligne, MEASURE(SUM(streams)), et elle a transformé 12,6 milliards en 5,1 milliards, pour vous et pour chaque requête que quelqu'un écrira ensuite sur ce graphique, y compris celles qu'un agent écrira.
Ce que vous avez appris
- Une mesure est un agrégat déclaré sur un nœud, à côté de la
KEYqui définit sa précision. GRAPH_EXPANDaplatit le graphique en un tableau large avecTableLabel_propertycolonnes.AGGest la seule fonction qui lit une mesure, et le système de typographie l'applique de quatre manières différentes.- Six agrégats sont disponibles, et les résultats
AGGsont des valeurs ordinaires que vous pouvez diviser. - Si vous modifiez
GROUP BY, la question est posée à nouveau sans que la mesure soit modifiée. GRAPH_EXPANDveut une racine, des arêtes de type "plusieurs à un" et des jonctions promues en nœuds.
Table de décision
Problème constaté | Cause | Solution |
Un total trop élevé |
| déclarer une mesure, la lire avec |
"expected MEASURE, found INT64" (MEASURE attendu, INT64 trouvé) |
| utiliser |
"Les expressions de type MEASURE ne sont pas autorisées" | colonne de mesures sans | enveloppez-le : |
"non exposé par le type d'élément" | mesure référencée dans GQL | mesures en direct derrière |
| jonction modélisée en tant qu'arête | Promouvoir la table de jonction en nœud |
Ancien numéro après modification du graphique | résultat en cache | désactivez l'option Utiliser les résultats mis en cache. |
Points non abordés (volontairement)
Descriptions et synonymes sur les propriétés ; requêtes validées et glossaires pour les agents de données ; GQL au-delà d'un MATCH ; mesures sur plusieurs graphiques. Chacun est un atelier à part entière.
En savoir plus
- Utiliser les mesures
- Créer et interroger un graphique de propriétés
- Présentation de Conversational Analytics
- Dépôt de l'atelier : toutes les requêtes de cet atelier de programmation, numérotées par étape