BigQuery Graph 度量使用入门

1. 概览

大多数通过代码审核的 SQL bug 都是聚合 bug。某个数字在它所在的表格中是正确的,但联接会复制该数字,导致总数错误。

借助 BigQuery Graph,您可以一次性将表的连接方式描述为属性图,然后查询这些连接。图表指标会在该说明中再添加一项内容:汇总本身。您可以在拥有该数字的节点上声明 MEASURE(SUM(streams)),这样一来,读取图表的每个查询都会获得正确的总数(以正确的粒度),而无需使用 GROUP BY 技巧,也无需使用有人必须记住的 CTE。

在此 Codelab 中,您将构建一个包含五张表的音乐目录,在其中放置一个衡量指标,并观察同一问题返回两个不同的答案:使用 SUM 时为 126 亿,使用 AGG 时为 51 亿。差额是目录的形状 - 一首位于四个播放列表中的歌曲在图表扁平化后会变成四行,而 SUM 会添加所有这四行。然后,您会学习使第二个句子正确的规则。

将一个“歌曲”行复制到四个“展示位置”行中:SUM 会添加副本,AGG 会按 song_id 进行折叠

您将在此处输入内容

本实验使用以下两个位置(按此顺序):

其中

您在那里做什么

1

Cloud Shell 终端

在设置步骤中,仅使用少量命令

2

BigQuery 控制台

其他所有操作:将查询粘贴到查询编辑器中、点击浏览图表建模器、与代理聊天

每条指令都带有标记,以便您随时了解其含义:

  • 👉💻 Cloud Shell - 在 Cloud Shell 终端中输入此命令。
  • 👉📋 查询编辑器 - 将此内容粘贴到 BigQuery 查询编辑器中,然后点击运行。
  • 👉🌐 控制台 - 在 BigQuery 控制台中随意点击。除非相应步骤要求,否则无需输入任何内容。
  • 👀 - 查看结果。

学习内容

  • 如何在节点表上声明度量 - 在可视化图表建模工具中或在 SQL 中
  • GRAPH_EXPAND 如何将图展平为一个宽表
  • 为什么 AGG 是唯一可以读取指标的函数,以及其他所有尝试的返回结果
  • 衡量指标支持的六种汇总方式,以及如何对这些汇总方式进行算术运算
  • 一个指标如何回答分组不同的问题(无需更改)
  • 可选:GQL 和“图”标签页如何回答总计无法回答的问题,以及 BigQuery 数据智能体如何在未被告知的情况下使用您的度量

所需条件

  • 具有有效结算功能的 Google 账号和浏览器。您无需预先创建 Google Cloud 项目,设置过程会为您创建一个。
  • 仅针对可选的聊天步骤:已启用 Gemini Data Analytics API,并且拥有 Gemini Data Analytics Data Agent Creator 角色。

无需在机器上安装任何内容。所有操作都在 Cloud Shell 和 BigQuery 控制台中运行。

商品状态和费用

BigQuery Graph 现已正式发布。图表度量、可视化图表建模工具、图表可视化和使用图表作为数据代理知识来源的功能处于预览状态,受非正式版产品使用条款约束。

GRAPH_EXPAND 和 AGG 按按需价格运行。根据文档,GQL 查询(可选路径步骤)需要使用企业版或企业 Plus 版的预留,这也是该步骤为可选的原因。

数据集的大小为几千字节。在按需价格模式下,每次查询的最低结算数据量为 10 MB,因此整个 Codelab 的费用不到一美分。

2. 设置

这是唯一一个使用 Cloud Shell 的步骤。它会创建一个项目,为其设置结算,并构建原始表。之后,您可以在 BigQuery 控制台中运行该图表、衡量指标和每个查询。

不过,本实验无法为您提供一个启用了结算功能的 Google Cloud 项目。第 1 步可让您获得一个可用于支付费用的账号;第 4 步可将该账号转换为项目。请按此顺序执行这些步骤 - 第 4 步之后的每个步骤都会读取第 4 步创建的项目。

1 · 获取具有结算功能的账号(约 1 分钟)

👉🌐 请务必先执行此操作。使用已启用结算功能的 Google 账号登录。任何此类账号均可。如果您的账号不符合要求,请开始免费试用;这样您就可以获得结算账号,而这正是第 4 步所需的。

2 · 打开 Cloud Shell (约 1 分钟)

👉🌐 控制台 - 点击 Google Cloud 控制台顶部的激活 Cloud Shell - 这是标题中的终端形状图标。系统会在页面底部打开一个终端。

从 Google Cloud 控制台顶部激活 Cloud Shell

👉🌐 控制台 - 如果系统在任何时候提示您进行授权,请点击授权以继续操作。

👉💻 Cloud Shell - 检查 Cloud Shell 以哪个账号登录:

gcloud auth list

👀 预期:您自己的账号会带有 * 标记,并且必须是您在第 1 步中登录的同一账号。如果不是,请运行 gcloud auth login 并选择正确的账号。

3 · 完成实验 (约 1 分钟)

👉💻 Cloud Shell - 克隆代码库:

cd ~
rm -rf ~/bq-graph-measures-lab
git clone https://github.com/cuppibla/bq-graph-measures-lab.git
cd ~/bq-graph-measures-lab

4 · 创建项目 (约 2 分钟)

👉💻 Cloud Shell - 一个脚本可查找您的结算账号、在其中创建一个项目,并将 Cloud Shell 指向该项目:

cd ~/bq-graph-measures-lab
./setup_project.sh

👀 预期:系统会为所选结算账号显示一个 ✓,为所创建的项目显示一个 ✓,并显示如下结束语:

Project ready: bq-graph-4271
  Next:  ./setup.sh

bq-graph-4271 是您的项目 ID,您的项目 ID 将包含不同的数字。其中会发生以下四件事:

结算

如果用户有最新的 [YYYY-MM-DD] GDP Credit: 账号,则使用该账号;否则,使用任何其他已开通的结算账号

项目

创建 bq-graph-XXXX 并将该结算账号与其相关联

选择

将 ID 写入 ~/project_id.txt 并运行 gcloud config set project,因此之后无需您输入 ID

等待

轮询,直到新项目实际上可以提供服务为止,最长轮询时间为两分钟

🌐 现在,控制台中显示了同一项目,其 ID 位于信息中心的项目信息卡片中:

控制台信息中心内的项目 ID

5 · 创建数据集 (约 3 分钟)

👉💻 Cloud Shell - 另一个脚本用于构建表格:

cd ~/bq-graph-measures-lab
./setup.sh

脚本会在运行时输出每个步骤。按顺序执行的操作:

项目

从 gcloud config 读取项目,如果没有项目,则拒绝继续

API

启用 bigquery.googleapis.com

数据集

在美国多区域位置创建 music 数据集和五个表,每个表都包含一个 PRIMARY KEY ... NOT ENFORCED

检查

计算每个表中的行数,如果任何计数有误,则停止

👀 预期:

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

您已完成 Cloud Shell 操作。您可以关闭它。

6 · 打开 BigQuery 和查询编辑器

👉🌐 控制台 - 点击输出末尾的链接,或从导航菜单中打开 BigQuery。在左侧的探索器窗格中,展开您的项目:一个包含五个表的 music 数据集。

展开了音乐的探索器窗格:音乐人、唱片公司、PlaylistEntries、播放列表、歌曲

👉🌐 控制台 - 在 BigQuery Studio 顶部的新建行中,点击 SQL 查询。系统随即会打开一个空白的编辑器标签页。

“使用 SQL 查询创建新行”以及下方的空白编辑器标签页

保留此标签页。本实验中的每个 👉📋 代码块都应放在此处:选择编辑器中的所有内容,将新代码块粘贴到其中,然后点击运行。结果会显示在下方的面板中。

了解表格

稍后,我们将用到这些表中的两项信息。每个表都将其主键声明为 NOT ENFORCED:BigQuery 不会检查它,但您即将构建的属性图需要一个明确的键来了解标识行的内容。最后一个表 PlaylistEntries 是一个联接表:如果一首歌曲位于四个播放列表中,则该表中有四行。该表是整个故事的发生地。

这些表按如下方式链接在一起:唱片公司签约音乐人,音乐人发布歌曲,歌曲通过 PlaylistEntries 放置到播放列表中。Cymbal Records 拥有 Ava Sterling 和 Mina Okafor,以及通过她们拥有的四首歌曲。Northwind Audio 拥有 Kai Nakamura 和一首歌曲“Midnight Drive”。

桌子

行

键

它包含的内容

标签

2

label_id

名称、营销预算

音乐人

3

artist_id

名称,哪个标签

歌曲

5

song_id

歌曲名、音乐人、播放量

播放列表

4

playlist_id

name, followers

PlaylistEntries

10

entry_id

哪些歌曲位于哪些播放列表中

✅ 检查点: 在 Cloud Shell 中,探索器中的 music 下有 5 个表,并且打开了一个空白的查询编辑器标签页。🎉 ready

3. 创建图表 - 选项 A:可视化建模工具

属性图是对表之间连接方式的描述。您可以在可视化图表建模工具中点击完成此操作(此步骤,全部点击),也可以将其写为 SQL(下一步,一次粘贴)。这两种方法会生成相同的对象,因此只需选择其中一种,而无需同时使用这两种方法。

1. 创建图表 shell

👉🌐 控制台 - 在 BigQuery Studio 中,在新建行中,打开 AI 和知识菜单,然后点击图表。

👉🌐 控制台 - 输入 MusicGraph 作为图表名称,输入 music 作为数据集中的图表目标位置。系统会根据数据集(美国)自动填充区域。

👉🌐 控制台 - 在数据源下,选择五个表格 - Artists、Labels、PlaylistEntries、Playlists、Songs - 然后点击创建。

建模工具创建页面:图表名称 MusicGraph,数据集 music,勾选了 5 个表

👀 画布打开,其中包含五个彩色节点,节点之间没有连线。右侧的详细信息面板列出了节点 (5) 和边 (0)。每个节点都已将其表的主键和所有列作为属性携带在其中 - 点击任意节点即可查看这些属性。

画布,包含 5 个节点,“详细信息”面板显示“节点 (5) / 边 (0)”

2. 绘制四个边

边是外键,已绘制。每个箭头都从包含键的表指向它所指向的表。

👉🌐 控制台 - 点击新边缘。在选择表格对话框中,选择 PlaylistEntries,然后点击添加。在添加边面板中:

  1. 将名称设置为 EntryToSong。
  2. 点击添加主键,然后接受 entry_id。
  3. 在来源下,将源节点设置为 PlaylistEntries,然后使用向节点添加引用列和在边上添加引用列,为两者选择 entry_id。
  4. 在目标下,将目标节点设置为“歌曲”,然后为节点中的引用列和边上的引用列选择 song_id。
  5. 点击完成。

“添加边”面板已针对 EntryToSong 填充完毕

👉🌐 控制台 - 再重复三次:

边缘名称

桌子

源节点 → 目标节点

两侧要选择的列

EntryToPlaylist

PlaylistEntries

PlaylistEntries → 播放列表

来源 entry_id,目标 playlist_id

SongToArtist

歌曲

歌曲 → 音乐人

来源 song_id,目标 artist_id

ArtistToLabel

音乐人

音乐人 → 唱片公司

来源 artist_id,目标 label_id

将每个边都保留为没有额外属性。

👀“详细信息”面板现在显示边 (4),画布上显示了链:唱片公司 ← 音乐人 ← 歌曲 ← PlaylistEntries → 播放列表。

画布,包含 5 个节点和 4 条边,详细信息显示“边 (4)”

3. 添加衡量指标

👉🌐 控制台 - 点击歌曲节点,然后点击“详情”面板顶部的修改(铅笔)图标。在标签的属性下,依次点击添加属性和添加字段。

👉🌐 控制台 - 输入 total_streams 作为字段名称,然后在表达式框中输入以下内容:

MEASURE(SUM(streams))

👉🌐 控制台 - 点击添加,然后点击完成。

“新媒体资源”卡片:字段名称 total_streams,表达式 MEASURE(SUM(streams))

4. 阅读其编写的代码,然后发布

👉🌐 控制台 - 打开右侧面板中的代码标签页。建模者已为您撰写了 CREATE PROPERTY GRAPH 语句。滚动到“歌曲”节点:其属性列表现在以以下内容结尾

MEASURE(SUM(streams)) AS `total_streams`

这就是整个功能,只需一行代码。此步骤之后的所有内容都与该行的作用有关。

“代码”标签页,滚动到“歌曲”节点,显示 MEASURE(SUM(streams)) AS total_streams

👉🌐 控制台 - 点击发布。

✅ 检查点:在探索器中,music 现在有一个包含 MusicGraph 的 Graphs 文件夹。点击它:五个节点、四条边以及“代码”标签页中的 MEASURE(SUM(streams)) 行。跳过下一步,前往并排显示 SUM 和 AGG。

4. 创建图 - 选项 B:SQL

如果您是在建模器中构建图表的,请跳过此步骤。

👉📋 查询编辑器 - 粘贴,然后点击运行:

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
  )

👀 结果面板显示已创建属性图。

编辑器中的 CREATE PROPERTY GRAPH 语句以及下方的成功消息

朗读一次

该图包含 5 个节点表和 4 个边表。每个节点表都会命名其 KEY 并列出其 PROPERTIES。以 MEASURE 开头的行是新增部分:每一行都将一个受支持的聚合函数封装在属性周围,并为其提供一个必需的别名。

每个边表都是一个外键,拼写如下:SOURCE KEY(在保存键的表中),DESTINATION KEY(在指向的表中)。NO PROPERTIES 表示边本身不携带任何内容。

请注意,PlaylistEntries 出现了 3 次:一次作为节点表,另外两次作为 EntryToSong 和 EntryToPlaylist 边的物理表。这是有意为之,稍后我们将解释原因。

看看你制作的内容

👉🌐 控制台 - 在“探索器”窗格中,依次展开 music 和图表,然后点击 MusicGraph。图表页面随即会在画布上打开,其中包含五个节点和四条边。点击某个节点即可查看其属性 - 系统会列出指标及其汇总值。打开代码标签页,查看图片背后的声明。

MusicGraph 页面:左侧是画布,右侧是“代码”标签页

✅ 检查点:music 具有包含 MusicGraph 的 Graphs 文件夹,并且“代码”标签页显示 MEASURE(SUM(streams)) AS total_streams。

5. SUM 和 AGG,并排显示

以下是一个查询中的整个 Codelab。这两个列都试图从同一表中回答同一问题,即每个唱片的总播放量。

该表是 GRAPH_EXPAND 生成的。它会展平图表:从一个表开始,将每个边向外延伸为 LEFT JOIN,直到每个关联的表并排显示在一个宽结果中。每项放置对应一行,而不是每首歌曲对应一行,并且每个列都命名为 TableLabel_property。在查询之前查看一下。

👉📋 查询编辑器 - 粘贴,然后点击运行:

SELECT Labels_label_name, Songs_title, Songs_streams, Playlists_playlist_name
FROM GRAPH_EXPAND("music.MusicGraph")
ORDER BY Songs_title, Playlists_playlist_name

👀 预期 10 行 - PlaylistEntries 中的每个放置位置对应一行,这正是设置加载的内容。“霓虹夏日”占据了其中的 4 个位置,每个位置对应一个播放列表,并且每个位置的播放量都显示为 2,000,000,000。Songs_streams这些行没有问题;播放列表确实是 4 个。但属于歌曲的列现在会在属于展示位置的行中重复出现,而这正是下一个查询要衡量的整个问题。

现在,我们来比较一下。

👉📋 查询编辑器 - 粘贴,然后点击运行:

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

👀 结果:

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

结果网格:Cymbal Records 的 with_sum 为 12,600,000,000,with_agg 为 5,100,000,000

手动检查。Cymbal 的四首歌曲的播放量分别为 20 亿、15 亿、12 亿和 4 亿。也就是 51 亿。AGG 是正确的。SUM 的大小是原来的 2.5 倍,而 Northwind 的单首歌曲大小是原来的 2 倍。

三项新语法

  • GRAPH_EXPAND("music.MusicGraph") 是一个表值函数,它通过将每条边视为 LEFT JOIN,将图转换为一个宽表。每个列都命名为 TableLabel_property,因此 Songs_streams 是 Songs 表中的原始列,而 Songs_total_streams 是您声明的指标。
  • SUM(Songs_streams) 是对该宽表的普通 SQL,它继承了扁平化产生的所有重复项。
  • AGG(Songs_total_streams) 会按照声明的方式评估指标:每个 song_id 评估一次,无论歌曲在宽表中占据多少行。SUM(streams)

✅ 检查点:两个标签、两列,且 with_agg 数字与手指数一致。

6. 为什么 SUM 会过高估计

选择一首歌曲,然后看看这些联接对歌曲有何影响。

👉📋 查询编辑器 - 粘贴,然后点击运行:

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

👀 结果:

步骤

title

溪流

播放列表

之前 - 歌曲表

霓虹夏日

2,000,000,000

null

之后 - 已加入播放列表

霓虹夏日

2,000,000,000

健身泵

之后 - 已加入播放列表

霓虹夏日

2,000,000,000

热门新作

之后 - 已加入播放列表

霓虹夏日

2,000,000,000

冉冉升起的流行音乐

之后 - 已加入播放列表

霓虹夏日

2,000,000,000

自驾行程

结果网格:1 个 BEFORE 行和 4 个 AFTER 行,均为 2,000,000,000

一行变成了四行。数据流数量未发生变化,只是进行了复制。将这些歌曲为 Cymbal Records 带来的收入加起来:四张播放列表中的《Neon Summer》20 亿次播放带来的收入为 80 亿,两张播放列表中的《Paper Crown》15 亿次播放带来的收入为 30 亿,一张播放列表中的《Double Shot》12 亿次播放带来的收入为 12 亿,一张播放列表中的《Slow Arrow》4 亿次播放带来的收入为 4 亿。即 126 亿,也就是返回的数字 SUM。Northwind 的《Midnight Drive》位于两个播放列表中,因此 45 亿变成了 90 亿。

这是扇出,而不是联接中的 bug。播放列表行是真实的。错误在于,将属于同一首歌曲的数字在属于不同展示位置的行中相加。

该度量修复了此问题,因为它与 KEY (song_id) 一起在 Songs 节点上声明。关键在于细分。当 AGG 评估 total_streams 时,它会按歌曲键汇总一次,然后才会汇总到您分组的任何内容。

有何变化

您之前写的内容

使用度量

SUM(streams) 在每个需要它的查询中

MEASURE(SUM(streams)) 一次(在图表中)

SUM(...) 即可阅读

AGG(...) 来读取它,并且只有 AGG

3 个 JOIN

GRAPH_EXPAND(),一个扁平表

在联接之前进行汇总的 CTE

一切正常。KEY决定了粒度

✅ 检查点:5 行 - 1 行在前面,4 行在后面 - 您可以从中重建 126 亿个。

7. 规则:只有 AGG 读取指标

度量具有自己的类型 MEASURE,并且只有一个函数接受该类型。以下四个查询会故意失败 - 请运行每个查询并阅读红色错误消息。这是每个人都会先尝试的四种方法,了解这些方法可以节省后续时间。

👉📋 查询编辑器 - 针对普通列的 AGG:

SELECT AGG(Songs_streams) FROM GRAPH_EXPAND("music.MusicGraph")

👀 No matching signature for aggregate function AGG ... Argument 1: expected MEASURE, found INT64

编辑器下方的红色错误

👉📋 查询编辑器 - 不含 AGG 的指标列:

SELECT Songs_total_streams FROM GRAPH_EXPAND("music.MusicGraph") LIMIT 1

👀 Returning expressions of type MEASURE is not allowed

编辑器下方的红色错误

👉📋 查询编辑器 - GQL 中引用的指标,但从未看到任何指标(这是一个 GQL 查询 - 请参阅概览中的版本说明):

GRAPH music.MusicGraph
MATCH (s:Songs)
RETURN s.total_streams
LIMIT 1

👀 total_streams is not exposed by element type GRAPH_NODE(...)

编辑器下方的红色错误

👉📋 查询编辑器 - SUM 衡量指标列,即最吸引人的列:

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

编辑器下方的红色错误

因此,类型系统会为您强制执行该规则。度量只能通过 GRAPH_EXPAND 查看,只能通过 AGG 读取。之前过高的 126 亿只能来自对原始列的求和,而绝不会来自对指标的错误处理。

✅ 检查点:四个红色错误,并且您能说出每个错误所强制执行的规则。

8. 相应指标支持的每个汇总函数

MEASURE() 封装了六个汇总之一:SUM、AVG、MIN、MAX、COUNT 和 COUNT(DISTINCT)。在“歌曲”节点上重新创建包含所有六个属性的图。边的值保持为 NO PROPERTIES,这是当边与其节点共享物理表时记录的最佳实践。

👉📋 查询编辑器 - 粘贴,然后点击运行:

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
  )

包含 6 个指标的 CREATE PROPERTY GRAPH 运行及其成功消息

现在,请将所有这些内容读出来。每个度量都会经历 AGG,最后一列显示 AGG 结果是您可以进行算术运算的常规值。图表刚刚更改,因此请先清除使用缓存的结果(更多 → 查询设置)。

👉📋 查询编辑器 - 粘贴,然后点击运行:

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

👀 结果:

Labels_label_name

total_streams

avg_streams

min_streams

max_streams

歌曲

音乐人

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

七列结果网格,两个标签

每个数字都是根据 Cymbal 的 4 首歌曲和 Northwind 的 1 首歌曲计算得出的,而不是根据 10 个展示位置行计算得出的。尤其是 AVG,如果反过来计算,结果会出错:对展示位置行的原始列求平均值,得出 Cymbal 的值为 15.75 亿,因为 Neon Summer 的 20 亿被计算了四次。

因此,“将 SUM 替换为 AGG”实际上是两项决策。在声明指标时,您仍然可以选择它是六个聚合函数中的哪一个。在读取指标时,该函数始终为 AGG。

✅ 检查点:每个标签一行,每行七个数字,且 streams_per_song 等于 avg_streams。

9. 更改分组,而非衡量指标

同一指标回答了不同的问题。按播放列表而非标签分组。

👉📋 查询编辑器 - 粘贴,然后点击运行:

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

👀 结果:

Playlists_playlist_name

溪流

歌曲

自驾行程

6,900,000,000

3

健身泵

6,500,000,000

2

热门新作

4,700,000,000

3

冉冉升起的流行音乐

3,500,000,000

2

结果网格,“Road Trip”位于顶部,显示为 6,900,000,000

这两个 AGG 行与 SUM 和 AGG 并排显示的行完全相同。只有 GROUP BY 发生了移动。“Road Trip”播放列表包含“Neon Summer”“Midnight Drive”和“Slow Arrow”三首歌曲,每首歌曲在该播放列表中都只算一次播放,这正是“播放次数达到此播放列表”的含义。

撰写度量的人员决定了粒度。任何人在之后提出的每个查询都会继承该变量。

✅ 检查点:“公路之旅”以 69 亿次观看次数位居榜首。

10. 了解 GRAPH_EXPAND 的功能

您无需运行 GRAPH_EXPAND 即可了解其形状。BQ.SHOW_GRAPH_EXPAND_SCHEMA 系统过程将列列表写入变量。

👉📋 查询编辑器 - 粘贴所有三行,然后点击运行:

DECLARE schema STRING DEFAULT '';
CALL BQ.SHOW_GRAPH_EXPAND_SCHEMA('music.MusicGraph', schema);
SELECT schema;

👀 结果是一个 JSON 字符串。每个字段都有一个名称、一个类型和一个模式,并且每个指标列都带有 "is_measure": true。对于您刚刚构建的图表,您会获得以下信息(以及其他信息):

name

类型

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

单个 JSON 单元格,展开后足以显示 is_measure true

您为媒体资源设置的任何说明或同义词也会显示在此处。可选的聊天步骤会显示谁会阅读这些内容。

✅ 检查点:一个 JSON 字符串,其中包含您使用 MEASURE 声明的每个列的 is_measure。

11. GRAPH_EXPAND 对图形状的要求

此步骤无需运行任何内容,只需阅读即可。GRAPH_EXPAND 通过 LEFT JOIN 链构建其宽表,从一个表开始,然后沿着边向外扩展。由此可得出三条规则,而 MusicGraph 满足所有这些规则。

GRAPH_EXPAND 所需的形状:PlaylistEntries 作为单个根,四个向外的多对一边,以及提升为节点的多对多 Songs-Playlists 对

一个根节点表。根是键未出现在任何其他表中的节点表,即没有边指向的节点表。在 MusicGraph 中,PlaylistEntries 是两个边的来源,但不是任何边的目的地。宽表有 10 行,每个展示位置对应一行。

每条边的方向都是从来源到目的地,属于多对一关系。每个条目都包含一首歌曲和一个播放列表,每首歌曲都有一位音乐人,每位音乐人都有一家唱片公司。因此,宽表永远不会扩展到根的自有行之外。根无法访问的数据不会显示:没有布置的歌曲将不会显示在宽表中。

多对多关系会成为一个节点。歌曲和播放列表之间存在多对多关系。如果您将联结表声明为从歌曲到播放列表的单个边,GRAPH_EXPAND 会因其不明确而忽略它;由于没有可用的边,它会失败。将 PlaylistEntries 提升为具有两个多对一出边的节点表是已记录的模式,也是您在构建图时所做的操作。

12. (可选)使用 GQL 的路径

到目前为止,每个问题都有一个数字答案。但此功能不会。Cymbal 的流媒体如何触达听众?是一个关于图表中路径的问题,总和无法回答这个问题,因为在您求和时,路径已经消失。

1. 询问数据流的着陆位置

👉📋 查询编辑器 - 粘贴,然后点击运行:

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

👀 预计会有 4 个播放列表:

pl.playlist_name

pl.followers

streams_reaching_this_playlist

歌曲

热门新作

35,000,000

4,700,000,000

Double Shot、Neon Summer、Paper Crown

冉冉升起的流行音乐

200 万

3,500,000,000

Neon Summer,Paper Crown

自驾行程

5000000

2,400,000,000

霓虹夏日,慢速箭头

健身泵

3,000,000

2,000,000,000

霓虹夏日

GQL 结果:四份播放列表,以及每份播放列表的播放次数

将 MATCH 行读作一个句子:从标签开始,返回到其音乐人,再返回到其歌曲,再返回到这些歌曲所在的播放列表条目,然后前进到播放列表。箭头指向边的声明方向;<- 向后走一步。

现在,将第三列中的数字相加。126 亿 - 您在第 4 步中得到的错误数字 SUM。

这里是正确的。当时的问题是 Cymbal 的播放量是多少,无论一首歌曲出现在多少个播放列表中,都只算一次。此问题是指这些播放流的覆盖范围,如果一首歌曲出现在 4 个播放列表中,则确实覆盖了 4 个受众群体。GRAPH_EXPAND 生成的重复内容对于一个问题来说是错误,但对于另一个问题来说却是答案。AGG 从来不是说重复不好,而是说算术要与问题相符。

2. 返回路径本身

舍弃聚合,并请求路线。

👉📋 查询编辑器 - 粘贴,然后点击运行:

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 = 命名整个匹配项,TO_JSON 呈现该匹配项。

👀 预期 8 行,1 列。path每个单元格都是一个 JSON 数组,其中包含一条路线上的节点和边,每个节点都带有其 labels 和 properties。

8 行,每行都是一个 JSON 路径,结果窗格中有一个“图表”标签页

8 行,对应 4 首歌曲,因为每个展示位置都有一条路线,而不是每首歌曲都有一条路线。

3. 打开“图表”标签页

👉🌐 控制台 - 在结果上方,点击图表。

仅当结果包含图表元素时,才会显示此标签页。上一个查询返回的是数字,因此没有“图表”标签页;而此查询返回的是路径,因此有“图表”标签页。

“图表”标签页,以力导向布局绘制 Cymbal 的子图

您将获得以力导向布局绘制的 Cymbal 图表一角,其中包含视图中节点和边的数量,以及每个标签对应的 chip。拖动节点可将布局拉开;点击节点可读取其属性。

✅ 检查点:第一个查询的第三列总计为 126 亿,第二个查询返回 8 条路径,并且“图表”标签页会绘制这些路径。

13. (可选)与图表对话

此步骤中没有 SQL。BigQuery 数据代理可以使用图作为知识来源,并且文档指出它们使用指标进行多级汇总。我们来看看是否属实。

1. 创建智能体

👉🌐 控制台 - 在左侧的 BigQuery 导航菜单中,依次点击代理、代理目录和创建代理。

👉🌐 控制台 - 在代理名称中输入 Music graph agent,在代理说明中输入以下内容:

Ask questions about labels, artists, songs and playlists in the music property graph.

👉🌐 控制台 - 在知识来源下,点击添加来源。在选择来源对话框中,展开您的项目,展开 music,展开图表,勾选 MusicGraph,然后点击确认。

👀“添加来源”按钮变灰。一个代理最多只能包含一个图表,并且不能将图表与表格混用。

“选择来源”对话框,其中“MusicGraph”已勾选,且显示“已选择 (1)”

👉🌐 控制台 - 将区域保留为图表位置设置的区域。点击保存,然后点击发布。在发布渠道面板中,点击发布代理,然后关闭确认窗口。

2. 提出问题

👉🌐 控制台 - 点击创建对话。系统会打开一个聊天页面。在底部的提问框中输入以下内容,然后按 Enter 键:

For each label, show total streams and how many playlists their songs are on

👀 智能体返回一个表格和一个单句摘要:

label_name

total_streams

playlist_placements

Cymbal Records

5,100,000,000

8

Northwind Audio

4,500,000,000

2

智能体的回答:表格及其单句摘要

51 亿。没有人告知代理有关扇出方面的信息,因此代理没有陷入扇出。

3. 阅读其编写的查询

👉🌐 控制台 - 在导航菜单中,点击作业探索器,然后点击最新作业。其标签包括 ca-bq-job: true 和 data-agent-id(包含代理的 ID),其查询如下:

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

作业探索器:作业的标签(ca-bq-job、data-agent-id)及其 SQL

它自行使用了 GRAPH_EXPAND 和 AGG,因为这些指标位于它读取的架构中。代理的说明中未提及它们。

✅ 检查点:聊天回答中为 51 亿,作业的 SQL 中为 AGG(...)。

14. 清理

👉💻 Cloud Shell - 舍弃数据集、其表和属性图:

cd ~/bq-graph-measures-lab
./cleanup.sh

👀 预计 ✓ dropped YOUR_PROJECT_ID.music (tables and graph)。

如果您创建了可选的数据代理,请在控制台中的代理 → 代理目录下将其删除。如果您只是为本实验创建了一个项目,最干净的清理方式是关停整个项目 - 项目 ID 位于 ~/project_id.txt 中。./setup_project.sh

15. 恭喜

您声明了一行代码 MEASURE(SUM(streams)),它将 126 亿更改为 51 亿,这不仅对您有效,而且对之后任何人针对该图谱编写的每个查询(包括智能体编写的查询)都有效。

要点回顾

  • 度量是在节点上声明的汇总,位于设置其粒度的 KEY 旁边。
  • GRAPH_EXPAND 将图展平为一个包含 TableLabel_property 列的宽表。
  • AGG 是唯一一个读取度量的函数,类型系统通过四种不同的方式强制执行此操作。
  • 有 6 个汇总函数可用,AGG 结果是可以相除的普通值。
  • 更改 GROUP BY 会重新提出问题,但不会影响衡量指标。
  • GRAPH_EXPAND 需要一个根、多对一的边,以及提升为节点的联接。

决策表

问题

原因

修复

总数过大

SUM 位于展开的列上方

声明一个指标,并使用 AGG 读取该指标

“expected MEASURE, found INT64”

AGG 在普通列上

使用 SUM,或声明度量

“不允许返回 MEASURE 类型的表达式”

不含 AGG 的指标列

封装:AGG(...)

“未按元素类型公开”

GQL 中引用的度量

仅衡量落后于 GRAPH_EXPAND 的直播

GRAPH_EXPAND 忽略边缘或失败

以边的形式建模的连接

将联结表提升为节点

更改图表后的旧数字

缓存结果

清除使用缓存的结果

我们有意未涵盖的内容

属性的说明和同义词 · 数据代理的已验证查询和术语表 · 多个 MATCH 之外的 GQL · 多个图表中的度量。每个都是一个独立的实验。

了解详情