首页 > 解决方案 > 从 CTE 输出中随机化部分选择

问题描述

这个问题中,@GordonLinoff 为我最初的问题提供了一个解决方案(递归公用表表达式)。这是一个后续问题。

最初的问题

我如何循环注册直到达到一定数量(总和)的 AmountPersons 并且如果下一个 AmountPersons 太高而无法被邀请检查下一行的 AmountPersons 是否合适?

请通过上面的链接检查最初的问题以获取完整的图片。

新情况

首先,我们有 20 个可用席位,我们遍历数据行来填补这些席位(初始问题)。

然后我对Count_Invitedorder by进行排序并从row_number()函数中更新。因此,被邀请最少的人应该获得优先权。

然后我还添加了Count_Registered列,因为注册最多但被邀请最少的人也应该获得优先权。

新问题

如果第三个、第四个、第五个 .. 用户也具有相同的值(Count_Invited 和 Count_Registered 和 AmountPersons 为 1),我如何从下面的结果中打乱被邀请的最后两个人?

顶部数据的排序正确,但只有最后几行需要随机化被邀请者。

我知道这种ORDER BY NEWID()随机化行的功能,但在我的情况下它不能应用于所有行。我不知道如何处理这个...更多信息如下。

新的 T-SQL 代码:

WITH tn AS ( 
            SELECT g.[Id], 
                        g.[FirstName], 
                        g.[LastName], 
                        g.[E-mail],
                        g.[Count_Invited],
                        g.[Count_Registered],
                        r.[DateReservation],
                        r.[AmountPersons],
                        row_number() over(order by g.[Count_Invited], g.[Count_Registered] DESC) as seqnum
            FROM USERTABLE g 
                INNER JOIN RESERVATION r ON r.[UserId] = g.[Id] 
            WHERE r.[PartyId] = 21
        ),
        cte AS ( 
            SELECT [Id], [FirstName], [LastName], [E-mail], [Count_Invited], [Count_Registered], [DateReservation], 
                    [AmountPersons], [AmountPersons] as total, 1 as is_included, seqnum
            FROM tn 
            WHERE seqnum = 1

            UNION ALL 

            SELECT tn.[Id], tn.[FirstName], tn.[LastName], tn.[E-mail], tn.[Count_Invited], tn.[Count_Registered], tn.[DateReservation], tn.[AmountPersons],
                    (case when tn.[AmountPersons] +cte.total <= 20
                            then tn.[AmountPersons] +cte.total 
                            else cte.total
                    end), 
                    (case when tn.[AmountPersons] +cte.total <= 20
                            then 1 
                            else 0 
                    end) as is_included, 
                    tn.seqnum
            FROM cte join 
                    tn 
                    on tn.seqnum = cte.seqnum + 1 
            WHERE cte.total < 20
        ) 
SELECT cte.Id AS userId, 
    cte.FirstName, 
    cte.LastName, 
    cte.[E-mail], 
    cte.Count_Invited, 
    cte.Count_Registered, 
    cte.AmountPersons, 
    cte.DateReservation
FROM cte 
WHERE is_included = 1

这是我每次执行上述代码时得到的结果: sql结果

我希望这对某人有意义。谢谢你。


@George Menoutis 建议答案的输出: ![输出newid NOK


编辑:额外的澄清步骤。这是应该发生的:

-- 声明 amountSeats = 25

-- 选择 Count_Invited 的第一个值

-- 如果该值为 0

-- 对 Count_Invited 为 0 的 AmountPersons(多行)求和

-- 如果该总和低于 amountSeats,假设现在是 10

-- 在临时表中插入所有值为 0 的行(不确定这是否是要走的路……)

-- 选择 Count_Invited 的第二个值(不是第二行) --> 所以 Count_Invited 不是 0

-- 如果该值为 1

-- 对 Count_Invited 为 1 的 AmountPersons(多行)求和

-- Count_Invited 的总计数 = 0 + Count_Invited = 1

-- 如果这个总和仍然低于 amountSeats,假设现在是 15

-- 在临时表中插入(添加)Count_Invited 1 的所有行

-- 选择 Count_Invited 的第 3 个值(不是第 3 行) --> 所以 Count_Invited NOT IN (0, 1)

-- 如果该值为 5

-- 计数 AmountPersons(多行),其中 Count_Invited 为 5

-- Count_Invited 的总计数 = 0 + Count_Invited = 1 + Count_Invited = 5

-- 现在假设 AmountPersons 的计数现在是 40

-- 这意味着不是每个 Count_Invited = 5 的人都可以被邀请,因为只有 10 个空位

-- 需要对这些行进行随机选择

-- 选择 Count_Invited 为 5 的随机行,直到这些行的总和为 10

-- 如果 10 不能匹配,则通过遍历剩余的行尽可能接近,但不要超过 10

标签: sql-servertsqlcommon-table-expressionnewid

解决方案


我实际上认为你的newid()想法是最好的。只是正确的说法是在seqnum的定义中:

row_number() over(order by g.[Count_Invited], g.[Count_Registered] DESC, newid() asc) as seqnum

附录:在OP的评论之后,我在这里提出了一个新问题。所以,它似乎会成功,但你必须先制作tn一个临时表,否则 newid() 会被以下 cte 多次触发。


推荐阅读