🙏 我们需要您的帮助!SQLtest.online 没有广告,完全依靠捐款运行。哪怕一点点也很有帮助。 立即捐助 →
SQL 代码已复制到剪贴板

不同数据库中的 SQL 大小写敏感性

第 1.9 课 · 阅读时间:约 9 分钟

SQL 的大小写敏感性分为三个层面:关键字、对象名称和数据。像 SELECT 这样的关键字在任何数据库中都不区分大小写,但 Customer 和 customer 是不是同一张表,取决于数据库、引号,有时甚至取决于操作系统。在本课中,你将了解各主流数据库的规则、带引号名称的经典陷阱、字符串比较如何对待大小写,以及一种能避开所有这些问题的命名风格。

在第 1.8 课中,你看到各数据库在遵循 SQL 标准的同时都有自己的方言。大小写是最明显的差异之一:在 SQL Server 中能运行的查询,在 PostgreSQL 中可能报错 relation "Customer" does not exist;在 Windows 笔记本上写的查询,到了 Linux 服务器上可能无法运行。

在写第一批查询之前了解这些规则,可以省下好几个小时去找一张“明明存在”的表。

SQL 大小写敏感性:关键字在任何地方都不区分大小写,不带引号的名称 Customer 在 PostgreSQL 中变成 customer,在 Oracle 和 Firebird 中变成 CUSTOMER,而带引号的 "Customer" 保留大小写

SQL 中大小写的三个层面

当有人问“SQL 区分大小写吗?”时,可能指的是三件不同的事:

  1. 关键字和函数名:SELECT、select、COUNT、count。
  2. 标识符:数据库、表、列和别名的名称,你在第 1.7 课中已经接触过。
  3. 数据:在 WHERE 条件中,字符串 'abc' 是否等于 'ABC'。

每个层面都有自己的规则,我们逐一来看。


SQL 关键字不区分大小写

在所有数据库中,关键字和内置函数名都可以用任意大小写书写:

sElEcT title
FrOm film
WhErE film_id = 1;

结果:ACADEMY DINOSAUR。MySQL、PostgreSQL、SQL Server、Oracle、Firebird 和 SQLite 都能执行大小写任意混合的查询。

把关键字写成大写只是一种约定:它把语言本身和表名、列名区分开,让长查询更易读。本课程用大写书写关键字、用小写书写名称,大多数风格指南也是这样建议的。


不带引号的标识符:每个数据库的转换方式不同

SQL 标准规定,不带引号的名称会转换为大写,因此 customer、Customer 和 CUSTOMER 是同一个名称。各数据库以不同方式实现这一规则。

PostgreSQL:名称变成小写

PostgreSQL 会把每个不带引号的名称转换为小写,与标准正好相反:

SELECT Model, AIRCRAFT_CODE
FROM Aircrafts
WHERE aircraft_code = '773';

结果:Boeing 777-300。表以 aircrafts 的名称保存,列标题返回为 model 和 aircraft_code。

别名同样会被转换:SELECT model AS TotalModel 返回名为 totalmodel 的列。你输入的大小写在查询解析时就消失了。

Oracle 和 Firebird:名称变成大写

Oracle 和 Firebird 遵循标准,把不带引号的名称转换为大写:

select first_name, Last_Name
from Employee
where emp_no = 2;

Firebird 中的结果:Robert Nelson,列标题为 FIRST_NAME 和 LAST_NAME。在 Oracle 中使用 hr.Employees 也是一样。

这就是为什么各种工具都用大写字母显示 Oracle 和 Firebird 的表名:EMPLOYEES、EMPLOYEE。

SQL Server:由排序规则决定

SQL Server 完全不转换名称。它保留 CREATE TABLE 中使用的大小写,并按照数据库的排序规则 (collation)(比较文本的一组规则)来比较名称。

默认排序规则(例如 SQL_Latin1_General_CP1_CI_AS)不区分大小写 (CI),因此下面这些名称是相同的:

select name, listprice
from PRODUCT
where productid = 680;

结果:HL Road Frame - Black, 58。在 AdventureWorks 中,列标题按你输入的样子返回:name 和 listprice。

使用区分大小写的排序规则 (_CS_) 创建的数据库会把 Product 和 PRODUCT 视为不同的名称。这样的数据库很少见,但确实存在,所以在做任何假设之前,先检查 DATABASEPROPERTYEX(DB_NAME(), 'Collation')。

MySQL 和 MariaDB:表名取决于操作系统

在 MySQL 和 MariaDB 中,数据库是一个文件夹,表是一个文件,所以数据库和表的大小写规则来自文件系统。由 lower_case_table_names 设置控制:

值默认于表名
0Linux按书写保存,比较时区分大小写
1Windows以小写保存,比较时不区分大小写
2macOS按书写保存,比较时不区分大小写

本课程的练习环境运行在 Linux 上,因此表名大小写写错就会报错:

SELECT title
FROM FILM
WHERE film_id = 1;

结果:Table 'sakila.FILM' doesn't exist。改成 FROM film 后查询就能运行。

在 Linux 上,Customer 和 customer 甚至可以是同一个数据库中的两张不同的表。表别名也遵循同样的规则:FROM film F 配合 WHERE f.film_id = 1 会报错 Unknown column 'f.title'。

而 MySQL 和 MariaDB 中的列名在任何系统上都不区分大小写:SELECT TITLE, Film_ID FROM film 可以正常运行。

SQLite 和 DuckDB:始终不区分大小写

SQLite 和 DuckDB 比较名称时不区分大小写,即使带引号也一样,并会记住创建对象时使用的大小写:

CREATE TEMP TABLE "Customer" ("FullName" text);
INSERT INTO "Customer" VALUES ('Orlando Gee');

SELECT fullname
FROM CUSTOMER;

SQLite 和 DuckDB 中的结果:Orlando Gee,列标题为 FullName。无法再创建名为 customer 的第二张表:这个名称已被占用。


带引号的标识符保留大小写

引号会把名称变成定界标识符 (delimited identifier):数据库会完全按照书写的样子使用它,包括大小写、空格和保留字。标准使用双引号;MySQL 使用反引号,SQL Server 还接受方括号:

数据库带引号的名称
PostgreSQL、Oracle、Firebird、SQLite、DuckDB"Customer"
MySQL、MariaDB`Customer`
SQL Server[Customer] 或 "Customer"

在 PostgreSQL、Oracle 和 Firebird 中,带引号的名称区分大小写。这正是最常见的大小写错误的根源。

PostgreSQL 中的 "Customer" 陷阱

许多工具、ORM 和迁移脚本会用带引号的名称创建表。接下来会发生这样的事:

CREATE TEMP TABLE "Customer" ("CustomerID" int, "FullName" text);
INSERT INTO "Customer" VALUES (1, 'Orlando Gee');

SELECT *
FROM Customer;

结果:relation "customer" does not exist。不带引号的 Customer 变成了 customer,而表名是 Customer。

一旦名称是用引号和大写字母创建的,每个查询都必须同样给它加上引号,并保持完全相同的大小写:

SELECT "FullName"
FROM "Customer";

结果:Orlando Gee。不加引号时,SELECT FullName 会报错 column "fullname" does not exist。

PostgreSQL 甚至会给出提示:Perhaps you meant to reference the column "Customer.FullName"。Oracle 和 Firebird 的问题正好相反:hr."employees" 会报错,因为表以 EMPLOYEES 的名称保存。

在 MySQL 中双引号表示字符串

在 MySQL 和 MariaDB 中,双引号根本不表示名称:默认情况下它和单引号一样表示字符串:

SELECT "title"
FROM film
WHERE film_id = 1;

结果:单词 title,而不是电影标题。MySQL 为每一行返回文本常量 'title'。

查询不会报错,所以这个错误很容易被忽略。在 MySQL 中请用反引号书写名称,或者开启 ANSI_QUOTES 模式,让双引号按标准工作。


数据中的大小写:比较字符串

第三个层面是数据本身。'abc' = 'ABC' 是否为真,取决于数据库和列的排序规则:

数据库默认 'abc' = 'ABC'LIKE不区分大小写的搜索
MySQL、MariaDB真不区分大小写默认即是;精确比较用 BINARY 或 _bin 排序规则
SQL Server真(CI 排序规则)不区分大小写默认即是;精确比较用 COLLATE ..._CS_AS
PostgreSQL假区分大小写ILIKE、LOWER()
Oracle假区分大小写UPPER()、LOWER()
Firebird假区分大小写CONTAINING、UPPER()
SQLite假不区分大小写(拉丁字母)COLLATE NOCASE、LOWER()
DuckDB假区分大小写ILIKE、COLLATE NOCASE

同一个条件会得到不同的结果。在 PostgreSQL 的 Bookings 数据库中,没有城市 'moscow':

SELECT COUNT(*)
FROM airports
WHERE city = 'moscow';

结果:0。使用 WHERE lower(city) = 'moscow' 或 WHERE city ILIKE 'moscow',查询能找到莫斯科的 3 个机场。

在 MySQL 中,对 Sakila 执行类似的查询会直接找到演员:

SELECT COUNT(*)
FROM actor
WHERE last_name = 'davis';

结果:3,尽管姓氏是以 DAVIS 保存的。使用 WHERE BINARY last_name = 'davis' 时结果为 0。

所以,从 MySQL 或 SQL Server 迁移到 PostgreSQL 的查询可能会悄无声息地少返回一些行。如果数据的大小写不重要,就在查询中明确说明:用 LOWER(列) 与小写值比较。排序规则的作用远不止大小写:它还决定排序和重音符号的处理,课程后面会有专门的一课。


各数据库的大小写规则

数据库不带引号的名称带引号的名称'abc' = 'ABC'
PostgreSQL小写区分大小写假
Oracle大写区分大小写假
Firebird大写区分大小写假
SQL Server按书写保存,按排序规则比较(通常不区分大小写)与不带引号相同使用 CI 排序规则时为真
MySQL、MariaDB表和数据库:取决于 lower_case_table_names(在 Linux 上区分大小写);列:不区分大小写与不带引号相同真
SQLite不区分大小写不区分大小写假
DuckDB不区分大小写不区分大小写假

在所有这些数据库中,关键字和函数名都不区分大小写。


避免大小写问题的命名规则

只要遵守几条规则,就不必记住整张表:

  • 表名和列名使用小写字母加下划线:customer_order、total_amount。这样的名称在任何数据库中含义都相同,永远不需要引号。
  • 不要创建带大写字母或空格的带引号名称。如果是工具创建的,就在所有地方用完全相同的大小写给它加上引号。
  • 每个名称在所有地方都用同样的大小写书写,即使数据库允许不一致:这样在 Windows 或 SQL Server 中写的查询,在 Linux 和 PostgreSQL 中也能运行。
  • 为了可读性用大写书写关键字;数据库并不在意。
  • 在文本条件中,先决定大小写是否重要,并用 LOWER()、UPPER() 或 ILIKE 明确写出来,而不是依赖默认的排序规则。

更多命名建议汇总在第 10.1 课中。记住这些规则,你就可以进入下一个模块,开始用 SELECT 查询数据了。


要点:

  • SQL 关键字和函数名在任何数据库中都不区分大小写;大写只是一种风格约定。
  • 不带引号的名称在 PostgreSQL 中转换为小写,在 Oracle 和 Firebird 中转换为大写;SQL Server 按数据库的排序规则比较它们。
  • 在 MySQL 和 MariaDB 中,表名在 Linux 上区分大小写(lower_case_table_names = 0),列名则不区分。
  • 带引号的名称保留大小写:在 PostgreSQL 中以 "Customer" 创建的表,必须始终写成 "Customer"。
  • 'abc' = 'ABC' 在 MySQL 和 SQL Server 中默认为真,在 PostgreSQL、Oracle、Firebird、SQLite 和 DuckDB 中为假。
  • 不带引号的小写 snake_case 名称在任何地方都表现一致。

常见问题

SQL 区分大小写吗?

SQL 关键字不区分:SELECT 和 select 在任何地方都一样。表名和列名取决于数据库:PostgreSQL、Oracle 和 Firebird 会把不带引号的名称转换为统一的大小写,SQL Server 使用排序规则,MySQL 的表名取决于操作系统。字符串比较取决于数据的排序规则。

MySQL 中的表名区分大小写吗?

在 Linux 上区分:在默认的 lower_case_table_names = 0 下,FILM 和 film 是不同的表。在 Windows 和 macOS 上它们是同一张表。列名在任何系统上都不区分大小写,所以为了保险,表名请使用小写。

为什么表明明存在,PostgreSQL 却说 relation does not exist?

这张表很可能是用带引号、含大写字母的名称创建的,例如 "Customer"。PostgreSQL 会把你查询中不带引号的 Customer 转换为 customer,这是另一个名称。请用双引号并保持完全相同的大小写书写名称:FROM "Customer"。

如何在 PostgreSQL 中进行不区分大小写的搜索?

用 ILIKE 代替 LIKE(WHERE city ILIKE 'moscow'),或者比较小写后的值:WHERE lower(city) = 'moscow'。对于频繁的搜索,在 lower(city) 上建立索引可以让第二种写法更快。

SQL 关键字应该大写吗?

数据库并不在意,所以这是风格问题。关键字大写、名称小写是最常见的约定,因为这样查询的结构一目了然。重要的是在一个项目中保持一致。