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. 业务导向:紧密围绕项目管理的核心指标设计

在实际使用中,建议:

  • 确保传入参数的合法性验证
  • 监控大数据量下的查询性能
  • 根据业务发展适时调整统计逻辑
相关推荐
数智启示录6 小时前
Apache Doris 4.0.8 混合检索实战(第 5 篇):三套系统只返回两条结果,一条 SQL 如何避开交集丢失
大数据·数据库·经验分享·sql·面试
今天AI了吗6 小时前
大模型技术全景(一):AI、机器学习、深度学习,三者的关系到底是什么?一文搞懂
数据库·人工智能·python·sql·深度学习·机器学习·rust
迷枫71211 小时前
SQL 优化排查记录:补充联合索引,避免大范围扫描
sql
迷枫71211 小时前
SQL 优化记录:OR 条件改写后利用现有联合索引
sql
拾光Ծ15 小时前
【MySQL】对表数据的操作:增删查改(CRUD)
android·数据库·sql·mysql
迷枫71216 小时前
SQL 优化排查记录:补充索引,利用 OR 展开消除大表重复扫描
sql
九皇叔叔2 天前
《MySQL 体系架构详解:Server 层、SQL 执行流程与 InnoDB、MyISAM、MEMORY 存储引擎》
sql·mysql·架构
这个DBA有点耶2 天前
SQL:2023新特性详解:JSON_TABLE、GREATEST/LEAST、QUALIFY 到底怎么用?
数据库·sql·mysql
小小龙学IT2 天前
Qt 数据库编程(Qt SQL 模块)深度解析
数据库·sql·qt
疯狂打码的少年2 天前
【数据库技术】SQL概述与数据定义(DDL:CREATE/DROP/ALTER)
数据库·笔记·sql·oracle