Поведение уникального индекса, столбца varchar и (пустых) пробелов - PullRequest
9 голосов
/ 27 февраля 2012

Я использую Microsoft SQL Server 2008 R2 (с последним пакетом обновлений / исправлений), и сортировка базы данных SQL_Latin1_General_CP1_CI_AS.

Следующий код:

SET ANSI_PADDING ON;
GO

CREATE TABLE Test (
   Code VARCHAR(16) NULL
);
CREATE UNIQUE INDEX UniqueIndex
    ON Test(Code);

INSERT INTO Test VALUES ('sample');
INSERT INTO Test VALUES ('sample ');

SELECT '>' + Code + '<' FROM Test WHERE Code = 'sample        ';
GO

дает следующие результаты:

(затронут 1 ряд)

Сообщение 2601, Уровень 14, Состояние 1, Строка 8

Невозможно вставить строку повторяющегося ключа в объект 'dbo.Test' с уникальным индексом 'UniqueIndex'. Дубликат значения ключа (образец).

Оператор был прекращен.

------------

> образец <</p>

(затронут 1 ряд)

Мои вопросы:

  1. Я предполагаю, что индекс не может хранить конечные пробелы. Может кто-нибудь указать мне официальную документацию, которая определяет / определяет это поведение?
  2. Есть ли параметр, чтобы изменить это поведение, то есть заставить его распознавать 'sample' и 'sample' как два разных значения (какими они, кстати, являются), так что оба могут быть в индексе.
  3. С какой стати SELECT возвращает строку? SQL Server должен делать что-то действительно смешное / умное с пробелами в предложении WHERE, потому что если я уберу уникальность в индексе, оба INSERT будут работать нормально, а SELECT вернет две строки!

Буду признателен за любую помощь / указатель в правильном направлении. Спасибо.

1 Ответ

13 голосов
/ 27 февраля 2012

Объяснение завершающих пробелов :

SQL Server соответствует спецификации ANSI / ISO SQL-92 (раздел 8.2, Общие правила № 3) о том, как сравнить строки с пробелами. Стандарт ANSI требует заполнения для символа строки, используемые в сравнениях, так что их длины совпадают до сравнивая их. Отступ напрямую влияет на семантику WHERE и предикаты предложения HAVING и другие строки Transact-SQL сравнения. Например, Transact-SQL считает строки 'abc' и 'abc' должно быть эквивалентно большинству операций сравнения.

Единственным исключением из этого правила является предикат LIKE. Когда право сторона предикатного выражения LIKE имеет значение с конечным SQL Server не дополняет два значения одинаковой длины до сравнения Потому что цель как Предикат, по определению, предназначен для облегчения поиска по шаблону чем простые тесты на равенство строк, это не нарушает раздел упомянутой ранее спецификации ANSI SQL-92.

Вот хорошо известный пример всех случаев, упомянутых выше:

DECLARE @a VARCHAR(10)
DECLARE @b varchar(10)

SET @a = '1'
SET @b = '1 ' --with trailing blank

SELECT 1
WHERE 
    @a = @b 
AND @a NOT LIKE @b
AND @b LIKE @a

Вот еще несколько подробностей о конечных пробелах и предложении LIKE .

Относительно индексов:

Вставка в столбец, значения которого должны быть уникальными, завершится неудачно, если вы предоставите значение, которое отличается от существующих значений только конечные пробелы. Следующие строки будут все рассмотрены эквивалентно уникальному ограничению, первичному ключу или уникальному индексу. Аналогично, если у вас есть существующая таблица с данными ниже, и попытайтесь добавить уникальное ограничение, он потерпит неудачу, потому что значения считается идентичным.

PaddedColumn
------------
'abc'
'abc '
'abc  '
'abc    '

(взято с здесь .)

...