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()
内部出现哪个特定运算符,都是如此。
9.5 之前的PostgreSQL版本使用略微不同的运算符优先级规则。特别是,<=
>=
和<>
过去被视为通用运算符;IS
测试过去具有更高的优先级;并且NOT BETWEEN
和相关构造的行为不一致,在某些情况下被认为具有NOT
而不是BETWEEN
的优先级。这些规则已更改,以更好地符合 SQL 标准并减少由逻辑等效构造的不一致处理造成的混淆。在大多数情况下,这些更改不会导致任何行为变化,或者可能导致“没有这样的运算符”错误,可以通过添加括号来解决。但是,在某些情况下,查询可能会更改行为,而不会报告任何解析错误。
如果您在文档中看到任何不正确的内容,与您对特定功能的体验不符或需要进一步澄清,请使用此表单报告文档问题。