web-dev-qa-db-fra.com

Utilisation de COLLATE avec UNION

Comment utiliser COLLATE avec UNION? Je veux réunir 2 tables (les deux ont les mêmes colonnes et les mêmes types: varchar,int, int, decimal).

J'ai eu l'erreur suivante:

sg 468, niveau 16, état 9, ligne 1 Impossible de résoudre le conflit de classement entre "Serbian_Latin_100_CI_AS" et "Croatian_CI_AS" dans l'opération UNION.

Mon instruction SQL:

select * from #IA_BIH
union 
select * from #IA_MNE  

Où dois-je insérer collate database_default? J'ai essayé différentes combinaisons, mais cela n'a pas fonctionné.

6
Anja

Sur la base des noms de classement, je suppose que vous utilisez Microsoft SQL Server.

COLLATE peut être utilisé au niveau de la base de données ou au niveau de la colonne. Dans la mesure où vous essayez d'UNIER deux tables, l'utilisation du classement des colonnes sur les colonnes nécessaires résoudra votre requête.

Voici un exemple de code pour vous aider:

use testdb
GO
CREATE TABLE dbo.Serbian  (Name VARCHAR(20) COLLATE Serbian_Latin_100_CI_AS);
CREATE TABLE dbo.Croatian (Name VARCHAR(20) COLLATE  Croatian_CI_AS);
GO
INSERT INTO dbo.Serbian VALUES ('serbian');
INSERT INTO dbo.Croatian VALUES ('croation');
GO

-- Collate to a particular named collation
SELECT Name COLLATE Serbian_Latin_100_CI_AS as CollatedNameSerbian from dbo.Serbian 
UNION ALL
SELECT Name COLLATE Serbian_Latin_100_CI_AS from dbo.Croatian 
GO
-- Collate to the database default collation
SELECT Name  COLLATE database_default as CollatedNameDBDefault from dbo.Serbian 
UNION ALL
SELECT Name COLLATE database_default from dbo.Croatian 
GO

DROP TABLE dbo.Serbian;
DROP TABLE dbo.Croatian;
GO

Bien sûr, si vous avez plusieurs colonnes avec des classements conflictuels, vous devrez également définir leurs classements.

5
RLF

Les classements peuvent être un peu délicats, en particulier pour les données VARCHAR. Pour les données NVARCHAR et VARCHAR, les classements contrôlent les règles de tri et de comparaison. Mais pour les données VARCHAR, les classements contrôlent également le jeu de caractères (c'est-à-dire quelle page de code est utilisée pour déterminer quel caractère chaque valeur de 8 bits représente). Ainsi, la modification des classements sur les données VARCHARcan entraîne une perte de données:

SELECT CHAR(230) AS [DB-Collation],
       CHAR(230) COLLATE Korean_100_CI_AS AS [Korean],
       CHAR(230) COLLATE Albanian_CI_AS AS [Albanian],
       CHAR(230) COLLATE Greek_100_CI_AS AS [Greek];
-- æ            a   ?

Cependant, étant donné que vous avez une incompatibilité de classement, vous devez en modifier un. Je déconseille d'utiliser database_default dans ce cas particulier, car cela sera relatif à la base de données à partir de laquelle la requête est exécutée, ce qui pourrait entraîner un comportement incohérent.

Heureusement, les deux classements mentionnés dans l'erreur ont la même page de codes: 1250. Vous pouvez trouver ces informations à l'aide de la requête suivante:

SELECT col.[name], COLLATIONPROPERTY(col.[name], 'CodePage') AS [CodePage]
FROM   sys.fn_helpcollations() col
WHERE  [name] IN (N'Serbian_Latin_100_CI_AS', N'Croatian_CI_AS');

À ce stade, vous devez choisir l'un de ces deux pour forcer l'autre à être le même. Étant donné que les deux classements utilisent la même page de codes, le choix n'affectera pas le jeu de caractères utilisé. La seule différence dont vous devez vous préoccuper est les règles de tri et de comparaison entre le serbe et le croate; choisissez celui qui correspond le mieux aux attentes des utilisateurs finaux.

Une option consiste à forcer le classement dans l'instruction SELECT comme il semble que vous essayez de le faire, et comme indiqué dans la réponse de @ RLF. L'inconvénient ici est que vous ne pouvez plus utiliser SELECT * (s'il s'agit de code dans une procédure stockée, il est probablement préférable de ne pas utiliser SELECT * en tous cas).

Une autre option consiste à forcer le classement pour l'une de ces tables lorsque vous créez la table temporaire, que cela se fasse à l'aide de CREATE TABLE ou SELECT INTO:

SELECT ac.*
FROM   sys.all_columns ac
WHERE  ac.[object_id] = OBJECT_ID(N'sys.objects')
AND    ac.[name] = N'name';
-- SQL_Latin1_General_CP1_CI_AS (on my system, at least)

SELECT [name] COLLATE Hebrew_100_CI_AS AS [name]
INTO   #coltest
FROM   sys.objects;

SELECT sc.*
FROM   [tempdb].sys.columns sc
WHERE  sc.[object_id] = OBJECT_ID(N'tempdb..#coltest');
-- Hebrew_100_CI_AS

Les avantages ici sont que vous pouvez alors:

  1. utilisation SELECT * dans votre requête UNION.
  2. exécuter plusieurs requêtes sur ces tables sans avoir besoin d'ajouter une option COLLATE à chacune.

Et, en supposant que le classement par défaut, pour la base de données dans laquelle CREATE TABLE ou SELECT INTO est en cours d'exécution, est l'un des deux classements indiqués dans le message d'erreur, alors vous pouvez continuer et utiliser COLLATE database_default.

2
Solomon Rutzky