Группа по регистру чувствительна к регистру в T-SQL, хотя сопоставления db и сервера являются CI

Я не нашел никакой документации, которая объясняет следующее поведение, как db, так и уровень сервера - CI (нечувствительный к регистру), почему он по-прежнему чувствителен к регистру в этом аспекте?

--Works
SELECT CASE name WHEN 'a' THEN 'adam' ELSE 'bertrand' END AS name, COUNT(value) FROM
(
SELECT 'a' AS name,1 AS value
UNION
SELECT 'b',1
UNION
SELECT 'b',2
)a
GROUP BY CASE name WHEN 'a' THEN 'adam' ELSE 'bertrand' END

--Returns an Error Message, please note the "B" in Bertrand in the GROUP BY
SELECT CASE name WHEN 'a' THEN 'adam' ELSE 'bertrand' END name, COUNT(value) FROM
(
SELECT 'a' AS name,1 AS value
UNION
SELECT 'b',1
UNION
SELECT 'b',2
)a
GROUP BY CASE name WHEN 'a' THEN 'adam' ELSE 'Bertrand' END

Второй запрос возвращает это сообщение об ошибке.

Msg 8120, уровень 16, состояние 1, строка 2

Столбец "a.name" недопустим в списке выбора, потому что он не содержится ни в агрегатной функции, ни в предложении GROUP BY.

Ответ 1

Это расширенный комментарий, который дает реальный ответ.

Я считаю, что эта проблема исходит из того, как SQL Server пытается оценить выражение выражения case.

Чтобы доказать, что сервер неактивен, вы можете запустить следующие два оператора

SELECT CASE WHEN 'Bertrand' = 'bertrand' THEN 'true' ELSE 'false' end

-

DECLARE @base TABLE
(NAME VARCHAR(1)
,value INT
)
INSERT INTO @base Values('a',0),('b',0),('B',0)

SELECT * FROM @base

SELECT name, COUNT(value) AS Cnt
FROM @base
GROUP BY NAME

результаты:

enter image description here

как вы можете видеть здесь, даже если буква во второй строке имеет нижний регистр, а в третьей строке - верхний регистр, а команда group by игнорирует случай. При взгляде на план выполнения есть два выражения для

Expr 1007 COUNT([value])    
Expr 1004 CONVERT_IMPLICIT(int,[Expr1007],0)    

теперь, когда мы меняем его на case

SELECT CASE WHEN name = 'a' THEN 'adam' ELSE 'bertrand' END AS name, COUNT(value) AS Cnt
FROM @base
GROUP BY CASE WHEN name = 'a' THEN 'adam' ELSE 'bertrand' END

план выполнения показывает 3 выражения. 2 сверху и новый

Expr 1004 CASE WHEN [NAME]='a' THEN 'adam' ELSE 'bertrand' END  

поэтому в этот момент агрегированная функция больше не оценивает значение столбца name, а теперь оценивает значение выражения.

То, что я думаю, происходит, может быть неправильным. Когда SQL-сервер преобразует оператор case в выражении SELECT и GROUP BY в выражение, он имеет разное значение выражения. В этом случае вы также можете сделать 'bertrand' в SELECT и 'charlie' в выражении GROUP BY, потому что если выражение case не соответствует 100% между предложением select и group by, SQL Server будет рассматривать их как разные Expr aka (columns), которые больше не совпадают.


Update:

Чтобы сделать этот шаг дальше, следующий оператор также потерпит неудачу.

SELECT CASE WHEN name = 'a' THEN 'adam' ELSE UPPER('bertrand') END AS name
    ,COUNT(value) AS Cnt
FROM @base
GROUP BY CASE WHEN name = 'a' THEN 'adam' ELSE UPPER('Bertrand') END

Даже перенос различных строк case в UPPER(), SQL Server по-прежнему не может обработать его.

Ответ 2

Проблема заключается в том, что оператор в select и group by должен быть таким же, но вы можете написать свой запрос ниже

select name, count(value) from
(
    SELECT CASE name WHEN 'a' THEN 'adam' ELSE 'bertrand' END name, value FROM
        (
        SELECT 'a' AS name,1 AS value
        UNION
        SELECT 'b',1
        UNION
        SELECT 'b',2
        )a
    )t
GROUP BY name

Это не касается чувствительности к случаю, взгляните на этот образец

This works

SELECT CASE name WHEN 'a' THEN 'adam' when 'b' then 'bertrand' end name, COUNT(value) FROM
(
SELECT 'a' AS name,1 AS value
UNION
SELECT 'b',1
UNION
SELECT 'b',2
)a
GROUP BY CASE name WHEN 'a' THEN 'adam' when 'b' then 'bertrand' END


This does not work
SELECT CASE name when 'b' then 'bertrand' WHEN 'a' THEN 'adam' end name, COUNT(value) FROM
(
SELECT 'a' AS name,1 AS value
UNION
SELECT 'b',1
UNION
SELECT 'b',2
)a
GROUP BY CASE name WHEN 'a' THEN 'adam' when 'b' then 'bertrand' END

В то время как случай 'adam' и 'bertrand' совпадают.

Ответ 3

Вы нашли нечто, что действительно странно, но я думаю, что проблема заключается в том, что вы используете оператор case вообще в заявлении группы. Это должно быть:

SELECT CASE name WHEN 'a' THEN 'adam' ELSE 'bertrand' END AS name   FROM
(
SELECT 'a' AS name,1 AS value
UNION
SELECT 'b',1
UNION
SELECT 'b',2
) a
GROUP BY name

Группа должна применяться ко всей таблице, а не к отдельным строкам. Мне не хватало какой-то причине для этого, но я не думаю, что имеет смысл условно группировать значение.

Я больше удивляюсь, что первый работает вообще, а второй не работает. Сравнение "a" = "A" существенно отличается от сравнения столбца с другим столбцом. SQL-сервер, похоже, не использует настройки сортировки в этой проверке, чтобы увидеть, находится ли столбец в группе. Сообщение об ошибке, которое вы получаете от второго запроса, говорит: "Этот столбец в элементе выбора не совпадает с столбцом в группе по", а не "эти значения не равны".

Ответ 4

Определить не работает
На втором я получаю синтаксическую ошибку

Msg 8120, уровень 16, состояние 1, строка 1 Столбец 'a.name' недействителен в выберите список, потому что он не содержится ни в агрегате или предложение GROUP BY.

В этот момент он даже не пытался обработать запрос
Почему вы настаиваете на синтаксической ошибке, потому что CI не удостоился

Это синтаксическая ошибка - не ошибка выполнения
Выбор должен соответствовать группе с помощью Поэтому по какой-то причине анализатор SQL требует совпадающего кода Важно то, как он обрабатывает запрос

Если бы у вас были чувствительные к регистру таблицы, вы бы ожидали, что TSQL потребует регистров, чувствительных к регистру, и имен таблиц?

Эта работа для меня
адам суммируется до 6 он чтит как нижний (а), так и верхний регистр (А)

Это демонстрирует, что эта группа не зависит от случая Подумайте, как объяснить?

SELECT CASE a.name WHEN 'a' THEN 'adam' ELSE 'bertrand' END AS name, sum(value)
from 
(
SELECT 'a' AS name,1 AS value
UNION
SELECT 'a',2
UNION
SELECT 'A',3
UNION
SELECT 'b',10
UNION
SELECT 'b',20
UNION
SELECT 'B',30
) as a
GROUP BY CASE name WHEN 'a' THEN 'adam' ELSE 'bertrand' END 

и этот признак нечувствителен к регистру

SELECT CASE a.name WHEN 'A' THEN 'adam' ELSE 'Bertrand' END AS name, sum(value)
from 
(
SELECT 'a' AS name,1 AS value
UNION
SELECT 'a',2
UNION
SELECT 'A',3
UNION
SELECT 'b',10
UNION
SELECT 'b',20
UNION
SELECT 'B',30
) as a
GROUP BY CASE name WHEN 'A' THEN 'adam' ELSE 'Bertrand' END

Ответ 5

Это не ответ, а комментарий, который требует большего количества места и возможностей, чем предлагает раздел комментариев.

Если основная проблема заключается в том, чтобы свести к минимуму риск сделать такую ​​ошибку в ваших запросах из-за необходимости поддерживать сложное выражение как в GROUP BY, так и в SELECT, вы могли бы избежать повторения в первую очередь, используя CROSS APPLY. Вместо

SELECT SomeComplexExpression, SomeAggregation
FROM ...
GROUP BY SomeComplexExpression;

вы могли бы сделать

SELECT x.GroupingCriterion, SomeAggregation
FROM ...
CROSS APPLY (SELECT SomeComplexExpression AS GroupingCriterion) AS x
GROUP BY x.GroupingCriterion;

и теперь вам нужно будет поддерживать сложное выражение группировки в одном месте.

Надеюсь, вы используете SQL Server 2005 или более позднюю версию, чтобы использовать этот метод. В противном случае вы всегда можете использовать производную таблицу:

SELECT GroupingCriterion, SomeAggregation
FROM (
  SELECT SomeComplexExpression AS GroupingCriterion, OtherData
  FROM ...
) AS s
GROUP BY GroupingCriterion;

Последний запрос менее изящный, но тот же результат достигается: выражение определяется только один раз.