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

如何通过SQL实现单用户测验结果的证书式格式化输出?

针对你想要生成类似证书格式的单用户测验结果需求,我整理了几个实用的SQL方案,其中直接用UNION ALL结合排序的方式最直观高效,CTE和STUFF也能配合实现,下面详细说明:

问题背景

首先,你的数据库结构和示例数据(补充了查询用到的quiz表)如下:

CREATE TABLE [dbo].[users] ( 
    [user_id] [bigint] IDENTITY(1,1) NOT NULL, 
    [user_name] [nvarchar](50) NULL, 
    [first_name] [nvarchar](50) NULL, 
    [last_name] [nvarchar](50) NULL, 
    [id_number] [nvarchar](50) NULL, 
    CONSTRAINT [PK_users] PRIMARY KEY CLUSTERED ( [user_id] ASC ) 
) 

insert into users (user_name, first_name, last_name, id_number) 
select 'user1','John','Brown','7707071231' union all 
select 'user2','Mary','Jane','7303034432' union all 
select 'user3','Peter','Pan','5503024441' 

CREATE TABLE [dbo].[quiz_results] ( 
    [result_id] [bigint] IDENTITY(1,1) NOT NULL, 
    [quiz_id] [bigint] NOT NULL, 
    [user_id] [bigint] NOT NULL, 
    [grade] [bigint] NULL, 
    CONSTRAINT [PK_quizresults] PRIMARY KEY CLUSTERED ( [result_id] ASC ) 
) 

insert into quiz_results (quiz_id, user_id, grade) 
select 1,1,88 union all 
select 2,1,84 union all 
select 3,1,33 union all 
select 1,2,65 

-- 补充查询依赖的quiz表
CREATE TABLE [dbo].[quiz] (
    [quiz_id] [bigint] NOT NULL,
    [quiz_name] [nvarchar](50) NULL,
    CONSTRAINT [PK_quiz] PRIMARY KEY CLUSTERED ([quiz_id] ASC)
)

insert into quiz (quiz_id, quiz_name)
select 1, 'quiz a' union all
select 2, 'quiz b' union all
select 3, 'quiz c'

你当前的查询会重复显示student_name在每行,而期望首行仅展示用户信息,后续行只显示测验名称和成绩,类似证书的格式。

最优实现方案

方案1:UNION ALL + 排序(推荐,直观高效)

这个方案直接将用户信息行和测验结果行合并,通过排序保证用户行在最前面,再用CASE控制每行显示的内容,完全在SQL中生成你想要的格式:

基础版(分列显示)

SELECT 
    -- 仅第一行显示用户名称,其余行留空
    CASE WHEN rn = 0 THEN student_name ELSE '' END AS student_name,
    -- 第一行测验名称留空,其余行显示测验名
    CASE WHEN rn = 0 THEN '' ELSE quiz_name END AS quiz_name,
    -- 第一行成绩留空,其余行显示成绩
    CASE WHEN rn = 0 THEN '' ELSE CAST(grade AS VARCHAR(10)) END AS grade
FROM (
    -- 首行:用户信息
    SELECT 
        users.first_name + ' ' + users.last_name + ' (' + users.id_number + ')' AS student_name,
        '' AS quiz_name,
        '' AS grade,
        0 AS rn  -- 标记为第一行
    FROM users
    WHERE users.user_id = 1  -- 替换为目标user_id
    UNION ALL
    -- 后续行:测验结果
    SELECT 
        '' AS student_name,
        quiz.quiz_name,
        CAST(quiz_results.grade AS VARCHAR(10)) AS grade,
        -- 用行号标记后续行,保证排序正确
        ROW_NUMBER() OVER (ORDER BY quiz_results.quiz_id) AS rn
    FROM quiz_results
    INNER JOIN quiz ON quiz_results.quiz_id = quiz.quiz_id
    WHERE quiz_results.user_id = 1  -- 替换为目标user_id
) AS combined_data
ORDER BY rn;

带分隔线的证书格式版(完全匹配示例)

如果要生成和你给出的示例完全一致的带分隔线的文本格式,可以直接拼接字符串:

SELECT display_text AS certificate_line
FROM (
    -- 顶部分隔线
    SELECT '+-------------------------+' AS display_text, 0 AS rn
    UNION ALL
    -- 用户信息行
    SELECT '| ' + users.first_name + ' ' + users.last_name + ' (' + users.id_number + ') |' AS display_text, 1 AS rn
    FROM users
    WHERE users.user_id = 1  -- 替换为目标user_id
    UNION ALL
    -- 中间分隔线
    SELECT '+-------------------------+' AS display_text, 2 AS rn
    UNION ALL
    -- 测验结果行
    SELECT '| ' + quiz.quiz_name + ' | ' + CAST(quiz_results.grade AS VARCHAR(10)) + ' |' AS display_text, 
           2 + ROW_NUMBER() OVER (ORDER BY quiz_results.quiz_id) AS rn
    FROM quiz_results
    INNER JOIN quiz ON quiz_results.quiz_id = quiz.quiz_id
    WHERE quiz_results.user_id = 1  -- 替换为目标user_id
    UNION ALL
    -- 底部分隔线
    SELECT '+-------------------------+' AS display_text, 999 AS rn
) AS certificate_data
ORDER BY rn;

这个查询的输出和你给出的示例完全一致,每行对应证书的一行,不需要客户端额外处理。

方案2:CTE(代码结构优化)

CTE和上面的子查询逻辑完全一致,只是把合并的数据集用CTE定义,让代码结构更清晰,适合逻辑更复杂的场景:

WITH certificate_data AS (
    -- 首行:用户信息
    SELECT 
        users.first_name + ' ' + users.last_name + ' (' + users.id_number + ')' AS student_name,
        '' AS quiz_name,
        '' AS grade,
        0 AS rn
    FROM users
    WHERE users.user_id = 1
    UNION ALL
    -- 后续行:测验结果
    SELECT 
        '',
        quiz.quiz_name,
        CAST(quiz_results.grade AS VARCHAR(10)),
        ROW_NUMBER() OVER (ORDER BY quiz_results.quiz_id) AS rn
    FROM quiz_results
    JOIN quiz ON quiz_results.quiz_id = quiz.quiz_id
    WHERE quiz_results.user_id = 1
)
SELECT 
    CASE WHEN rn = 0 THEN student_name ELSE '' END,
    CASE WHEN rn = 0 THEN '' ELSE quiz_name END,
    CASE WHEN rn = 0 THEN '' ELSE grade END
FROM certificate_data
ORDER BY rn;

方案3:STUFF + XML PATH(生成单字符串证书)

如果需要将整个证书作为单个字符串输出(比如导出为文本文件),可以用STUFF配合XML PATH拼接所有行:

SELECT 
    '+-------------------------+' + CHAR(13)+CHAR(10) +
    '| ' + users.first_name + ' ' + users.last_name + ' (' + users.id_number + ') |' + CHAR(13)+CHAR(10) +
    '+-------------------------+' + CHAR(13)+CHAR(10) +
    -- 拼接所有测验结果行
    STUFF((
        SELECT CHAR(13)+CHAR(10) + '| ' + quiz.quiz_name + ' | ' + CAST(quiz_results.grade AS VARCHAR(10)) + ' |'
        FROM quiz_results
        JOIN quiz ON quiz_results.quiz_id = quiz.quiz_id
        WHERE quiz_results.user_id = users.user_id
        FOR XML PATH(''), TYPE
    ).value('.', 'NVARCHAR(MAX)'), 1, 0, '') +
    CHAR(13)+CHAR(10) + '+-------------------------+' AS full_certificate
FROM users
WHERE users.user_id = 1;

这个查询会返回一个包含所有证书内容的字符串,每行用换行符分隔。

方案对比
  • UNION ALL方案:最优选择,逻辑简单,性能优秀,直接生成多行结果,完全匹配你的需求。
  • CTE方案:和UNION ALL性能一致,只是代码结构更清晰,适合复杂场景的逻辑拆分。
  • STUFF方案:适合需要单个字符串输出的场景,比如导出文本,但如果需要多行结果则不如UNION ALL直接。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 06:44:48