这是否需要递归CTE,只需要创意窗口函数,循环?

时间:2016-02-11 01:34:51

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

我不能为我的生活找出如何获得X类别的分数的加权排名。例如,学生需要回答3个类别中的10个问题(问题#和类别#最终都是变量)。要获得总分,每个X(3)类别中的前1个分数将被添加到剩余的任何内容中以添加总共10个问题分数。

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

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

小提琴有更多的学生。

| Question | Student | Category | Score | 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可能是无效或其他什么。

| Question | Student | Category | Score | TopInCat | Sort |
|----------|---------|----------|-------|----------|------|
|   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)阈值,该类别就不再重要。所以第四类只是正常排序。

| Question | Student | Category | Score | TopInCat | Sort |
|----------|---------|----------|-------|----------|------|
|   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 |

我非常感谢任何帮助。几天来一直在敲我的脑袋。

1 个答案:

答案 0 :(得分:2)

有了这些事情,我喜欢继续采用“积木”的方法。遵循的格言,首先让它工作,然后如果你需要快速,这第一步通常就足够了。

所以,给定

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
)

现在我们为每个学生提供了三个顶级类别的问题。我们现在需要通过分数识别并按顺序排列每个学生的顶级类别问题:

-- 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 {1}}(即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个类别。