You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SQL三表关联查询实现重复列仅首行显示的层级化数据输出

业务场景与问题描述

我有三张存在层级一对多关联的表,基础表结构如下:

Group             ID|Title
RiskFactor        GroupID|Question
Choices           RiskFactorID|Label|Score

表关联规则:一个Group对应多条RiskFactor记录,每条RiskFactor记录对应多条带分值的选项Choices。这种关联关系下,INNER JOIN和LEFT JOIN的查询返回结果完全一致。

现有查询语句:

SELECT        
       Groups.Title, 
       RiskFactors.Question,  
       Choices.Label, 
       Choices.Score
FROM            
       Groups 
INNER JOIN RiskFactors ON Groups.GroupId = RiskFactors.GroupId 
INNER JOIN Choices ON RiskFactors.RiskFactorId = Choices.RiskFactorId
ORDER BY Groups.groupid

该查询的现有输出存在字段重复问题,示例如下:

Title                       Question                                                   Label  Score
4.1 - Employment & Finance  Is your current job in JEOPARDY?  (Describe why in notes)   Yes 3
4.1 - Employment & Finance  Is your current job in JEOPARDY?  (Describe why in notes)   Maybe   2
4.1 - Employment & Finance  Is your current job in JEOPARDY?  (Describe why in notes)   NA  1
4.1 - Employment & Finance  Is your current job in JEOPARDY?  (Describe why in notes)   No  1
4.1 - Employment & Finance  How LONG was (is) your longest FULL TIME job?   NA  1
4.1 - Employment & Finance  How LONG was (is) your longest FULL TIME job?   0-6 Months  1
4.1 - Employment & Finance  How LONG was (is) your longest FULL TIME job?   1-3 Years   1
4.1 - Employment & Finance  How LONG was (is) your longest FULL TIME job?   6-12 Months 1
4.1 - Employment & Finance  How LONG was (is) your longest FULL TIME job?   Only Part Time  1
4.1 - Employment & Finance  How LONG was (is) your longest FULL TIME job?   More than 3 Years   1

期望的输出效果是把同组下重复的Title、Question字段值替换为NULL或空值,实现层级化展示,目标格式如下:

TITLE                       QUESTION                                                    Label SCORE
4.1 - Employment & Finance  Is your current job in JEOPARDY?  (Describe why in notes)   Yes 3
                                                                                        Maybe   2
                                                                                        NA  1
                                                                                        No  1
                            How LONG was (is) your longest FULL TIME job?   NA  1
                                                                            0-6 Months  1
                                                                            1-3 Years   1
                                                                            6-12 Months 1
                                                                            Only Part Time  1
                                                                            More than 3 Years   1

之前尝试过CTE、Pivot表的写法都没有实现预期效果,我不是专业SQL开发人员,知道这是通用的展示需求,之前都是靠前端Grid控件的列去重功能实现该效果,现在想知道如何在SQL层面直接输出符合要求的层级化数据。

实现方案

这个场景不需要用到Pivot或者复杂的递归CTE,核心逻辑非常直白:只保留每个分组第一行的分组字段值,同组后续行的对应分组字段全部置空,用标准窗口函数就可以快速实现。
逻辑拆解:

  • 先对查询结果按照业务层级做固定排序,保证同一个Group、同一个RiskFactor下的行是连续排列的
  • 对Title字段:取当前行上一行的GroupId做对比,如果值相等,说明当前行不是当前Group的第一行,将Title置为NULL;如果值不相等,保留Title的原始值
  • 对Question字段:取当前行上一行的RiskFactorId做对比,如果值相等,说明当前行不是当前问题的第一行,将Question置为NULL;如果值不相等,保留Question的原始值

可直接运行的兼容主流数据库的SQL写法:

WITH OrderedResult AS (
    SELECT        
           Groups.Title,
           Groups.GroupId,
           RiskFactors.Question,
           RiskFactors.RiskFactorId,
           Choices.Label,
           Choices.Score
    FROM            
           Groups 
    INNER JOIN RiskFactors 
        ON Groups.GroupId = RiskFactors.GroupId 
    INNER JOIN Choices 
        ON RiskFactors.RiskFactorId = Choices.RiskFactorId
)
SELECT
    CASE 
        WHEN GroupId = LAG(GroupId) OVER(ORDER BY GroupId, RiskFactorId, Score DESC) 
        THEN NULL 
        ELSE Title 
    END AS Title,
    CASE 
        WHEN RiskFactorId = LAG(RiskFactorId) OVER(ORDER BY GroupId, RiskFactorId, Score DESC) 
        THEN NULL 
        ELSE Question 
    END AS Question,
    Label,
    Score
FROM OrderedResult
ORDER BY GroupId, RiskFactorId, Score DESC

注意事项

  • 窗口函数的排序规则必须和业务要求的展示顺序完全一致,否则会出现同一个Group的行被拆分、重复字段散落在不同位置的问题
  • 如果使用的是不支持窗口函数的老旧数据库(比如MySQL 5.7及以下版本),可以用会话变量逐行记录上一行的分组ID做判断,核心逻辑和上述写法完全一致,只是语法写法略有不同
  • 这种在SQL层输出空值的方式更适合固定格式的报表打印、纯文本导出场景;如果是前端交互式页面,更推荐返回原始关联结果,由前端Grid组件做单元格合并,调整样式的灵活性更高

内容的提问来源于stack exchange,提问作者john

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.27 04:45:04