2016-09-22 46 views
6

在配置爲不區分大小寫的SQL Server上,當[n][var]char列不是第一個group by列時,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] 

返回,對我來說:

day   name count 
----------- ---- ----------- 
1   A 2 
2   a 2 

使用min(x.[name])沒有任何效果,因爲分組已經發生了。

我不能在之前添加order by,因爲這是非法的;並在之後添加order bygroup by只是定義了分組後的輸出順序 - 它仍然給出aA

所以:在這種情況下,是否有一種合理的方式來實現這一點,即所有分組的大小都至少保持一致? (我會離開的另一天被單獨運行相一致的問題)

所需的輸出,無論是:

day   name count 
----------- ---- ----------- 
1   A 2 
2   A 2 

或:

day   name count 
----------- ---- ----------- 
1   a 2 
2   a 2 

編輯:沒有在團體之間一致時摧毀大寫字母。所以沒有上/下。因此,如果其中一個組始終具有值BcDeF,我希望該行的結果爲BcDeF,而不是bcdefBCDEF

+2

(1,'A'),(2,'a')',你會喜歡什麼輸出? (1,'A'),(2,'a'),(3,'A')'? (1,'A'),(1,'a'),(2,'A'),(2,'a')'? –

+0

@AndriyM優秀的問題;也許這只是不可能在DB層解決;我想天真地我在想「無論是全部'a'還是全部'第二列',但... –

+0

爲了完整起見,我最終通過對返回的結果進行歸一化,最終在調用站點」修復「了這一點。窗口函數的方法可能是最接近我需要的,但是*在我的場景中*(運行時生成的任意複雜的報告查詢)實現起來並不實際。 –

回答

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; 

它返回:

╔═════╦═══════╦════╦═══╗ 
║ day ║ name ║ RN ║ N ║ 
╠═════╬═══════╬════╬═══╣ 
║ 1 ║ A  ║ 1 ║ 2 ║ 
║ 2 ║ A  ║ 1 ║ 2 ║ 
║ 3 ║ BcDeF ║ 1 ║ 2 ║ 
╚═════╩═══════╩════╩═══╝ 

繼@ 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; 
2

使用upper()lower()

select x.[day], lower(x.[name]) as 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]; 

你是正確的SQL Server選擇從一個不確定行的值。 min()max()沒有幫助,因爲這些值是等價的。最簡單的解決方案是明確選擇您想要的案例。

+0

你不需要'group by x。[一天],較低(x。[姓名])'? – jarlh

+0

我的意思是不破壞大小寫,當它是一致的跨組,對不起:我應該更清楚 –

0

您可以在GROUP BY子句中使用UPPER將所有值轉換爲相同的大寫。

+0

我的意思是不破壞大小寫,當它是一致的跨組,對不起:我應該更清楚 –

2

使用不區分大小寫的排序規則在Group by,e.g:

select day, name, count(*) 
from tablename 
group by day, name collate SQL_Latin1_General_Cp1_CI_AS_KI_WI 

也許SQL Server的這裏有問題?使用另一個dbms,它執行如下:

SQL>create table t (d int, name varchar(10)); 
SQL>insert into t values (1,'A'); 
SQL>insert into t values (2,'A'); 
SQL>insert into t values (2,'a'); 
SQL>insert into t values (3,'BcDeF'); 
SQL>insert into t values (3,'bCdEf'); 
SQL>insert into t values (4,'a'); 
SQL>select d, name, count(*) 
SQL&from t 
SQL&group by d, name collate english_1; 
      d name 
=========== ========== ==================== 
      1 A        1 
      2 A        2 
      3 BcDeF       2 
      4 a        1 

        4 rows found 

其中,english_1是不區分大小寫的排序規則。

如預期的那樣?

+0

我的意思是不破壞大寫時它是一致的跨組,對不起:我應該更清楚 –

+0

現在編輯!試試看。 – jarlh

+1

我得到'無效的排序規則'SQL_Latin1_General_Cp1_CI_AS_KI_WI'' - 但我不明白這有助於:整個服務器已經不區分大小寫。我可以看到如何改變*區分大小寫*會改變結果,但是我會得到一個不同的值的分組,這不是我想要的:) –