从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。发生了例外的问题- Latin1_General_CI_AS数据库在 其中的#temp表所在的位置 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. 当我们从2008年迁移到SQL 2012时(例如,在2年内)怎么办?那我有问题吗?
  4. 我会在某个时候被迫去Latin1_General_CI_AS吗?

  5. 我读到一些DBA的脚本完成了完整数据库的行,然后使用新的排序规则将插入脚本运行到数据库中-我对此感到非常害怕和警惕-您会建议这样做吗?


2
如果您认为您可以在SQL Server 2014中使用Hekaton,可以考虑阅读以下内容
亚伦·伯特兰

Answers:


20

首先,对这么长的答案表示歉意,因为我觉得当人们谈论排序规则,排序顺序,代码页等术语时,仍然存在很多困惑。

BOL

SQL Server中的归类为数据提供排序规则,大小写和重音敏感性属性。与字符数据类型(例如char和varchar)一起使用的排序规则规定了代码页以及可以为该数据类型表示的相应字符。无论您是要安装SQL Server的新实例,还原数据库备份还是将服务器连接到客户端数据库,理解您要使用的数据的语言环境要求,排序顺序以及大小写和重音敏感性都非常重要。 。

这意味着归类非常重要,因为它指定了有关如何对数据字符串进行排序和比较的规则。

注意:有关COLLATIONPROPERTY的更多信息

现在让我们首先了解差异......

在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

结果将是:

在此处输入图片说明

从上面的结果来看,唯一的区别是2个排序规则之间的排序顺序,但这是不正确的,您可以看到为什么如下:

测试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:

正如Erland Sommarskog 在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

---这将具有隐式转换

在此处输入图片说明

---运行查询

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和非Unicode数据进行排序。
  • 如上面的测试所示,在将unichar和非unicode数据进行比较时,SQL归类将无法使用Index,在将nvarchar数据与varchar数据进行比较时,它会进行索引扫描而不是查找。
  • Latin1_General_CI_AS 是Windows排序规则的排序规则,允许您对Unicode和非Unicode相同的数据进行排序。
  • 在比较unicode和非unicode数据时,Windows排序规则仍可以使用Index(在上面的示例中为Index seek),但是您会发现性能略有下降。
  • 强烈建议阅读Erland Sommarskog的答案以及他所指向的连接项。

这将使我在#temp表上没有问题,但是有陷阱吗?

请参阅上面的答案。

如果不使用SQL 2008的“当前”排序规则,是否会丢失任何类型的功能或特性?

这完全取决于您所指的功能。整理是存储和排序数据。

当我们从2008年迁移到SQL 2012时(例如,在2年内)怎么办?那我有问题吗?我会在某个时候被迫去Latin1_General_CI_AS吗?

不能担保!由于情况可能会发生变化,并且与Microsoft的建议保持一致总是很有益的,因此您需要了解您的数据和我上面提到的陷阱。也可参考这个这个连接项目。

我读到一些DBA的脚本完成了完整数据库的行,然后使用新的排序规则将插入脚本运行到数据库中-我对此感到非常害怕和警惕-您会建议这样做吗?

当您要更改排序规则时,此类脚本很有用。我发现自己多次更改数据库排序规则以匹配服务器排序规则,并且我有一些脚本可以使它整洁。让我知道你是否需要它。

参考文献:


5

除了@Kin在回答中详述的内容外,切换服务器(即实例)的默认排序规则时还需要注意一些其他事项(水平线上方的项目与Question中提到的两个排序规则直接相关;水平线以下与一般相关):

  • 如果你的数据库的默认排序规则是发生变化,那么“隐式转换”性能问题@健的回答应该描述不是因为字符串文字和局部变量使用数据库的默认排序规则,而不是服务器的一个问题。更改实例级别排序规则而不更改数据库级别排序规则的情况的唯一影响是(均在下面进行了详细描述):

    • 与临时表(而不是表变量)潜在的排序规则冲突。
    • 如果变量和/或游标的大小写与声明不匹配,则可能会破坏代码(但这仅在移动到具有二进制或区分大小写的排序规则的实例时才会发生)。
  • 这两种归类之间的区别在于它们如何对VARCHAR数据的某些字符进行排序(这不会影响NVARCHAR数据)。非EBCDIC SQL_归类使用所谓的“字符串排序”作为VARCHAR数据,而所有其他归类,甚至NVARCHAR非EBCDIC SQL_归类使用的数据也使用“单词排序”。区别在于,在“单词排序”中,破折号-和撇号'(也许还有其他几个字符?)的权重非常低,除非字符串中没有其他区别,否则它们将被忽略。要查看此行为的实际效果,请运行以下命令:

    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数据),并且您可能基于“字符串排序”行为而具有代码和/或客户期望。这需要测试您的代码,并可能需要进行研究以查看这种行为更改是否会对用户产生负面影响。

  • 之间的另一个区别SQL_Latin1_General_CP1_CI_ASLatin1_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排序规则的另一个好处。但是,就像从“字符串排序”到“单词排序”的移动一样,同样的注意事项也适用:这是两个排序规则之间行为的肯定差异(同样,仅用于VARCHAR数据),并且您可能拥有代码和/或客户期望基于没有这些映射。这需要测试您的代码,并可能需要进行研究以查看这种行为更改是否会对用户产生负面影响。

    (在此SO答案中首先由@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_ASLatin1_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数据的这种行为))。

  • 对于此特定更改而言,它的重要性不那么重要,但仍然值得一听,因为将服务器更改为二进制或区分大小写的排序规则会影响服务器的排序规则也会影响:

    • 局部变量名
    • 光标名称
    • 转到标签
    • sysname数据类型的名称解析


    意思是,如果您或显然是所有不良代码的负责人的“最近离开的程序员” ;-)对套管不小心,并声明了一个变量,@SomethingID但随后再引用它@somethingId,则在处理案例时会中断敏感或二进制排序规则。同样,使用sysname数据类型但将其引用为SYSNAMESysName或其他所有小写形式的代码,如果使用区分大小写或二进制排序规则将其移至实例,则它们也会中断。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.