El concepto de Intercalación (Collation) hace referencia al patrón de bits utilizado para representar/almacenar cada carácter, y en consecuencia también se refiere a las reglas utilizadas para ordenar y comparar caracteres. Evidentemente, se trata de un concepto que afecta sólo a los campos de texto.
En la instalación de SQL Server se indica la Intercalación (Collation) de la Instancia, que definirá la intercalación utilizada por las bases de datos del sistema. De aquí podemos inferir (de hecho, es así), que todas las bases de datos del sistema utilizan la misma Intercalación (Collation). Como anécdota, en una ocasión me encontré con una Instancia que durante un proceso de recuperación de una catástrofe, dejaron las bases de datos master y msdb con una intercalación, y las bases de datos model y tempdb con una intercalación diferente (reinstalarón SQL Server con una Intercalación distinta a la original, y posteriormente, recuperaron master y msdb). La Instancia funcionaba aparentemente bien, pero empezamos a encontrar que algunas tareas realizadas con el Enterprise Manager (era una instalación de SQL Server 2000 Enterprise), producía un error, como era el caso de agregar un usuario a una base de datos. Investigando (con trazas, y un poquitín de tiempo), encontramos que Enterprise Manager utilizaba para dicha acción un procedimiento almacenado que creaba una tabla temporal (sobre tempdb, evidentemente) con datos obtenidos de master, y seguidamente hacía un join a través de campos de texto entre dicha tabla temporal y otras tablas de master. El error evidente: La intercalación era distinta, y dicho procedimiento almacenado no se podía ejecutar. De aquí podemos sacar como moraleja, la importancia de tener bien identificada y documentada la Intercalación utilizada por nuestras Instancias y bases de datos de SQL Server, tanto para recuperación de catástrofes como en otros procesos como migraciones y consolidaciones de servidores, etc.
Otra particularidad de la Intercalación (Collation) de una Instancia de SQL Server, es que siempre que se cree una base de datos, por defecto se creará utilizando la Intercalación de la Instancia, excepto que especifiquemos lo contrario.
Aunque la Intercalación (Collation) de todas las bases de datos del sistema debe ser la misma, la Intercalación (Collation) de cada base de datos de usuario puede ser diferente, es decir, podemos tener una base de datos utilizando una Intercalación por requisitos de la aplicación que la utiliza, y otra base de datos de la misma Instancia de SQL Server, utilizando una Intercalación diferente, siendo ésta una configuración natural y habitual.
Es importante tener en cuenta que la Intercalación (Collation) de una base de datos, sólo afecta a:
- La Intercalación de las tablas y objetos del sistema.
- La Intercalación de las nuevas tablas y objetos que se creen en la base de datos. Esto es debido, a que si no se especifica de forma explícita la Intercalación deseada en la sentencia CREATE correspondiente, se tomará por defecto la Intercalación de la base de datos.
Es posible cambiar la Intercalación de una base de datos utilizando el comando ALTER DATABASE COLLATE (ej: ALTER DATABASE miBaseDatos COLLATE Modern_Spanish_CI_AS). Sin embargo, lo más probable es que no consigamos lo que creemos estar consiguiendo (ya me decían de pequeño: "hay que tener cuidado con lo que se pide"... jeje ;-). El comando ALTER DATABASE COLLATE se limita a cambiar la Intercalación (Collation) de las tablas del sistema de la base de datos y el valor de Intercalación que por defecto utilizarán los nuevos objetos que se creen sin especificar de forma explícita la Intercalación deseada. Sin embargo, ALTER DATABASE COLLATE no cambiará la intercalación de las tablas existentes. Si deseamos cambiar la Intercalación de una base de datos y de todos sus objetos, una solución es crear una nueva base de datos con la Intercalación deseada, generar el Script de creación de objetos de la base de datos original (sin especificar la Intercalación en dicho Script), ejecutar el Script en la nueva base de datos, y cargar los datos desde la base de datos original utilizando el Asistente de Importación de SQL Server (Import Data). Esta no es la única opción posible, pero en general, es quizás la más apropiada (en general... en cada caso específico, habría que ver). También podemos crear a mano las descargas y cargas de las tablas, con la utilidad BCP.EXE y/o con el comando BULK INSERT, pero a fin de cuentas, esto es lo que hará el Asistente de Importación de forma automática para todas las tablas, así que para qué perder el tiempo, ¿no?. También cabe la posibilidad de utilizar el comando ALTER TABLE ALTER COLUMN COLLATE (ej: ALTER TABLE Facturas ALTER COLUMN ClientID CHAR(10) COLLATE Modern_Spanish_CI_AS), sin embargo, en el momento en que el campo que deseamos alterar tenga definido sobre él un índice, una clave externa, etc., la ejecución de dicho ALTER TABLE será fallida, produciéndose un error como el siguiente:
Server: Msg 5074, Level 16, State 8, Line 1 The object 'PK_Facturas' is dependent on column 'cod'. Server: Msg 4922, Level 16, State 1, Line 1 ALTER TABLE ALTER COLUMN cod failed because one or more objects access this column. |
Evidentemente, eliminar los índices, claves externas, etc., y luego volver a crearlos, es un trabajo extra que tendremos que considerar si realmente nos interesa asumir.
También es posible definir un valor de Intercalación distinto para cada campo de cada tabla. De éste modo, podríamos tener en cada campo de texto de cada tabla, una Intercalación distinta, lo cual se trata de una posibilidad que ofrece gran flexibilidad. Sin embargo, puede producirse un efecto colateral: en algunas instalaciones que he conocido, me he encontrado que en una misma base de datos conviven tablas con diferentes intercalaciones, no porque se desee, sino por descuido de los programadores. Al principio no hay problema, hasta que llega ese momento en el cual deseamos hacer alguna consulta y no podemos (ej: un JOIN entre dos campos de texto con distintas intercalaciones sin utilizar COLLATE en dicho JOIN).
Para conocer las distintas Intercalaciones que tenemos disponibles, podemos consultar la función del sistema fn_helpcollations (ej: select * from ::fn_helpcollations()).
En la práctica, habitualmente al crear una base de datos se crea con la Intercalación por defecto de la Instancia (que ya vimos que se determinó en la instalación de la Instancia), de tal modo que al crear las tablas en la base de datos, sus campos de texto se crean con la Intercalación por defecto de la base de datos. Esto, siempre y cuando no se especifique de forma explícita la Intercalación en las sentencias DDL.
Otro problema habitual relacionado con la Intercalación (Collation), es cuando deseamos realizar consultas entre tablas realizando un JOIN entre campos de texto con distinta Intercalación. Esto no es posible de realizar (obtendremos un error como Cannot resolve collation conflict for equal to operation). Sin embargo, cabe la posibilidad de utilizar la cláusula COLLATE en el JOIN, como si estuviésemos realizando un Casting (en vez de convertir entre tipos de datos, convertimos entre Intercalaciones). Así, podríamos utilizar una cláusula WHERE similar a la del siguiente ejemplo: WHERE a.client_name = b.client_name COLLATE Modern_Spanish_CI_AS.