【问题标题】:Does this require recursive CTE, just creative window functions, a loop?这是否需要递归 CTE、创意窗口函数、循环?
【发布时间】:2016-02-11 01:34:50
【问题描述】:

我这辈子都不知道如何获得 X 类别分数的加权排名。例如,学生需要回答 3 个类别的 10 个问题(问题的数量和类别的数量最终都会变化)。要获得总分,每个 X (3) 个类别中的前 1 分将被添加到剩下的任何内容中,从而总计 10 个问题总分。

这是数据。我使用 CASE WHEN Row_Number() 来获取 TopInCat

http://sqlfiddle.com/#!6/e6e9f/1

小提琴有更多的学生。

|问题 |学生 |类别 |分数 | TopInCat | |---------|----------|----------|--------|--------- -| | 120149 | 125 | 6 | 1 | 1 | | 120127 | 125 | 6 | 0.9 | 0 | | 120124 | 125 | 6 | 0.8 | 0 | | 120125 | 125 | 6 | 0.7 | 0 | | 120130 | 125 | 6 | 0.6 | 0 | | 120166 | 125 | 6 | 0.5 | 0 | | 120161 | 125 | 6 | 0.4 | 0 | | 120138 | 125 | 4 | 0.15 | 1 | | 120069 | 125 | 4 | 0.15 | 0 | | 120022 | 125 | 4 | 0.15 | 0 | | 120002 | 125 | 4 | 0.15 | 0 | | 120068 | 125 | 2 | 0.01 | 1 | | 120050 | 125 | 3 | 0.05 | 1 | | 120139 | 125 | 2 | 0 | 0 | | 120156 | 125 | 2 | 0 | 0 |

这是我设想的外观,但不一定是这样。我只需要按 3 个类别详细数据列出 10 个问题,这样我就可以对下面的排序 1-10 列进行求和和平均。 999 可以为 null 或其他任何内容,只要我能总结出重要的内容并提供详细信息即可。

|问题 |学生 |类别 |分数 | TopInCat |排序 | |---------|----------|----------|--------|--------- -|--------| | 120149 | 125 | 6 | 1 | 1 | 1 | | 120138 | 125 | 4 | 0.15 | 1 | 2 | | 120068 | 125 | 2 | 0.01 | 1 | 3 | | 120127 | 125 | 6 | 0.9 | 0 | 4 | | 120124 | 125 | 6 | 0.8 | 0 | 5 | | 120125 | 125 | 6 | 0.7 | 0 | 6 | | 120130 | 125 | 6 | 0.6 | 0 | 7 | | 120166 | 125 | 6 | 0.5 | 0 | 8 | | 120161 | 125 | 6 | 0.4 | 0 | 9 | | 120069 | 125 | 4 | 0.15 | 0 | 10 | | 120022 | 125 | 4 | 0.15 | 0 | 999 | | 120002 | 125 | 4 | 0.15 | 0 | 999 | | 120050 | 125 | 3 | 0.05 | 1 | 999 | | 120139 | 125 | 2 | 0 | 0 | 999 | | 120156 | 125 | 2 | 0 | 0 | 999 |

最后一件事,一旦达到 X (3) 阈值,类别就不再重要。所以第 4 个类别会正常排序。

|问题 |学生 |类别 |分数 | TopInCat |排序 | |---------|----------|----------|--------|--------- -|--------| | 120149 | 126 | 6 | 1 | 1 | 1 | | 120138 | 126 | 4 | 0.75 | 1 | 2 | | 120068 | 126 | 2 | 0.50 | 1 | 3 | | 120127 | 126 | 6 | 0.9 | 0 | 4 | | 120124 | 126 | 6 | 0.8 | 0 | 5 | | 120125 | 126 | 6 | 0.7 | 0 | 6 | | 120130 | 126 | 6 | 0.6 | 0 | 7 | | 120166 | 126 | 6 | 0.5 | 0 | 8 | | 120050 | 126 | 3 | 0.45 | 1 | 9 |******** | 120161 | 126 | 6 | 0.4 | 0 | 10 | | 120069 | 126 | 4 | 0.15 | 0 | 999 | | 120022 | 126 | 4 | 0.15 | 0 | 999 | | 120002 | 126 | 4 | 0.15 | 0 | 999 | | 120139 | 126 | 2 | 0 | 0 | 999 | | 120156 | 126 | 2 | 0 | 0 | 999 |

我非常感谢任何帮助。这几天一直在纠结这个问题。

【问题讨论】:

    标签: sql-server sql-server-2008 window-functions recursive-cte


    【解决方案1】:

    对于这些问题,我喜欢采用“构建块”的方法。遵循先让它工作,然后如果你需要让它快的格言,这第一步通常就足够了。

    所以,给定

    CREATE TABLE WeightedScores
        ([Question] int, [Student] int, [Category] int, [Score] dec(3,2))
    ;
    

    和你的样本数据

    INSERT INTO WeightedScores
        ([Question], [Student], [Category], [Score])
    VALUES
        (120161, 123, 6, 1),    (120166, 123, 6, 0.64),    (120138, 123, 4, 0.57),    (120069, 123, 4, 0.5),
        (120068, 123, 2, 0.33),    (120022, 123, 4, 0.18),    (120061, 123, 6, 0),    (120002, 123, 4, 0),
        (120124, 123, 6, 0),    (120125, 123, 6, 0),    (120137, 123, 6, 0),    (120154, 123, 6, 0),
        (120155, 123, 6, 0),   (120156, 123, 6, 0),    (120139, 124, 2, 1),    (120156, 124, 2, 1),
        (120050, 124, 3, 0.88),    (120068, 124, 2, 0.87),    (120161, 124, 6, 0.87),    (120138, 124, 4, 0.85),
        (120069, 124, 4, 0.51),    (120166, 124, 6, 0.5),    (120022, 124, 4, 0.43),    (120002, 124, 4, 0),
        (120130, 124, 6, 0),    (120125, 124, 6, 0),    (120124, 124, 6, 0),    (120127, 124, 6, 0),
        (120149, 124, 6, 0),    (120149, 125, 6, 1),    (120127, 125, 6, 0.9),    (120124, 125, 6, 0.8),
        (120125, 125, 6, 0.7),    (120130, 125, 6, 0.6),    (120166, 125, 6, 0.5),    (120161, 125, 6, 0.4),
        (120138, 125, 4, 0.15),    (120069, 125, 4, 0.15),    (120022, 125, 4, 0.15),    (120002, 125, 4, 0.15),
        (120068, 125, 2, 0.01),    (120050, 125, 3, 0.05),    (120139, 125, 2, 0),    (120156, 125, 2, 0),
        (120149, 126, 6, 1),    (120138, 126, 4, 0.75),    (120068, 126, 2, 0.50),    (120127, 126, 6, 0.9),
        (120124, 126, 6, 0.8),    (120125, 126, 6, 0.7),    (120130, 126, 6, 0.6),    (120166, 126, 6, 0.5),
        (120050, 126, 3, 0.45),    (120161, 126, 6, 0.4),    (120069, 126, 4, 0.15),    (120022, 126, 4, 0.15),
        (120002, 126, 4, 0.15),    (120139, 126, 2, 0),    (120156, 126, 2, 0)
    ;
    

    让我们继续吧。


    这里的复杂部分是确定前三个顶级问题;每个学生感兴趣的十个问题中的其他问题只是按分数排序,这很容易。因此,让我们从确定前三个最重要的问题开始。

    首先,为每一行分配一个行号,为学生在类别中给出该分数的排序:

    ;WITH Numbered1 ( Question, Student, Category, Score, SeqInStudentCategory ) AS
    (
        SELECT Question, Student, Category, Score
            , ROW_NUMBER() OVER (PARTITION BY Student, Category ORDER BY Score DESC) SeqInStudentCategory 
        FROM WeightedScores
    )
    

    现在我们只对SeqInStudentCategory1 的行感兴趣。只考虑这些行,让我们按学生中的分数对它们进行排序,并对这些行进行编号:

    -- within the preceding WITH
    , Numbered2 ( Question, Student, Category, Score, SeqInStudent ) AS
    (
        SELECT 
            Question, Student, Category, Score
            , ROW_NUMBER() OVER (PARTITION BY Student ORDER BY Score DESC) SeqInStudent
        FROM
            Numbered1
        WHERE
            SeqInStudentCategory = 1
    )
    

    现在我们只对SeqInStudent 最多为3 的行感兴趣。让我们把它们拉出来,这样我们就知道要包含它(并从简单的按分数排序中排除它,我们将用它来组成剩余的七行):

    -- within the preceding WITH
    , TopInCat ( Question, Student, Category, Score, SeqInStudent ) AS
    (
         SELECT Question, Student, Category, Score, SeqInStudent FROM Numbered2 WHERE SeqInStudent <= 3
    )
    

    现在我们为每个学生准备了三个顶级问题。我们现在需要为每个学生识别和排序 not 类别最高的问题:

    -- within the preceding WITH
    , NotTopInCat ( Question, Student, Category, Score, SeqInStudent ) AS
    (
        SELECT
            Question, Student, Category, Score
            , ROW_NUMBER() OVER (PARTITION BY Student ORDER BY Score DESC) SeqInStudent
        FROM
            WeightedScores WS
        WHERE
            NOT EXISTS ( SELECT 1 FROM TopInCat T WHERE T.Question = WS.Question AND T.Student = WS.Student )
    )
    

    最后,我们将TopInCatNotTopInCat 组合在一起,对NotTopInCat.SeqInStudent 应用适当的偏移量和限制 - 我们需要将3 添加到原始值,并取顶部的7(即10 - 3 ):

    -- within the preceding WITH
    , Combined ( Question, Student, Category, Score, CombinedSeq ) AS
    (
        SELECT
            Question, Student, Category, Score, SeqInStudent AS CombinedSeq
        FROM
            TopInCat
        UNION
        SELECT
            Question, Student, Category, Score, SeqInStudent + 3 AS CombinedSeq
        FROM
            NotTopInCat
        WHERE
            SeqInStudent <= 10 - 3
    )
    

    要得到我们的最终结果:

    SELECT * FROM Combined ORDER BY Student, CombinedSeq
    ;
    

    你可以看到结果on sqlfiddle


    请注意,这里我假设每个学生总是会从至少三个类别中获得答案。此外,最终输出没有有一个 TopInCat 列,但希望你能看到如果你想要它如何重新获得它。

    此外,“(问题的数量和类别的数量最终都会发生变化)”在这里处理起来应该相对简单。但请注意我的假设,即(在这种情况下)每个学生的答案中肯定会出现 3 个类别。

    【讨论】:

    • 这太棒了!谢谢!我开始了这样的事情,但认为必须有更好的方法。我发现你的代码有一个小问题。在 NotTopInCat - WHERE Question NOT IN 不是针对学生的,所以它应该是 WHERE NOT EXISTS(从 TopInCat TIC WHERE TIC.Student= WS.Student AND TIC.Question= WS.Question 中选择 1)你能更新答案吗?我要做更多的验证,但我认为这可能是最好的解决方案。再次感谢。
    • @cporichie 啊,是的,那时我还认为Question 是PK,而实际上(Question, Student) 是PK。现在更新。
    猜你喜欢
    • 1970-01-01
    • 2016-09-28
    • 1970-01-01
    • 1970-01-01
    • 2021-11-04
    • 2020-02-08
    • 2017-02-01
    • 2018-04-29
    • 1970-01-01
    相关资源
    最近更新 更多