为什么同样写着 students,加一对单引号就成了文本,而漏掉一个分号,按回车后又迟迟没有结果?SQL 中的词和符号各有用途:关键字规定语句结构,表名、列名指出对象,常量直接表示一个值;客户端还要根据引号和语句结束符判断输入是否完整。把这些区别看清楚,就能判断一段内容是在引用名称、提供数据,还在等着SQl输入。

一条 SQL 中,哪些是关键字、标识符和常量

面对一段 SQL,可以先把“语言规定的词”“对象的名字”和“直接写出的值”分开看。假设当前数据库已有 students 表,每行记录一个学生,其中 student_name 列保存姓名。看一下下面这句SQL:

SELECT student_name, 18 FROM students;

SELECT 和 FROM 是 SQL 关键字,语言已经规定了它们的含义和位置;student_name 和 students 分别是列名和表名。这样的对象名称叫标识符,作用是指出要引用哪个对象。18 直接写出了整数值,叫数值常量。“常量”的意思就是:值已经写在语句里,不需要把这几个字符当作对象名称去查找。逗号分隔这里的两个字段,末尾分号结束语句。

这三类内容可以按用途区分:

Mermaid 图 1

关键字来自SQL语言,标识符对应已经命名的对象,常量提供一个具体值。因此,不能只看一个词是不是英文来判断它的用途,还要看它在语句中的位置和所用的引号。比如下面的这句SQL

SELECT 18, '小林', 'students';

18 是数值常量,通常不加引号;'小林' 和 'students' 都是字符串常量,也就是文本值。最后一项虽然拼成 students,但单引号已经表明这里提供的是这段文本,MySQL 不会把它当成要读取的表名。若只是把数字写成 '18',语句中的这一项也成为字符串常量。

那标识符也要加引号吗?例如表 students 可以直接写,不需要加引号;在 MySQL 中,给标识符加引号时使用反引号,例如代码中的 students。名称包含特殊字符或与保留字重名时,需要加反引号。保留字是一类不能随意用的关键字。学生表中的date可以写成这样:

SELECT `student_name`, `date` FROM `students`;

反引号中的 students 仍是表名,单引号中的 'students' 则是文本。反引号和单引号不能互换。SQL 中的这些符号要使用英文半角字符;中文输入法的弯引号不能替代代码里的单引号。

关键字大小写又怎样处理?SELECT、select、SeLeCt 的关键字含义相同,FROM 也一样。常把关键字写成大写,是为了让结构更容易辨认;把 SELECT 改成 select,并不会改变查询含义。但表名的大小写行为与操作系统、文件系统和服务器设置有关。已有表叫 students,就沿用 students,不凭关键字的规则擅自改成 Students 或 STUDENTS。

按了回车还没执行:先看分号和换行提示

关键字和标识符写对了,客户端还需要知道一条语句在哪里结束。在 mysql 命令行客户端中,按回车只是把当前输入行送入输入缓冲;默认情况下,分号才是这里使用的语句结束符。换行可以让 SQL 更好读,但不能代替分号。可以把它想成在窗口填写多行单据:按回车只相当于另起一行,还不等于把单据交出去;分号才相当于把单据交给窗口的结束动作。下面是输入过程示意,左边的 mysql> 和 -> 是客户端显示的提示符,不需要自己输入:

mysql> SELECT 18
    -> ;

第一行没有分号,客户端把 SELECT 18 留在当前输入缓冲中,也就是暂存尚未提交的语句,然后用 -> 等待下一行。第二行补上分号,才会把完整语句交给服务器执行。如果已经决定放弃这段普通输入,可以输入反斜杠加小写 c:

mysql> SELECT 18
    -> \c
mysql>

\c 清除当前尚未执行的输入,提示符恢复为 mysql>,可以重新写语句。它是 mysql 客户端命令,不是发给服务器执行的 SQL,也不需要追加分号。

如果漏的不是分号,而是引号,提示符会更具体。输入 SELECT '小林 后按回车,客户端会显示 '>,表示单引号字符串尚未结束。此时继续敲一个分号,分号也会落在字符串里,不能结束语句;直接敲 \c,同样可能被当成字符串的一部分。放弃这段输入时,先补单引号,再取消:

mysql> SELECT '小林
    '> '\c
mysql>

第二行实际输入的是单引号紧接 \c。单引号先结束字符串,随后客户端才能把 \c 识别为取消命令。

提示符反映的是客户端正在等待什么;根据提示符采取动作,通常比反复按回车更有效:

Mermaid 图 2

常见提示还包括:双引号后跟 >,表示等待双引号闭合;反引号后跟 >,表示等待名称的反引号闭合;/*> 表示块注释尚未结束。看到 '> 就先处理单引号,看到反引号等待输入就先补反引号,不能只补分号。-> 表示语句尚未补完,漏分号是常见原因,但也要看前面究竟输入了什么。

注释在哪里结束,代码就从哪里继续

给 SQL 加上解释,可以使用普通注释。注释中的文字不作为 SQL 操作执行,但注释的结束位置决定了后面哪些内容仍会被当作代码。可以把注释想成在页面上做标记:行注释像从某个词起到本行末尾画删除线,块注释像用一对书挡夹住多行文字;标记结束的位置之后,后面的内容继续按 SQL 解析。MySQL 支持三种常见写法:

-- 数值与中文文本
SELECT 18, '小林';

SELECT 'students'; # 返回的文本内容

/*
保留姓名列,便于辨认标识符
*/
SELECT `student_name` FROM `students`;

第一种从 -- 开始,一直到这一行结束。在 MySQL 中,第二个横线后必须接至少一个空格或控制字符,例如空格或制表符;写注释时用“-- 加一个空格”最直观。不要写成 --说明,再期待它按行注释处理。第二种从 # 开始,也到行尾结束,这是 MySQL 支持的行注释形式。

第三种用 / 开始、用 / 结束,中间可以跨行。上面块注释中的中文说明和换行都在注释范围内,直到 */ 出现,之后的 SELECT 才继续作为代码。与行注释相比,块注释的边界由结束符决定,换行不会自动结束它。两种边界可以这样对照:

Mermaid 图 3

分号如果写在行注释范围内,也只是注释内容。例如下面故意把分号放错了位置:

SELECT 18 -- 这是注释 ;

这一行里的分号已经被注释包含,不能结束 SELECT 18。在 mysql 中换行后仍会等待补完语句;应把结束语句的分号放在注释前,写成 SELECT 18; -- 这是注释。判断时先找到注释从哪里开始、到哪里结束,再看分号是否在代码范围内。

普通块注释也不要嵌套:不要在一对 / 和 / 中再放另一对,并指望它们按内外层次配对。需要多行说明,就用一对起止符把整段包起来,确认已经写出 /,再继续写 SQL。若 mysql 显示 />,先补上 */ 结束注释;想放弃当前尚未执行的语句,再使用 \c。这样看到的注释边界、语句边界和客户端提示,才能相互对应。