SQL 输入由一系列命令组成。一个命令由一系列记号组成,并以分号(“;”)结尾。输入流的结束也会终止一个命令。哪些记号是有效的取决于特定命令的语法。
一个记号可以是一个关键字,一个标识符,一个带引号的标识符,一个字面量(或常量),或者一个特殊字符符号。记号通常用空白字符(空格、制表符、换行符)分隔,但如果没有歧义则不需要(这通常只在特殊字符与某些其他记号类型相邻时才会出现)。
例如,以下是(语法上)有效的 SQL 输入
SELECT * FROM MY_TABLE; UPDATE MY_TABLE SET A = 5; INSERT INTO MY_TABLE VALUES (3, 'hi there');
这是一系列三个命令,每行一个(虽然这不是必需的;一行可以有多个命令,并且命令可以跨多行)。
此外,注释可以出现在 SQL 输入中。它们不是记号,它们实际上等同于空白字符。
SQL 语法在标识命令的记号以及哪些是操作数或参数方面不是很一致。前几个记号通常是命令名称,因此在上面的示例中,我们通常会说一个“SELECT”、一个“UPDATE”和一个“INSERT”命令。但是,例如,UPDATE
命令总是需要在特定位置出现一个 SET
记号,而 INSERT
的这个特定变体也需要一个 VALUES
才能完成。每个命令的精确语法规则在第六部分中描述。
上面的示例中,诸如 SELECT
、UPDATE
或 VALUES
之类的记号是关键字的示例,即在 SQL 语言中具有固定含义的单词。MY_TABLE
和 A
记号是标识符的示例。它们标识表、列或其他数据库对象的名称,具体取决于它们在哪个命令中使用。因此,它们有时也简称为“名称”。关键字和标识符具有相同的词法结构,这意味着在不了解语言的情况下,无法知道一个记号是标识符还是关键字。关键字的完整列表可以在附录 C中找到。
SQL 标识符和关键字必须以字母(a
-z
,但也包括带有变音符号的字母和非拉丁字母)或下划线(_
)开头。标识符或关键字中的后续字符可以是字母、下划线、数字(0
-9
)或美元符号($
)。请注意,按照 SQL 标准的字面意思,美元符号不允许在标识符中使用,因此使用它们可能会降低应用程序的可移植性。SQL 标准不会定义包含数字或以带下划线开头或结尾的关键字,因此这种形式的标识符对于未来标准的扩展是安全的,不会发生冲突。
系统最多使用标识符的 NAMEDATALEN
-1 个字节;更长的名称可以写在命令中,但它们将被截断。默认情况下,NAMEDATALEN
为 64,因此最大标识符长度为 63 个字节。如果此限制有问题,可以通过更改 src/include/pg_config_manual.h
中的 NAMEDATALEN
常量来提高此限制。
UPDATE MY_TABLE SET A = 5;
可以等效地写成
uPDaTE my_TabLE SeT a = 5;
一种常用的约定是将关键字大写,而将名称小写,例如
UPDATE my_table SET a = 5;
还有第二种标识符:带分隔符的标识符或带引号的标识符。它是通过将任意字符序列用双引号("
)括起来形成的。带分隔符的标识符始终是标识符,而不是关键字。因此,"select"
可以用来引用一个名为“select”的列或表,而不带引号的 select
将被视为关键字,因此当在需要表或列名称的地方使用时,会引发解析错误。该示例可以用带引号的标识符这样写
UPDATE "my_table" SET "a" = 5;
带引号的标识符可以包含任何字符,除了代码为零的字符。(要包含双引号,请写两个双引号。)这允许构造其他方式不可能的表或列名称,例如包含空格或与号的名称。长度限制仍然适用。
引用标识符也会使其区分大小写,而不带引号的名称总是被折叠为小写。例如,标识符 FOO
、foo
和 "foo"
被 PostgreSQL 视为相同,但 "Foo"
和 "FOO"
与这三个以及彼此不同。(PostgreSQL 中不带引号的名称折叠为小写与 SQL 标准不兼容,SQL 标准规定不带引号的名称应折叠为大写。因此,根据标准,foo
应该等同于 "FOO"
而不是 "foo"
。如果你想编写可移植的应用程序,建议始终引用特定的名称或永远不要引用它。)
带引号的标识符的一种变体允许包含由其代码点标识的转义 Unicode 字符。此变体以 U&
开头(大写或小写 U 后跟与号),紧接在开头的双引号之前,中间没有任何空格,例如 U&"foo"
。(请注意,这会与操作符 &
产生歧义。请在操作符周围使用空格以避免此问题。)在引号内,Unicode 字符可以通过写入反斜杠后跟四位十六进制代码点数字或可选地反斜杠后跟加号后跟六位十六进制代码点数字以转义形式指定。例如,标识符 "data"
可以写成
U&"d\0061t\+000061"
以下不太简单的示例用西里尔字母书写俄语单词“slon”(大象)
U&"\0441\043B\043E\043D"
如果需要使用除反斜杠之外的其他转义字符,可以使用字符串后的 UESCAPE
子句指定,例如
U&"d!0061t!+000061" UESCAPE '!'
转义字符可以是任何单个字符,但不能是十六进制数字、加号、单引号、双引号或空白字符。请注意,转义字符是在 UESCAPE
之后用单引号而不是双引号写的。
要将转义字符按字面意思包含在标识符中,请写两次。
可以使用 4 位或 6 位转义形式来指定 UTF-16 代理对,以组成代码点大于 U+FFFF 的字符,尽管从技术上讲,6 位形式的可用性使得这变得没有必要。(代理对不会直接存储,而是组合成单个代码点。)
如果服务器编码不是 UTF-8,则由这些转义序列之一标识的 Unicode 代码点会转换为实际的服务器编码;如果无法转换,则会报告错误。
PostgreSQL 中有三种隐式类型常量:字符串、位串和数字。常量也可以使用显式类型指定,这样可以实现更精确的表示,并让系统进行更高效的处理。这些替代方案将在以下小节中讨论。
SQL 中的字符串常量是用单引号 ('
) 括起来的任意字符序列,例如 'This is a string'
。要在字符串常量中包含单引号字符,请写入两个相邻的单引号,例如,'Dianne''s horse'
。请注意,这与双引号字符 ("
) 不同。
两个仅由空格(且至少有一个换行符)分隔的字符串常量会连接在一起,并被有效地视为该字符串是以一个常量写入的。例如
SELECT 'foo' 'bar';
等效于
SELECT 'foobar';
但是
SELECT 'foo' 'bar';
不是有效的语法。(这种略显怪异的行为是由SQL指定的;PostgreSQL 遵循该标准。)
PostgreSQL 也接受“转义”字符串常量,这是 SQL 标准的扩展。转义字符串常量通过在开头的单引号前写入字母 E
(大写或小写)来指定,例如 E'foo'
。(在跨行继续转义字符串常量时,仅在第一个开头的引号前写入 E
。)在转义字符串中,反斜杠字符 (\
) 开始一个类似 C 的反斜杠转义序列,其中反斜杠和后面的字符的组合表示一个特殊的字节值,如表 4.1所示。
表 4.1. 反斜杠转义序列
反斜杠转义序列 | 解释 |
---|---|
\b |
退格 |
\f |
换页 |
\n |
换行 |
\r |
回车 |
\t |
制表符 |
\ , \ , \ (o = 0–7) |
八进制字节值 |
\x , \x (h = 0–9, A–F) |
十六进制字节值 |
\u , \U (x = 0–9, A–F) |
16 或 32 位十六进制 Unicode 字符值 |
反斜杠后面的任何其他字符都按字面意义理解。因此,要包含反斜杠字符,请写入两个反斜杠 (\\
)。此外,除了通常的 ''
方式之外,还可以通过写入 \'
将单引号包含在转义字符串中。
您有责任确保您创建的字节序列,尤其是在使用八进制或十六进制转义时,在服务器字符集编码中组成有效的字符。一个有用的替代方法是使用 Unicode 转义或替代 Unicode 转义语法,详见第 4.1.2.3 节;然后服务器将检查字符转换是否可行。
如果配置参数 standard_conforming_strings 为 off
,则 PostgreSQL 在常规字符串常量和转义字符串常量中都会识别反斜杠转义。但是,从 PostgreSQL 9.1 开始,默认值为 on
,这意味着仅在转义字符串常量中识别反斜杠转义。此行为更符合标准,但可能会破坏依赖于历史行为的应用程序,在历史行为中始终识别反斜杠转义。作为一种变通方法,您可以将此参数设置为 off
,但最好是避免使用反斜杠转义。如果您需要使用反斜杠转义来表示特殊字符,请使用 E
写入字符串常量。
除了 standard_conforming_strings
,配置参数 escape_string_warning 和 backslash_quote 也控制字符串常量中反斜杠的处理。
代码为零的字符不能在字符串常量中。
PostgreSQL 还支持另一种字符串转义语法,该语法允许按代码点指定任意 Unicode 字符。Unicode 转义字符串常量以 U&
(大写或小写字母 U 后跟与号)开头,紧接在开头的引号之前,中间没有任何空格,例如 U&'foo'
。(请注意,这会与运算符 &
产生歧义。请在运算符周围使用空格以避免此问题。)在引号内,可以通过写入一个反斜杠,后跟四位十六进制代码点编号,或者写入一个反斜杠,后跟一个加号,后跟一个六位十六进制代码点编号,来以转义形式指定 Unicode 字符。例如,字符串 'data'
可以写成
U&'d\0061t\+000061'
以下不太简单的示例用西里尔字母书写俄语单词“slon”(大象)
U&'\0441\043B\043E\043D'
如果需要除反斜杠之外的其他转义字符,可以使用字符串后的 UESCAPE
子句来指定,例如
U&'d!0061t!+000061' UESCAPE '!'
转义字符可以是除十六进制数字、加号、单引号、双引号或空格字符之外的任何单个字符。
要按字面意义将转义字符包含在字符串中,请写入两次。
可以使用 4 位或 6 位转义形式来指定 UTF-16 代理对,以组成代码点大于 U+FFFF 的字符,尽管从技术上讲,6 位形式的可用性使得这变得没有必要。(代理对不会直接存储,而是组合成单个代码点。)
如果服务器编码不是 UTF-8,则由这些转义序列之一标识的 Unicode 代码点会转换为实际的服务器编码;如果无法转换,则会报告错误。
此外,字符串常量的 Unicode 转义语法仅在配置参数 standard_conforming_strings 打开时才有效。这是因为,否则此语法可能会使解析 SQL 语句的客户端感到困惑,以至于可能导致 SQL 注入和类似的安全问题。如果参数设置为 off,则此语法将被拒绝并显示错误消息。
虽然指定字符串常量的标准语法通常很方便,但当所需的字符串包含多个单引号时,可能很难理解,因为每个单引号都必须加倍。为了在这种情况下允许更易于阅读的查询,PostgreSQL 提供了另一种方法,称为“美元引号”,用于编写字符串常量。美元引号字符串常量由一个美元符号 ($
)、一个可选的零个或多个字符的“标记”、另一个美元符号、构成字符串内容的任意字符序列、一个美元符号、开始此美元引号的相同标记和一个美元符号组成。例如,以下是使用美元引号指定字符串“Dianne's horse”的两种不同方式
$$Dianne's horse$$ $SomeTag$Dianne's horse$SomeTag$
请注意,在美元引号字符串内部,可以使用单引号,而无需转义。实际上,美元引号字符串中的任何字符都不会被转义:字符串内容始终按字面意义写入。反斜杠不是特殊的,美元符号也不是特殊的,除非它们是与开头标记匹配的序列的一部分。
可以通过在每个嵌套级别选择不同的标记来嵌套美元引号字符串常量。这最常用于编写函数定义。例如
$function$ BEGIN RETURN ($1 ~ $q$[\t\r\n\v\\]$q$); END; $function$
在此,序列 $q$[\t\r\n\v\\]$q$
表示美元引号文字字符串 [\t\r\n\v\\]
,该字符串将在 PostgreSQL 执行函数体时被识别。但是,由于该序列与外部美元引号定界符 $function$
不匹配,因此就外部字符串而言,它只是常量中的一些其他字符。
美元引号字符串的标记(如果有)遵循与未加引号的标识符相同的规则,但它不能包含美元符号。标记区分大小写,因此 $tag$String content$tag$
是正确的,但 $TAG$String content$tag$
是不正确的。
后跟关键字或标识符的美元引号字符串必须与它用空格分隔;否则,美元引号定界符将被视为前面标识符的一部分。
美元引号不是 SQL 标准的一部分,但它通常是一种比符合标准的单引号语法更方便的编写复杂字符串文字的方法。当表示其他常量中的字符串常量时,它特别有用,这在过程函数定义中经常需要。使用单引号语法,上述示例中的每个反斜杠都必须写成四个反斜杠,这将会在解析原始字符串常量时减少为两个反斜杠,然后在函数执行期间重新解析内部字符串常量时减少为一个反斜杠。
位串常量看起来像常规字符串常量,在开头的引号前紧跟一个 B
(大写或小写)(没有中间的空格),例如 B'1001'
。位串常量中唯一允许的字符是 0
和 1
。
另外,可以使用十六进制表示法指定位串常量,方法是使用前导的 X
(大写或小写),例如 X'1FF'
。这种表示法等效于每个十六进制数字使用四个二进制数字的位串常量。
两种形式的位串常量都可以像常规字符串常量一样跨行继续。美元符号引用不能在位串常量中使用。
数值常量接受以下一般形式
digits
digits
.[digits
][e[+-]digits
] [digits
].digits
[e[+-]digits
]digits
e[+-]digits
其中 digits
是一个或多个十进制数字(0 到 9)。如果使用了小数点,则小数点前后至少要有一个数字。如果存在指数标记(e
),则其后至少要有一个数字。常量中不能嵌入任何空格或其他字符,但可以使用下划线进行视觉分组,如下所述。请注意,任何前导加号或减号实际上都不被视为常量的一部分;它是应用于常量的运算符。
以下是一些有效的数值常量示例
42
3.5
4.
.001
5e2
1.925e-3
此外,以下形式接受非十进制整数常量
0xhexdigits
0ooctdigits
0bbindigits
其中 hexdigits
是一个或多个十六进制数字 (0-9, A-F),octdigits
是一个或多个八进制数字 (0-7),bindigits
是一个或多个二进制数字 (0 或 1)。十六进制数字和基数前缀可以使用大写或小写字母。请注意,只有整数才能使用非十进制形式,带有小数部分的数字不能使用。
以下是一些有效的非十进制整数常量示例
0b100101
0B10011001
0o273
0O755
0x42f
0XFFFF
为了视觉分组,可以在数字之间插入下划线。这些对常量的值没有进一步的影响。例如
1_500_000_000
0b10001000_00000000
0o_1_755
0xFFFF_FFFF
1.618_034
不允许在数值常量或一组数字的开头或结尾使用下划线(即,紧接小数点或指数标记之前或之后),并且不允许连续使用多个下划线。
如果数值常量不包含小数点和指数,则如果其值适合 integer
类型(32 位),则最初假定其类型为 integer
;否则,如果其值适合 bigint
类型(64 位),则假定其类型为 bigint
;否则,将其视为 numeric
类型。包含小数点和/或指数的常量始终最初假定为 numeric
类型。
数值常量的初始分配数据类型只是类型解析算法的起点。在大多数情况下,常量将根据上下文自动强制转换为最合适的类型。必要时,可以通过强制转换将数值解释为特定的数据类型。 例如,您可以通过编写以下内容来强制将数值视为 real
(float4
) 类型
REAL '1.23' -- string style 1.23::REAL -- PostgreSQL (historical) style
这些实际上只是接下来讨论的通用强制转换表示法的特殊情况。
可以使用以下任何一种表示法输入任意类型的常量
type
'string
' 'string
'::type
CAST ( 'string
' AStype
)
字符串常量的文本被传递到名为 type
的类型的输入转换例程。结果是指定类型的常量。如果没有关于常量必须是什么类型的歧义(例如,当它直接分配给表列时),则可以省略显式的类型强制转换,在这种情况下,它会自动被强制转换。
可以使用常规 SQL 表示法或美元符号引用编写字符串常量。
也可以使用类似函数的语法指定类型强制转换
typename
( 'string
' )
但并非所有类型名称都可以以这种方式使用;有关详细信息,请参阅第 4.2.9 节。
::
、CAST()
和函数调用语法也可以用于指定任意表达式的运行时类型转换,如第 4.2.9 节所述。为避免语法歧义,
语法只能用于指定简单文字常量的类型。type
'string
'
语法的另一个限制是它不适用于数组类型;请使用 type
'string
'::
或 CAST()
来指定数组常量的类型。
CAST()
语法符合 SQL 标准。
语法是标准的泛化:SQL 仅为少数数据类型指定此语法,但 PostgreSQL 允许它用于所有类型。带有 type
'string
'::
的语法是历史 PostgreSQL 用法,函数调用语法也是如此。
运算符名称是来自以下列表的最多 NAMEDATALEN
-1 (默认为 63) 个字符的序列
+ - * / < > = ~ ! @ # % ^ & | ` ?
但是,运算符名称有一些限制
--
和 /*
不能出现在运算符名称的任何位置,因为它们将被视为注释的开始。
多字符运算符名称不能以 +
或 -
结尾,除非该名称还包含以下字符之一
~ ! @ # % ^ & | ` ?
例如,@-
是允许的运算符名称,但 *-
不是。此限制允许 PostgreSQL 解析符合 SQL 的查询,而无需在标记之间使用空格。
当使用非 SQL 标准的运算符名称时,通常需要使用空格分隔相邻的运算符以避免歧义。例如,如果您定义了一个名为 @
的前缀运算符,则不能编写 X*@Y
;您必须编写 X* @Y
以确保 PostgreSQL 将其读取为两个运算符名称而不是一个。
一些非字母数字字符具有与作为运算符不同的特殊含义。有关用法详细信息,请参阅描述相应语法元素的位置。本节仅旨在说明这些字符的存在并总结其用途。
后跟数字的美元符号 ($
) 用于表示函数定义或预处理语句正文中的位置参数。在其他上下文中,美元符号可以是标识符或美元符号引用的字符串常量的一部分。
括号 (()
) 具有其通常的含义,用于对表达式进行分组并强制执行优先级。在某些情况下,括号是特定 SQL 命令的固定语法的一部分。
方括号 ([]
) 用于选择数组的元素。有关数组的更多信息,请参见第 8.15 节。
逗号 (,
) 用于某些语法结构中,以分隔列表的元素。
分号 (;
) 终止 SQL 命令。它不能出现在命令中的任何位置,除非在字符串常量或引号标识符中。
冒号 (:
) 用于从数组中选择“切片”。(请参阅第 8.15 节。)在某些 SQL 方言(例如嵌入式 SQL)中,冒号用于为变量名称添加前缀。
星号 (*
) 在某些上下文中用于表示表行或复合值的所有字段。当用作聚合函数的参数时,它也具有特殊含义,即聚合不需要任何显式参数。
句点 (.
) 用于数值常量,并用于分隔模式、表和列名称。
注释是以双破折号开头的字符序列,并延伸到行尾,例如
-- This is a standard SQL comment
或者,可以使用 C 样式块注释
/* multiline comment * with nesting: /* nested block comment */ */
其中注释以 /*
开头,并扩展到匹配的 */
。这些块注释是嵌套的,如 SQL 标准中所述,但与 C 不同,因此可以注释掉可能包含现有块注释的较大代码块。
在进一步的语法分析之前,会从输入流中删除注释,并有效地将其替换为空格。
表 4.2 显示了 PostgreSQL 中运算符的优先级和结合性。大多数运算符具有相同的优先级并且是左结合的。运算符的优先级和结合性硬编码到解析器中。如果您希望以不同于优先级规则的方式解析包含多个运算符的表达式,请添加括号。
表 4.2. 运算符优先级(从高到低)
运算符/元素 | 结合性 | 描述 |
---|---|---|
. |
左 | 表/列名称分隔符 |
:: |
左 | PostgreSQL 样式类型强制转换 |
[ ] |
左 | 数组元素选择 |
+ - |
右 | 一元加号、一元减号 |
COLLATE |
左 | 排序规则选择 |
AT |
左 | AT TIME ZONE , AT LOCAL |
^ |
左 | 指数 |
* / % |
左 | 乘法、除法、取模 |
+ - |
左 | 加法、减法 |
(任何其他运算符) | 左 | 所有其他本机和用户定义的运算符 |
BETWEEN IN LIKE ILIKE SIMILAR |
范围包含、集合成员资格、字符串匹配 | |
< > = <= >= <> |
比较运算符 | |
IS ISNULL NOTNULL |
IS TRUE 、IS FALSE 、IS NULL 、IS DISTINCT FROM 等。 |
|
NOT |
右 | 逻辑否定 |
AND |
左 | 逻辑合取 |
OR |
左 | 逻辑析取 |
请注意,运算符优先级规则也适用于与上述内置运算符同名的用户定义运算符。例如,如果您为某些自定义数据类型定义了“+”运算符,则无论您的运算符做什么,它都将具有与内置“+”运算符相同的优先级。
当在 OPERATOR
语法中使用模式限定的运算符名称时,例如在
SELECT 3 OPERATOR(pg_catalog.+) 4;
中,OPERATOR
构造被认为具有表 4.2中针对“任何其他运算符”显示的默认优先级。无论 OPERATOR()
内部出现哪个特定运算符,都是如此。
PostgreSQL 9.5之前的版本使用了略有不同的运算符优先级规则。特别是,<=
、>=
和 <>
以前被视为通用运算符;IS
测试以前具有更高的优先级;而 NOT BETWEEN
和相关的构造行为不一致,在某些情况下被视为具有 NOT
的优先级,而不是 BETWEEN
的优先级。 这些规则的修改是为了更好地符合SQL标准,并减少因逻辑上等效的构造处理不一致而造成的混淆。在大多数情况下,这些更改不会导致行为变化,或者可能会导致“没有此运算符”失败,可以通过添加括号来解决。然而,在某些极端情况下,查询的行为可能会在没有任何解析错误报告的情况下发生变化。
如果您发现文档中的任何内容不正确,与您使用特定功能的经验不符或需要进一步澄清,请使用此表单来报告文档问题。