J'ai importé une base de données avec des données à comparer avec une autre base de données.
La base de données cible a un classement Latin1_General_CI_AS
et la base de données source a SQL_Latin1_General_CP1_CI_AS
.
J'ai modifié le classement de la base de données source en général en Latin1_General_CI_AS
à l'aide de SQL Server Management Studio. Mais les tables et les colonnes à l'intérieur restent avec l'ancien classement.
Je sais que je peux changer une colonne en utilisant:
ALTER TABLE [table]
ALTER COLUMN [column] VARCHAR(100) COLLATE Latin1_General_CI_AS
Mais je dois le faire pour toutes les tables et toutes les colonnes à l'intérieur.
Avant que je sache, commencez à écrire une procédure stockée qui lit toutes les tables et toutes les colonnes de type varchar
et les modifie en boucle de curseur de table et de colonne ...
Est-ce que quelqu'un connaît un moyen plus simple ou est le seul moyen de le faire avec un script exécutant toutes les tables d'une procédure?
N'ayant pas trouvé le moyen approprié, j'ai écrit un script pour le faire et je le partage ici pour ceux qui en ont besoin. Le script parcourt toutes les tables utilisateur et collecte les colonnes. Si le type de colonne est un type de caractère, il essaie de la convertir dans le classement donné.
Les colonnes doivent être libres d’index et de contraintes pour que cela fonctionne.
Si quelqu'un a encore une meilleure solution à cela s'il vous plaît poster!
DECLARE @collate nvarchar(100);
DECLARE @table nvarchar(255);
DECLARE @column_name nvarchar(255);
DECLARE @column_id int;
DECLARE @data_type nvarchar(255);
DECLARE @max_length int;
DECLARE @row_id int;
DECLARE @sql nvarchar(max);
DECLARE @sql_column nvarchar(max);
SET @collate = 'Latin1_General_CI_AS';
DECLARE local_table_cursor CURSOR FOR
SELECT [name]
FROM sysobjects
WHERE OBJECTPROPERTY(id, N'IsUserTable') = 1
OPEN local_table_cursor
FETCH NEXT FROM local_table_cursor
INTO @table
WHILE @@FETCH_STATUS = 0
BEGIN
DECLARE local_change_cursor CURSOR FOR
SELECT ROW_NUMBER() OVER (ORDER BY c.column_id) AS row_id
, c.name column_name
, t.Name data_type
, c.max_length
, c.column_id
FROM sys.columns c
JOIN sys.types t ON c.system_type_id = t.system_type_id
LEFT OUTER JOIN sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id
LEFT OUTER JOIN sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
WHERE c.object_id = OBJECT_ID(@table)
ORDER BY c.column_id
OPEN local_change_cursor
FETCH NEXT FROM local_change_cursor
INTO @row_id, @column_name, @data_type, @max_length, @column_id
WHILE @@FETCH_STATUS = 0
BEGIN
IF (@max_length = -1) OR (@max_length > 4000) SET @max_length = 4000;
IF (@data_type LIKE '%char%')
BEGIN TRY
SET @sql = 'ALTER TABLE ' + @table + ' ALTER COLUMN ' + @column_name + ' ' + @data_type + '(' + CAST(@max_length AS nvarchar(100)) + ') COLLATE ' + @collate
PRINT @sql
EXEC sp_executesql @sql
END TRY
BEGIN CATCH
PRINT 'ERROR: Some index or constraint rely on the column' + @column_name + '. No conversion possible.'
PRINT @sql
END CATCH
FETCH NEXT FROM local_change_cursor
INTO @row_id, @column_name, @data_type, @max_length, @column_id
END
CLOSE local_change_cursor
DEALLOCATE local_change_cursor
FETCH NEXT FROM local_table_cursor
INTO @table
END
CLOSE local_table_cursor
DEALLOCATE local_table_cursor
GO
Je ne suis donc pas satisfait de la réponse. J'étais chargé de mettre à niveau JIRA 6.4.x vers JIRA Software 7.x et je suis allé à ce problème particulier avec la collation de base de données et de colonne.
Dans SQL Server, si vous ne supprimez pas les contraintes telles que la clé primaire ou la clé étrangère ou même les index, le script fourni ci-dessus en tant que réponse ne fonctionne pas du tout. Il va cependant changer ceux sans ces propriétés. C'est vraiment problématique, car je ne veux pas supprimer manuellement toutes les contraintes et les reconstituer. Cette opération pourrait probablement se solder par des erreurs. De l'autre côté, créer un script automatisant le changement peut prendre des siècles.
J'ai donc trouvé un moyen de faire la migration simplement en utilisant SQL Management Studio. Voici la procédure:
EXEC sp_msforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT all"
exec sp_msforeachtable @command1="print '?'", @command2="ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"
Une fois terminé, j'ai redémarré JIRA et mon classement dans la base de données était en ordre. J'espère que ça aide beaucoup de gens!
Problème de longueur fixe nvarchar et ajout de NULL/NOT NULL
DECLARE @collate nvarchar(100);
DECLARE @table nvarchar(255);
DECLARE @column_name nvarchar(255);
DECLARE @column_id int;
DECLARE @data_type nvarchar(255);
DECLARE @max_length int;
DECLARE @row_id int;
DECLARE @sql nvarchar(max);
DECLARE @sql_column nvarchar(max);
DECLARE @is_Nullable bit;
DECLARE @null nvarchar(25);
SET @collate = 'Latin1_General_CI_AS';
DECLARE local_table_cursor CURSOR FOR
SELECT [name]
FROM sysobjects
WHERE OBJECTPROPERTY(id, N'IsUserTable') = 1
OPEN local_table_cursor
FETCH NEXT FROM local_table_cursor
INTO @table
WHILE @@FETCH_STATUS = 0
BEGIN
DECLARE local_change_cursor CURSOR FOR
SELECT ROW_NUMBER() OVER (ORDER BY c.column_id) AS row_id
, c.name column_name
, t.Name data_type
, c.max_length
, c.column_id
, c.is_nullable
FROM sys.columns c
JOIN sys.types t ON c.system_type_id = t.system_type_id
LEFT OUTER JOIN sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id
LEFT OUTER JOIN sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
WHERE c.object_id = OBJECT_ID(@table)
ORDER BY c.column_id
OPEN local_change_cursor
FETCH NEXT FROM local_change_cursor
INTO @row_id, @column_name, @data_type, @max_length, @column_id, @is_nullable
WHILE @@FETCH_STATUS = 0
BEGIN
IF (@max_length = -1) SET @max_length = 4000;
set @null=' NOT NULL'
if (@is_nullable = 1) Set @null=' NULL'
if (@Data_type='nvarchar') set @max_length=cast(@max_length/2 as bigint)
IF (@data_type LIKE '%char%')
BEGIN TRY
SET @sql = 'ALTER TABLE ' + @table + ' ALTER COLUMN [' + rtrim(@column_name) + '] ' + @data_type + '(' + CAST(@max_length AS nvarchar(100)) + ') COLLATE ' + @collate + @null
PRINT @sql
EXEC sp_executesql @sql
END TRY
BEGIN CATCH
PRINT 'ERROR: Some index or contraint rely on the column ' + @column_name + '. No conversion possible.'
PRINT @sql
END CATCH
FETCH NEXT FROM local_change_cursor
INTO @row_id, @column_name, @data_type, @max_length, @column_id, @is_Nullable
END
CLOSE local_change_cursor
DEALLOCATE local_change_cursor
FETCH NEXT FROM local_table_cursor
INTO @table
END
CLOSE local_table_cursor
DEALLOCATE local_table_cursor
GO
Pour ce faire, j'ai une solution facile qui fonctionne pour moi.
Cependant, vous devez faire preuve de prudence si votre base de données a des déclencheurs, des procédures ou similaires - davantage que des données et des tables.
Problème de longueur fixe nvarchar (inclure max), texte inclus et ajout de NULL/NOT NULL.
USE [put your database name here];
begin tran
DECLARE @collate nvarchar(100);
DECLARE @table nvarchar(255);
DECLARE @column_name nvarchar(255);
DECLARE @column_id int;
DECLARE @data_type nvarchar(255);
DECLARE @max_length int;
DECLARE @max_length_str nvarchar(100);
DECLARE @is_nullable bit;
DECLARE @row_id int;
DECLARE @sql nvarchar(max);
DECLARE @sql_column nvarchar(max);
SET @collate = 'Latin1_General_CI_AS';
DECLARE local_table_cursor CURSOR FOR
SELECT [name]
FROM sysobjects
WHERE OBJECTPROPERTY(id, N'IsUserTable') = 1
ORDER BY [name]
OPEN local_table_cursor
FETCH NEXT FROM local_table_cursor
INTO @table
WHILE @@FETCH_STATUS = 0
BEGIN
DECLARE local_change_cursor CURSOR FOR
SELECT ROW_NUMBER() OVER (ORDER BY c.column_id) AS row_id
, c.name column_name
, t.Name data_type
, col.CHARACTER_MAXIMUM_LENGTH
, c.column_id
, c.is_nullable
FROM sys.columns c
JOIN sys.types t ON c.system_type_id = t.system_type_id
JOIN INFORMATION_SCHEMA.COLUMNS col on col.COLUMN_NAME = c.name and c.object_id = OBJECT_ID(col.TABLE_NAME)
LEFT OUTER JOIN sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id
LEFT OUTER JOIN sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
WHERE c.object_id = OBJECT_ID(@table) AND (t.Name LIKE '%char%' OR t.Name LIKE '%text%')
AND c.collation_name <> @collate
ORDER BY c.column_id
OPEN local_change_cursor
FETCH NEXT FROM local_change_cursor
INTO @row_id, @column_name, @data_type, @max_length, @column_id, @is_nullable
WHILE @@FETCH_STATUS = 0
BEGIN
set @max_length_str = @max_length
IF (@max_length = -1) SET @max_length_str = 'max'
IF (@max_length > 4000) SET @max_length_str = '4000'
BEGIN TRY
SET @sql =
CASE
WHEN @data_type like '%text%'
THEN 'ALTER TABLE ' + @table + ' ALTER COLUMN [' + @column_name + '] ' + @data_type + ' COLLATE ' + @collate + ' ' + CASE WHEN @is_nullable = 0 THEN 'NOT NULL' ELSE 'NULL' END
ELSE 'ALTER TABLE ' + @table + ' ALTER COLUMN [' + @column_name + '] ' + @data_type + '(' + @max_length_str + ') COLLATE ' + @collate + ' ' + CASE WHEN @is_nullable = 0 THEN 'NOT NULL' ELSE 'NULL' END
END
--PRINT @sql
EXEC sp_executesql @sql
END TRY
BEGIN CATCH
PRINT 'ERROR (' + @table + '): Some index or constraint rely on the column ' + @column_name + '. No conversion possible.'
--PRINT @sql
END CATCH
FETCH NEXT FROM local_change_cursor
INTO @row_id, @column_name, @data_type, @max_length, @column_id, @is_nullable
END
CLOSE local_change_cursor
DEALLOCATE local_change_cursor
FETCH NEXT FROM local_table_cursor
INTO @table
END
CLOSE local_table_cursor
DEALLOCATE local_table_cursor
commit tran
GO
Remarque: dans le cas où vous devez simplement modifier une condition d'utilisation de collation spécifique comme celle-ci:
WHERE c.object_id = OBJECT_ID(@table) AND (t.Name LIKE '%char%' OR t.Name LIKE '%text%')
AND c.collation_name = 'collation to change'
par exemple.NOTthe: AND c.collation_name <> @collate
Dans mon cas, j'avais un classement correct/spécifié de certaines colonnes et je ne voulais pas les changer.
Pour ceux qui recherchent cette solution et qui utilisent PHPMyAdmin, une option se trouve au bas de l'onglet "Opérations" (cliquez d'abord sur une base de données); deux cases à cocher (une apparaît lorsque la première est cochée):
Semble exécuter un script semblable à ceux affichés; la console affiche la dernière commande exécutée:
ALTER TABLE `DB_NAME`.`LAST_TABLE_IN_DB`DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci
Le script suivant fonctionnera avec le schéma de table ainsi que les derniers types tels que (MAX), IMAGE, etc., modifiez votre type de classement en fonction de vos besoins sur cette ligne (SET @collate = 'DATABASE_DEFAULT';)
SQL SCRIPT ICI:
BEGIN
DECLARE @collate nvarchar(100);
declare @schema nvarchar(255);
DECLARE @table nvarchar(255);
DECLARE @column_name nvarchar(255);
DECLARE @column_id int;
DECLARE @data_type nvarchar(255);
DECLARE @max_length varchar(100);
DECLARE @row_id int;
DECLARE @sql nvarchar(max);
DECLARE @sql_column nvarchar(max);
SET @collate = 'DATABASE_DEFAULT';
DECLARE tbl_cursor CURSOR FOR SELECT (s.[name])schemaName, (o.[name])[tableName]
FROM sysobjects sy
INNER JOIN sys.objects o on o.name = sy.name
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
WHERE OBJECTPROPERTY(sy.id, N'IsUserTable') = 1
OPEN tbl_cursor FETCH NEXT FROM tbl_cursor INTO @schema,@table
WHILE @@FETCH_STATUS = 0
BEGIN
DECLARE tbl_cursor_changed CURSOR FOR
SELECT ROW_NUMBER() OVER (ORDER BY c.column_id) AS row_id
, c.name column_name
, t.Name data_type
, c.max_length
, c.column_id
FROM sys.columns c
JOIN sys.types t ON c.system_type_id = t.system_type_id
LEFT OUTER JOIN sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id
LEFT OUTER JOIN sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
WHERE c.object_id like OBJECT_ID(@schema+'.'+@table)
ORDER BY c.column_id
OPEN tbl_cursor_changed
FETCH NEXT FROM tbl_cursor_changed
INTO @row_id, @column_name, @data_type, @max_length, @column_id
WHILE @@FETCH_STATUS = 0
BEGIN
IF (@max_length = -1) SET @max_length = 'MAX';
IF (@data_type LIKE '%char%')
BEGIN TRY
SET @sql = 'ALTER TABLE ' +@schema+'.'+ @table + ' ALTER COLUMN ' + @column_name + ' ' + @data_type + '(' + CAST(@max_length AS nvarchar(100)) + ') COLLATE ' + @collate
print @sql
EXEC sp_executesql @sql
END TRY
BEGIN CATCH
PRINT 'ERROR:'
PRINT @sql
END CATCH
FETCH NEXT FROM tbl_cursor_changed
INTO @row_id, @column_name, @data_type, @max_length, @column_id
END
CLOSE tbl_cursor_changed
DEALLOCATE tbl_cursor_changed
FETCH NEXT FROM tbl_cursor
INTO @schema, @table
END
CLOSE tbl_cursor
DEALLOCATE tbl_cursor
PRINT 'Collation For All Tables Done!'