PostgreSQL SQL 语法基础

环境装好后,我们来系统过一遍 SQL 的基础语法。这一章不涉及高级查询,只讲清"SQL 语句长什么样"——子句顺序、执行顺序、注释、标识符规则。读完你就能看懂任何 SQL 代码的框架。

1. SQL 语句的基本结构

SQL 是"声明式"语言——你描述"想要什么",数据库决定"怎么算"。一条 SELECT 查询由若干子句按固定顺序组成,看这个最经典的模板:

-- SQL 语句以分号结尾,一条 SELECT 通常由这些子句按顺序组成:
SELECT   name, age, COUNT(*) AS cnt   -- 1. 选哪些列/算什么
FROM     users                         -- 2. 从哪张表
WHERE    age >= 18                     -- 3. 行级过滤
GROUP BY name, age                     -- 4. 分组
HAVING   COUNT(*) > 1                  -- 5. 组级过滤
ORDER BY cnt DESC, name ASC            -- 6. 排序
LIMIT    10 OFFSET 0;                  -- 7. 分页

-- 单行注释用双横线,-- 后到行尾都是注释
/* 多行注释用斜杠星号包裹,
   可以跨多行,但不能嵌套 */

/* 块注释也常用于临时禁用一大段 SQL:
SELECT * FROM orders;
*/

每个子句的职责要分清:

SELECT 外其他都可选。最简单的查询就一句 SELECT 1;,连 FROM 都不需要。

2. 执行顺序(关键!)

这是初学者最容易踩的坑:SQL 的书写顺序和执行顺序不一样。理解执行顺序能解释 80% 的"为什么这条 SQL 报错"。

-- 重要!SQL 关键词的"书写顺序"和"执行顺序"不一样
-- 书写顺序:SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY → LIMIT
-- 执行顺序:FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT

-- 因此可以推出几条规则:
-- 1. WHERE 里不能用 SELECT 中起的别名(因为 SELECT 还没执行)
SELECT age * 2 AS double_age FROM users
WHERE age > 18;          -- ✅ 正确
-- WHERE double_age > 36;   ❌ 错误!别名在 WHERE 里看不到

-- 2. HAVING 可以用聚合函数(WHERE 时还没分组,看不到聚合结果)
SELECT user_id, COUNT(*) AS cnt
FROM orders
GROUP BY user_id
HAVING COUNT(*) > 5;     -- ✅ 用聚合函数过滤

-- 3. ORDER BY / LIMIT 在最后,所以能用 SELECT 里的别名
SELECT age * 2 AS double_age FROM users
ORDER BY double_age DESC;  -- ✅ ORDER BY 可以用别名

把执行顺序记牢:FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT。从此你写复杂查询就不会再问"为什么别名在 WHERE 里不能用"。

3. 关键字与标识符

SQL 大小写规则有点反直觉,务必记住下面这几条:

-- 1. 关键字不区分大小写:select / SELECT / Select 完全等价
select * from users;       -- 完全可以,但社区约定关键字大写
SELECT * FROM users;       -- 推荐写法,易读

-- 2. 标识符(表名/列名)默认不区分大小写,但 Postgres 会折叠成小写
CREATE TABLE MyTable (id INT);     -- 实际表名变成 mytable
SELECT * FROM MyTable;             -- ✅ 能查到
SELECT * FROM mytable;             -- ✅ 也能查到

-- 3. 如果想保留大小写或用关键字做列名,用双引号包起来
CREATE TABLE "MyTable" ("select" INT, "order" TEXT);
SELECT "select", "order" FROM "MyTable";
-- 但日常强烈不推荐这么干,自找麻烦

-- 4. 字符串字面量用单引号(不是双引号!)
SELECT 'hello';            -- ✅ 字符串
SELECT "hello";            -- ❌ Postgres 会把 hello 当列名,报错

-- 5. 数字字面量直接写
SELECT 42, 3.14, 1.5e-10;

-- 6. 美元符号引用的字符串(避免转义引号)
SELECT $$It's a 'test'$$;          -- 等价于 'It''s a ''test'''
SELECT $tag$包含'单引号'$tag$;      -- 自定义标签,适合嵌套

最常踩的两个坑:

4. 注释

SQL 注释和很多语言相似:

多行注释常用来临时禁用一段 SQL 调试。注意 -- 必须在行尾或独立行,写在字符串字面量里就不算注释了。

5. 字符串字面量

SQL 字符串的标准写法是单引号包裹。如果字符串内部包含单引号,有两种写法:

6. Postgres 特色方言

除标准 SQL 外,Postgres 有一些独特的语法糖,实战中极其常用:

-- Postgres 有一些"特色"方言,在标准 SQL 上做了扩展:

-- 1. ILIKE:不区分大小写的 LIKE
SELECT * FROM users WHERE name ILIKE 'john%';  -- 匹配 John / JOHN / john

-- 2. ~ 正则匹配(区分大小写),~* 不区分,!~ 不匹配
SELECT * FROM users WHERE name ~ '^A[a-z]+$';

-- 3. :: 类型转换语法(CAST 的简写)
SELECT '2026-08-05'::DATE;
SELECT 42::TEXT;
-- 等价于 CAST('2026-08-05' AS DATE)

-- 4. RETURNING:INSERT/UPDATE/DELETE 后返回修改的行
INSERT INTO users (name) VALUES ('小明') RETURNING id, name;
UPDATE users SET age = 20 WHERE id = 1 RETURNING *;
DELETE FROM users WHERE id = 1 RETURNING name;

-- 5. DISTINCT ON:每组只留第一条(类似窗口函数的简化版)
SELECT DISTINCT ON (user_id) user_id, created_at, total
FROM orders
ORDER BY user_id, created_at DESC;
-- 取每个用户的最新一条订单

-- 6. EXTRACT / date_part 取日期部分
SELECT EXTRACT(YEAR FROM created_at) AS y FROM orders;
SELECT date_part('month', now()) AS m;

其中 RETURNINGDISTINCT ON 是 Postgres 区别于 MySQL 的两大杀器,后面 DML 和高级查询章节会再展开。

小结

这一章你了解了 SQL 语句的整体骨架、执行顺序、注释规则、标识符约定。下一篇我们看 PostgreSQL 的数据类型——这是定义任何表之前必须搞清楚的事。

← 上一篇 PostgreSQL 安装与 psql

下一篇 PostgreSQL 数据类型

✈️💬