Passando do SQL 2005 [SQL_Latin1_General_CP1_CI_AS] para 2008 - perderei todos os recursos usando 'compatibilidade com versões anteriores'

18

Estamos mudando do SQL 2005 [Instância e banco de dados têm agrupamento de SQL_Latin1_General_CP1_CI_AS] para o SQL 2008 [cujo padrão é Latin1_General_CI_AS].

Concluí uma instalação do SQL 2008 R2 e usei o padrão Latin1_General_CI_AS agrupamento , com a restauração do banco de dados ainda ativada SQL_Latin1_General_CP1_CI_AS. Os problemas excluídos ocorreram - as tabelas #temp em Latin1_General_CI_ASque o banco de dados estava SQL_Latin1_General_CP1_CI_ASe é aqui que estou agora - preciso de conselhos sobre as armadilhas agora, por favor.

Na instalação do SQL 2008 R2, tenho a opção na instalação para usar 'SQL Collation, used for backwards compatibility' onde eu tenho a opção de selecionar o mesmo agrupamento como o banco de dados de 2005: SQL_Latin1_General_CP1_CI_AS.

  1. Isso me permitirá não ter problemas com as #temp tables, mas existem armadilhas?

  2. Perderia qualquer funcionalidade ou recurso de qualquer tipo ao não usar um agrupamento "atual" do SQL 2008?

  3. E quando mudamos (por exemplo, em 2 anos) de 2008 para o SQL 2012? Terei problemas então?
  4. Em algum momento eu seria forçado a ir para Latin1_General_CI_AS ?

  5. Li que alguns scripts do DBA concluem as linhas de bancos de dados completos e, em seguida, executam o script de inserção no banco de dados com o novo agrupamento - estou com muito medo e desconfiado disso - você recomendaria fazer isso?

Peter PitLock
fonte
2
Se você acha que pode entrar no Hekaton no SQL Server 2014, aqui está outra coisa que você pode querer considerar ler .
Aaron Bertrand

Respostas:

20

Antes de tudo, peço desculpas por uma resposta tão longa, pois sinto que ainda há muita confusão quando as pessoas falam sobre termos como agrupamento, ordem de classificação, página de código etc.

De BOL :

Os agrupamentos no SQL Server fornecem regras de classificação, maiúsculas e minúsculas propriedades de sensibilidade aos seus dados . Os agrupamentos usados ​​com tipos de dados de caracteres, como char e varchar, determinam a página de código e os caracteres correspondentes que podem ser representados para esse tipo de dados. Esteja você instalando uma nova instância do SQL Server, restaurando um backup do banco de dados ou conectando o servidor aos bancos de dados do cliente, é importante entender os requisitos de localidade, a ordem de classificação e a sensibilidade de maiúsculas e minúsculas dos dados com os quais você trabalhará .

Isso significa que o agrupamento é muito importante, pois especifica regras sobre como as seqüências de caracteres dos dados são classificadas e comparadas.

Nota: Mais informações sobre COLLATIONPROPERTY

Agora vamos primeiro entender as diferenças ......

Executando abaixo do 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

Os resultados seriam:

insira a descrição da imagem aqui

Olhando para os resultados acima, a única diferença é a ordem de classificação entre os dois agrupamentos. Mas isso não é verdade, e você pode ver o motivo:

Teste 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

Resultados do Teste 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.

A partir dos resultados acima, podemos ver que, se não podemos comparar diretamente valores em colunas com diferentes agrupamentos, você deve usar COLLATEpara comparar os valores da coluna.

TESTE 2:

A principal diferença é o desempenho, como Erland Sommarskog aponta nesta discussão no 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')

--- Crie índices nas duas tabelas

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)

--- Execute as consultas

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

--- Isso terá conversão IMPLICIT

insira a descrição da imagem aqui

--- Execute as consultas

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

--- Isso NÃO terá conversão IMPLICIT

insira a descrição da imagem aqui

A razão para a conversão implícita é porque, eu tenho meu banco de dados e servidor de agrupamento tanto como SQL_Latin1_General_CP1_CI_ASea tabela Table_Latin1_General_CI_AS tem coluna Comentários definidos como VARCHAR(50)com COLLATE Latin1_General_CI_AS , por isso durante a pesquisa SQL Server tem que fazer uma conversão implícita.

Teste 3:

Com a mesma configuração, agora compararemos as colunas varchar com os valores nvarchar para ver as alterações nos planos de execução.

- execute a consulta

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

insira a descrição da imagem aqui

- execute a consulta

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

insira a descrição da imagem aqui

Observe que a primeira consulta é capaz de fazer a busca de índice, mas precisa fazer a conversão implícita, enquanto a segunda realiza uma varredura de índice que se mostra ineficiente em termos de desempenho, quando varre tabelas grandes.

Conclusão:

  • Todos os testes acima mostram que ter o agrupamento correto é muito importante para a instância do servidor de banco de dados.
  • SQL_Latin1_General_CP1_CI_AS é um agrupamento SQL com as regras que permitem classificar dados para unicode e não unicode são diferentes.
  • O agrupamento SQL não poderá usar o Index ao comparar dados unicode e não unicode, como visto nos testes acima, que, ao comparar dados nvarchar com dados varchar, ele faz a varredura de índice e não procura.
  • Latin1_General_CI_AS é um agrupamento do Windows com as regras que permitem classificar dados para unicode e não unicode.
  • O agrupamento do Windows ainda pode usar o Índice (busca de índice no exemplo acima) ao comparar dados unicode e não unicode, mas você vê uma pequena penalidade no desempenho.
  • É altamente recomendável ler a resposta de Erland Sommarskog + os itens de conexão que ele apontou.

Isso me permitirá não ter problemas com as #temp tables, mas existem armadilhas?

Veja minha resposta acima.

Perderia qualquer funcionalidade ou recurso de qualquer tipo ao não usar um agrupamento "atual" do SQL 2008?

Tudo depende de quais funcionalidades / recursos você está se referindo. Agrupar é armazenar e classificar dados.

E quando mudamos (por exemplo, em 2 anos) de 2008 para o SQL 2012? Terei problemas então? Em algum momento eu seria forçado a ir para Latin1_General_CI_AS?

Não posso garantir! Como as coisas podem mudar e é sempre bom estar alinhado com a sugestão da Microsoft +, você precisa entender seus dados e as armadilhas que mencionei acima. Consulte também este e este itens de conexão.

Li que alguns scripts do DBA concluem as linhas de bancos de dados completos e, em seguida, executam o script de inserção no banco de dados com o novo agrupamento - estou com muito medo e desconfiado disso - você recomendaria fazer isso?

Quando você deseja alterar o agrupamento, esses scripts são úteis. Eu me encontrei alterando o agrupamento de bancos de dados para coincidir com o agrupamento do servidor muitas vezes e eu tenho alguns scripts que o fazem muito bem. Deixe-me saber se você precisar.

Referências :

Kin Shah
fonte
5

Além do que o @Kin detalhou em sua resposta , há mais algumas coisas a serem lembradas ao alternar o agrupamento padrão do servidor (ou seja, da instância) (itens acima da linha horizontal são diretamente relevantes para os dois agrupamentos mencionados na pergunta; itens abaixo da linha horizontal são relevantes para o geral):

  • Se Agrupamento padrão do seu banco de dados NÃO MUDAR, então o problema "implícita conversão" desempenho descrito na resposta da @ Kin deve não ser um problema, já que strings literais e variáveis locais utilizar agrupamento padrão do banco de dados, não o servidor do. Os únicos impactos para o cenário em que o agrupamento no nível da instância é alterado, mas não o agrupamento no nível do banco de dados são (ambos descritos em detalhes abaixo):

    • possíveis agrupamentos em conflito com tabelas temporárias (mas não com variáveis ​​da tabela).
    • código quebrado potencial se o revestimento de variáveis ​​e / ou cursores não corresponder às suas declarações (mas isso só pode acontecer se você mudar para uma instância com um agrupamento binário ou com distinção entre maiúsculas e minúsculas).
  • Uma diferença entre esses dois agrupamentos está em como eles classificam certos caracteres para VARCHARdados (isso não afeta os NVARCHARdados). Os agrupamentos não EBCDIC SQL_usam o que é chamado "Classificação por Cadeia" para VARCHARdados, enquanto todos os outros agrupamentos e até mesmo os NVARCHARdados para os agrupamentos não EBCDIC SQL_usam o que é chamado "Classificação por Palavra". A diferença é que, em "Classificação da palavra", o traço -e o apóstrofo '(e talvez alguns outros caracteres?) Recebem um peso muito baixo e são essencialmente ignorados, a menos que não haja outras diferenças nas strings. Para ver esse comportamento em ação, execute o seguinte:

    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

    Devoluções:

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

    e:

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

    Embora você "perca" o comportamento "Classificação da cadeia", não tenho certeza se chamaria isso de "recurso". É um comportamento que é considerado indesejável (como evidenciado pelo fato de não ter sido antecipado em nenhum dos agrupamentos do Windows). No entanto, é uma diferença definida de comportamento entre os dois agrupamentos (novamente, apenas para VARCHARdados não EBCDIC ), e você pode ter código e / ou expectativas do cliente com base no comportamento "Classificação da cadeia". Isso requer testar seu código e possivelmente pesquisar para ver se essa mudança de comportamento pode ter algum impacto negativo nos usuários.

  • Outra diferença entre SQL_Latin1_General_CP1_CI_ASe Latin1_General_100_CI_ASé a capacidade de fazer expansões nos VARCHARdados (os NVARCHARdados já podem fazer isso na maioria dos SQL_agrupamentos), como lidar com æcomo se fosse 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;

    Devoluções:

    Latin1_General_100_CI_AS

    A única coisa que você está "perdendo" aqui é não conseguir fazer essas expansões. De um modo geral, esse é outro benefício de mudar para um agrupamento do Windows. No entanto, assim como no movimento "Classificação da string" para "Classificação da palavra", o mesmo cuidado se aplica: é uma diferença definida de comportamento entre os dois agrupamentos (novamente, apenas para VARCHARdados), e você pode ter código e / ou cliente expectativas baseadas em não ter esses mapeamentos. Isso requer testar seu código e possivelmente pesquisar para ver se essa mudança de comportamento pode ter algum impacto negativo nos usuários.

    (observado pela primeira vez nesta resposta do SO por @Zarepheth: O SQL Server SQL_Latin1_General_CP1_CI_AS pode ser convertido com segurança em Latin1_General_CI_AS? )

  • O agrupamento no nível do servidor é usado para definir o agrupamento dos bancos de dados do sistema, o que inclui [model]. O [model]banco de dados é usado como um modelo para criar novos bancos de dados, que inclui [tempdb]cada inicialização do servidor. Mas, mesmo com uma alteração no agrupamento no nível do servidor alterando o agrupamento [tempdb], existe uma maneira fácil de corrigir diferenças de agrupamento entre o banco de dados que é "atual" quando CREATE #TempTableé executado e [tempdb]. Ao criar tabelas temporárias, declare um agrupamento usando a COLLATEcláusula e especifique um agrupamento de DATABASE_DEFAULT:

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

  • É melhor usar a versão mais recente do agrupamento desejado, se houver várias versões disponíveis. A partir do SQL Server 2005, uma série de agrupamentos "90" foi introduzida e o SQL Server 2008 introduziu uma série de agrupamentos "100". Você pode encontrar esses agrupamentos usando as seguintes consultas:

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

    Como você está no SQL Server 2008 R2, use em Latin1_General_100_CI_ASvez de Latin1_General_CI_AS.

  • Uma diferença entre as versões com diferenciação de maiúsculas e minúsculas desses agrupamentos específicos (ie SQL_Latin1_General_CP1_CS_ASe Latin1_General_100_CS_AS) está na ordem das letras maiúsculas e minúsculas ao fazer a classificação com distinção entre maiúsculas e minúsculas. Isso também afeta os intervalos de classe de caractere único (ou seja [start-end]) que podem ser usados ​​com o LIKEoperador e a PATINDEXfunção. As três consultas a seguir mostram esse efeito para a classificação e o intervalo de caracteres .:

    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

    A única maneira de ordenar maiúsculas antes de minúsculas (para a mesma letra) é usar um dos 31 agrupamentos que suportam esse comportamento, que são os Hungarian_Technical_*agrupamentos e um punhado de SQL_agrupamentos (que apenas suportam esse comportamento para VARCHARdados )

  • Menos importante para essa alteração específica, mas ainda é bom saber, já que seria impactante se alterar o servidor para um agrupamento binário ou com distinção entre maiúsculas e minúsculas, é que o agrupamento no nível do servidor também afeta:

    • nomes de variáveis ​​locais
    • Nomes de CURSOR
    • Etiquetas GOTO
    • resolução de nomes do sysnametipo de dados


    Ou seja, se você ou "o programador que saiu recentemente", aparentemente responsável por todo o código incorreto ;-), não tiver cuidado com o caso e declarar uma variável como, @SomethingIDmas depois se referir a ela mais @somethingIdtarde, isso será interrompido se você mudar para um caso agrupamento sensível ou binário. Da mesma forma, o código que utiliza o sysnametipo de dados, mas refere-se a ela como SYSNAME, SysNameou algo diferente de todos minúscula também vai quebrar se moveu para uma instância usando um agrupamento caso-sensível ou binário.

Solomon Rutzky
fonte