☰
MySQL 8.0 与 PostgreSQL 15 对比:5 类复杂 SQL 查询的语法差异与性能实测
2026/10/25 22:14:25 网站建设 项目流程

MySQL 8.0 与 PostgreSQL 15 深度对比:5类典型复杂查询的语法差异与性能优化实践

在当今数据驱动的时代,关系型数据库仍然是企业核心系统的基石。MySQL和PostgreSQL作为两大开源关系型数据库的代表,在语法特性、执行计划和性能表现上各有千秋。本文将聚焦5类典型复杂查询场景,通过实测对比揭示两种数据库在高级查询功能上的差异,为技术选型提供实操性参考。

1. 环境准备与测试方法论

在开始对比之前,我们需要建立统一的测试环境以确保结果的可比性。本次测试使用以下配置:

  • 硬件环境:

    • CPU: Intel i7-12700K (12核20线程)
    • 内存: 32GB DDR4 3200MHz
    • 存储: 1TB NVMe SSD
  • 软件版本:

    • MySQL 8.0.32 (InnoDB引擎,默认配置)
    • PostgreSQL 15.2 (默认配置)
  • 测试数据集: 我们使用经典的"学生-课程-选课"关系模型,包含三个表:

    -- 学生表 CREATE TABLE students ( sid INT PRIMARY KEY, sname VARCHAR(50), age INT, gender CHAR(1) ); -- 课程表 CREATE TABLE courses ( cid INT PRIMARY KEY, cname VARCHAR(50), credit INT ); -- 选课表 CREATE TABLE enrollments ( sid INT, cid INT, grade INT, PRIMARY KEY (sid, cid), FOREIGN KEY (sid) REFERENCES students(sid), FOREIGN KEY (cid) REFERENCES courses(cid) );

测试方法采用相同数据集(10万学生记录,1000课程记录,100万选课记录)分别在两种数据库中执行,每种查询执行5次取平均耗时。

2. 全称量词查询:查找选修全部课程的学生

这类查询在SQL中需要转换为"不存在任何一门课程该学生没有选修"的逻辑。这是关系代数除法运算的典型应用场景。

MySQL实现方案:

SELECT s.sid, s.sname FROM students s WHERE NOT EXISTS ( SELECT 1 FROM courses c WHERE NOT EXISTS ( SELECT 1 FROM enrollments e WHERE e.sid = s.sid AND e.cid = c.cid ) );

PostgreSQL实现方案: PostgreSQL除了支持上述标准SQL写法外,还提供了更直观的EXCEPT语法:

SELECT s.sid, s.sname FROM students s WHERE NOT EXISTS ( (SELECT cid FROM courses) EXCEPT (SELECT cid FROM enrollments WHERE sid = s.sid) );

性能对比:

数据库执行时间(ms)执行计划特点
MySQL1250依赖嵌套循环反连接
PostgreSQL980能更好利用哈希反连接

PostgreSQL的查询优化器在处理这类复杂嵌套查询时表现更优,特别是在大数据量场景下。MySQL 8.0虽然改进了优化器,但对于多层嵌套的NOT EXISTS仍存在优化空间。

3. 分组聚合与窗口函数:计算学生成绩排名

窗口函数是现代SQL的重要特性,让我们看看两种数据库的实现差异。

MySQL实现方案:

SELECT sid, cid, grade, RANK() OVER (PARTITION BY cid ORDER BY grade DESC) AS rank_in_course, PERCENT_RANK() OVER (PARTITION BY cid ORDER BY grade DESC) AS percent_rank FROM enrollments WHERE cid = 101;

PostgreSQL实现方案: PostgreSQL支持更丰富的窗口函数选项:

SELECT sid, cid, grade, RANK() OVER w AS rank_in_course, PERCENT_RANK() OVER w AS percent_rank, FIRST_VALUE(grade) OVER w AS top_grade, LAST_VALUE(grade) OVER w AS bottom_grade FROM enrollments WHERE cid = 101 WINDOW w AS (PARTITION BY cid ORDER BY grade DESC ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING);

功能对比:

特性MySQL 8.0PostgreSQL 15
基本窗口函数支持支持
命名窗口不支持支持
窗口帧定制有限支持完全支持
统计类窗口函数基础支持全面支持

性能提示:

  • 两种数据库在简单窗口函数上性能相当
  • 对于复杂窗口场景,PostgreSQL优化更充分
  • MySQL在处理大型窗口时内存使用更保守

4. 递归查询:查找课程先修关系

递归CTE是处理层次结构数据的利器,如课程先修关系、组织结构等。

测试数据准备:

ALTER TABLE courses ADD COLUMN prerequisite INT REFERENCES courses(cid); -- 添加示例数据 UPDATE courses SET prerequisite = NULL WHERE cid = 101; UPDATE courses SET prerequisite = 101 WHERE cid = 102; UPDATE courses SET prerequisite = 102 WHERE cid = 103;

MySQL实现方案:

WITH RECURSIVE course_path AS ( SELECT cid, cname, prerequisite, 1 AS depth FROM courses WHERE cid = 103 UNION ALL SELECT c.cid, c.cname, c.prerequisite, cp.depth + 1 FROM course_path cp JOIN courses c ON cp.prerequisite = c.cid ) SELECT * FROM course_path;

PostgreSQL实现方案: PostgreSQL支持更丰富的递归查询特性:

WITH RECURSIVE course_path AS ( SELECT cid, cname, prerequisite, 1 AS depth, ARRAY[cid] AS path_array FROM courses WHERE cid = 103 UNION ALL SELECT c.cid, c.cname, c.prerequisite, cp.depth + 1, c.cid || cp.path_array FROM course_path cp JOIN courses c ON cp.prerequisite = c.cid WHERE NOT c.cid = ANY(cp.path_array) -- 防止循环引用 ) SELECT cid, cname, depth, path_array FROM course_path;

高级特性对比:

特性MySQL 8.0PostgreSQL 15
基本递归CTE支持支持
循环检测无支持
递归路径追踪需手动实现内置数组支持
递归深度限制1000(默认)可配置

5. JSON数据处理:学生信息扩展

现代应用常需要处理半结构化数据,JSON支持成为数据库的重要能力。

测试数据准备:

-- MySQL/PostgreSQL通用 ALTER TABLE students ADD COLUMN profile JSON; -- 更新示例数据 UPDATE students SET profile = JSON_OBJECT( 'hobbies', JSON_ARRAY('reading', 'swimming'), 'contact', JSON_OBJECT('email', 'test@example.com', 'phone', '123456789') ) WHERE sid = 1001;

MySQL JSON查询:

SELECT sid, sname, JSON_EXTRACT(profile, '$.contact.email') AS email, JSON_CONTAINS(profile->'$.hobbies', '"swimming"') AS has_swimming_hobby FROM students WHERE JSON_EXTRACT(profile, '$.contact.phone') IS NOT NULL;

PostgreSQL JSON查询: PostgreSQL提供了更丰富的操作符和索引支持:

SELECT sid, sname, profile->'contact'->>'email' AS email, profile->'hobbies' @> '"swimming"'::jsonb AS has_swimming_hobby FROM students WHERE profile->'contact' ? 'phone'; -- 创建GIN索引加速查询 CREATE INDEX idx_students_profile ON students USING GIN (profile jsonb_path_ops);

JSON支持对比:

特性MySQL 8.0PostgreSQL 15
JSON数据类型支持支持
JSON路径表达式支持支持
JSON索引有限支持全面支持
JSON聚合函数基础支持全面支持
JSON性能中等优秀

6. 多表连接与查询优化

复杂业务场景常涉及多表连接,两种数据库的优化策略有所不同。

测试查询:查找平均分高于90且选修超过5门课程的学生

-- MySQL优化方案 SELECT s.sid, s.sname, AVG(e.grade) AS avg_grade FROM students s JOIN enrollments e ON s.sid = e.sid GROUP BY s.sid, s.sname HAVING AVG(e.grade) > 90 AND COUNT(e.cid) > 5 ORDER BY avg_grade DESC; -- PostgreSQL优化方案(使用CTE提高可读性) WITH student_stats AS ( SELECT sid, AVG(grade) AS avg_grade, COUNT(cid) AS course_count FROM enrollments GROUP BY sid HAVING AVG(grade) > 90 AND COUNT(cid) > 5 ) SELECT s.sid, s.sname, ss.avg_grade FROM students s JOIN student_stats ss ON s.sid = ss.sid ORDER BY ss.avg_grade DESC;

优化建议对比:

优化技巧MySQL建议PostgreSQL建议
连接顺序重要优化器自动处理
临时表谨慎使用CTE是优化利器
聚合下推手动优化自动优化
并行查询有限支持全面支持

实测发现,对于这个查询:

  • MySQL执行时间:320ms
  • PostgreSQL执行时间:210ms

PostgreSQL的CTE优化和并行查询能力在此场景表现更优。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询