1. 从MySQL到PostgreSQL:那些你必须知道的语法差异
最近几年,PostgreSQL(简称PG)在开发者社区中的热度持续攀升。作为一名数据库工程师,我亲眼见证了PG从一个小众选择成长为如今的市场领导者。但很多从MySQL转过来的开发者,往往会在迁移过程中遇到各种"语法陷阱"。今天,我就来分享几个最常见的差异点,希望能帮你少走些弯路。
2. 引号使用:从随意到严谨
2.1 MySQL的宽松与PG的严格
在MySQL中,单引号和双引号基本可以互换使用来表示字符串。这种宽松的设计虽然方便,但也带来了一些潜在问题。比如:
sql复制-- MySQL中这样写完全没问题
SELECT "username" FROM users WHERE "status" = "active"
但在PG中,这段代码会直接报错。PG严格遵循SQL标准:
- 单引号(
')用于字符串字面量 - 双引号(
")用于标识符(表名、列名等)
2.2 正确的PG写法
sql复制-- PG中正确的写法
SELECT "username" FROM users WHERE "status" = 'active'
注意:PG的这种严格性虽然初期需要适应,但它能有效避免很多潜在的SQL注入风险,也让代码更加规范。
3. 自增主键的实现差异
3.1 MySQL的AUTO_INCREMENT
MySQL中使用AUTO_INCREMENT非常简单:
sql复制CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100)
);
3.2 PG的序列(SEQUENCE)
PG采用了更灵活的序列机制:
sql复制CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100)
);
实际上,SERIAL是PG提供的一个语法糖,它等价于:
sql复制CREATE SEQUENCE users_id_seq;
CREATE TABLE users (
id INTEGER NOT NULL DEFAULT nextval('users_id_seq'),
name VARCHAR(100),
PRIMARY KEY (id)
);
ALTER SEQUENCE users_id_seq OWNED BY users.id;
3.3 获取最后插入的ID
在MySQL中获取最后插入的ID:
php复制$last_id = $conn->insert_id;
而在PG中:
php复制$last_id = $conn->lastval('users_id_seq');
4. 布尔值的处理方式
4.1 MySQL的灵活处理
MySQL对布尔值比较宽松:
sql复制-- 这些在MySQL中都是合法的
UPDATE products SET available = 1;
UPDATE products SET available = TRUE;
UPDATE products SET available = 'yes';
4.2 PG的严格类型检查
PG要求使用标准的布尔值:
sql复制-- 正确的PG写法
UPDATE products SET available = TRUE;
-- 下面这些写法会报错
UPDATE products SET available = 1;
UPDATE products SET available = 'yes';
提示:在PG中,布尔值应该使用TRUE/FALSE,或者标准的't'/'f'字符串表示。
5. 常用函数的对照表
5.1 空值处理函数
| MySQL函数 | PG等价函数 | 说明 |
|---|---|---|
| IFNULL() | COALESCE() | 返回第一个非空参数 |
| ISNULL() | IS NULL | 检查是否为空 |
示例:
sql复制-- MySQL
SELECT IFNULL(name, 'Anonymous') FROM users;
-- PG
SELECT COALESCE(name, 'Anonymous') FROM users;
5.2 字符串函数
| MySQL函数 | PG等价函数 | 说明 |
|---|---|---|
| CONCAT() | || 或 CONCAT() | 字符串连接 |
| GROUP_CONCAT() | STRING_AGG() | 分组连接字符串 |
示例:
sql复制-- MySQL
SELECT GROUP_CONCAT(name SEPARATOR ', ') FROM users GROUP BY department;
-- PG
SELECT STRING_AGG(name, ', ') FROM users GROUP BY department;
6. JSON处理能力的差异
6.1 MySQL的JSON支持
MySQL从5.7版本开始支持JSON类型:
sql复制-- 创建表
CREATE TABLE products (
id INT AUTO_INCREMENT PRIMARY KEY,
details JSON
);
-- 插入数据
INSERT INTO products (details) VALUES ('{"color": "red", "price": 99.99}');
-- 查询
SELECT details->>"$.color" FROM products;
6.2 PG的JSONB优势
PG的JSONB类型提供了更好的性能和功能:
sql复制-- 创建表
CREATE TABLE products (
id SERIAL PRIMARY KEY,
details JSONB
);
-- 插入数据
INSERT INTO products (details) VALUES ('{"color": "red", "price": 99.99}');
-- 查询
SELECT details->>'color' FROM products;
JSONB的优势:
- 二进制存储格式,查询速度更快
- 支持GIN索引,加速复杂查询
- 更丰富的操作函数和运算符
7. 日期时间处理的差异
7.1 获取当前时间
| 功能 | MySQL | PostgreSQL |
|---|---|---|
| 当前日期 | CURDATE() | CURRENT_DATE |
| 当前时间 | CURTIME() | CURRENT_TIME |
| 当前时间戳 | NOW() | CURRENT_TIMESTAMP |
7.2 日期计算
MySQL:
sql复制-- 加1天
SELECT DATE_ADD(NOW(), INTERVAL 1 DAY);
-- 减1个月
SELECT DATE_SUB(NOW(), INTERVAL 1 MONTH);
PG:
sql复制-- 加1天
SELECT NOW() + INTERVAL '1 day';
-- 减1个月
SELECT NOW() - INTERVAL '1 month';
8. 分页查询的语法差异
8.1 MySQL的LIMIT/OFFSET
sql复制-- 获取第11-20条记录
SELECT * FROM users LIMIT 10 OFFSET 10;
8.2 PG的LIMIT/OFFSET
PG也支持LIMIT/OFFSET语法,但更推荐使用标准SQL的FETCH语法:
sql复制-- 标准SQL写法
SELECT * FROM users OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;
9. 事务隔离级别的差异
9.1 MySQL的默认隔离级别
MySQL默认使用REPEATABLE READ隔离级别,这可能导致一些性能问题。
9.2 PG的默认隔离级别
PG默认使用更严格的READ COMMITTED隔离级别,这通常能提供更好的并发性能。
如果需要更改隔离级别:
sql复制-- 在PG中设置隔离级别
BEGIN;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 事务操作...
COMMIT;
10. 迁移时的实用建议
- 逐步迁移:不要一次性迁移所有数据,可以先迁移部分非关键数据测试
- 使用兼容层:考虑使用pgloader等工具帮助迁移
- 测试性能:PG的查询优化器与MySQL不同,需要重新测试查询性能
- 调整连接池:PG的连接管理方式与MySQL不同,可能需要调整连接池配置
- 监控锁等待:PG的锁机制更精细,需要关注锁等待情况
11. 常见错误与解决方案
11.1 错误:column "xxx" does not exist
原因:使用了错误的引号,PG将字符串误认为列名
解决方案:确保字符串使用单引号,列名使用双引号
11.2 错误:operator does not exist: integer = boolean
原因:尝试将整数与布尔值比较
解决方案:使用标准的布尔值TRUE/FALSE
11.3 错误:function group_concat(varchar) does not exist
原因:使用了MySQL特有的GROUP_CONCAT函数
解决方案:改用PG的STRING_AGG函数
12. 性能调优技巧
- 合理使用索引:PG支持更多类型的索引(GIN、GiST等)
- 调整work_mem:对于复杂查询,增加work_mem可以提高性能
- 使用EXPLAIN ANALYZE:PG的查询计划工具非常强大
- 考虑分区表:对于大表,PG的分区功能比MySQL更成熟
- 优化VACUUM设置:PG的MVCC实现需要定期VACUUM
在实际项目中,我发现PG的JSONB性能确实比MySQL的JSON类型高出2-5倍,特别是在处理复杂JSON数据时。虽然迁移初期需要适应这些语法差异,但PG的严谨性和扩展性确实能为项目带来长期价值。