Intercalaciones de bases de datos independientes

Se aplica a:SQL ServerAzure SQL Managed Instance

Varias propiedades afectan a la semántica de igualdad y al criterio de ordenación de los datos de texto, como son la distinción entre mayúsculas y minúsculas y de los acentos, y el idioma básico que se usa. Estas cualidades se transmiten a SQL Server mediante la elección de la ordenación para los datos. Para obtener una explicación más detallada sobre las intercalaciones, consulte Intercalación y soporte para Unicode.

Las intercalaciones se aplican no solo a los datos almacenados en las tablas de usuario, sino a todo el texto que se administra en SQL Server, como son los metadatos, los objetos temporales, los nombres de variables, etc. Su tratamiento difiere en las bases de datos independientes y en las dependientes. Este cambio no afecta a muchos usuarios, pero ayuda a proporcionar independencia de instancia y uniformidad. Pero esto también puede provocar cierta confusión y problemas para las sesiones que acceden a bases de datos contenidas y no contenidas.

El comportamiento de intercalación de las bases de datos independientes difiere de manera sutil del comportamiento de las bases de datos no independientes. Este comportamiento suele ser beneficioso y proporciona independencia de la instancia y simplicidad. Algunos usuarios pueden tener problemas, especialmente cuando una sesión accede a bases de datos contenidas y no contenidas.

En este artículo se aclara el contenido del cambio y se examinan las áreas en las que el cambio puede causar problemas.

Nota:

En Azure SQL Database, los criterios de ordenación de las bases de datos contenidas son diferentes. La intercalación de la base de datos y la intercalación del catálogo se pueden establecer durante la creación de la base de datos y no se pueden actualizar. Especifique un ordenamiento para los datos (COLLATE) y un ordenamiento de catálogo para los metadatos del sistema y los identificadores de objetos (CATALOG_COLLATION). Para obtener más información, consulte CREATE DATABASE.

Bases de datos no contenidas

Todas las bases de datos tienen una intercalación predeterminada (que se puede establecer al crear o modificar una base de datos). Esta intercalación se usa para todos los metadatos de la base de datos y el valor predeterminado para todas las columnas de cadena dentro de la base de datos. Los usuarios pueden elegir una intercalación diferente para una columna en particular utilizando la cláusula COLLATE.

Ejemplo 1

Por ejemplo, si estuviéramos trabajando en Beijing, podríamos usar un criterio de ordenación chino:

ALTER DATABASE MyDB
    COLLATE Chinese_Simplified_Pinyin_100_CI_AS;

Ahora, si creamos una columna, su intercalación predeterminada es esta intercalación china, pero podemos elegir otra si queremos:

CREATE TABLE MyTable
(
    mycolumn1 NVARCHAR,
    mycolumn2 NVARCHAR COLLATE Frisian_100_CS_AS
);
GO

SELECT name, collation_name
FROM sys.columns
WHERE name LIKE 'mycolumn%';
GO

Este es el conjunto de resultados.

name            collation_name
--------------- ----------------------------------
mycolumn1       Chinese_Simplified_Pinyin_100_CI_AS
mycolumn2       Frisian_100_CS_AS

Esto parece relativamente sencillo, pero surgen varios problemas. Dado que la intercalación de una columna depende de la base de datos en la que se crea la tabla, surgen problemas con el uso de tablas temporales almacenadas en tempdb. La intercalación de tempdb suele coincidir con la intercalación de la instancia, que no tiene que coincidir con la intercalación de la base de datos.

Ejemplo 2

Por ejemplo, tenga en cuenta la base de datos china anteriormente mostrada, cuando se usa en una instancia con una intercalación Latin1_General:

CREATE TABLE T1 (T1_txt NVARCHAR (MAX));
GO

CREATE TABLE #T2 (T2_txt NVARCHAR (MAX));
GO

A primera vista, estas dos tablas tienen el mismo esquema, pero dado que las intercalaciones de las bases de datos difieren, los valores son incompatibles:

SELECT T1_txt, T2_txt
FROM T1
     INNER JOIN #T2
         ON T1.T1_txt = #T2.T2_txt;

Este es el conjunto de resultados.

Mensaje 468, Nivel 16, Estado 9, Línea 2

No puede resolver el conflicto de la intercalación entre "Latin1_General_100_CI_AS_KS_WS_SC" y Chinese_Simplified_Pinyin_100_CI_AS" en la operación igual que.

Podemos corregir esto intercalando la tabla temporal explícitamente. SQL Server facilita esta tarea proporcionando la DATABASE_DEFAULT palabra clave para la COLLATE cláusula .

CREATE TABLE T1 (T1_txt NVARCHAR (MAX));
GO

CREATE TABLE #T2 (T2_txt NVARCHAR (MAX) COLLATE DATABASE_DEFAULT);
GO

SELECT T1_txt, T2_txt
FROM T1
     INNER JOIN #T2
         ON T1.T1_txt = #T2.T2_txt;

Esta consulta ahora se ejecuta sin errores.

También podemos ver el comportamiento dependiente de la intercalación con variables. Considere la función siguiente:

CREATE FUNCTION f (@x INT)
RETURNS INT
AS
BEGIN
    DECLARE @I AS INT = 1;
    DECLARE @İ AS INT = 2;
    RETURN @x * @i;
END

Esta es una función bastante peculiar. En una intercalación con distinción entre mayúsculas y minúsculas, el @i de la cláusula de devolución no puede enlazar a @I o . En una intercalación Latin1_General sin distinción entre mayúsculas y minúsculas, @i enlaza a @I y la función devuelve 1. Pero en una ordenación turca que no distingue entre mayúsculas y minúsculas, @i se asocia con , y la función devuelve 2. Esto puede causar confusión en una base de datos que se mueva entre instancias con intercalaciones diferentes.

Bases de datos contenidas

Dado que uno de los objetivos de diseño de las bases de datos independientes es hacer que sean autodependientes, la dependencia de las intercalaciones de tempdb e instancia debe romperse. Para ello, las bases de datos independientes presentan el concepto de intercalación de catálogo. La intercalación de catálogo se utiliza para los objetos transitorios y los metadatos del sistema. Los detalles se proporcionan de la manera siguiente.

En una base de datos contenida, la intercalación de catálogo es Latin1_General_100_CI_AS_WS_KS_SC. Esta intercalación es la misma para todas las bases de datos contenidas en todas las instancias de SQL Server y no se puede cambiar.

La intercalación de la base de datos se conserva, pero solo se usa como intercalación predeterminada para los datos del usuario. De forma predeterminada, la intercalación de la base de datos coincide con la intercalación de la base de datos model, pero el usuario puede cambiarla mediante un comando CREATE o ALTER DATABASE, igual que en las bases de datos no independientes.

Una palabra clave nueva, CATALOG_DEFAULT, está disponible en la cláusula COLLATE. Se utiliza como atajo a la ordenación actual de los metadatos tanto en las bases de datos contenidas como en las no contenidas. Es decir, en una base de datos dependiente, CATALOG_DEFAULT devolverá la intercalación de la base de datos actual, dado que los metadatos se intercalan en la intercalación de la base de datos. En una base de datos independiente, estos dos valores pueden ser diferentes, ya que el usuario puede cambiar la intercalación de la base de datos para que no coincida con la intercalación del catálogo.

El comportamiento de diversos objetos tanto en las bases de datos no contenidas como en las contenidas se resume en esta tabla:

Elemento Base de datos no contenida Base de datos contenida
Datos de usuario (valor predeterminado) DATABASE_DEFAULT DATABASE_DEFAULT
Datos temporales (valor predeterminado) tempdb intercalación DATABASE_DEFAULT
Metadatos DATABASE_DEFAULT / CATALOG_DEFAULT CATALOG_DEFAULT
Metadatos temporales tempdb intercalación CATALOG_DEFAULT
Variables Intercalación de instancia CATALOG_DEFAULT
Etiquetas Goto Intercalación de instancia CATALOG_DEFAULT
Nombres de cursor Intercalación de instancia CATALOG_DEFAULT

En el ejemplo de la tabla temporal descrito previamente, podemos ver que este comportamiento de la intercalación elimina la necesidad de una cláusula COLLATE explícita en la mayor parte de los usos de las tablas temporales. En una base de datos independiente, este código se ejecuta ahora sin error, aun cuando las intercalaciones de instancia y base de datos difieren:

CREATE TABLE T1 (T1_txt NVARCHAR (MAX));
GO

CREATE TABLE #T2 (T2_txt NVARCHAR (MAX));
GO

SELECT T1_txt, T2_txt
FROM T1
     INNER JOIN #T2
         ON T1.T1_txt = #T2.T2_txt;

Esta consulta funciona porque T1_txt y T2_txt se intercalan en la intercalación de base de datos de la base de datos independiente.

Cruce entre contextos confinados y no confinados

Mientras una sesión contenida en una base de datos independiente siga siendo contenida, debe permanecer en la base de datos a la que se conectó. En este caso, el comportamiento es sencillo. Pero si una sesión cruza de un contexto contenido a uno no contenido, el comportamiento se vuelve más complejo, porque los dos conjuntos de reglas deben unirse. Esto puede ocurrir en una base de datos parcialmente contenida, ya que un usuario puede USE a otra base de datos. El siguiente principio administra la diferencia en las reglas de intercalación en este caso.

  • La base de datos en la que el lote comienza determina el comportamiento de la intercalación de un lote.

Esta decisión se toma antes de que se emita cualquier comando, incluida una inicial USE. Es decir, si un lote comienza en una base de datos independiente, pero el primer comando es USE para una base de datos no independiente, el comportamiento de intercalación independiente se sigue usando para el lote. Dado este escenario, una referencia a una variable, por ejemplo, podría tener varios resultados posibles:

  • La referencia puede encontrar una coincidencia exactamente. En este caso, la referencia funciona sin error.

  • La referencia puede no encontrar una coincidencia en la intercalación actual donde hubiera otra antes. Esto genera un error que indica que la variable no existe, aunque aparentemente se creó.

  • La referencia puede encontrar varias coincidencias que eran distintas originalmente. Esto también genera un error.

Esto se ilustra con algunos ejemplos. Para estos casos, se supone que hay una base de datos parcialmente contenida denominada MyCDB con la intercalación de base de datos configurada en la intercalación predeterminada, Latin1_General_100_CI_AS_WS_KS_SC. Suponemos que la intercalación de la instancia es Latin1_General_100_CS_AS_WS_KS_SC. Las dos intercalaciones solo difieren en la distinción de mayúsculas y minúsculas.

Ejemplo 1

El siguiente ejemplo ilustra el caso en el que la referencia encuentra exactamente una coincidencia.

USE MyCDB;
GO

CREATE TABLE #a (x INT);

INSERT INTO #a VALUES (1);
GO

USE master;
GO

SELECT * FROM #a;
GO

Results:

Este es el conjunto de resultados.

x
-----------
1

En este caso, el #a identificado enlaza en la intercalación del catálogo sin distinción entre mayúsculas y minúsculas y la intercalación de la instancia con distinción entre mayúsculas y minúsculas, y el código se ejecuta bien.

Ejemplo 2

En el siguiente ejemplo se ilustra el caso en el que la referencia no encuentra una coincidencia en la intercalación actual donde antes había una.

USE MyCDB;
GO

CREATE TABLE #a (x INT);

INSERT INTO #A VALUES (1);
GO

Aquí, #A se vincula con #a en la ordenación predeterminada que no distingue entre mayúsculas y minúsculas, y la inserción se realiza correctamente,

Este es el conjunto de resultados.

(1 row(s) affected)

Pero si continuamos el script...

USE master;
GO

SELECT * FROM #A;
GO

Obtenemos un error al intentar enlazar #A en la intercalación de la instancia con distinción entre mayúsculas y minúsculas;

Este es el conjunto de resultados.

Msg 208, Level 16, State 0, Line 2
Invalid object name '#A'.

Ejemplo 3

El siguiente ejemplo ilustra el caso en el que la referencia encuentra varias coincidencias que eran distintas originalmente. Primero, comenzamos en tempdb (que tiene la misma intercalación con distinción entre mayúsculas y minúsculas que nuestra instancia) y ejecutamos las siguientes instrucciones.

USE tempdb;
GO

CREATE TABLE #a (x INT);
GO

CREATE TABLE #A (x INT);
GO

INSERT INTO #a VALUES (1);
GO

INSERT INTO #A VALUES (2);
GO

Se ejecuta correctamente, dado que las tablas son distintas en esta intercalación:

Este es el conjunto de resultados.

(1 row(s) affected)
(1 row(s) affected)

Sin embargo, si pasamos a nuestra base de datos independiente, encontramos que ya no podemos enlazar a estas tablas.

USE MyCDB;
GO

SELECT * FROM #a;
GO

Este es el conjunto de resultados.

Msg 12800, Level 16, State 1, Line 2
The reference to temp table name #a is ambiguous and cannot be resolved. Possible candidates are #a and #A.