词汇结构和语法

Cloud Spanner SQL 语句由一系列标记组成。标记包括标识符、带英文引号的标识符、字面量、关键字、运算符和特殊字符。您可以使用空格符(如空格、退格、制表符、换行符)或注释来分隔这些标记。

标识符

标识符是指与列、表和其他数据库对象关联的名称。标识符可以不带英文引号,也可以带英文引号。

  • 标识符可用于返回 STRUCT 的路径表达式。
  • 一些标识符区分大小写,一些则不区分大小写。如需了解详情,请参阅区分大小写
  • 不带英文引号的标识符必须以字母或下划线字符开头。后续字符可以是字母、数字或下划线。
  • 带英文引号的标识符必须用反引号 (`) 字符括起来。
    • 带英文引号的标识符可以包含任何字符(如空格或符号)。
    • 带英文引号的标识符不能为空。
    • 带英文引号的标识符与字符串字面量具有相同的转义序列。
    • 如果预留关键字是独立关键字或路径表达式的第一个组成部分,则必须是带英文引号的标识符。它可能为不带英文引号的路径表达式的第二或后面的部分。

示例

以下是有效的标识符:

Customers5
`5Customers`
dataField
_dataField1
ADGROUP
`tableName~`
`GROUP`

以下路径表达式包含有效的标识符:

foo.`GROUP`
foo.GROUP
foo().dataField
list[OFFSET(3)].dataField
list[ORDINAL(3)].dataField
@parameter.dataField

以下是无效的标识符:

5Customers
_dataField!
GROUP

5Customers 以数字开头,而不是以字母或下划线开头。_dataField! 包含特殊字符“!”,这个特殊字符不是字母、数字或下划线。 GROUP 是一个预留关键字,因此未用反引号括起时不能用作标识符。

字面

字面量代表内置数据类型的常量值。某些数据类型可以使用字面量表示,但并非全部数据类型都是如此。

字符串和字节字面量

字符串和字节字面量都必须用英文单引号 (')、英文双引号 (") 或英文三引号(可以是三个英文单引号 (''') 或三个英文双引号 ("""))括起来。

带英文引号的字面量

字面量 示例 说明
带英文引号的字符串
  • "abc"
  • "it's"
  • 'it\'s'
  • 'Title: "Boy"'
用英文单引号 (') 括起的字符串可以包含未转义的英文双引号 ("),反之亦然。
反斜杠 (\) 可引入转义序列。请参阅下面的“转义序列”表。
带英文引号的字符串不能包含换行符,即使前面加了反斜杠 (\) 也不允许。
带英文三引号的字符串
  • """abc"""
  • '''it's'''
  • '''Title:"Boy"'''
  • '''two
    lines'''
  • '''why\?'''
您可以嵌入换行符和英文引号,而无需转义(请参见第四个示例)。
反斜杠 (\) 可引入转义序列。请参阅下面的“转义序列”表。
不允许在行尾处使用未转义的尾随反斜杠 (\)。
行中与起始英文引号匹配的三个未转义英文引号表示此字符串结束。
原始字符串
  • R"abc+"
  • r'''abc+'''
  • R"""abc+"""
  • r'f\(abc,(.*),def\)'
具有原始字符串字面量前缀(rR)且带英文引号或三引号的字面量会被解释为原始/正则表达式字符串。
反斜杠字符 (\) 不充当转义字符。如果字符串字面量内出现反斜杠后跟另一个字符的形式,则这两个字符都将保留。
原始字符串不能以奇数个反斜杠结束。
原始字符串对于构造正则表达式很有用。

在带英文引号或三引号的字符串中,前缀字符(rRbB))是可选项,分别指示该字符串是原始/正则表达式字符串或字节序列。例如,b'abc'b'''abc''' 都会被解释为字节类型。前缀字符不区分大小写。

带前缀且带英文引号的字面量

字面量 示例 说明
字节
  • B"abc"
  • B'''abc'''
  • b"""abc"""
具有字节字面量前缀(bB)且带英文引号或三引号的字面量会被解释为字节。
原始字节
  • br'abc+'
  • RB"abc+"
  • RB'''abc'''
rb 前缀可按任意顺序组合。例如,rb'abc*' 等同于 br'abc*'

下表列出了可在字符串和字节字面量中代表非字母数字字符的所有有效转义序列。 此表未列出的任何序列都会引发错误。

转义序列 说明
\a 响铃
\b 退格符
\f 换页符
\n 换行符
\r 回车符
\t 制表符
\v 垂直制表符
\\ 反斜杠 (\)
\? 英文问号 (?)
\" 英文双引号 (")
\' 英文单引号 (')
\` 英文反引号 (`)
\ooo 由 3 位数字(介于 0 到 7 范围内)组成的八进制转义字符。解码为单个 Unicode 字符(字符串字面量形式)或字节(字节字面量形式)。
\xhh\Xhh 由 2 个十六进制数字(0-9、A-F 或 a-f)组成的十六进制转义字符。解码为单个 Unicode 字符(字符串字面量形式)或字节(字节字面量形式)。示例:
  • '\x41' == 'A'
  • '\x41B''AB'
  • '\x4' 是错误
\uhhhh Unicode 转义字符,包含小写“u”和 4 个十六进制数字。仅在字符串字面量或标识符中有效。
请注意,系统不允许使用 D800-DFFF 范围内的值,因为这些是代理 Unicode 值。
\Uhhhhhhhh Unicode 转义字符,包含大写“U”和 8 个十六进制数字。仅在字符串字面量或标识符中有效。
系统不允许使用 D800-DFFF 范围内的值,因为这些值是代理 Unicode 值。此外,不允许大于 10FFFF 的值。

整数字面量

整数字面量可以是一系列十进制数字(0 到 9),也可以是一个前缀为“0x”或“0X”的十六进制值。整数可以使用“+”或“-”作为前缀,分别表示正值和负值。 示例:

123
0xABC
-123

整数字面量会被解释为 INT64

数字字面量

您可以使用 NUMERIC 关键字后接括在英文引号中的浮点值的形式,构造 NUMERIC 字面量。

示例:

SELECT NUMERIC '0';
SELECT NUMERIC '123456';
SELECT NUMERIC '-3.14';
SELECT NUMERIC '-0.54321';
SELECT NUMERIC '1.23456e05';
SELECT NUMERIC '-9.876e-3';

浮点数字面量

语法选项:

[+-]DIGITS.[DIGITS][e[+-]DIGITS]
[DIGITS].DIGITS[e[+-]DIGITS]
DIGITSe[+-]DIGITS

DIGITS 表示一个或多个十进制数字(0 到 9),e 表示指数标记(e 或 E)。

示例:

123.456e-67
.1E4
58.
4e2

系统会假定包含小数点或指数标记的数值字面量属于双精度类型。

如果值在有效的浮点数范围内,则可以将浮点数字面量的类型隐式强制转换为浮点数类型。

NaN 或无穷大没有字面量表示,但以下不区分大小写的字符串可显式强制转换为浮点数:

  • “NaN”
  • “inf”或“+inf”
  • “-inf”

数组字面量

数组字面量是用方括号括起来且以英文逗号分隔的元素列表。ARRAY 关键字是可选的,显式元素类型 T 也是可选的。

示例:

[1, 2, 3]
['x', 'y', 'xy']
ARRAY[1, 2, 3]
ARRAY<string>['x', 'y', 'xy']
ARRAY<int64>[]

结构体字面量

语法:

(elem[, elem...])

其中,elem 是结构体中的一个元素。elem 必须是字面量数据类型,而不是表达式或列名称。

输出类型是匿名 struct 类型(struct 不是命名类型),其匿名字段的类型与输入表达式的类型匹配。

示例 输出类型
(1, 2, 3) STRUCT<int64,int64,int64>
(1, 'abc') STRUCT<int64,string>

日期字面量

语法:

DATE 'YYYY-M[M]-D[D]'

日期字面量包含 DATE 关键字,后跟遵循规范日期格式的字符串字面量(用英文单引号括起)。日期字面量支持 1 至 9999(含)的年份范围。此范围之外的日期无效。

例如,以下日期字面量代表 2014 年 9 月 27 日:

DATE '2014-09-27'

在应使用“日期”类型表达式的情况下使用时,规范日期格式中的字符串字面量也会隐式强制转换为“日期”类型。例如,在以下查询中:

SELECT * FROM foo WHERE date_col = "2014-09-27"

字符串字面量 "2014-09-27" 将被强制转换为日期字面量。

时间戳字面量

语法:

TIMESTAMP 'YYYY-[M]M-[D]D [[H]H:[M]M:[S]S[.DDDDDD] [timezone]]'

时间戳字面量包含 TIMESTAMP 关键字和一个字符串字面量,该字符串字面量遵循规范时间戳格式并用英文单引号括起。

时间戳字面量支持 1 至 9999(含)的年份范围。 此范围之外的时间戳是无效的。

时间戳字面量可以包含一个数字后缀来指示时区:

TIMESTAMP '2014-09-27 12:30:00.45-08'

如果此后缀不存在,则将使用默认时区 America/Los_Angeles。

例如,以下时间戳表示 2014 年 9 月 27 日中午 12:30,时区为 America/Los_Angeles 时区:

TIMESTAMP '2014-09-27 12:30:00.45'

如需详细了解时区,请参阅时区

在应使用时间戳表达式的位置使用规范时间戳格式的字符串字面量(包括具有时区名称的那些字面量)时,该字符串字面量会隐式强制转换为时间戳字面量。例如,在以下查询中,字符串字面量 "2014-09-27 12:30:00.45 America/Los_Angeles" 会被强制转换为时间戳字面量。

SELECT * FROM foo
WHERE timestamp_col = "2014-09-27 12:30:00.45 America/Los_Angeles"

时间戳字面量可以包含以下可选的字符:

  • Tt:时间标志。用作日期和时间之间的分隔符。
  • Zz:默认时区标志。这两个字符不能与 [timezone] 搭配使用。

如果您使用这些字符之一,则不能在其前面或后面添加空格。以下日期时间字面量是有效的:

TIMESTAMP '2017-01-18T12:34:56.123456Z'
TIMESTAMP '2017-01-18t12:34:56.123456'
TIMESTAMP '2017-01-18 12:34:56.123456z'
TIMESTAMP '2017-01-18 12:34:56.123456Z'

时区

由于时间戳字面量必须映射到特定时间点,因此需要时区来正确解释字面量。如果未在字面量本身内指定时区,则 Cloud Spanner SQL 将使用 Cloud Spanner SQL 实现设置的默认时区值。

Cloud Spanner SQL 使用以下规范格式的字符串表示时区,该格式表示相对于世界协调时间 (UTC) 的偏移量。

格式:

(+|-)H[H][:M[M]]

示例:

'-08:00'
'-8:15'
'+3:00'
'+07:30'
'-7'

也可以使用 tz 数据库中的字符串时区名称来表示时区。请参阅维基百科上的 tz 数据库时区列表,其中提供了虽不全面但较为简单的参考信息。规范时区名称的格式为 <continent/[region/]city>,例如 America/Los_Angeles

示例:

TIMESTAMP '2014-09-27 12:30:00 America/Los_Angeles'
TIMESTAMP '2014-09-27 12:30:00 America/Argentina/Buenos_Aires'

区分大小写

Cloud Spanner SQL 遵循以下区分大小写规则:

类别 是否区分大小写? 备注
关键字  
函数名称  
表名称 请参见备注 表名称通常不区分大小写,但在查询使用区分大小写的表名的数据库时可能区分大小写。
列名称  
字符串值
字符串比较  
查询中的别名  
正则表达式匹配 请参见备注 默认情况下,正则表达式匹配区分大小写,除非表达式本身指定不区分大小写。
LIKE 匹配  

预留关键字

关键字是 Cloud Spanner SQL 语言中具有特殊含义的一组标记,具有以下特征:

  • 使用反引号 (`) 字符引起时,关键字才能用作标识符。
  • 关键字不区分大小写。

Cloud Spanner SQL 包含以下预留关键字。

ALL
AND
ANY
ARRAY
AS
ASC
ASSERT_ROWS_MODIFIED
AT
BETWEEN
BY
CASE
CAST
COLLATE
CONTAINS
CREATE
CROSS
CUBE
CURRENT
DEFAULT
DEFINE
DESC
DISTINCT
ELSE
END
ENUM
ESCAPE
EXCEPT
EXCLUDE
EXISTS
EXTRACT
FALSE
FETCH
FOLLOWING
FOR
FROM
FULL
GROUP
GROUPING
GROUPS
HASH
HAVING
IF
IGNORE
IN
INNER
INTERSECT
INTERVAL
INTO
IS
JOIN
LATERAL
LEFT
LIKE
LIMIT
LOOKUP
MERGE
NATURAL
NEW
NO
NOT
NULL
NULLS
OF
ON
OR
ORDER
OUTER
OVER
PARTITION
PRECEDING
PROTO
RANGE
RECURSIVE
RESPECT
RIGHT
ROLLUP
ROWS
SELECT
SET
SOME
STRUCT
TABLESAMPLE
THEN
TO
TREAT
TRUE
UNBOUNDED
UNION
UNNEST
USING
WHEN
WHERE
WINDOW
WITH
WITHIN

终止分号

在通过应用编程接口 (API) 提交查询字符串语句时,您可以视情况使用终止分号 (;)。

在包含多个语句的请求中,您必须使用分号分隔各个语句,但最后一个语句后的分号通常是可选的。 一些交互式工具要求语句使用终止分号。

尾随逗号

您可以视情况选择在 SELECT 语句的列表末尾使用尾随逗号 (,)。

示例

SELECT name, release_date, FROM Books

查询参数

您可以使用查询参数替换任意表达式。 但查询参数不能用于替换标识符、列名称、表名称或查询本身的其他部分。查询参数是在查询语句之外定义的。

客户端 API 允许将参数名称与值绑定;查询引擎会在执行时将参数替换成绑定的值。

命名的查询参数

语法:

@parameter_name

命名的查询参数使用以 @ 字符开头的标识符来表示。

示例:

此示例返回 LastName 等于命名的查询参数 myparam 的值的所有行。

SELECT * FROM Roster WHERE LastName = @myparam

提示

@{ hint [, ...] }

hint:
  [engine_name.]hint_name = value

提示的目的是修改查询执行策略,而不更改查询结果。提示通常不会影响查询语义,但可能会对性能产生影响。可用的提示类型包括:

提示语法要求 @ 字符后跟大括号。您可以创建一个或一组提示。可选的 engine_name. 前缀允许多个引擎定义具有相同 hint_name 的提示。如果您需要建议不同的引擎特定执行策略或不同的引擎支持不同的提示,这一点很重要。

您可以为提示分配标识符字面量

  • 标识符对于旨在充当枚举的提示很有用。您可以使用标识符以避免使用带英文引号的字符串。在解析的 AST 中,标识符提示以字符串字面量表示,因此 @{hint="abc"}@{hint=abc} 相同。标识符提示还可用于将表名称或列名称用作单个标识符的提示。
  • 允许 NULL 字面量并推断为整数。

提示仅适用于它们附加的节点,而不适用于范围更广的节点。例如,FROM 子句中间的 JOIN 的提示仅适用于该 JOIN,而不是 FROM 子句中的其他 JOIN。语句级提示可用于修改整个语句执行的提示,例如总体内存预算或截止时间。

示例

在此示例中,为提示分配了一个字面量。此提示仅用于 database_engine_adatabase_engine_b 这两个数据库引擎。每个数据库引擎的提示值不同。

@{ database_engine_a.file_count=23, database_engine_b.file_count=10 }

在此示例中,为提示分配了一个标识符。每个提示类型都有唯一标识符。您可以在本主题的开头查看提示类型列表。

@{ JOIN_METHOD=HASH_JOIN }

注释

注释是会被解析器忽略的字符序列。 Cloud Spanner SQL 支持以下类型的注释。

单行注释

如果您希望注释单独显示在一行上,请使用单行注释。

示例

# this is a single-line comment
SELECT book FROM library;
-- this is a single-line comment
SELECT book FROM library;
/* this is a single-line comment */
SELECT book FROM library;
SELECT book FROM library
/* this is a single-line comment */
WHERE book = "Ulysses";

内嵌注释

如果您希望注释与语句显示在同一行上,请使用内嵌注释。前缀为 #-- 的注释必须位于语句的右侧。

示例

SELECT book FROM library; # this is an inline comment
SELECT book FROM library; -- this is an inline comment
SELECT book FROM library; /* this is an inline comment */
SELECT book FROM library /* this is an inline comment */ WHERE book = "Ulysses";

多行注释

如果您需要添加跨越多行的注释,请使用多行注释。 系统不支持嵌套的多行注释。

示例

SELECT book FROM library
/*
  This is a multiline comment
  on multiple lines
*/
WHERE book = "Ulysses";
SELECT book FROM library
/* this is a multiline comment
on two lines */
WHERE book = "Ulysses";