适用于 Bigtable 的 GoogleSQL 概览
您可以使用 GoogleSQL 语句来查询 Bigtable 数据。GoogleSQL 是一种符合 ANSI 的结构化查询语言 (SQL),也适用于 BigQuery 和 Spanner 等其他 Google Cloud 服务。
本文档简要介绍了适用于 Bigtable。其中提供了一些 SQL 查询示例, 并介绍了它们与 Bigtable Bigtable 表架构。在阅读本文档之前,您应该: 您需要熟悉Bigtable Storage 模型 和架构设计 概念。
您可以在 Google Cloud 控制台的 Bigtable Studio 中创建和运行查询,也可以使用 Java 版 Bigtable 客户端库以编程方式运行查询。如需了解详情,请参阅将 SQL 与 Bigtable 客户端库搭配使用。
使用场景
GoogleSQL for Bigtable 是实现低延迟的最佳选择 开发应用。此外,在 Google Cloud 控制台有助于快速获取直观的表示 表架构、验证特定数据是否已写入或调试 可能的数据问题。
适用于 Bigtable 的 GoogleSQL 的当前版本不支持某些常见的 SQL 结构,包括但不限于:
- 除了
SELECT
之外的数据操纵语言 (DML) 语句,例如INSERT
、UPDATE
或DELETE
- 数据定义语言 (DDL) 语句,例如
CREATE
、ALTER
或DROP
- 数据访问权限控制语句
- 子查询、
JOIN
、UNION
、GROUP BY
、UNNEST
和CTEs
的查询语法
如需了解详情(包括受支持的函数、运算符、数据类型和查询语法),请参阅 GoogleSQL for Bigtable 参考文档。
主要概念
本部分讨论在使用 使用 GoogleSQL 查询您的 Bigtable 数据。
SQL 响应中的列族
在 Bigtable 中,一个表包含一个或多个列族, 用于对列进行分组。使用以下内容查询 Bigtable 表时 GoogleSQL,该表的架构由以下内容组成:
- 名为
_key
的特殊列,与所查询表中的行键相对应 - 表中每个 Bigtable 列族对应一个列,该列包含该行中列族的数据
映射数据类型
GoogleSQL for Bigtable 包括数据类型
MAP<key, value>
,
它是专为容纳列族而设计的
默认情况下,映射列中的每一行都包含键值对,其中键为 所查询表中的 Bigtable 列限定符以及值 是该列的最新值。
下面是一个 SQL 查询示例,该查询返回一个包含
行键值和限定符的最新值,来自名为
columnFamily
。
SELECT _key, columnFamily['qualifier'] FROM myTable
如果您的 Bigtable 架构涉及在列中存储多个单元格(或数据版本),您可以向 SQL 语句添加时间过滤条件,例如 with_history
。
在这种情况下,表示列族的映射会嵌套并作为数组返回。在数组中,每个键本身都是一个映射,其中包含时间戳作为
键和单元格数据作为值。格式为
MAP<key, ARRAY<STRUCT<timestamp, value>>>
。
以下示例返回“info”中的所有单元格单个列族 。
SELECT _key, info FROM users(with_history => TRUE) WHERE _key = 'user_123';
返回的映射如下所示。在查询的表中,info
是
user_123
为行键,city
和 state
为列族
限定符。数组中的每个时间戳-值对 (STRUCT
) 表示
该行中相应的列,并按时间戳降序排序。
/*----------+------------------------------------------------------------------+
| _key | info |
+----------+------------------------------------------------------------------+
| user_123 | {"city":{<t5>:"Brooklyn", <t0>:"New York"}, "state":{<t0>:"NY"}} |
+----------+------------------------------------------------------------------*/
稀疏表
Bigtable 的一个重要特性是灵活的数据模型。在 Bigtable 表中,如果某行中某个列未使用,则系统不会存储该列的数据。一行可能包含一列,下一行可能有 100
列。相比之下,在关系型数据库表中,所有行都包含全部
而 NULL
值通常存储在没有数据的行的列中。
该列的数据。
使用 GoogleSQL 查询 Bigtable 表时,
不过,未使用的列用空映射表示,并作为
NULL
值。这些 NULL
值可用作查询谓词。例如,只有在行中使用 column1
时,WHERE family['column1'] IS NOT NULL
等谓词才能用于返回行。
字节
提供字符串时,GoogleSQL 默认隐式类型转换
从 STRING
个值更改为 BYTES
个值。例如,这意味着您可以提供字符串 'qualifier'
,而不是字节序列 b'qualifier'
。
由于 Bigtable 默认情况下将所有数据均视为字节,因此大多数
Bigtable 列不包含类型信息。不过,如果
GoogleSQL,您可以使用 CAST
在读取时定义架构
函数。如需详细了解如何投放,请参阅转换
函数。
时间过滤器
下表列出了您在访问表格的时态元素时可以使用的参数。参数会按照
会被滤除例如,with_history
会在 latest_n
之前应用。您必须
提供有效的时间戳。
参数 | 说明 |
---|---|
as_of |
Timestamp。返回时间戳较少的最新值 大于或等于提供的时间戳。 |
with_history |
Boolean。用于控制是将最新值作为标量返回,还是将带时间戳的值作为 STRUCT 返回。 |
after_or_equal |
Timestamp。值的后缀为时间戳,时间戳大于等于输入时间。需要 with_history => TRUE |
before |
Timestamp。时间戳在输入之前的值,
(不含两端值)。需要 with_history => TRUE |
latest_n |
整数。每个列限定符(映射键)返回带时间戳的值的数量。必须大于或等于 1。需要指定 with_history => TRUE 。 |
如需查看更多示例,请参阅高级查询模式。
基础查询
本部分介绍并展示基本 Bigtable SQL 的示例 查询及其工作原理如需更多示例查询,请参阅 GoogleSQL for Bigtable 查询模式 示例。
检索最新版本
虽然 Bigtable 允许您在每个列中存储多个版本的数据,但 Bigtable 专用 GoogleSQL 默认会返回每行数据的最新版本(即最新的单元格)。
请考虑以下示例数据集,该数据集显示 user1
进行了两次重定位
还有一次在布鲁克林市工作在此示例中,address
是列族,列限定符分别为 street
、city
和 state
。列中的单元格之间以空行分隔。
地址 | |||
---|---|---|---|
_key | street | 城市 | state |
user1 | 2023/01/10-14:10:01.000: '113 Xyz Street' 2021/12/20-09:44:31.010: '76 Xyz Street' 2005/03/01-11:12:15.112: '123 Abc Street' |
2021/12/20-09:44:31.010: 'Brooklyn' 2005/03/01-11:12:15.112: 'Queens' |
2005/03/01-11:12:15.112: “NY” |
如需检索 user1
的每个列的最新版本,您可以使用如下 SELECT
语句。
SELECT * FROM myTable WHERE _key = 'user1'
响应中包含当前地址,该地址是最常 最近用过的街道、城市和州/省/自治区/直辖市的值(在不同时间书写),输出为 JSON。时间戳不会包含在响应中。
_key | 地址 | ||
---|---|---|---|
user1 | {street:'113 Xyz Street', city:'Brooklyn', state:'NY'} |
检索所有版本
如需检索数据的旧版本(单元格),请使用 with_history
标志。您
还可以为列和表达式添加别名,如以下示例所示。
SELECT _key, columnFamily['qualifier'] AS col1
FROM myTable(with_history => TRUE)
如需更好地了解导致某行当前状态的事件,您可以通过检索完整历史记录来检索每个值的时间戳。例如,如需了解 user1
何时搬到了当前地址以及从何处搬迁而来,您可以运行以下查询:
SELECT
address['street'][0].value AS moved_to,
address['street'][1].value AS moved_from,
FORMAT_TIMESTAMP('%Y-%m-%d', address['street'][0].timestamp) AS moved_on,
FROM myTable(with_history => TRUE)
WHERE _key = 'user1'
在 SQL 查询中使用 with_history
标志时,系统会将响应返回为 MAP<key, ARRAY<STRUCT<timestamp, value>>>
。数组中的每个项都是指定行、列族和列的时间戳值。时间戳按时间逆序排列,因此数据显示最新的
始终返回第一项。
查询响应如下所示。
moved_to | moved_from | moved_on | ||
---|---|---|---|---|
朝阳路 113 号 | 76 Xyz Street | 2023/01/10 |
您还可以使用数组函数检索每行中的版本数量,如以下查询所示:
SELECT _key, ARRAY_LENGTH(MAP_ENTRIES(address)) AS version_count
FROM myTable(with_history => TRUE)
检索指定时间的数据
使用 as_of
过滤条件,您可以检索行在
特定时间点例如,如果您想知道 user
的地址是
您可以运行以下查询。
SELECT address
FROM myTable(as_of => TIMESTAMP('2022/01/10-13:14:00'))
WHERE _key = 'user1'
结果中显示的是 1 月 10 日最后的已知地址 2022 年下午 1:14,这是 2021/12/20-09:44:31.010 更新,以及 2005/03/01-11:12:15.112 这一期间的状态。
地址 | ||
---|---|---|
{street:'76 Xyz Street', city:'Brooklyn', state: :'NY'} |
使用 Unix 时间戳也可以实现相同的结果。
SELECT address
FROM myTable(as_of => TIMESTAMP_FROM_UNIX_MILLIS(1641820440000))
WHERE _key = 'user1'
请考虑以下数据集,该数据集显示了烟雾的开启或关闭状态
一氧化碳警报声。列族为 alarmType
,列限定符为 smoke
和 carbonMonoxide
。每列中的单元格由空行分隔。
alarmType |
||
---|---|---|
_key | smoke | carbonMonoxide |
建筑物 1#部分 1 | 2023/04/01-09:10:15.000: “关闭” 2023/04/01-08:41:40.000: “ ” 2020/07/03-06:25:31.000: “关闭” 2020/07/03-06:02:04.000: “开启” |
2023/04/01-09:22:08.000: “关闭” 2023/04/01-08:53:12.000: “开启” |
building1#section2 | 2021/03/11-07:15:04.000: “关闭” 2021/03/11-07:00:25.000: “开启” |
您可以使用以下查询查找 building1
中 2023 年 4 月 1 日上午 9 点处于开启状态的烟雾报警器的部分,以及当时一氧化碳报警器的状态。
SELECT _key AS location, sensorType['carbonMonoxide'] AS CO_sensor
FROM alarms(as_of => TIMESTAMP('2023/04/01-09:00:00.000'))
WHERE _key LIKE 'building1%' and sensorType['smoke'] = 'on'
结果如下:
位置 | CO_sensor |
---|---|
building1#section1 | “开启” |
查询时序数据
Bigtable 的一个常见使用场景是
时序数据。
请参考以下示例数据集,其中显示了温度和湿度
天气传感器的读数。列族 ID 为 metrics
,列限定符为 temperature
和 humidity
。一列中的单元格以
空行,每个单元格表示一个带时间戳的传感器读数。
指标 |
||
---|---|---|
_key | temperature | 湿度 |
传感器 A#20230105 | 2023/01/05-02:00:00.000: 54 2023/01/05-01:00:00.000: 56 2023/01/05-00:00:00.000: 55 个 |
2023/01/05-02:00:00.000: 0.89 2023/01/05-01:00:00.000: 0.9 2023/01/05-00:00:00.000: 0.91 |
sensorA#20230104 | 2023/01/04-23:00:00.000: 56 2023/01/04-22:00:00.000: 57 个 |
2023/01/04-23:00:00.000: 0.9 2023/01/04-22:00:00.000: 0.91 |
您可以使用时间过滤条件检索特定范围的时间戳值
after
、before
或 after_or_equal
。以下示例使用 after
:
SELECT metrics['temperature'] AS temp_versioned
FROM
sensorReadings(after => TIMESTAMP('2023/01/04-23:00:00'),
before => TIMESTAMP('2023/01/05-01:00:00'))
WHERE _key LIKE 'sensorA%'
该查询会以以下格式返回数据:
temp_versioned |
---|
{timestamp: '2023/01/05-01:00:00.000', value:56} {timestamp: '2023/01/05-00:00:00.000', value: 55} |
{timestamp: '2023/01/04-23:00:00.000', value:56} |
查询 JSON
JSON 函数可让您处理存储为 Bigtable 值的 JSON 和运维工作负载
例如,您可以从abc
session
列族中最新的单元格以及行键,方法是使用
。
SELECT _key, JSON_VALUE(session['payload'],'$.abc') AS abc FROM analytics
转义特殊字符和预留字词
Bigtable 在为表和列命名方面非常灵活。因此,在您的 SQL 查询中,您的表名称可能需要进行转义,因为 特殊字符或保留字。
例如,由于表名称中包含英文句号,以下查询是无效的 SQL。
-- ERROR: Table name format not supported
SELECT * FROM my.table WHERE _key = 'r1'
不过,您可以通过用反引号 (`) 字符括起这些项来解决此问题。
SELECT * FROM `my.table` WHERE _key = 'r1'
如果将 SQL 预留关键字用作标识符,则可以同样对其进行转义。
SELECT * FROM `select` WHERE _key = 'r1'
将 SQL 与 Bigtable 客户端库搭配使用
Java 和 Python 版 Bigtable 客户端库支持使用 executeQuery
API 通过 SQL 查询数据。以下示例展示了如何
发出查询并访问数据:
Java
如需使用此功能,您必须使用 java-bigtable
2.41.0 或更高版本。对于
请参阅
executeQuery,
对账单、
和
ResultSet
。
static void query(BigtableDataClient client) {
try (ResultSet resultSet =
client.executeQuery(
Statement.of(
"SELECT cf1['bytesCol'] AS bytesCol, CAST(cf2['stringCol'] AS STRING) AS stringCol, cf3 FROM myTable WHERE _key='mykey'"))) {
while (resultSet.next()) {
ByteString byteValue = resultSet.getBytes("bytesCol");
String stringValue = resultSet.getString("stringCol");
Map<ByteString, ByteString> cf3Value =
resultSet.getMap("cf3", SqlType.mapOf(SqlType.bytes(), SqlType.bytes()));
// Do something with the data
}
}
}
Python asyncio
如需使用此功能,您必须使用 python-bigtable
2.26.0 或更高版本。
from google.cloud.bigtable.data import BigtableDataClientAsync
async def execute_query(project_id, instance_id, table_id):
async with BigtableDataClientAsync(project=project_id) as client:
query = (
"SELECT cf1['bytesCol'] AS bytesCol, CAST(cf2['stringCol'] AS STRING) AS stringCol,"
" cf3 FROM {table_id} WHERE _key='mykey'"
)
async for row in await client.execute_query(query, instance_id):
print(row["_key"], row["bytesCol"], row["stringCol"], row["cf3"])