Chapter 1. SQL 语法

Table of Contents
1.1. 词法结构
1.1.1. 标识符和关键字
1.1.2. 常量
1.1.3. 操作符
1.1.4. 特殊字符
1.1.5. 注释
1.2. 字段
1.3. 值表达式
1.3.1. 字段引用
1.3.2. 位置参数
1.3.3. 函数调用
1.3.4. 聚集表达式
1.4. 词法优先级

描述通用的 SQL 语法。

1.1. 词法结构

SQL 输入由一系列命令组成. 一条命令是由一系列记号构成, 用一个分号(“;”)结尾. 输入流的终止也结束一条命令.那些记号是合法的取决于特定命令的语法.

记号可以是一个关键字, 一个标识符,一个 引起的标识符, 一个文本(或常量),或者是特殊的字符符号. 记号通常由空白分隔(空格,tab,换行符),但如果不存在混淆 的时候也可以不用(通常只是一个特殊字符与一些其它记号类型相联 的时候).

另外,在 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 才完整.每条命令的准确语法规则都在 参考手册里描写.

1.1.1. 标识符和关键字

象上面的例子里的 SELECTUPDATE, 或 VALUES 这样的记号都是关键字 的例子,也就是那些在 SQL 语言里有固定含义的单词. 记号 MY_TABLEA标识符的例子.根据使用它们的命令的不同, 它们标识表,字段,或者其它数据库对象的名字. 因此,有时候只是简单地叫它们“名字”. 关键字和标识符有着同样的词法结构,意思是我们在没有认识这种语言 之前是无法区分一个记号是标识符还是名字.你可以在 Appendix B 里找到一个关键字的完整列表.

SQL 标识符和关键字必须以一个字母开头 (a-z) 或下划线开头 (_)开头.标识符和关键字里随后的字符可以是 字母,数字(0-9), 或者下划线,但 SQL 标准不会定义包含数字或者以下划线开头或结尾的 关键字.

PostgreSQL 系统使用不超过 NAMEDATALEN-1 个字符作为标识符; 你可以在命令中写更长的名字,但它们会被截断.缺省时, NAMEDATALEN 是 32,因此标识符最大长度是31 (不过在制作系统的时候,你可以在 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;

引起标识符可以包含除引号本身以外的任何其它字符. 这样我们就可以构造那些原本是不允许的表或者字段名字, 比如那些包含空白或与号的名字.但长度限制依旧.

把一个标识符引起来同时也令它大小写相关,而没有引起来的名字 总是转成小写.比如,我们认为标识符 FOOfoo"foo" 是一样的 Postgres名字, 但 "Foo""FOO" 与上面三个以及它们之间都是不同的. [1]

1.1.2. 常量

Postgres 里有四种 隐含类型的常量: 字符串,位串,整数,和浮点数. 常量也可以声明为明确的类型,这样就可以使用更准确的表现形式 以及可以通过系统更有效地处理.隐含常量在下面描述; 明确常量稍后描述.

1.1.2.1. 字符串常量

SQL 里的字符串常量是一个由单引号(“'”) 圈定范围的任意字符的序列,比如, 'This is a string'. SQL 允许你在字符串里嵌入单引号,方法是敲入两个连续的单引号 (比如,'Dianne''s horse'). 在 Postgres 里,单引号还可以用 一个反斜扛(“\”)来逃逸,比如, 'Dianne\'s horse'

还可以使用 C-风格的反斜扛逃逸: \b 是一个退格,\f 是一个进纸,\n 是一个换行符, \r 是一个回车,\t 是一个水平制表符,而\xxx, 这里 xxx 是一个八进制数,是对应 ASCII 码的字符.任何其它跟在反斜扛后面的字符都当做文本看待. 因此,要在字符串常量里包含反斜扛,你可以敲两个反斜扛.

编码为零的字符不能出现在字符串常量中.

两个只是通过至少有一个换行符的空白 分隔的字符串常量会被连接在一起,并当做它们是写成一个常量处理. 比如:

SELECT 'foo'
'bar';
等效于
SELECT 'foobar';
SELECT 'foo'      'bar';
是非法的语法.

1.1.2.2. 位串常量

位串常量看起来很象在开引号前面有一个 B (大写或小写)的字符串(它们之间没有空白), 比如 B'1001'.位串常量里可以用的字符只有 01.位串常量可以象普通字符串常量那样跨多个行.

1.1.2.3. 整数常量

SQL 里的整数常量是用不带小数点的十进制数据位(0 到 9) 的序列表示的.合法值的范围取决于所用的整数数据类型, 简单的 integer 类型接受的范围是 从 -2147483648 到 +2147483647.(可选的正号或负号实际上 是一个独立的单目操作符,而不是整数常量的一部分.)

1.1.2.4. 浮点数常量

浮点数常量接受下列通用的形式:

digits.[digits][e[+-]digits]
[digits].digits[e[+-]digits]
digitse[+-]digits
这里的 digits 是一个或多个十进制位. 至少有一位在小数点后面或者如果你使用了 e 选项的话在它后面.因此,浮点数常量与整数常量的区别是以是否 存在小数点或者指数子句(或者两者都有)为准的.在常量里不能 有空格或者其他字符嵌入在内.

这里是一些合法的浮点数常量的例子:

3.5
4.
.001
5e2
1.925e-3

浮点数常量的类型是 DOUBLE PRECISION. 你可以用 SQL 字符串表示法或者 Postgres 类型表示法明确声明 REAL

REAL '1.23'  -- string style
'1.23'::REAL -- Postgres (historical) style
     

1.1.2.5. 其它类型的常量

任意类似的常量可以用下列表示法中的 任何一种来输入:

type 'string'
'string'::type
CAST ( 'string' AS type )
在字符串里的数值传递给那种叫type 的类型的输入转换过程.结果是这种类型的一个常量. 如果不存在该常量所属类型的歧义,那么明确的类型映射可以省略 (比如,当你把它当做一个没有重载的函数的参数传递时), 这种情况下它会自动转换.

我们还可以用函数样的语法来声明类型转换:

typename ( value )
不过这种方法只能用于那些名字同时也是函数名字的类型. (比如,double precision 就不能这么用 -- 但等效的 float8 却可以.)

::CAST(),和 函数调用语法也可以用于声明任意表达式的类型,但是 type 'string' 的形式只能用于声明一个文本常量的类型.

1.1.2.6. 数组常量

数组常量的通用格式如下:

'{ val1 delim val2 delim ... }'
这里 delim 是该类型的分隔符字符, 和在它的 pg_type 记录里记录的一样. (对于所有内建类型,它是逗号字符 ",".) 每个 val 要么是该数组元素类型的常量, 要么是一个子数组.下面是一个数组常量的例子
'{{1,2,3},{4,5,6},{7,8,9}}'
这个常量是两维,3 乘 3数组,组成三个整数的子数组.

独立的数组元素可以放在双引号(" ) 中间以避免 因空白带来的歧义. 如果没有引号,那么数组值分析器就会忽略开头的空白.

(数组常量实际上只是我们前面讨论的通用类型常量的一种特例. 该常量开始是当做字符串对待的然后传递给数组输入转换过程. 可能需要明确地类型声明.)

1.1.3. 操作符

一个操作符是最多 NAMEDATALEN-1 (缺省 31 个字符)个下列字符的序列:

+ - * / < > = ~ ! @ # % ^ & | ` ? $

不过,对操作符名字有几个限制:

  • "$" (美元)不能是单字符操作符,但它可以是一个多字符操作符 名字的一部分.

  • --/* 不能出现在操作符 名字中的任何地方,因为它们会被当做注释开始对待.

  • 多字符操作符不能以 "+" 或 "-" 结束, 除非其名字至少还包含下列操作符之一:

    ~ ! @ # % ^ & | ` ? $

    比如,@- 是允许的操作符名字, 但 *- 不是.这个限制允许 Postgres 在不要求记号之间有 空白的情况下分析 SQL 兼容的查询.

当你使用非 SQL 标准的操作符名字的时候,你通常需要用 空白分隔相邻的操作符以避免歧义.比如,如果你定义了一个 叫 "@" 的左单目操作符,那么你就不能写 X*@Y;而是要写成 X* @Y 以确保 Postgres 把它读成两个操作符,而不是一个.

1.1.4. 特殊字符

有些非字母数字字符有一些特殊含义,因此不能用做操作符. 它们的用法的细节可以在相应的描述语法元素的地方找到. 本节只是描述它们的存在和概括一下这些字符的目的.

  • 美元符号($)后面跟着数字用于在一个函数体中 表示参数的位置.在其他环境里美元符号可能是一个操作符名字的一部分.

  • 圆括弧(())用于分组和强制优先级的时候含义 与平常一样.有些场合里圆括弧是作为一个特定 SQL 命令的固定 语法的一部分要求的.

  • 方括弧([])用于选取数组元素. 参阅 Chapter 6 获取更多信息.

  • 逗号(,在一些语法构造里用于分隔 一个列表的元素.

  • 分号(;)结束一条 SQL 命令. 它不能出现在一条命令里的任何地方,除非引起来当做字符串常量 或者标识符用.

  • 冒号 (:)用于从数组中选取 “片段”.(参阅 Chapter 6.)在一些 SQL 方言里(比如嵌入 SQL ), 冒号用于前缀变量名.

  • 星号 (*在和 SELECT 命令或 COUNT 聚集函数一起使用时有特殊含义.

  • 句点 (.用在浮点数常量里, 并用于分隔表和字段名字.

1.1.5. 注释

注释是任意以双划线开头并延伸到行尾的任意字符序列,比如:

-- This is a standard SQL92 comment

另外,还可以使用 C-风格的块注释:

/* multi-line comment
 * with nesting: /* nested block comment */
 */
这里注释以 /* 开头并扩展到对应的 */.这些块注释可以嵌套,就象 SQL99 里说的那样, 但和 C 不一样,因此我们可以注释掉一大块已经包含块注释的代码.

注释在进一步的语法分析之前被从输入流删除并有效地用空白代替.

Notes

[1]

Postgres 对未加引号的名子总是 转换成小写, 这和 SQL 是不兼容的,SQL 里未引起来的名字总是转成大写. 因此 foo 等于 "FOO". 如果你想写可移植的程序,那么我们建议你要么就总是引起某个 名字,要么就坚决不引.