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 代码点将被转换成实际的服务器编码;如果无法这样做,则会报告错误。
当配置参数standard_conforming_strings打开时,字符串常量的 Unicode 转义语法的才有效。这是因为否则此语法可能会混淆解析 SQL 语句的客户端,直至导致 SQL 注入和类似的安全问题。如果将此参数设置为关闭,此语法将被拒绝并显示错误消息。
虽然用于指定字符串常量的标准语法通常很方便,但当所需字符串包含许多单引号时,理解起来会很困难,因为每个单引号都必须加倍。为了在这种情况允许更可读性更高的查询,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 方言(如 Embedded 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 标准,并减少对逻辑等效构件采取不一致处理时产生的混乱。在大多数情况下,这些更改不会导致行为更改,也许会出现 “无此操作符” 故障,但可以添加括号来解决这些故障。但在某些极端情况下,查询的行为可能会发生更改,而不会报告任何解析错误。