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
才能完整。每个命令的精确语法规则在 VI. 部分 参考 中描述。
上面示例中的 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 标准不兼容,标准规定未加引号的名称应折叠成大写。因此,根据标准,foo
应等同于 "FOO"
而不是 "foo"
。如果要编写可移植的应用程序,建议始终引用特定名称或从不引用它。)
带引号标识符的变体允许包含由其代码点标识的转义 Unicode 字符。该变体以 U&
(大写或小写 U 后跟 ampersand)开头,紧接在开头的双引号之前,中间没有空格,例如 U&"foo"
。(请注意,这会与操作符 &
产生歧义。在操作符周围使用空格以避免此问题。)在引号内,可以通过写入反斜杠后跟四位十六进制代码点数字,或另一种选择是反斜杠后跟加号后跟六位十六进制代码点数字来指定 Unicode 字符的转义形式。例如,标识符 "data"
可以写成
U&"d\0061t\+000061"
下面的一个不太简单的例子用西里尔字母写了俄语单词 “slon”(大象)
U&"\0441\043B\043E\043D"
如果需要一个不同于反斜杠的转义字符,可以使用字符串后的 UESCAPE
子句,例如
U&"d!0061t!+000061" UESCAPE '!'
转义字符可以是除十六进制数字、加号、单引号、双引号或空格字符以外的任何单个字符。请注意,转义字符写在 UESCAPE
之后的单引号中,而不是双引号中。
要将转义字符本身字面包含在标识符中,请写两次。
可以使用四位或六位转义形式来指定 UTF-16 代替对以构成代码点大于 U+FFFF 的字符,尽管六位形式的存在使得这在技术上不必要。(替代对不会直接存储,而是组合成单个代码点。)
如果服务器编码不是 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. 带 Unicode 转义的字符串常量 所述;然后服务器将检查字符转换是否可能。
如果配置参数 standard_conforming_strings 为 off
,则 PostgreSQL 会在常规字符串常量和转义字符串常量中都识别反斜杠转义。然而,从 PostgreSQL 9.1 开始,默认值为 on
,这意味着反斜杠转义仅在转义字符串常量中识别。这种行为更符合标准,但可能会破坏依赖于历史行为(反斜杠转义始终被识别)的应用程序。作为一种变通方法,您可以将此参数设置为 off
,但最好放弃使用反斜杠转义。如果您需要使用反斜杠转义来表示特殊字符,请使用 E
来写字符串常量。
除了 standard_conforming_strings
之外,配置参数 escape_string_warning 和 backslash_quote 控制了字符串常量中反斜杠的处理。
代码为零的字符不能在字符串常量中。
PostgreSQL 还支持另一种转义语法,用于字符串,允许通过代码点指定任意 Unicode 字符。Unicode 转义字符串常量以 U&
(大写或小写字母 U 后跟 ampersand)开头,紧接在开头的引号之前,中间没有空格,例如 U&'foo'
。(请注意,这会与操作符 &
产生歧义。在操作符周围使用空格以避免此问题。)在引号内,可以通过写入反斜杠后跟四位十六进制代码点数字,或另一种选择是反斜杠后跟加号后跟六位十六进制代码点数字来指定 Unicode 字符的转义形式。例如,字符串 'data'
可以写成
U&'d\0061t\+000061'
下面的一个不太简单的例子用西里尔字母写了俄语单词 “slon”(大象)
U&'\0441\043B\043E\043D'
如果需要一个不同于反斜杠的转义字符,可以使用字符串后的 UESCAPE
子句,例如
U&'d!0061t!+000061' UESCAPE '!'
转义字符可以是除十六进制数字、加号、单引号、双引号或空格字符以外的任何单个字符。
要将转义字符本身字面包含在字符串中,请写两次。
可以使用四位或六位转义形式来指定 UTF-16 代替对以构成代码点大于 U+FFFF 的字符,尽管六位形式的存在使得这在技术上不必要。(替代对不会直接存储,而是组合成单个代码点。)
如果服务器编码不是 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
类型(如果其值适合 integer
类型(32 位));否则,如果其值适合 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)中,冒号用于前缀变量名。
星号(*
)在某些上下文中用于表示表行或复合值的全部字段。当用作聚合函数的参数时,它也有特殊含义,即聚合不需要显式参数。
句点(.
)用于数字常量,并用于分隔模式、表和列名。
注释是beginning with double dashes and extending to the end of the line 的字符序列,例如
-- 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 标准并减少逻辑等效构造处理不一致性带来的混淆而更改的。在大多数情况下,这些更改不会导致行为变化,或者最多会导致 “无此操作符” 错误,可以通过添加括号来解决。然而,在某些边缘情况下,查询可能会在不报告任何解析错误的情况下改变行为。
如果您在文档中看到任何不正确、与您对特定功能的使用体验不符或需要进一步澄清的内容,请使用 此表格 来报告文档问题。