SQL重难点语句

【达梦数据库】UNION ALL 动态拼接 + LIMIT 分页实战:员工在职/离职统一查询

📌 前言

在实际业务中,我们经常遇到这样的需求:

  • 当前在职员工基本信息
  • 当某个标志位 includeResigned == 1 时,还要追加已离职员工数据
  • 最终结果统一排序 + 分页返回

这个需求涉及三个核心知识点:ADD_MONTHS 时间计算UNION ALL 纵向拼接达梦 LIMIT 分页。本文以达梦数据库(DM8)为例,从思路到代码一步步拆解。


一、需求拆解

需求点技术映射
近6个月入职的在职员工hire_date >= ADD_MONTHS(SYSDATE, -6) AND resign_date IS NULL
includeResigned==1 追加离职员工MyBatis <if> 动态控制 UNION ALL 子查询
统一排序分页外层包装子查询 + ORDER BY + LIMIT ... 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 达梦分页语法

--  推荐:简洁直观
LIMIT 20 OFFSET 0;

-- 备选:SQL:2008 标准写法(达梦8也支持)
OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY;

分页公式OFFSET = (pageNum - 1) × pageSize


三、完整 SQL(达梦 DM8)

SELECT *
FROM (
    /* ===== 基础查询:近6个月入职的在职员工 ===== */
    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 NULL

    UNION ALL

    /* ===== 离职员工(includeResigned=1 时拼入) ===== */
    SELECT emp_id,
           emp_name,
           dept_code,
           hire_date,
           'RESIGNED' AS emp_status
    FROM   t_employee_resigned
    WHERE  resign_date >= ADD_MONTHS(SYSDATE, -6)
) tmp
ORDER BY tmp.hire_date DESC
LIMIT 20 OFFSET 0;   -- 每页20条,第1页

执行流程图

┌──────────────────────────────────────────────────────────┐
│  最外层:ORDER BY hire_date DESC + LIMIT 分页             │
│                                                          │
│   ┌──────────────────────────────────────────────────┐   │
│   │  子查询 tmp(UNION ALL 的结果集)                  │   │
│   │                                                  │   │
│   │  ┌──────────────────┐  ┌──────────────────────┐  │   │
│   │  │ t_employee       │  │ t_employee_resigned  │  │   │
│   │  │ 近6月入职+在职   │  │ 近6月离职            │  │   │
│   │  │ ADD_MONTHS计算   │  │ (条件性拼入)         │  │   │
│   │  └──────────────────┘  └──────────────────────┘  │   │
│   └──────────────────────────────────────────────────┘   │
└──────────────────────────────────────────────────────────┘

四、MyBatis 动态 SQL 集成

<select id="queryEmployees" resultType="com.example.entity.Employee">
    SELECT *
    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 NULL

        <if test="includeResigned == 1">
            UNION 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}
</select>

Java 调用层:

int offset = (pageNum - 1) * pageSize;

Map<String, Object> params = new HashMap<>();
params.put("includeResigned", includeResigned);
params.put("pageSize", pageSize);
params.put("offset", offset);

List<Employee> list = employeeMapper.queryEmployees(params);

五、不同数据库分页语法对照

数据库分页写法
达梦 DM8LIMIT m OFFSET n
MySQLLIMIT m OFFSET n
PostgreSQLLIMIT m OFFSET n
Oracle 12c+OFFSET n ROWS FETCH NEXT m ROWS ONLY
SQL ServerOFFSET n ROWS FETCH NEXT m ROWS ONLY

如果你正在做 Oracle → 达梦迁移,只需替换分页语法,ADD_MONTHSSYSDATEUNION 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 叠加行,列结构必须对齐
③ 条件控制MyBatis <if> 决定是否拼入第二段
④ 排序分页外层包装 + ORDER BY + LIMIT ... OFFSET ...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值