pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
目录
本章描述 SQL 的语法。它是理解后续各章的基础,后续各章将详细介绍如何使用 SQL 命令定义和修改数据。
我们也建议已经熟悉 SQL 的用户仔细阅读本章,因为其中包含一些在各 SQL 数据库之间实现并不一致的规则和概念,以及一些 PostgreSQL 特有的规则和概念。
SQL 输入由一系列命令组成。一个命令由一系列词元组成,并以分号(“;”)终止。输入流的结束也会终止一个命令。哪些词元是合法的,取决于具体命令的语法。
一个词元可以是关键词、标识符、带引号的标识符、字面量(或常量),或者特殊字符符号。词元通常由空白(空格、制表符、换行)分隔,但如果不存在歧义,则不必如此(一般只有特殊字符与其他类型的词元相邻时才会这样)。
此外,注释也可以出现在 SQL 输入中。它们不是词元,实际上等价于空白。
例如,下面是一个(语法上)合法的SQL输入:
SELECT * FROM MY_TABLE; UPDATE MY_TABLE SET A = 5; INSERT INTO MY_TABLE VALUES (3, 'hi there');
这是一个由三个命令组成的序列,每行一个命令(虽然这并非必须;一行中可以包含多个命令,命令也可以有意义地跨多行书写)。
在哪些词元标识命令、哪些词元是操作数或参数这一点上,SQL 语法并不十分一致。最前面的几个词元通常是命令名,因此在上面的示例中,我们通常会说是 “SELECT”、“UPDATE” 和 “INSERT” 命令。但是例如 UPDATE 命令总是要求在特定位置出现一个 SET 词元,而这种形式的 INSERT 也要求有一个 VALUES 才算完整。每个命令的精确语法规则见 PostgreSQL 7.3.21 参考手册。
上例中的 SELECT、UPDATE 或 VALUES 这类词元都是关键词,也就是在 SQL 语言中具有固定含义的词。词元 MY_TABLE 和 A 则是标识符的例子。它们标识表、列或其他数据库对象的名称,这取决于它们所在的命令。因此,它们有时也简称为“名称”。关键词和标识符具有相同的词法结构,这意味着如果不了解该语言,就无法判断一个词元究竟是标识符还是关键词。完整的关键词列表可以在附录 B中找到。
SQL 标识符和关键词必须以字母(a-z,也包括带变音符的字母和非拉丁字母)或下划线(_)开头。标识符或关键词中的后续字符可以是字母、数字(0-9)或下划线,不过 SQL 标准不会定义包含数字或以下划线开头、结尾的关键词。
系统对标识符最多只使用 NAMEDATALEN-1 个字符;在命令中可以写更长的名称,但它们会被截断。默认情况下, NAMEDATALEN 为 64,因此标识符的最大长度是 63(但在编译 PostgreSQL 时,可以修改 src/include/postgres_ext.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" 与这三个都不同,彼此之间也不同。 [2]
在 PostgreSQL 中有三种隐式类型常量:字符串、位串和数字。也可以为常量指定显式类型,这样系统就能更准确地表示并更高效地处理它们。隐式常量将在下面介绍;显式常量在随后讨论。
SQL 中的字符串常量是由单引号(“'”)括起的任意字符序列,例如 'This is a string'。SQL 允许在字符串中嵌入单引号,方法是写两个相邻的单引号,例如 'Dianne''s horse'。在 PostgreSQL 中,也可以用反斜线(“\”)对单引号转义,例如 'Dianne\'s horse'。
也可以使用 C 风格的反斜线转义:\b 是退格,\f 是换页,\n 是换行,\r 是回车,\t 是制表符,而 \(其中 xxxxxx 是一个八进制数)是具有相应 ASCII 代码的字符。跟随在反斜线后面的任何其他字符都按其字面意思对待。因此,要在字符串常量中包含一个反斜线,请写两个反斜线。
编码为零的字符不能出现在字符串常量中。
两个只由空白及至少一个新行分隔的字符串常量会被连接在一起,并且实际上被当作该字符串写成一个常量来对待。例如:
SELECT 'foo' 'bar';
等同于
SELECT 'foobar';
但是
SELECT 'foo' 'bar';
不是合法的语法。(这一略显古怪的行为是 SQL 标准规定的;PostgreSQL 遵循该标准。)
位串常量看起来像在常规字符串常量的开引号之前(中间无空白)加了一个 B(大写或小写形式),例如 B'1001'。位串常量中允许的字符只有 0 和 1。
或者,也可以用十六进制记法指定位串常量,即在开头使用 X(大写或小写形式),例如 X'1FF'。这种记法等价于每个十六进制位对应四个二进制位的位串常量。
两种形式的位串常量都可以以与常规字符串常量相同的方式跨行继续。
数字常量接受下列一般形式:
digitsdigits.[digits][e[+-]digits] [digits].digits[e[+-]digits]digitse[+-]digits
其中 digits 是一个或多个十进制数字(0 到 9)。如果使用小数点,小数点前后必须至少有一位数字。如果存在指数标记(e),其后必须至少跟随一位数字。常量中不能嵌入任何空格或其他字符。注意,任何前导的加号或减号实际上不被视为常量的一部分;它是应用于该常量的操作符。
这些是合法数字常量的一些示例:
42
3.5
4.
.001
5e2
1.925e-3
一个既不包含小数点也不包含指数的数字常量,如果其值适合 integer 类型(32 位),最初被假定为 integer 类型;否则如果其值适合 bigint 类型(64 位),被假定为 bigint 类型;再否则被当作 numeric 类型。包含小数点和/或指数的常量总是最初被假定为 numeric 类型。
为数字常量最初指定的数据类型,只是类型解析算法的起点。在大多数情况下,常量会根据上下文被自动强制转换成最合适的类型。必要时,你可以通过类型转换强制把一个数值解释为特定的数据类型。例如,你可以通过写成
REAL '1.23' -- string style 1.23::REAL -- PostgreSQL (historical) style
强制把一个数值当作 real(float4)类型对待。
一个任意类型的常量可以用下列记法之一输入:
type'string' 'string'::typeCAST ( 'string' AStype)
字符串的文本被传递给名为 type 的类型的输入转换例程。结果就是所指示类型的一个常量。如果常量必须是哪个类型没有歧义(例如,当它作为参数传递给一个未重载的函数时),显式类型转换可以省略,此时它会被自动强制转换。
也可以用类似函数调用的语法来指定类型强制转换:
typename( 'string' )
但并非所有类型名都可以这样使用;详见 第 1.2.6 节。
::、CAST() 以及函数调用语法也可以用来指定任意表达式的运行时类型转换,如 第 1.2.6 节 中所述。但 type 'string' 形式只能用来指定字面常量的类型。type 'string' 的另一个限制是它不适用于数组类型;要用 :: 或 CAST() 来指定数组常量的类型。
数组常量的一般格式如下:
'{ val1 delim val2 delim ... }'
其中 delim 是该类型的分隔符字符,记录在它的 pg_type 项中。(对于所有内建类型,这都是逗号字符 “,”。)每个 val 要么是数组元素类型的一个常量,要么是一个子数组。一个数组常量的示例是
'{{1,2,3},{4,5,6},{7,8,9}}'
这个常量是一个二维的 3×3 数组,由三个整数子数组组成。
单个数组元素可以放在双引号(") 之间,以避免与空白相关的歧义问题。若不加引号,数组值解析器将跳过前导空白。
(数组常量实际上只是前一节讨论的一般类型常量的一种特例。该常量最初被当作字符串处理并被传给数组的输入转换例程。可能需要显式的类型说明。)
一个操作符是由下列列表中的字符组成的一个序列,长度最多为 NAMEDATALEN-1 个字符(默认是 63):
+ - * / < > = ~ ! @ # % ^ & | ` ? $
不过,操作符名称也有一些限制:
$(美元符号)不能作为单字符操作符,但它可以作为多字符操作符名称的一部分。
-- 和 /* 不能出现在操作符名称中的任何位置,因为它们会被视为注释的开始。
多字符操作符名称不能以 + 或 - 结尾,除非该名称中还至少包含下列字符中的一个:
~ ! @ # % ^ & | ` ? $
例如,@- 是合法的操作符名称,但 *- 不是。这个限制使 PostgreSQL 能够在不要求词元之间必须有空格的情况下解析符合 SQL 的查询。
当使用非 SQL 标准的操作符名时,你通常需要用空格分隔相邻的操作符来避免歧义。例如,如果你定义了一个名为@的左一元操作符,你不能写X*@Y,你必须写X* @Y来确保PostgreSQL把它读作两个操作符名而不是一个。
某些非字母数字字符具有不同于操作符的特殊含义。关于它们的详细用法,可以在描述相应语法元素的位置找到。本节只是为了提示它们的存在,并概述这些字符的用途。
美元符号($)后跟数字时,用来表示函数定义体中的位置参数。在其他上下文中,美元符号可以是操作符名称的一部分。
圆括号(())具有它们通常的含义,用来对表达式分组并确定运算优先级。在某些情况中,圆括号被要求作为一个特定 SQL 命令的固定语法的一部分。
方括号([])被用来选择一个数组中的元素。更多关于数组的信息见第 5.12 节。
逗号(,)被用在某些语法结构中来分割一个列表的元素。
分号(;)结束一个 SQL 命令。它不能出现在一个命令中间的任何位置,除了在一个字符串常量中或者一个带引号的标识符中。
冒号(:)被用来从数组中选择“切片”(见第 5.12 节)。在某些 SQL 的“方言”(例如嵌入式 SQL)中,冒号被用来作为变量名的前缀。
星号(*)在SELECT命令中或与COUNT聚合函数一起使用时有特殊含义。
句点(.)被用在浮点常量中,并且被用来分割模式、表和列名。
注释是一串以双连字符开始并延伸到行尾的字符,例如:
-- 这是一条标准 SQL92 注释
另外,也可以使用 C 风格注释块:
/* 多行注释 * 包含嵌套:/* 嵌套块注释 */ */
这里该注释开始于/*并且延伸到匹配出现的*/。这些注释块可按照 SQL99 中指定的方式嵌套,但和 C 中不同。这样我们可以注释掉一大段可能包含注释块的代码。
在进一步进行语法分析之前,注释会从输入流中移除,并实际被替换为空白。
表 1.1展示了 PostgreSQL 中操作符的优先级和结合性。大多数操作符具有相同的优先级,并且是左结合的。操作符的优先级和结合性是固定写在解析器中的。这可能导致不直观的行为;例如,布尔操作符 < 和 > 与布尔操作符 <= 和 >= 的优先级不同。此外,在组合使用二元和一元操作符时,有时需要添加圆括号。例如:
SELECT 5 ! - 6;
将被解析为:
SELECT 5 ! (- 6);
因为解析器并不知道 — 等到知道时已经太迟了 — !被定义为后缀操作符,而不是中缀操作符。要在这种情况下得到所需的行为,必须写成:
SELECT (5 !) - 6;
这是为可扩展性付出的代价。
表 1.1. 操作符优先级(递减)
| 操作符/元素 | 结合性 | 描述 |
|---|---|---|
. |
左 | 表/列名分隔符 |
:: |
左 | PostgreSQL-风格的类型转换 |
[ ] |
左 | 数组元素选择 |
- |
右 | 一元负号 |
^ |
左 | 求幂 |
* / % |
左 | 乘、除、取模 |
+ - |
左 | 加、减 |
IS |
IS TRUE、IS FALSE、IS UNKNOWN、IS NULL |
|
ISNULL |
空值测试 | |
NOTNULL |
非空测试 | |
| (任意其他) | 左 | 所有其他内建和用户定义的操作符 |
IN |
集合成员关系 | |
BETWEEN |
范围包含 | |
OVERLAPS |
时间区间重叠 | |
LIKE ILIKE SIMILAR |
字符串模式匹配 | |
< > |
小于、大于 | |
= |
右 | 相等、赋值 |
NOT |
右 | 逻辑否定 |
AND |
左 | 逻辑合取 |
OR |
左 | 逻辑析取 |
注意,这些操作符优先级规则也适用于名称与上述内置操作符相同的用户定义操作符。例如,如果你为某种自定义数据类型定义了一个 “+” 操作符,那么无论你的操作符做什么,它都将具有与内置 “+” 操作符相同的优先级。
当一个模式限定的操作符名被用在OPERATOR语法中时,如下面的示例:
SELECT 3 OPERATOR(pg_catalog.+) 4;
OPERATOR 结构在优先级上会被视为 表 1.1 中为“任意其他”操作符所示的优先级。无论 OPERATOR() 中指定的是哪个具体操作符,这一点都成立。
[2] PostgreSQL 将不加引号的名称折叠为小写,这与 SQL 标准不兼容;标准规定,不加引号的名称应折叠为大写。因此,按照标准, foo 应当等价于 "FOO",而不是 "foo"。如果你想编写可移植的应用,建议对某个特定名称要么始终加引号,要么始终不加引号。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。