一条SQL实现高考赋分制
本文于 2 天前发布,最后更新于 2 天前

原文地址:
高考赋分制解析【上】——从理解规则到计算实践,一文读懂!
高考赋分制解析【下】——从模型设计到SQL实现,一文读懂!

一、赋分制的由来

想象一下:化学考试特别难,全省平均只有45分;而生物考试简单,平均高达75分。此时如果直接用原始分计入高考总分,选化学的同学岂不是太吃亏了?

所以赋分制就是要建立一个公平竞技场,让不同科目的成绩能够公平比较!

二、赋分规则详解

根据福建省教育厅文件,赋分规则分三部分:需赋分科目、等级划分和分数转换。物理、历史直接使用原始分,满分100分;政治、地理、化学、生物则需赋分转换,最终同样满分100分。

为什么物理和历史不用赋分呢?

好问题!因为物理和历史是首选科目,每个学生必须二选一,它们被认为具有较强的区分度,所以直接使用原始分。

接下来是等级划分,赋分制将所有考生按原始分从高到低排列,划分为5个等级,人数比例分别为:A级15%、B级35%、C级35%、D级13%、E级2%。每个等级对应不同的赋分区间:A级对应86-100分,B级71-85分,C级56-70分,D级41-55分,E级30-40分。

三、赋分制的模拟

假设全省仅有20名同学参加高考化学考试,考题特别难,分数从高到低排列如下。

首先是按比例划分等级,全部20名同学按照之前说的比例划分,结果是:

  • A级(前15%):3人(20×15%=3)
  • B级(接着35%):7人(20×35%=7)
  • C级(接着35%):7人(20×35%=7)
  • D级(接着13%):2人(20×13%=2.6)
  • E级(最后2%):1人(20×2%=0.4)

注意,因为人数计算有小数,实际分配会有微调。在此例中,D级和E级的人数就有调整。

于是,20个学生的等级分就出来了。现在,让我们按照赋分制的规则来给这20名同学分配等级和赋分,如图所示。

完整的表格

那这些同学的赋分到底是多少呢?让我们看看几个具体同学的情况。

  • 李学神:原始分66,排名第1,属于A级,是全部考生的最高分,直接得100分;郝厉害:原始分是60分,排名第3,也是A级,处于A级的下边界。获得A级最低赋分,即86分。
  • 牛二爷:原始分是59分,排名第4,处于B级最高,得88分;甄聪明:原始分是50,排名第10,处于B级最低,得71分。
  • 小牛:原始分是49,排名第11,处于C级最高,得70分;小强:原始分是40,排名第17,处于C级最低,得56分。
  • 牛漂亮:原始分18,排名第18,处于D级最高,得55分;羊美丽:原始分8,排名第19,处于D级最低,得41分。
  • 朱坚强:原始分0分,排名第20,处于E级最低(亦是全部考生最低),得30分。

如此,一些赋分就可填写了,如下图所示。

不过,这些不处在头尾,而是位于中间的同学,如张丫霸,艾学习等,他们的赋分又该如何计算呢?

四、赋分制公式

其实就是把考生在原始分中的排位,平移到赋分的分数段中。用一个公式来表达这个转换过程:
赋分 = 等级下限分 + [(原始分 – 等级原始分下限) ÷ (等级原始分上限 – 等级原始分下限)] × (等级上限分 – 等级下限分)

来,接下来我们应用公式算算张丫霸的赋分。

张丫霸(63)的原始分
= 等级下限分+[(原始分-等级原始分下限)÷(等级原始分上限-等级原始分下限)]×(等级上限分-等级下限分)
= 86 + [(63 - 60) ÷ (66 - 60)] × (100 - 86)
= 86 + [3 ÷ 6] × 14
= 86 + 7
= 93

也就是说,张丫霸化学成绩经过赋分转换后,从原始的63分变成了93分!

这正是赋分制的调节作用。你63分在A级原始分范围内的相对位置,对应到了A级赋分范围内的93分。

其实总分第一即满分的规矩,通过代公式也能计算得到,牛学神66分同时也是最高分,带入公式计算会发现,(66 – 60) ÷ (66 – 60)=1,然后86+1× 14=100分。

五、公式带入

记住这几个要点:政治、地理、化学、生物四科需要赋分转换;按全省排名分为A-E五级;每个等级内,通过等比例转换计算最终赋分。

接下来,我尝试和大家探讨探讨如何写出高效健壮的SQL,来模拟赋分制的系统实现。

六、数据准备

--创建学生成绩表:这就是我们的数字化考场
CREATE TABLE student_scores (
    student_id NUMBER(10),                -- 学生ID,每个学生的唯一标识
    student_name VARCHAR2(50),            -- 学生姓名
    raw_score NUMBER(5,2),                -- 原始分数,支持小数点后两位
    grade CHAR(1),                        -- 等级(A-E),系统计算后填入
    converted_score NUMBER(5,2)           -- 赋分结果,这就是最终的神奇转换结果
);

-- 重现上次的考试现场:20名同学的化学考试原始成绩
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (1, '牛学神', 66);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (2, '张丫霸', 63);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (3, '郝厉害', 60);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (4, '牛二爷', 59);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (5, '艾学习', 58);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (6, '孙奋斗', 55);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (7, '钱多多', 53);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (8, '陈努力', 52);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (9, '王大锤', 51);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (10, '甄聪明', 50);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (11, '小牛', 49);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (12, '小马', 47);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (13, '小翠', 45);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (14, '小丽', 43);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (15, '小南', 42);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (16, '小北', 41);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (17, '小强', 40);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (18, '牛漂亮', 18);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (19, '羊美丽', 8);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (20, '朱坚强', 0);
COMMIT;

七、算法核心

第一步

排名计算 – 给每个学生找到自己的位置

-- 第一步:排名计算 - 这是一切计算的基础
ranked_students AS (
    SELECT 
        student_id,
        student_name,
        raw_score,
        -- 使用RANK()函数处理同分情况,确保同分学生得到相同排名
        RANK() OVER (ORDER BY raw_score DESC) as rank_num,
        -- COUNT() OVER()获取总人数,这个技巧避免了额外的子查询
        COUNT(*) OVER () as total_count
    FROM student_scores
)

说明:
ROW_NUMBER():1,2,3,4…(强制给同分学生不同排名)
RANK():1,2,2,2,5,6…(同分学生得到相同排名,后续排名跳跃)

第二步

等级划分 – 按福建省标准分配ABCDE五个等级

接下来是关键的等级分配,这一步必须严格按照福建省教育厅的规定:A级15%,B级35%,C级35%,D级13%,E级2%。

-- 第二步:等级划分 - 严格按照福建省的15%、35%、35%、13%、2%比例
graded_students AS (
    SELECT 
        student_id, student_name, raw_score, rank_num, total_count,
        CASE
            -- A级:前15%(对于20人:CEIL(20 × 0.15) = 3,即前3名)
            WHEN rank_num <= CEIL(total_count * 0.15) THEN 'A'
            -- B级:前15%之后到前50%(第4名到第10名)
            WHEN rank_num <= CEIL(total_count * 0.50) THEN 'B'
            -- C级:前50%之后到前85%(第11名到第17名)
            WHEN rank_num <= CEIL(total_count * 0.85) THEN 'C'
            -- D级:前85%之后到前98%(第18名到第19名)
            WHEN rank_num <= CEIL(total_count * 0.98) THEN 'D'
            -- E级:剩下的就是最后2%(第20名)
            ELSE 'E'
        END as grade
    FROM ranked_students
)

说明:
这里使用累积比例的简洁写法。由于CASE语句按顺序执行,前15%先被分配A级,剩下的15%-50%自动成为B级,以此类推。这比写区间判断更简洁且不易出错。

第三步

计算等级边界 – 找出每个等级的分数范围

现在我们要找出每个等级内的最高分和最低分,这些边界值是等比例转换公式的关键参数。

-- 第三步:计算每个等级内的原始分数范围,这些数据是公式计算的基础
grade_ranges AS (
    SELECT 
        grade,
        -- 找出等级内的最高和最低原始分数
        MAX(raw_score) as max_raw_score,      -- 等级内最高原始分
        MIN(raw_score) as min_raw_score,      -- 等级内最低原始分
        -- 对应的赋分区间上限(福建省标准)
        CASE grade 
            WHEN 'A' THEN 100 WHEN 'B' THEN 85 WHEN 'C' THEN 70 
            WHEN 'D' THEN 55 ELSE 40 
        END as max_converted_score,
        -- 对应的赋分区间下限(福建省标准)
        CASE grade 
            WHEN 'A' THEN 86 WHEN 'B' THEN 71 WHEN 'C' THEN 56 
            WHEN 'D' THEN 41 ELSE 30 
        END as min_converted_score
    FROM graded_students 
    GROUP BY grade
)

这一步很巧妙!通过GROUP BY grade,我们能分别统计出每个等级的分数范围。比如A级的原始分范围是60-66分(郝厉害到牛学神),对应的赋分范围是86-100分。

这样A级原始分的60-66这6分的跨度,要映射到赋分的86-100这14分的跨度上。这就是等比例转换的数学基础。

第四步

特殊情况处理 – 让系统更加智能和健壮

现在到了算法的核心部分,我们必须处理所有可能的边界情况,确保系统的数学稳定性和业务合理性。

-- 第四步:特殊情况处理与等比例转换公式应用
final_calculation AS (
    SELECT 
        gs.student_id, gs.student_name, gs.raw_score, gs.rank_num, gs.grade,
        CASE
            -- 特殊处理1:全省第1名必须获得满分100分
            WHEN gs.rank_num = 1 THEN 100
            -- 特殊处理2:全省最低分必须获得保底30分
            WHEN gs.raw_score = (SELECT MIN(raw_score) FROM student_scores) THEN 30
            -- 特殊处理3:单人等级的健壮性处理
            -- 当等级内只有1人时,max_raw_score = min_raw_score,公式分母为0
            -- 这种情况在20万考生中几乎不可能出现,但为了代码健壮性必须处理
            WHEN gr.max_raw_score = gr.min_raw_score THEN 
                CASE gs.grade
                    WHEN 'A' THEN 100  -- A级唯一学生给满分
                    WHEN 'B' THEN 85   -- B级唯一学生给该级最高分
                    WHEN 'C' THEN 70   -- C级唯一学生给该级最高分
                    WHEN 'D' THEN 55   -- D级唯一学生给该级最高分
                    WHEN 'E' THEN 30   -- E级唯一学生给保底分
                END
            -- 一般情况:应用等比例转换公式
            -- 公式:赋分 = 等级下限 + (个人原始分 - 等级原始分下限) × 
            --              (等级赋分跨度) ÷ (等级原始分跨度)
            ELSE ROUND(
                gr.min_converted_score + 
                (gs.raw_score - gr.min_raw_score) * 
                (gr.max_converted_score - gr.min_converted_score) / 
                (gr.max_raw_score - gr.min_raw_score), 0
            )
        END as converted_score
    FROM graded_students gs, grade_ranges gr
    WHERE gs.grade = gr.grade
)

为什么等级内只有一个人就要特殊处理呢?

如果等级内仅一人,max_raw_score等于min_raw_score,等比例公式的分母就是零,会导致数学错误。虽然这种情况在实际考试中极其罕见,但程序必须处理所有边界条件。我们的处理策略是:ABCD级唯一学生给该级最高分,E级唯一学生给30分,这样确保整个赋分体系始终有最高分100和最低分30。当然,这只是我们的一种设计方案,实际的官方系统可能会有不同的处理逻辑。

八、完整实现

四步合并一气呵成

形成一个完整、优雅、高效的SQL查询。

-- 福建省高考赋分制完整实现算法
WITH 
-- 第一步:排名计算,确定每个学生的相对位置
ranked_students AS (
    SELECT 
        student_id, student_name, raw_score,
        RANK() OVER (ORDER BY raw_score DESC) as rank_num,
        COUNT(*) OVER () as total_count
    FROM student_scores
),
-- 第二步:等级划分,按福建省15%、35%、35%、13%、2%标准分配
graded_students AS (
    SELECT 
        student_id, student_name, raw_score, rank_num, total_count,
        CASE
            WHEN rank_num <= CEIL(total_count * 0.15) THEN 'A'
            WHEN rank_num <= CEIL(total_count * 0.50) THEN 'B'
            WHEN rank_num <= CEIL(total_count * 0.85) THEN 'C'
            WHEN rank_num <= CEIL(total_count * 0.98) THEN 'D'
            ELSE 'E'
        END as grade
    FROM ranked_students
),
-- 第三步:计算各等级的原始分数范围和对应的赋分区间
grade_ranges AS (
    SELECT 
        grade,
        MAX(raw_score) as max_raw_score,
        MIN(raw_score) as min_raw_score,
        CASE grade 
            WHEN 'A' THEN 100 WHEN 'B' THEN 85 WHEN 'C' THEN 70 
            WHEN 'D' THEN 55 ELSE 40 
        END as max_converted_score,
        CASE grade 
            WHEN 'A' THEN 86 WHEN 'B' THEN 71 WHEN 'C' THEN 56 
            WHEN 'D' THEN 41 ELSE 30 
        END as min_converted_score
    FROM graded_students 
    GROUP BY grade
),
-- 第四步:特殊情况处理与等比例转换公式应用
final_calculation AS (
    SELECT 
        gs.student_id, gs.student_name, gs.raw_score, gs.rank_num, gs.grade,
        CASE
            -- 特殊情况1:全省第1名固定100分
            WHEN gs.rank_num = 1 THEN 100
            -- 特殊情况2:全省最低分固定30分
            WHEN gs.raw_score = (SELECT MIN(raw_score) FROM student_scores) THEN 30
            -- 特殊情况3:单人等级处理(健壮性考虑)
            WHEN gr.max_raw_score = gr.min_raw_score THEN 
                CASE gs.grade
                    WHEN 'A' THEN 100 WHEN 'B' THEN 85 WHEN 'C' THEN 70 
                    WHEN 'D' THEN 55 WHEN 'E' THEN 30
                END
            -- 一般情况:应用等比例转换公式
            ELSE ROUND(
                gr.min_converted_score + 
                (gs.raw_score - gr.min_raw_score) * 
                (gr.max_converted_score - gr.min_converted_score) / 
                (gr.max_raw_score - gr.min_raw_score), 0
            )
        END as converted_score
    FROM graded_students gs, grade_ranges gr
    WHERE gs.grade = gr.grade
)
-- 显示最终的赋分结果,按排名顺序展示
SELECT 
    rank_num as "排名", 
    student_name as "姓名", 
    raw_score as "原始分", 
    grade as "等级", 
    converted_score as "赋分"
FROM final_calculation 
ORDER BY rank_num;

九、见证奇迹

完美复现手工计算结果

十、完美收官

从手工到智能实现

第一步

批量更新:将计算结果写回数据库

光有查询结果还不够,我们需要把这些宝贵的计算结果保存到数据库中,供后续的高考录取系统使用,代码思路如下。

UPDATE student_scores 
SET (grade, converted_score) = (
    SELECT grade, converted_score 
    FROM (
        -- 这里是完整的赋分计算逻辑
        WITH 
        ranked_students AS (
            SELECT 
                student_id, student_name, raw_score,
                RANK() OVER (ORDER BY raw_score DESC) as rank_num,
                COUNT(*) OVER () as total_count
            FROM student_scores
        ),
        graded_students AS (
            SELECT 
                student_id, student_name, raw_score, rank_num, total_count,
                CASE
                    WHEN rank_num <= CEIL(total_count * 0.15) THEN 'A'
                    WHEN rank_num <= CEIL(total_count * 0.50) THEN 'B'
                    WHEN rank_num <= CEIL(total_count * 0.85) THEN 'C'
                    WHEN rank_num <= CEIL(total_count * 0.98) THEN 'D'
                    ELSE 'E'
                END as grade
            FROM ranked_students
        ),
        grade_ranges AS (
            SELECT 
                grade,
                MAX(raw_score) as max_raw_score,
                MIN(raw_score) as min_raw_score,
                CASE grade 
                    WHEN 'A' THEN 100 WHEN 'B' THEN 85 WHEN 'C' THEN 70 
                    WHEN 'D' THEN 55 ELSE 40 
                END as max_converted_score,
                CASE grade 
                    WHEN 'A' THEN 86 WHEN 'B' THEN 71 WHEN 'C' THEN 56 
                    WHEN 'D' THEN 41 ELSE 30 
                END as min_converted_score
            FROM graded_students 
            GROUP BY grade
        ),
        final_calculation AS (
            SELECT 
                gs.student_id, gs.grade,
                CASE
                    WHEN gs.rank_num = 1 THEN 100
                    WHEN gs.raw_score = (SELECT MIN(raw_score) FROM student_scores) THEN 30
                    WHEN gr.max_raw_score = gr.min_raw_score THEN 
                        CASE gs.grade
                            WHEN 'A' THEN 100 WHEN 'B' THEN 85 WHEN 'C' THEN 70 
                            WHEN 'D' THEN 55 WHEN 'E' THEN 30
                        END
                    ELSE ROUND(
                        gr.min_converted_score + 
                        (gs.raw_score - gr.min_raw_score) * 
                        (gr.max_converted_score - gr.min_converted_score) / 
                        (gr.max_raw_score - gr.min_raw_score), 0
                    )
                END as converted_score
            FROM graded_students gs, grade_ranges gr
            WHERE gs.grade = gr.grade
        )
        SELECT grade, converted_score FROM final_calculation 
        WHERE student_id = student_scores.student_id
    )
);
commit;

第二步

数据质量检查:确保系统的可靠性

任何一个生产系统都必须有完善的质量检查机制,特别是像高考赋分这样关系到学生前途的重要计算。

-- 全面的数据质量检查
SELECT '数据完整性检查' as 检查类型, '总人数' as 检查项目, COUNT(*) as 检查结果 
FROM student_scores
UNION ALL
SELECT '数据完整性检查', '已赋分人数', COUNT(*) 
FROM student_scores WHERE converted_score IS NOT NULL
UNION ALL
SELECT '等级分布检查', grade || '级人数', COUNT(*) 
FROM student_scores WHERE grade IS NOT NULL GROUP BY grade
UNION ALL
SELECT '分数范围检查', '赋分最高分', MAX(converted_score) 
FROM student_scores WHERE converted_score IS NOT NULL
UNION ALL
SELECT '分数范围检查', '赋分最低分', MIN(converted_score) 
FROM student_scores WHERE converted_score IS NOT NULL
UNION ALL
SELECT '逻辑性检查', '原始分与赋分差异数', COUNT(*)
FROM student_scores 
WHERE converted_score IS NOT NULL AND converted_score < raw_score
ORDER BY 检查类型, 检查项目;

这个检查查询能帮我们发现各种潜在问题,比如是否所有学生都完成了赋分,等级分布是否合理,赋分是否在30-100的正常范围内,以及赋分后是否真的比原始分更高等等。

第三步

性能优化考虑:面向大规模数据

如果考生数据量达数百万甚至更多,这套算法性能如何?

优化手段:

  1. 为raw_score字段建立索引,加速排序;
  2. 使用并行查询提高大数据量的处理速度;
  3. 分批处理,比如按学校或地区分批计算等等。

总结

原来复杂的赋分制可以用一条这么优雅简洁的SQL实现了,代码改变世界。

这个案例展示了如何将教育政策精确转化为数据库算法。从排名到赋分,每一步都体现了技术服务教育公平的理念。技术让复杂的教育评价变得高效精准,更重要的是,它让公平变得可验证、可重现、易维护。每一行代码背后,都承载着对教育公平的坚持。

需要特别说明的是,本文展示的仅仅是一个极简示例。真实的高考赋分系统要复杂得多!是绝不可能仅用一条SQL就能搞定的,需要考虑太多场景细节了。比如当大量学生出现同分时,可能会打破等级比例:按15%划分A级理论上应该有3000人,但如果第2999名到第3500名都是同分,系统需要决定是严格按比例切分还是保持同分同级。不同省份对此可能有不同的处理策略。

此外还有数据校验、异常处理、并发控制、审计日志等工程问题,以及如何处理缺考、作弊、成绩复议等特殊情况。

但无论系统多复杂,核心思想不变:通过等级划分和等比例转换,将原始分数映射到标准分数区间,实现不同科目分数的可比性。这正是新高考制度的精髓所在。

祝莘莘高考学子们:

      心想事成,金榜题名!!!

暂无评论

发送评论 编辑评论


|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇