Переходячи від SQL 2005 [SQL_Latin1_General_CP1_CI_AS] до 2008 року - я втрачу будь-які функції, використовуючи "зворотну сумісність"


18

Ми переходимо від SQL 2005 [Ідентифікатор і БД зіставлено SQL_Latin1_General_CP1_CI_AS] до SQL 2008 [який за замовчуванням Latin1_General_CI_AS].

Я завершив установку SQL 2008 R2 і застосував за замовчуванням Latin1_General_CI_ASзіставлення з відновленням бази даних SQL_Latin1_General_CP1_CI_AS. Вирішені проблеми виникли - таблиці #temp, де в Latin1_General_CI_ASтой час був db, SQL_Latin1_General_CP1_CI_ASі ось де я зараз - мені потрібна порада щодо підводних каменів, будь ласка.

На установці SQL 2008 R2, у мене є можливість по установці для використання , 'SQL Collation, used for backwards compatibility'де у мене є можливість вибрати один і той же порядок зіставлення в якості бази даних 2005: Додати SQL_Latin1_General_CP1_CI_AS.

  1. Це дозволить мені не мати проблем із таблицями #temp, але чи існують підводні камені?

  2. Чи втратив би я будь-які функціональні можливості чи функції, не використовуючи "поточне" порівняння SQL 2008?

  3. Що робити, коли ми переходимо (наприклад, через 2 роки) з 2008 року на SQL 2012? У мене тоді будуть проблеми?
  4. Чи мене в якийсь момент змусять піти Latin1_General_CI_AS?

  5. Я читав, що деякі сценарії DBA заповнюють рядки повних баз даних, а потім запустіть сценарій вставки в базу даних з новим зіставленням - я дуже боюся і з цим насторожуюсь - рекомендуєте ви це робити?


2
Якщо ви думаєте, що можете потрапити в Хекатон у SQL Server 2014, ось ще щось, що ви можете розглянути , читайте .
Аарон Бертран

Відповіді:


20

Перш за все, вибачте за таку довгу відповідь, оскільки я відчуваю, що все ще існує велика плутанина, коли люди говорять про такі терміни, як зіставлення, порядок сортування, кодова сторінка тощо.

Від BOL :

Збірники на SQL Server надають правила сортування, регістри та чутливості до акцентів для ваших даних . Збірники, які використовуються для типів даних символів, таких як char та varchar, диктують кодову сторінку та відповідні символи, які можуть бути представлені для цього типу даних. Незалежно від того, чи встановлюєте ви новий екземпляр SQL Server, відновлюєте резервне копіювання бази даних або підключаєте сервер до клієнтських баз даних, важливо, щоб ви розуміли вимоги локалі, порядок сортування, чутливість до регістру та акценти даних, з якими ви будете працювати .

Це означає, що Collation є дуже важливим, оскільки він визначає правила щодо сортування та порівняння символьних рядків даних.

Примітка: Більше інформації про КОЛОТАЦІЮ

Тепер давайте спочатку зрозуміємо відмінності ......

Запуск нижче T-SQL:

SELECT *
FROM::fn_helpcollations()
WHERE NAME IN (
        'SQL_Latin1_General_CP1_CI_AS'
        ,'Latin1_General_CI_AS'
        )
GO

SELECT 'SQL_Latin1_General_CP1_CI_AS' AS 'Collation'
    ,COLLATIONPROPERTY('SQL_Latin1_General_CP1_CI_AS', 'CodePage') AS 'CodePage'
    ,COLLATIONPROPERTY('SQL_Latin1_General_CP1_CI_AS', 'LCID') AS 'LCID'
    ,COLLATIONPROPERTY('SQL_Latin1_General_CP1_CI_AS', 'ComparisonStyle') AS 'ComparisonStyle'
    ,COLLATIONPROPERTY('SQL_Latin1_General_CP1_CI_AS', 'Version') AS 'Version'

UNION ALL

SELECT 'Latin1_General_CI_AS' AS 'Collation'
    ,COLLATIONPROPERTY('Latin1_General_CI_AS', 'CodePage') AS 'CodePage'
    ,COLLATIONPROPERTY('Latin1_General_CI_AS', 'LCID') AS 'LCID'
    ,COLLATIONPROPERTY('Latin1_General_CI_AS', 'ComparisonStyle') AS 'ComparisonStyle'
    ,COLLATIONPROPERTY('Latin1_General_CI_AS', 'Version') AS 'Version'
GO

Результати:

введіть тут опис зображення

Дивлячись на вищезазначені результати, єдина відмінність - це Порядок сортування між двома порівняннями. Але це неправда, яку ви можете зрозуміти, як наведено нижче:

Тест 1:

--Clean up previous query
IF OBJECT_ID('Table_Latin1_General_CI_AS') IS NOT NULL
    DROP TABLE Table_Latin1_General_CI_AS;

IF OBJECT_ID('Table_SQL_Latin1_General_CP1_CI_AS') IS NOT NULL
    DROP TABLE Table_SQL_Latin1_General_CP1_CI_AS;

-- Create a table using collation Latin1_General_CI_AS 
CREATE TABLE Table_Latin1_General_CI_AS (
    ID INT IDENTITY(1, 1)
    ,Comments VARCHAR(50) COLLATE Latin1_General_CI_AS
    )

-- add some data to it 
INSERT INTO Table_Latin1_General_CI_AS (Comments)
VALUES ('kin_test1')

INSERT INTO Table_Latin1_General_CI_AS (Comments)
VALUES ('Kin_Tester1')

-- Create second table using collation SQL_Latin1_General_CP1_CI_AS 
CREATE TABLE Table_SQL_Latin1_General_CP1_CI_AS (
    ID INT IDENTITY(1, 1)
    ,Comments VARCHAR(50) COLLATE SQL_Latin1_General_CP1_CI_AS
    )

-- add some data to it 
INSERT INTO Table_SQL_Latin1_General_CP1_CI_AS (Comments)
VALUES ('kin_test1')

INSERT INTO Table_SQL_Latin1_General_CP1_CI_AS (Comments)
VALUES ('Kin_Tester1')

--Now try to join both tables
SELECT *
FROM Table_Latin1_General_CI_AS LG
INNER JOIN Table_SQL_Latin1_General_CP1_CI_AS SLG ON LG.Comments = SLG.Comments
GO

Результати тесту 1:

Msg 468, Level 16, State 9, Line 35
Cannot resolve the collation conflict between "SQL_Latin1_General_CP1_CI_AS" and "Latin1_General_CI_AS" in the equal to operation.

З наведених вище результатів ми бачимо, що ми не можемо безпосередньо порівнювати значення стовпців з різними зіставленнями, ви повинні використовувати COLLATEдля порівняння значень стовпців.

ТЕСТ 2:

Головною відмінністю є продуктивність, на що Ерланд Соммарського вказує на цій дискусії на msdn .

--Clean up previous query
IF OBJECT_ID('Table_Latin1_General_CI_AS') IS NOT NULL
    DROP TABLE Table_Latin1_General_CI_AS;

IF OBJECT_ID('Table_SQL_Latin1_General_CP1_CI_AS') IS NOT NULL
    DROP TABLE Table_SQL_Latin1_General_CP1_CI_AS;

-- Create a table using collation Latin1_General_CI_AS 
CREATE TABLE Table_Latin1_General_CI_AS (
    ID INT IDENTITY(1, 1)
    ,Comments VARCHAR(50) COLLATE Latin1_General_CI_AS
    )

-- add some data to it 
INSERT INTO Table_Latin1_General_CI_AS (Comments)
VALUES ('kin_test1')

INSERT INTO Table_Latin1_General_CI_AS (Comments)
VALUES ('kin_tester1')

-- Create second table using collation SQL_Latin1_General_CP1_CI_AS 
CREATE TABLE Table_SQL_Latin1_General_CP1_CI_AS (
    ID INT IDENTITY(1, 1)
    ,Comments VARCHAR(50) COLLATE SQL_Latin1_General_CP1_CI_AS
    )

-- add some data to it 
INSERT INTO Table_SQL_Latin1_General_CP1_CI_AS (Comments)
VALUES ('kin_test1')

INSERT INTO Table_SQL_Latin1_General_CP1_CI_AS (Comments)
VALUES ('kin_tester1')

--- Створення індексів на обох таблицях

CREATE INDEX IX_LG_Comments ON  Table_Latin1_General_CI_AS(Comments)
go
CREATE INDEX IX_SLG_Comments ON  Table_SQL_Latin1_General_CP1_CI_AS(Comments)

--- Виконати запити

DBCC FREEPROCCACHE
GO
SELECT Comments FROM Table_Latin1_General_CI_AS WHERE Comments = 'kin_test1'
GO

--- Це матиме IMPLICIT конверсію

введіть тут опис зображення

--- Виконати запити

DBCC FREEPROCCACHE
GO
SELECT Comments FROM Table_SQL_Latin1_General_CP1_CI_AS WHERE Comments = 'kin_test1'
GO

--- Це НЕ матиме ІМПЛІЦІТНОГО перетворення

введіть тут опис зображення

Причина неявного перетворення тому, що у мене є бази даних і сервер сортування і як SQL_Latin1_General_CP1_CI_ASі таблиця Table_Latin1_General_CI_AS має стовпець Коментарі , певні як VARCHAR(50)з COLLATE Latin1_General_CI_AS , тому під час пошуку SQL Server повинен зробити неявне перетворення.

Тест 3:

З тим же налаштуванням ми зараз порівняємо стовпчики varchar зі значеннями nvarchar, щоб побачити зміни в планах виконання.

- запустіть запит

DBCC FREEPROCCACHE
GO
SELECT Comments FROM Table_Latin1_General_CI_AS WHERE Comments =  (SELECT N'kin_test1' COLLATE Latin1_General_CI_AS)
GO

введіть тут опис зображення

- запустіть запит

DBCC FREEPROCCACHE
GO
SELECT Comments FROM Table_SQL_Latin1_General_CP1_CI_AS WHERE Comments = N'kin_test1'
GO

введіть тут опис зображення

Зауважте, що перший запит може виконувати пошук індексу, але повинен виконувати неявне перетворення, тоді як другий виконує сканування індексів, які виявляються неефективними з точки зору продуктивності при скануванні великих таблиць.

Висновок:

  • Всі вищеперелічені тести показують, що правильне зіставлення дуже важливо для екземпляра сервера вашої бази даних.
  • SQL_Latin1_General_CP1_CI_AS є зіставленням SQL з правилами, що дозволяють сортувати дані за unicode та non-unicode.
  • SQL-зіставлення не зможе використовувати Index при порівнянні даних unicode та unicodode, як видно у вищевказаних тестах, що при порівнянні даних nvarchar із варчарськими даними він робить індексне сканування, а не пошук.
  • Latin1_General_CI_AS це зіставлення Windows з правилами, що дозволяють сортувати дані для unicode та non-unicode однакові.
  • Для порівняння Windows все ще можна використовувати Index (Index search у наведеному вище прикладі) при порівнянні даних unicode та unicodode, але ви бачите невелику штрафну ефективність.
  • Настійно рекомендую прочитати відповідь Ерланда Соммарського + підключити елементи, на які він вказав.

Це дозволить мені не мати проблем із таблицями #temp, але чи існують підводні камені?

Дивіться мою відповідь вище.

Чи втратив би я будь-які функціональні можливості чи функції, не використовуючи "поточне" порівняння SQL 2008?

Все залежить від того, які функціональні можливості / функції ви маєте на увазі. Збір - це зберігання та сортування даних.

Що робити, коли ми переходимо (наприклад, через 2 роки) з 2008 року на SQL 2012? У мене тоді будуть проблеми? Мене в якийсь момент змусять перейти до Latin1_General_CI_AS?

Не може поручити! Оскільки все може змінитися і завжди добре узгоджуватись із пропозицією Microsoft + вам потрібно зрозуміти свої дані та підводні камені, про які я згадував вище. Також зверніться до цього та цього підключення елементів.

Я читав, що деякі сценарії DBA заповнюють рядки повних баз даних, а потім запустіть сценарій вставки в базу даних з новим зіставленням - я дуже боюся і з цим насторожуюсь - рекомендуєте ви це робити?

Коли ви хочете змінити зіставлення, такі сценарії корисні. Я виявив, що я багато разів змінював зіставлення баз даних, щоб вони відповідали збірці сервера, і у мене є кілька сценаріїв, що робить це досить акуратно. Дайте мені знати, якщо вам це потрібно.

Список літератури:


5

На додаток до того, що @Kin детально описав у своїй відповіді , є ще кілька речей, про які слід пам’ятати, перемикаючи порівняння сервера (тобто екземпляра) за замовчуванням (елементи над горизонтальною лінією мають безпосереднє відношення до двох порівнянь, згаданих у Питання; Пункти; нижче горизонтальної лінії мають відношення до загальної):

  • ЯКЩО ЗАВДАННЯ СВІДУВАННЯ ВАШИХ ДАНИХ НЕ ЗМІНЕНО , то проблема з неявною конверсією, описана у відповіді @ Kin, не повинна бути проблемою, оскільки рядкові літерали та локальні змінні використовують Збір за замовчуванням Бази даних, а не сервер. Єдині наслідки для сценарію, при якому зіставлення рівня екземпляра змінюється, але не рівень рівня баз даних зіставлення (обидва детально описані нижче):

    • потенційне зіставлення суперечить тимчасовим таблицям (але не табличним змінним).
    • потенційний зламаний код, якщо корпус змінних та / або курсорів не відповідає їхнім деклараціям (але це може статися лише при переході до екземпляра з бінарним або залежним від регістру зіставленням).
  • Одна відмінність цих двох зібрань полягає в тому, як вони сортують певні символи для VARCHARданих (це не впливає на NVARCHARдані). SQL_Колекції, що не є EBCDIC, використовують дані, що називаються "Сортування рядків" для VARCHARданих, тоді як усі інші зібрання, і навіть NVARCHARдані для SQL_зібрань, що не є EBCDIC , використовують те, що називається "Сортування слів". Різниця полягає в тому, що в "Сортуванні слів" тире -та апостроф '(а може, кілька інших символів?) Надаються дуже низькою вагою і по суті ігноруються, якщо немає інших відмінностей у рядках. Щоб побачити таку поведінку в дії, виконайте наступне:

    DECLARE @Test TABLE (Col1 VARCHAR(10) NOT NULL);
    INSERT INTO @Test VALUES ('aa');
    INSERT INTO @Test VALUES ('ac');
    INSERT INTO @Test VALUES ('ah');
    INSERT INTO @Test VALUES ('am');
    INSERT INTO @Test VALUES ('aka');
    INSERT INTO @Test VALUES ('akc');
    INSERT INTO @Test VALUES ('ar');
    INSERT INTO @Test VALUES ('a-f');
    INSERT INTO @Test VALUES ('a_e');
    INSERT INTO @Test VALUES ('a''kb');
    
    SELECT * FROM @Test ORDER BY [Col1] COLLATE SQL_Latin1_General_CP1_CI_AS;
    -- "String Sort" puts all punctuation ahead of letters
    
    SELECT * FROM @Test ORDER BY [Col1] COLLATE Latin1_General_100_CI_AS;
    -- "Word Sort" mostly ignores dash and apostrophe

    Повернення:

    String Sort
    -----------
    a'kb
    a-f
    a_e
    aa
    ac
    ah
    aka
    akc
    am
    ar

    і:

    Word Sort
    ---------
    a_e
    aa
    ac
    a-f
    ah
    aka
    a'kb
    akc
    am
    ar

    Хоча ви "втратите" поведінку "Сортування рядків", я не впевнений, що я б назвав це "функцією". Це поведінка, яку визнали небажаною (про що свідчить той факт, що вона не була висунута в жодне зі збірок Windows). Тим НЕ менше, це певна різниця в поведінці між двома (знову сортування, тільки для не-EBCDIC VARCHARданих), і ви можете мати код і / або клієнта очікування , засновані на «Струнний Sort» поведінку. Для цього потрібно перевірити свій код і, можливо, вивчити, чи може ця зміна поведінки мати негативний вплив на користувачів.

  • Ще одна відмінність SQL_Latin1_General_CP1_CI_ASі Latin1_General_100_CI_AS- це можливість робити розширення для VARCHARданих ( NVARCHARдані вже можуть робити це для більшості SQL_зібрань), наприклад, обробляти æтак, ніби це ae:

    IF ('æ' COLLATE SQL_Latin1_General_CP1_CI_AS =
        'ae' COLLATE SQL_Latin1_General_CP1_CI_AS)
    BEGIN
      PRINT 'SQL_Latin1_General_CP1_CI_AS';
    END;
    
    IF ('æ' COLLATE Latin1_General_100_CI_AS =
        'ae' COLLATE Latin1_General_100_CI_AS)
    BEGIN
      PRINT 'Latin1_General_100_CI_AS';
    END;

    Повернення:

    Latin1_General_100_CI_AS

    Єдине, що ви тут «втрачаєте» - це не в змозі зробити ці розширення. Взагалі кажучи, це ще одна перевага переходу до Windows Collation. Однак, як і при русі "Сортування рядків" до "Сортування слів", застосовується така ж обережність: це певна різниця в поведінці між двома порівняннями (знову ж таки, лише для VARCHARданих), і у вас може бути код та / або клієнт очікування , засновані на НЕ мають ці відображення. Для цього потрібно перевірити свій код і, можливо, вивчити, чи може ця зміна поведінки мати негативний вплив на користувачів.

    (вперше зазначено у цій відповіді ТА на @Zarepheth: Чи може SQL Server SQL_Latin1_General_CP1_CI_AS бути безпечно перетворений на Latin1_General_CI_AS? )

  • Порівнювання на рівні сервера використовується для встановлення порівняння системних баз даних, що включає [model]. База [model]даних використовується як шаблон для створення нових баз даних, що включає [tempdb]при кожному запуску сервера. Але навіть при зміні рівняння на рівні сервера, що змінює зіставлення [tempdb], існує дещо простий спосіб виправити відмінності порівняння між базою даних, яка є "поточною" при CREATE #TempTableїї виконанні та [tempdb]. Створюючи тимчасові таблиці, оголосіть порівняння за допомогоюCOLLATE пункту та вкажіть порівняння DATABASE_DEFAULT:

    CREATE TABLE #Temp (Col1 NVARCHAR(40) COLLATE DATABASE_DEFAULT);

  • Найкраще використовувати найновішу версію потрібного порівняння, якщо є кілька версій. Починаючи з SQL Server 2005, було введено серію зібрань "90", а SQL Server 2008 представив серію зіставлень "100". Ви можете знайти ці порівняння, скориставшись наступними запитами:

    SELECT * FROM sys.fn_helpcollations() WHERE [name] LIKE N'%[_]90[_]%'; -- 476
    
    SELECT * FROM sys.fn_helpcollations() WHERE [name] LIKE N'%[_]100[_]%'; -- 2686

    Оскільки ви перебуваєте на SQL Server 2008 R2, вам слід використовувати Latin1_General_100_CI_ASзамість Latin1_General_CI_AS.

  • Різниця між великими регістровими версіями цих конкретних зіставлень (тобто SQL_Latin1_General_CP1_CS_ASі Latin1_General_100_CS_AS) полягає в порядку великої та малої літери під час сортування з урахуванням регістру. Це також впливає на однозначні діапазони класів (тобто [start-end]), які можна використовувати разом з LIKEоператором і PATINDEXфункцією. Наступні три запити показують цей ефект як для сортування, так і для діапазону символів:

    SELECT tmp.col AS [Upper-case first]
    FROM (VALUES ('a'), ('A'), ('b'), ('B'), ('c'), ('C')) tmp(col)
    WHERE tmp.col LIKE '%[A-C]%' COLLATE SQL_Latin1_General_CP1_CS_AS
    ORDER BY tmp.col COLLATE SQL_Latin1_General_CP1_CS_AS; -- Upper-case first
    
    SELECT tmp.col AS [Lower-case first]
    FROM (VALUES ('a'), ('A'), ('b'), ('B'), ('c'), ('C')) tmp(col)
    WHERE tmp.col LIKE '%[A-C]%' COLLATE Latin1_General_100_CS_AS
    ORDER BY tmp.col COLLATE Latin1_General_100_CS_AS; -- Lower-case first
    
    SELECT tmp.col AS [Lower-case first]
    FROM (VALUES (N'a'), (N'A'), (N'b'), (N'B'), (N'c'), (N'C')) tmp(col)
    WHERE tmp.col LIKE N'%[A-C]%' COLLATE SQL_Latin1_General_CP1_CS_AS
    ORDER BY tmp.col COLLATE SQL_Latin1_General_CP1_CS_AS; -- Lower-case first

    Єдиний спосіб встановити верхній регістр для сортування перед малим регістром (для однієї і тієї ж літери) - використовувати одне з 31 зібрання, яке підтримує таку поведінку, а саме " Hungarian_Technical_*Збірки" та "Жменька SQL_зібрань" (які підтримують цю поведінку лише для VARCHARданих) ).

  • Менш важливе значення для цієї конкретної зміни, але все-таки добре знати, оскільки це може вплинути на зміну сервера на бінарне чи залежне від регістру порівняння, полягає в тому, що порівняння рівня сервера також впливає на:

    • локальні назви змінних
    • Імена КУРСОРУ
    • GOTO етикетки
    • роздільна здатність імені sysnameтипу даних


    Значить, якщо ви або "програміст, який нещодавно пішов", який, мабуть, відповідав за всі погані коди ;-) не були обережні щодо корпусу і оголосили змінну як, @SomethingIDале потім посилалися на неї як @somethingIdпізніше, це порушиться, якщо перейти до справи -чутливе або бінарне порівняння. Аналогічно, код, який використовує sysnameтип даних, але посилається на нього як SYSNAME, SysNameабо щось інше, ніж усі малі регістри, також порушиться, якщо переміститься до екземпляра, використовуючи чутливий до регістру чи двійковий порівняння.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.