2012-02-27 3 views
8

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

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

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 строку (ы) пострадавших)

Msg 2601, уровень 14, состояние 1, строка 8

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

Заявление было прекращено.

‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐

> образец <

(1 строка (ы) пострадавших)

Мой вопрос s:

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

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

ответ

11

Trailing blanks explained:

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

Единственным исключением из этого правила является предикат LIKE.Когда правая сторона предиката выражения LIKE имеет значение с конечным пространством , SQL Server не заполняет два значения до той же длины , прежде чем произойдет сравнение. Поскольку целью предиката LIKE по определению является упрощение поиска шаблонов, а не , чем простые тесты равенства строк, это не нарушает раздел упомянутой ранее спецификации 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 

Вот еще некоторые детали о trailing blanks and the LIKE clause.

Что касается индексов:

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

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

(Взято из here.)

+2

Спасибо за указатели, ребята. Mea culpa за то, что я слишком ленив к Google. На мой взгляд, поведение, определенное стандартом, не является интуитивным. Я бы предположил, что 9 из 10 разработчиков скажут, что «a» и «a» не являются одной и той же строкой, но хорошо. – Eric

Смежные вопросы