如何规范分组列的大小写?

Mar*_*ell 6 sql sql-server group-by case-sensitive case-insensitive

在配置为不区分大小写的SQL Server上,group by[n][var]char列不是第一group by列时,可能会产生有趣的结果.从本质上讲,它看起来像它遇到的任何行"第一"(在没有订单的情况下"第一"未定义):为该分组获胜.例如:

select x.[day], x.[name], count(1) as [count]
from (
    select 1 as [day], 'a' as [name]
    union all select 1, 'A'
    union all select 2, 'A'
    union all select 2, 'a'
    ) x group by x.[day], x.[name]
Run Code Online (Sandbox Code Playgroud)

哪个回来,对我来说:

day         name count
----------- ---- -----------
1           A    2
2           a    2
Run Code Online (Sandbox Code Playgroud)

使用min(x.[name])没有效果,因为分组已经发生.

我不能添加order by 之前group by,因为这是非法的; 并添加order by group by只是定义了分组后的输出顺序-它仍然给aA.

所以:有没有一种理智的方式来实现这一点,资本化至少对所有分组都是一致的?(我将在另一天离开单独运行的问题)

期望的输出,或者:

day         name count
----------- ---- -----------
1           A    2
2           A    2
Run Code Online (Sandbox Code Playgroud)

要么:

day         name count
----------- ---- -----------
1           a    2
2           a    2
Run Code Online (Sandbox Code Playgroud)

编辑:在组之间保持一致时破坏大小写.所以没有上/下.因此,如果其中一个组始终具有该值BcDeF,我希望该行的结果为BcDeF,而不是bcdefBCDEF.

Lam*_*mak 9

我会为此使用窗口函数.通过使用ROW_NUMBER不区分大小写的排序规则来使用和分区,但是通过区分大小写的排序,我们将始终选择一个带有原始大小写的结果,但它会将它们分组,就好像它们是相同的:

WITH CTE AS
(
    SELECT  *,
            RN = ROW_NUMBER() OVER(PARTITION BY [day], [name]
                                   ORDER BY [name] COLLATE SQL_Latin1_General_Cp1_Cs_AS),
            N = COUNT(*) OVER(PARTITION BY [day], [name])
    FROM (  select 1 as [day], 'a' as [name]
            union all select 1, 'A'
            union all select 2, 'A'
            union all select 2, 'a'
            union all select 3, 'BcDeF'
            union all select 3, 'bCdEf') X
)
SELECT *
FROM CTE
WHERE RN = 1;
Run Code Online (Sandbox Code Playgroud)

它返回:

????????????????????????
? day ? name  ? RN ? N ?
????????????????????????
?   1 ? A     ?  1 ? 2 ?
?   2 ? A     ?  1 ? 2 ?
?   3 ? BcDeF ?  1 ? 2 ?
????????????????????????
Run Code Online (Sandbox Code Playgroud)

按照@ AndriyM的评论,如果你想在整个结果集上使用相同的大小写,而不是在同一天,你可以使用:

WITH CTE AS
(
    SELECT  *,
            RN = ROW_NUMBER() OVER(PARTITION BY [day], [name]
                                   ORDER BY [name] COLLATE SQL_Latin1_General_Cp1_Cs_AS),
            N = COUNT(*) OVER(PARTITION BY [day], [name])
    FROM (  select 1 as [day], 'a' as [name]
            union all select 1, 'A'
            union all select 2, 'A'
            union all select 2, 'a'
            union all select 3, 'BcDeF'
            union all select 3, 'bCdEf') X
)
SELECT  [day],
        MAX([name] COLLATE SQL_Latin1_General_Cp1_CS_AS) OVER (PARTITION BY [name]) [name],
        N
FROM CTE
WHERE RN = 1;
Run Code Online (Sandbox Code Playgroud)