如何在纯(即ANSI)SQL中进行'评分'

时间:2010-06-22 10:02:12

标签: sql mysql postgresql

我有一张看起来像这样的表:

CREATE TABLE student_results(id integer,name varchar(32),score float);

让我们做出以下两个假设:

  1. 假设得分从0到最大值为100.
  2. 假设我想对“步长”为10
  3. 的学生进行评分

    所以我想申请以下评分:

    Score      Grade Awarded
    0-10       GRADE9
    10-20      GRADE8
    20-30      GRADE7
    30-40      GRADE6
    40-50      GRADE5
    50-60      GRADE4
    60-70      GRADE3
    70-80      GRADE2
    80-90      GRADE1
    99-100     GENIUS
    

    我想编写一个SQL查询,它接受以下输入参数:

    lowest score:  0 in this example
    highest score: 100 in this example
    'step' size:   10 in this example
    

    与以往一样,如果可能,我想使用ANSI SQL编写这样的查询。如果我必须选择一个数据库,那么按照减少优先顺序,它必须是:

    • 的PostgreSQL
    • MySQL的

    有人可以解释一下如何使用上表作为示例来编写执行此类评分的SQL查询吗?

    [编辑]

    Sample input data
    
    1, 'homer', 10.5
    2. 'santas little helper', 15.2
    3, 'bart',  20.5
    4, 'marge', 40.5
    5. 'lisa', 100
    

    我将有一个SQL函数grade_rank() - 对学生进行排名:

    函数grade_rank()的参数是:

    1st argument: LOWEST possible score value
    2nd argument: HIGHEST possible score value
    3rd argument: step size, which determines the levels/divisions between the ranks
    
    select id, name, grade_rank(0,100, 10) grade from student_scores;
    

    输出(基于上面的输入)应为:

    1, homer,               GRADE9
    2. santas liitle helper GRADE9
    3, bart,                GRADE8
    4, marge,               GRADE6
    5. lisa,                GENIUS
    

4 个答案:

答案 0 :(得分:1)

这样的事情?

SELECT 
     [name],
     score,
     CASE 
          WHEN score > @max - @stepsize THEN 'GENIUS'
          ELSE CONCAT('GRADE',
                    CAST(
                         FLOOR((@max - score)/@stepsize - 
                              CASE score 
                                   WHEN @min THEN 1
                                   ELSE 0
                              END CASE
                         ) as char(3)
                    )
               )
     END CASE
FROM 
     student_results

你可能需要稍微调整一下 - 我不太明白最小部分(它是否仅用于因为最后一个范围比其他范围大1个?)

修改

为了每个Ivar的清晰度,

将@step重命名为@stepsize(@step可能被误解为步数)

答案 1 :(得分:1)

有几个选择:

1) 创建一个包含成绩(分钟,最大)的表并加入该表

SELECT score, grades.grade
FROM table 
     INNER JOIN grades ON table.score >= grades.min AND table.score <= grades.max

2) 创建一个临时表(甚至从DUAL中选择)并加入它,例如在上面而不是成绩中你可以编写子查询

(SELECT 0 as MIN, 10 as max, 'GRADE9' as grade FROM DUAL
 UNION ALL
 SELECT 11 as MIN, 20 as max, 'GRADE8' as grade FROM DUAL
 UNION ALL
 ...
 SELECT 91 as min, 100 as max, 'GENIUS' as grade FROM DUAL
 ) AS grades

3) 使用案例

SELECT score,
   CASE WHEN score = 0 THEN 'GRADE9'
        WHEN score >= 1 AND score <= 90 THEN 'GRADE' || (9 - (score-1) / 10)
        WHEN score >= 91 THEN 'GENIUS'
        ELSE 'ERROR'
   END grade
FROM table

(注意在上面的查询中你可以用0,100和10代替最低,最高和步骤来获得动态sql)

4) 创建user function(但这将获得RDBMS特定的)

答案 2 :(得分:1)

这个怎么样? (原来我已经使用了@steps,就像步数一样,而不是@step。如果你想指定@step,@ step可以计算为@steps = (@highest-@lowest)/@step

SET @lowest = 0;
SET @highest = 100;
SET @steps = 10;

SELECT
    name,
    CASE
        WHEN score >= (@highest-@steps) THEN 'GENIUS'
        ELSE 
            CONCAT(
                'GRADE',
                @steps-FLOOR((score-@lowest)/((@highest-@lowest)/@steps))-1)
    END
FROM
    student_results

每当您通过下一步时,这将为您提供新的成绩。

0-9.999   => GRADE1
10-19.999 => GRADE2
etc.

答案 3 :(得分:1)

通过这种方式你可以做到更一般,但等级将是相反的顺序,从1到N,即

  • 0-10 Grade1
  • 10-20 Grade2
  • 20-30 Grade3
  • 30-40 Grade4
  • ...

例如使用值 第10步 得分43

此算法

SELECT (((score-1)-((score-1) % step))/step)+1

将返回5

您无需知道最高分数。如果最高分数为100,则无法执行高于100的分数,您只需确定步骤的大小即可。例如,如果您想要步长为25.知道最高分为100,则将有4个年级。因此,通过将步骤级别设置为25而不是10,结果将为2,即2级。

SELECT (((43-1)-((43-1) % 25))/25)+1

也许你的预期并不正确,但也许通用到足以使用。这是函数在SQL中的样子。

CREATE OR REPLACE FUNCTION grade_rank(IN score integer, IN step integer, OUT rank integer)
AS 'SELECT ((($1-1)-(($1-1) % $2))/$2)+1'
LANGUAGE 'SQL';

现在调用此函数

select * from grade_rank(43,10)

返回5.

这是plpgsql的等价物:

CREATE OR REPLACE FUNCTION grade_rank(IN score integer, IN step integer)
  RETURNS integer AS
$BODY$ 
DECLARE rank integer;
BEGIN
    SELECT (((score-1)-((score-1) % step))/step)+1 INTO rank;
    RETURN rank;
END;
$BODY$
  LANGUAGE 'plpgsql';