SQL语法 一个统计的查询

sql 复制代码
 <select id="getPersonInfo" resultType="com.purvar.report.facade.vo.jxappuser.PersonDetailVO">
        SELECT
            #{request.memberId} AS id,
            NULL AS departRank,
            NULL AS levelRank,
            NVL(f.visitCount, 0) AS visitCount,
            CASE
                WHEN NVL(p.totalProjectCount, 0) = 0 THEN 0
                ELSE ROUND(NVL(p.futureIndustryCount, 0) * 100.0 / p.totalProjectCount, 2)
            END AS futureIndustryRate,
            NVL(d.projectCount, 0) AS projectCount,
            NULL AS irr,
            NULL AS tvpi,
            NULL AS leadRatio,
            NULL AS manageNum
        FROM DUAL
        LEFT JOIN (
            SELECT COUNT(1) AS visitCount
            FROM ADS.jx_proj_follow_info f
            WHERE (f.is_delete = 0 OR f.is_delete IS NULL)
            <choose>
                <when test="request.memberName != null and request.memberName != ''">
                    AND f.follower IS NOT NULL
                    AND INSTR(
                        ',' || REPLACE(REPLACE(f.follower, ',', ','), ' ', '') || ',',
                        ',' || REPLACE(REPLACE(#{request.memberName}, ',', ','), ' ', '') || ','
                    ) > 0
                </when>
                <otherwise>
                    AND 1 = 0
                </otherwise>
            </choose>
        ) f ON 1 = 1
        LEFT JOIN (
            SELECT
                COUNT(DISTINCT p.proj_id) AS totalProjectCount,
                COUNT(DISTINCT CASE WHEN p.is_future_industry = '1' THEN p.proj_id END) AS futureIndustryCount
            FROM ADS.jx_proj_info p
            WHERE (p.is_delete = 0 OR p.is_delete IS NULL)
            <choose>
                <when test="request.memberId != null">
                    AND p.member_id = #{request.memberId}
                </when>
                <otherwise>
                    AND 1 = 0
                </otherwise>
            </choose>
        ) p ON 1 = 1
        LEFT JOIN (
            SELECT COUNT(DISTINCT d.proj_id) AS projectCount
            FROM ADS.jx_decision_info d
            WHERE (d.is_delete = 0 OR d.is_delete IS NULL)
            <choose>
                <when test="request.memberId != null">
                    AND d.member_id = TO_CHAR(#{request.memberId})
                </when>
                <otherwise>
                    AND 1 = 0
                </otherwise>
            </choose>
        ) d ON 1 = 1
    </select>

SQL 语句详细解析

这段 SQL 是一个 MyBatis 映射文件中的查询语句,用于获取个人(成员)的详细信息。下面我将从多个维度进行详细解析:

1. 整体功能

此 SQL 的主要功能是查询指定成员(员工)的项目相关统计信息,包括:

  • 访问次数(visitCount)
  • 未来产业项目占比(futureIndustryRate)
  • 负责项目数量(projectCount)
  • 其他预留的统计字段(如 IRR、TVPI 等,当前为 NULL)

2. 核心结构解析

2.1 主查询部分
sql 复制代码
SELECT
    #{request.memberId} AS id,  -- 直接使用传入的成员ID作为结果ID
    NULL AS departRank,         -- 部门排名(暂未实现)
    NULL AS levelRank,          -- 级别排名(暂未实现)
    NVL(f.visitCount, 0) AS visitCount,  -- 访问次数,空值转为0
    CASE
        WHEN NVL(p.totalProjectCount, 0) = 0 THEN 0
        ELSE ROUND(NVL(p.futureIndustryCount, 0) * 100.0 / p.totalProjectCount, 2)
    END AS futureIndustryRate,  -- 未来产业项目占比(百分比)
    NVL(d.projectCount, 0) AS projectCount,  -- 决策项目数量
    NULL AS irr,                -- 内部收益率(暂未实现)
    NULL AS tvpi,               -- 投资回报倍数(暂未实现)
    NULL AS leadRatio,          -- 领导力比例(暂未实现)
    NULL AS manageNum           -- 管理数量(暂未实现)
FROM DUAL
  • DUAL:Oracle 中的虚拟表,用于构建单行查询
  • NVL():Oracle 的空值处理函数,将 NULL 转为指定值(这里是0)
  • CASE WHEN:计算未来产业项目占比,避免除零错误
2.2 三个 LEFT JOIN 子查询
2.2.1 访问次数统计(f 子查询)
sql 复制代码
LEFT JOIN (
    SELECT COUNT(1) AS visitCount
    FROM ADS.jx_proj_follow_info f
    WHERE (f.is_delete = 0 OR f.is_delete IS NULL)
    <choose>
        <when test="request.memberName != null and request.memberName != ''">
            AND f.follower IS NOT NULL
            AND INSTR(
                ',' || REPLACE(REPLACE(f.follower, ',', ','), ' ', '') || ',',
                ',' || REPLACE(REPLACE(#{request.memberName}, ',', ','), ' ', '') || ','
            ) > 0
        </when>
        <otherwise>
            AND 1 = 0  -- 条件不满足时不统计
        </otherwise>
    </choose>
) f ON 1 = 1

功能 :统计该成员的项目跟进记录数

关键逻辑

  • jx_proj_follow_info 表统计未删除的记录
  • 当传入成员姓名时,使用 INSTR 函数在 follower 字段中查找该姓名
  • REPLACE 函数处理中文逗号和空格,确保格式统一
  • 1 = 0 表示条件不满足时返回空结果
2.2.2 项目统计(p 子查询)
sql 复制代码
LEFT JOIN (
    SELECT
        COUNT(DISTINCT p.proj_id) AS totalProjectCount,
        COUNT(DISTINCT CASE WHEN p.is_future_industry = '1' THEN p.proj_id END) AS futureIndustryCount
    FROM ADS.jx_proj_info p
    WHERE (p.is_delete = 0 OR p.is_delete IS NULL)
    <choose>
        <when test="request.memberId != null">
            AND p.member_id = #{request.memberId}
        </when>
        <otherwise>
            AND 1 = 0
        </otherwise>
    </choose>
) p ON 1 = 1

功能 :统计成员负责的项目总数和未来产业项目数

关键逻辑

  • totalProjectCount:成员负责的独立项目总数
  • futureIndustryCount:标记为未来产业(is_future_industry = '1')的项目数
  • 条件判断:当传入成员ID时才进行统计
2.2.3 决策项目统计(d 子查询)
sql 复制代码
LEFT JOIN (
    SELECT COUNT(DISTINCT d.proj_id) AS projectCount
    FROM ADS.jx_decision_info d
    WHERE (d.is_delete = 0 OR d.is_delete IS NULL)
    <choose>
        <when test="request.memberId != null">
            AND d.member_id = TO_CHAR(#{request.memberId})
        </when>
        <otherwise>
            AND 1 = 0
        </otherwise>
    </choose>
) d ON 1 = 1

功能 :统计成员参与的决策项目数量

关键逻辑

  • jx_decision_info 表统计未删除的决策记录
  • TO_CHAR():将数值型成员ID转为字符串进行匹配
  • 同样使用条件判断确保参数有效性

3. MyBatis 动态 SQL 标签解析

<choose><when><otherwise>
  • 作用:实现条件分支逻辑
  • 示例 :当 request.memberName 不为空时,执行姓名匹配逻辑;否则返回空结果
  • 优势:避免在参数为空时产生无效查询
#{request.memberId}#{request.memberName}
  • 作用:MyBatis 的参数占位符,防止 SQL 注入
  • 区别
    • memberId:用于精确匹配(项目表和决策表)
    • memberName:用于模糊匹配(跟进表的 follower 字段)

4. 业务场景分析

4.1 适用场景

此 SQL 可能用于以下业务场景:

  1. 个人工作台/仪表盘:展示个人的关键绩效指标
  2. 领导查看下属数据:了解团队成员的项目参与情况
  3. 绩效考核:作为项目参与度和贡献度的参考数据
  4. 人才盘点:评估员工在重点项目(未来产业)中的参与比例
4.2 数据流向
复制代码
成员ID/姓名 → 参数传入 → 三个子查询并行统计 → 主查询汇总 → VO对象返回
4.3 设计特点
  1. 模块化设计:三个子查询职责清晰,便于维护
  2. 空值安全 :全程使用 NVL 处理 NULL 值
  3. 条件防御:参数为空时返回空结果,避免全表扫描
  4. 扩展性:预留了多个统计字段(如 IRR、TVPI 等)为后续功能留出接口

5. 潜在优化建议

5.1 性能方面
  • 三个子查询可考虑合并或使用 WITH 子句(CTE)
  • INSTR 函数对 follower 字段的模糊匹配可能影响性能,建议建立专门的关联表
  • 频繁查询时可考虑添加适当的索引
5.2 可读性方面
  • 可添加注释说明各字段的业务含义
  • 复杂的字符串处理逻辑可提取为数据库函数
  • 魔法数字(如 '1' 表示未来产业)可定义为常量
5.3 功能扩展
  • 当前许多字段返回 NULL,后续可根据业务需求逐步实现
  • 可添加时间范围过滤(如统计最近一年的数据)
  • 可考虑添加项目状态过滤(只统计进行中的项目)

6. 总结

这个 SQL 语句是一个典型的个人数据统计查询,具有以下特点:

  1. 安全性:使用参数化查询防止 SQL 注入
  2. 健壮性:全面处理 NULL 值和边界条件
  3. 模块化:不同统计维度分离,便于理解和维护
  4. 可扩展:预留字段支持未来功能扩展
  5. 业务导向:紧密围绕项目管理的核心指标设计

在实际使用中,建议:

  • 确保传入参数的合法性验证
  • 监控大数据量下的查询性能
  • 根据业务发展适时调整统计逻辑
相关推荐
竹枝溪3 小时前
MySQL数据库零基础入门
数据库·sql·mysql·navicat·ddl·dml·dql
smilejingwei17 小时前
CodeBuddy+SQLazy 编写复杂 SQL 详解
sql·codebuddy·sqlazy
蒲公英eric1 天前
从布尔盲注到参数化查询:DVWA SQL 盲注模块完整漏洞分析教程
sql·web安全·ai·dvwa·ai安全·sql 盲注
今天AI了吗1 天前
MCP 协议打通 AI 与国产数据库,SQL 调优全流程一站式闭环
数据库·人工智能·sql
灯澜忆梦1 天前
【MySQL11】进阶篇 | 索引_#3使用规则
数据库·sql·mysql·性能优化
云技纵横1 天前
线上接口突然超时,怎么判断卡在 Nginx、线程池、连接池还是 SQL?
后端·sql·mysql
sugar__salt1 天前
MyBatis ③动态 SQL 完全指南 —— 从条件拼接到批量操作
java·sql·mybatis
fchen5211 天前
JSON:一站式开发者工具集,SQL日志解析并填充 、JSON格式化 与文本比对
sql·microsoft·json
x-cmd1 天前
用 Rust 打造 AI 时代的 SQL:把重复任务变成可执行文件
数据库·人工智能·sql·ai·容器·rust·workflow