SQL重难点语句

📅 2026/8/19 9:05:54
SQL重难点语句
【达梦数据库】UNION ALL 动态拼接 LIMIT 分页实战员工在职/离职统一查询 前言在实际业务中我们经常遇到这样的需求查当前在职员工基本信息当某个标志位includeResigned 1时还要追加已离职员工数据最终结果统一排序 分页返回这个需求涉及三个核心知识点ADD_MONTHS时间计算、UNION ALL纵向拼接、达梦LIMIT分页。本文以达梦数据库DM8为例从思路到代码一步步拆解。一、需求拆解需求点技术映射近6个月入职的在职员工hire_date ADD_MONTHS(SYSDATE, -6)ANDresign_date IS NULLincludeResigned1 追加离职员工MyBatisif动态控制 UNION ALL 子查询统一排序分页外层包装子查询 ORDER BYLIMIT ... OFFSET ...二、关键概念2.1 UNION ALL 纵向拼加行【UNION ALL 纵向拼加行】 【JOIN 横向拼加列】 表A 表B 表A 表B JOIN结果 ┌─────┐ ┌─────┐ ┌───┬───┐ ┌───┬────┐ ┌───┬───┬────┐ │ a1 │ │ b1 │ │id │nm │ │id │dept│ │id │nm │dept│ │ a2 │ │ b2 │ │ 1 │张三│ │ 1 │研发│ │ 1 │张三│研发│ │ a3 │ │ b3 │ │ 2 │李四│ │ 2 │测试│ │ 2 │李四│测试│ └─────┘ └─────┘ └───┴───┘ └───┴────┘ └───┴───┴────┘ 2列2列4列 ↓ UNION ALL 结果 (列数变多行数不变) ┌─────┐ │ a1 │ ← 来自表A │ a2 │ ← 来自表A │ a3 │ ← 来自表A │ b1 │ ← 来自表B │ b2 │ ← 来自表B │ b3 │ ← 来自表B └─────┘ 3行3行6行 (行数变多列数不变)2.2 为什么用 UNION ALL 而不是 UNION关键字行为性能UNION合并后去重隐式排序慢UNION ALL合并后保留所有行快两张表数据本身不重叠时永远优先用UNION ALL。2.3 达梦分页语法-- 推荐简洁直观LIMIT20OFFSET0;-- 备选SQL:2008 标准写法达梦8也支持OFFSET0ROWSFETCHNEXT20ROWSONLY;分页公式OFFSET (pageNum - 1) × pageSize三、完整 SQL达梦 DM8SELECT*FROM(/* 基础查询近6个月入职的在职员工 */SELECTemp_id,emp_name,dept_code,hire_date,ACTIVEASemp_statusFROMt_employeeWHEREhire_dateADD_MONTHS(SYSDATE,-6)ANDresign_dateISNULLUNIONALL/* 离职员工includeResigned1 时拼入 */SELECTemp_id,emp_name,dept_code,hire_date,RESIGNEDASemp_statusFROMt_employee_resignedWHEREresign_dateADD_MONTHS(SYSDATE,-6))tmpORDERBYtmp.hire_dateDESCLIMIT20OFFSET0;-- 每页20条第1页执行流程图┌──────────────────────────────────────────────────────────┐ │ 最外层ORDER BY hire_date DESC LIMIT 分页 │ │ │ │ ┌──────────────────────────────────────────────────┐ │ │ │ 子查询 tmpUNION ALL 的结果集 │ │ │ │ │ │ │ │ ┌──────────────────┐ ┌──────────────────────┐ │ │ │ │ │ t_employee │ │ t_employee_resigned │ │ │ │ │ │ 近6月入职在职 │ │ 近6月离职 │ │ │ │ │ │ ADD_MONTHS计算 │ │ (条件性拼入) │ │ │ │ │ └──────────────────┘ └──────────────────────┘ │ │ │ └──────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────┘四、MyBatis 动态 SQL 集成selectidqueryEmployeesresultTypecom.example.entity.EmployeeSELECT * FROM ( SELECT emp_id, emp_name, dept_code, hire_date, ACTIVE AS emp_status FROM t_employee WHERE hire_date ADD_MONTHS(SYSDATE, -6) AND resign_date IS NULLiftestincludeResigned 1UNION ALL SELECT emp_id, emp_name, dept_code, hire_date, RESIGNED AS emp_status FROM t_employee_resigned WHERE resign_date ADD_MONTHS(SYSDATE, -6)/if) tmp ORDER BY tmp.hire_date DESC LIMIT #{pageSize} OFFSET #{offset}/selectJava 调用层intoffset(pageNum-1)*pageSize;MapString,ObjectparamsnewHashMap();params.put(includeResigned,includeResigned);params.put(pageSize,pageSize);params.put(offset,offset);ListEmployeelistemployeeMapper.queryEmployees(params);五、不同数据库分页语法对照数据库分页写法达梦 DM8LIMIT m OFFSET nMySQLLIMIT m OFFSET nPostgreSQLLIMIT m OFFSET nOracle 12cOFFSET n ROWS FETCH NEXT m ROWS ONLYSQL ServerOFFSET n ROWS FETCH NEXT m ROWS ONLY如果你正在做 Oracle → 达梦迁移只需替换分页语法ADD_MONTHS、SYSDATE、UNION ALL等用法基本一致。六、避坑指南⚠️ 常见坑正确做法UNION ALL 两边列数/类型不一致确保 SELECT 列数量、顺序、类型完全对应ORDER BY 写在子查询内部子查询排序无意义且浪费性能只在最外层排用 UNION 代替 UNION ALL不需要去重就用 ALL避免隐式排序开销OFFSET 写成页码OFFSET 是偏移行数不是页码需换算ADD_MONTHS 参数写反-6是往前推6个月6是往后推常量列忘记加别名ACTIVE AS emp_status必须起别名否则 UNION ALL 报错七、总结步骤要点① 时间范围ADD_MONTHS(SYSDATE, -6)计算起始时间② 纵向拼接UNION ALL叠加行列结构必须对齐③ 条件控制MyBatisif决定是否拼入第二段④ 排序分页外层包装 ORDER BYLIMIT ... OFFSET ...