Olá pessoal…
Hoje vou mostrar como alterar a Collation do SQL Server para todas as tabelas via script.
Um amigo (um salve ai Rafinha) meu recentemente precisou alterar o collation do banco, e me passou um script interessante que ele encontrou através do link https://gallery.technet.microsoft.com/scriptcenter/Column-Collation-Changer-34321eaa#content.
Basicamente a ideia é criar uma procedure, que varre todos os campos de todas as tabelas e vai alterando o que for necessário. A procedure que será criada se chama ChangeCollation_SP, e como parâmetro basta você passar qual Collation que você quer.
Abaixo o código fonte da procedure.
IF OBJECT_ID('ChangeCollation_SP','P') IS NOT NULL
DROP PROC ChangeCollation_SP
GO
CREATE PROCEDURE ChangeCollation_SP
@ToCollation sysname = 'Latin1_General_BIN'
,@TableName sysname = ''
,@Columnname sysname = ''
,@SchemaName sysname = 'dbo'
,@FromCollation sysname = 'Latin1_General_100_BIN'
,@GenerateScriptsOnly BIT = 0
AS
/*
Parameters
@ToCollation - To which collation columns needs to be moved
@TableName - TableName for which the collation needs to be changed.
Default value is '' and all the tables will be considered for changing the collation.
@Columnname - Columnname for which the collation needs to be changed.
Default value is '' and all the columns will be considered for changing the collation.
@SchemaName - SchemaName for which the collation needs to be changed.
Default value is '' and all the columns will be considered for changing the collation.
@FromCollation - The columns with which collation needs to be changed to the To collation.
Default value is '' and all the columns with all collation will be considered for changing the collation.
@GenerateScriptsOnly - Generates the scripts only for changing the collation.
Default value is 1 and generates only script. When changed to 0 the collation change will be applied
*/
SET NOCOUNT ON
DECLARE @DBname sysname
DECLARE @SchemaID INT
DECLARE @TableID INT
DECLARE @IndexID INT
DECLARE @isPrimaryKey BIT
DECLARE @IndexType INT
DECLARE @CreateSQL VARCHAR(MAX)
DECLARE @IndexColsSQL VARCHAR(MAX)
DECLARE @WithSQL VARCHAR(MAX)
DECLARE @IncludeSQL VARCHAR(MAX)
DECLARE @WhereSQL VARCHAR(MAX)
DECLARE @sql VARCHAR(MAX)
DECLARE @DropSQL VARCHAR(MAX)
DECLARE @ExistsSQL VARCHAR(MAX)
DECLARE @Indexname sysname
DECLARE @TblSchemaName sysname
IF OBJECT_ID('#ChangeCollationTables','U') IS NOT NULL
BEGIN
DROP TABLE #ChangeCollationTables
END
CREATE TABLE #ChangeCollationTables
(
SchemaID INT
,SchemaName sysname
,TableID INT
,TableName sysname
,Processed BIT
,RunRank INT NULL
)
IF OBJECT_ID('#ChangeCollationColumns','U') IS NOT NULL
BEGIN
DROP TABLE #ChangeCollationColumns
END
CREATE TABLE #ChangeCollationColumns
(
SchemaID INT
,SchemaName sysname
,TableID INT
,TableName sysname
,ColumnID INT
,Col sysname
,AlterScript VARCHAR(MAX) NULL
)
IF OBJECT_ID('#ChangeCollationObjectsBackupTbl','U') IS NOT NULL
BEGIN
DROP TABLE #ChangeCollationObjectsBackupTbl
END
CREATE TABLE #ChangeCollationObjectsBackupTbl
(
BackupID INT IDENTITY(1,1)
,SchemaID INT
,TableID INT
,ObjectName sysname
,ObjectType VARCHAR(50)
,CreateScript VARCHAR(MAX) NULL
,DropScript VARCHAR(MAX) NULL
,ExistsScript VARCHAR(MAX) NULL
,Processed BIT NULL
)
---------------------------------------------------------------------------------------------------------------------------
-- Get List of columns needs the collation to be changed
---------------------------------------------------------------------------------------------------------------------------
INSERT INTO #ChangeCollationColumns
SELECT SCH.schema_id
,SCH.name
,ST.object_id
,ST.name
,SC.column_id
,SC.name
,'ALTER TABLE ' + QUOTEname(SCH.name) + '.' + QUOTEname(ST.name)
+ ' ALTER COLUMN ' + QUOTEname(SC.name) + ' '
+ STY.name +
+ CASE
WHEN STY.name IN ('char','varchar','nchar','nvarchar') AND SC.max_length = -1 THEN '(max)'
WHEN STY.name IN ('char','varchar') AND SC.max_length <> -1 THEN '(' + CONVERT(VARCHAR(5),SC.max_length) + ')'
WHEN STY.name IN ('nchar','nvarchar') AND SC.max_length <> -1 THEN '(' + CONVERT(VARCHAR(5),SC.max_length/2) + ')'
ELSE ''
END
+ ' COLLATE ' + @ToCollation
+ CASE SC.is_nullable
WHEN 0 THEN ' NOT NULL'
ELSE ' NULL'
END
FROM sys.tables ST
JOIN sys.schemas SCH
ON SCH.schema_id = ST.schema_id
JOIN sys.columns SC
ON SC.object_id = ST.object_id
JOIN sys.types STY
ON STY.system_type_id = SC.system_type_id
AND STY.user_type_id = SC.user_type_id
WHERE SCH.name = CASE
WHEN @SchemaName = '' THEN SCH.name
ELSE @SchemaName
END
AND ST.name = CASE
WHEN @TableName = '' THEN ST.name
ELSE @TableName
END
AND SC.name = CASE
WHEN @Columnname = '' THEN SC.name
ELSE @Columnname
END
AND SC.collation_name = CASE
WHEN @FromCollation = '' THEN SC.collation_name
ELSE @FromCollation
END
AND STY.name in ('char', 'varchar', 'text', 'nchar', 'nvarchar', 'ntext')
AND SC.is_computed = 0
-----------------------------------------------------------------------------------------------------------------------------
-- Get the list of tables need to be processed
-----------------------------------------------------------------------------------------------------------------------------
INSERT INTO #ChangeCollationTables
SELECT DISTINCT SchemaID
,SchemaName
,TableID
,TableName
,convert(bit,0) as Processed
,0 as RunRank
FROM #ChangeCollationColumns;
----------------------------------------------------------------------------------------------------------------------------
-- Order by foreignkey
-----------------------------------------------------------------------------------------------------------------------------
WITH fkey (ReferencingObjectid,ReferencingSchemaid,ReferencingTableName,PrimarykeyObjectid,PrimarykeySchemaid,PrimarykeyTableName,level)AS
(
SELECT DISTINCT
convert(int,null)
,convert(INT,null)
,convert(sysname,null)
,ST.object_id
,ST.schema_id
,ST.name
,0 as level
FROM sys.tables ST
LEFT JOIN sys.foreign_keys SF
ON SF.parent_object_id = ST.object_id
WHERE SF.object_id IS NULL
UNION ALL
SELECT STP.object_id
,STP.schema_id
,STP.name
,STC.object_id
,STC.schema_id
,STC.name
,f.level+1 as level
FROM sys.foreign_keys SFK
JOIN fkey f
ON SFK.referenced_object_id = ISNULL(f.ReferencingObjectid, f.PrimarykeyObjectid)
JOIN sys.tables STP
ON STP.object_id = SFK.parent_object_id
JOIN sys.tables STC
ON STC.object_id = SFK.referenced_object_id
)
UPDATE CT
SET RunRank = F.Lvl
FROM #ChangeCollationTables CT
JOIN
(
SELECT TableId = ISNULL(ReferencingObjectid,PrimarykeyObjectid)
, Lvl = MAX(level)
FROM fkey
GROUP BY ISNULL(ReferencingObjectid,PrimarykeyObjectid)
) F
ON F.TableId = CT.TableID
---------------------------------------------------------------------------------------------------------------------------
-- Backup Views
---------------------------------------------------------------------------------------------------------------------------
INSERT INTO #ChangeCollationObjectsBackupTbl
(SchemaID, TableID, ObjectName, ObjectType, CreateScript, DropScript, ExistsScript, Processed)
SELECT SchemaID, TableID, ObjectName, ObjectType, CreateScript, DropScript, ExistsScript, Processed
FROM
(
SELECT SchemaID=SV.schema_id
,TableID= x.referenced_major_id
,ObjectName=SV.name
,ObjectType='View'
,CreateScript=definition
,DropScript='DROP VIEW ' + QUOTEname(SCH.name) + '.' + QUOTEname(SV.name)
,ExistsScript=' EXISTS (SELECT 1
FROM sys.Views SV
JOIN sys.schemas SCH
ON SV.Schema_id = SCH.Schema_ID
WHERE SV.name =''' + SV.name + '''
AND SCH.name =''' + SCH.name + ''')'
,Processed=0
,Rnk = Rank() Over(Partition by SV.name order by x.referenced_major_id)
FROM sys.views SV
JOIN sys.sql_modules SQM
ON SV.object_id = SQM.object_id
JOIN
(
SELECT DISTINCT SD.object_id,SD.referenced_major_id
FROM sys.sql_dependencies SD
JOIN sys.objects so
ON SD.referenced_major_id = so.object_id
JOIN sys.columns SC
ON SC.object_id = so.object_id
AND SC.column_id = SD.referenced_minor_id
JOIN #ChangeCollationColumns CCC
ON SC.column_id = CCC.ColumnID
AND so.object_id = CCC.TableID
AND so.schema_id = CCC.SchemaID
) x
ON x.object_id = SV.object_id
JOIN sys.schemas SCH
ON SCH.schema_id = SV.schema_id
) Vie
WHERE Vie.Rnk = 1
---------------------------------------------------------------------------------------------------------------------------
-- Backup Computed Columns
---------------------------------------------------------------------------------------------------------------------------
INSERT INTO #ChangeCollationObjectsBackupTbl
(SchemaID, TableID, ObjectName, ObjectType, CreateScript, DropScript, ExistsScript, Processed)
SELECT CCC.SchemaID
,CCC.TableID
,SCC.name
,'ComputedColumn'
,'ALTER TABLE ' + QUOTEname(CCC.SchemaName) + '.' + QUOTEname(CCC.TableName) +
' ADD ' + QUOTEname(SCC.name) + ' as ' + SCC.definition
,'ALTER TABLE ' + QUOTEname(CCC.SchemaName) + '.' + QUOTEname(CCC.TableName) +
' DROP COLUMN ' + QUOTEname(SCC.name)
,'EXISTS (SELECT 1
FROM sys.computed_columns SCC
JOIN sys.tables ST
ON ST.object_id = SCC.object_id
JOIN sys.schemas SCH
ON ST.schema_id = SCH.schema_id
WHERE SCC.name =''' + SCC.name + '''
AND ST.name =''' + CCC.TableName + '''
AND SCH.name =''' + CCC.SchemaName + ''')'
,0
FROM sys.computed_columns SCC
JOIN #ChangeCollationTables CCC
ON SCC.object_id = CCC.TableID
JOIN sys.tables ST
ON ST.object_id = CCC.TableID
AND ST.schema_id = CCC.SchemaID
-----------------------------------------------------------------------------------------------------------------------------
-- Backup Statistics
-----------------------------------------------------------------------------------------------------------------------------
INSERT INTO #ChangeCollationObjectsBackupTbl
(SchemaID, TableID, ObjectName, ObjectType, CreateScript, DropScript, ExistsScript, Processed)
SELECT CCC.SchemaID
,CCC.TableID
,STA.name
,'Statistics'
,NULL
,'DROP STATISTICS' + QUOTEname(CCC.SchemaName) + '.' + QUOTEname(CCC.TableName) + '.' + QUOTEname(STA.name)
,' EXISTS ( SELECT * FROM sys.stats WHERE name = ''' + STA.name + ''' AND object_id = ' + CONVERT(VARCHAR(50),STA.object_id) + ')'
, 0
FROM sys.stats_columns STAC
JOIN #ChangeCollationColumns CCC
ON STAC.object_id = CCC.TableID
AND STAC.column_id = CCC.ColumnID
JOIN sys.stats STA
ON STA.stats_id = STAC.stats_id
AND STA.object_id = STAC.object_id
---------------------------------------------------------------------------------------------------------------------------
-- Backup Indexes
---------------------------------------------------------------------------------------------------------------------------
IF OBJECT_ID('#CollationIDXTable','U') IS NOT NULL
BEGIN
DROP TABLE #CollationIDXTable
END
CREATE TABLE #CollationIDXTable
(
Schema_ID INT
,Object_ID INT
,Index_ID INT
,SchemaName sysname
,TableName sysname
,Indexname sysname
,IsPrimaryKey BIT
,IndexType INT
,CreateScript VARCHAR(MAX) NULL
,DropScript VARCHAR(MAX) NULL
,ExistsScript VARCHAR(MAX) NULL
,Processed BIT NULL
)
INSERT INTO [dbo].[#CollationIDXTable]
(
Schema_ID
,Object_ID
,Index_ID
,SchemaName
,TableName
,Indexname
,IsPrimaryKey
,IndexType
)
SELECT DISTINCT ST.schema_id
,ST.object_id
,SI.index_id
,SCH.name
,ST.name
,SI.name
,SI.is_primary_key
,SI.type
FROM sys.indexes SI
JOIN sys.tables ST
ON SI.object_id = ST.object_id
JOIN sys.schemas SCH
ON SCH.schema_id = ST.schema_id
JOIN sys.index_columns SIC
ON SIC.object_id = SI.object_id
AND SIC.index_id = SI.index_id
--AND SIC.is_included_column = 0
JOIN #ChangeCollationColumns CCC
ON SIC.column_id = CCC.ColumnID
AND ST.object_id = CCC.TableID
AND ST.schema_id = CCC.SchemaID
WHERE SI.type IN (1,2)
UNION
SELECT DISTINCT ST.schema_id
,ST.object_id
,SI.index_id
,SCH.name
,ST.name
,SI.name
,SI.is_primary_key
,SI.type
FROM sys.indexes SI
JOIN sys.tables ST
ON SI.object_id = ST.object_id
JOIN sys.schemas SCH
ON SCH.schema_id = ST.schema_id
JOIN sys.index_columns SIC
ON SIC.object_id = SI.object_id
AND SIC.index_id = SI.index_id
--AND SIC.is_included_column = 0
JOIN #ChangeCollationTables CCC
ON ST.object_id = CCC.TableID
AND ST.schema_id = CCC.SchemaID
JOIN sys.columns SC
ON SC.object_id = CCC.TableID
AND SC.column_id = SIC.column_id
AND SC.is_computed = 1
WHERE SI.Type IN (1,2)
SELECT @CreateSQL = ''
SELECT @IndexColsSQL = ''
SELECT @WithSQL = ''
SELECT @IncludeSQL = ''
SELECT @WhereSQL = ''
WHILE EXISTS(SELECT 1
FROM [dbo].[#CollationIDXTable]
WHERE CreateScript IS NULL)
BEGIN
SELECT TOP 1 @SchemaID = Schema_ID
,@TableID = Object_ID
,@IndexID = Index_ID
,@isPrimaryKey = IsPrimaryKey
,@Indexname = Indexname
,@IndexType = IndexType
,@SchemaName = SchemaName
,@TableName = TableName
FROM [dbo].[#CollationIDXTable]
WHERE CreateScript IS NULL
--AND SchemaName = @SchemaName
--AND TableName = @TableName
ORDER BY Index_ID
SELECT @TblSchemaName = QUOTEname(@SchemaName) + '.' + QUOTEname(@TableName)
IF @isPrimaryKey = 1
BEGIN
SELECT @ExistsSQL = ' EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N''' + @TblSchemaName + ''') AND name = N''' + @Indexname + ''')'
SELECT @DropSQL = ' ALTER TABLE '+ @TblSchemaName + ' DROP CONSTRAINT [' + @Indexname + ']'
END
ELSE
BEGIN
SELECT @ExistsSQL = ' EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N''' + @TblSchemaName + ''') AND name = N''' + @Indexname + ''')'
SELECT @DropSQL = ' DROP INDEX [' + @Indexname + '] ON ' + @TblSchemaName +
CASE
WHEN @IndexType IN (1,2) THEN ' WITH ( ONLINE = OFF )'
ELSE ''
END
END
IF @IndexType IN (1,2)
BEGIN
SELECT @CreateSQL = CASE
WHEN SI.is_primary_key = 1 THEN
'ALTER TABLE ' + @TblSchemaName + ' ADD CONSTRAINT [' + @Indexname + '] PRIMARY KEY ' + SI.type_desc
WHEN SI.Type IN (1,2) THEN
' CREATE ' + CASE SI.is_unique WHEN 1 THEN ' UNIQUE ' ELSE '' END + SI.type_desc + ' INDEX ' + QUOTEname(SI.name) + ' ON ' + @TblSchemaName
END
,@IndexColsSQL = ( SELECT SC.name + ' '
+ CASE SIC.is_descending_key
WHEN 0 THEN ' ASC '
ELSE 'DESC'
END + ','
FROM sys.index_columns SIC
JOIN sys.columns SC
ON SIC.object_id = SC.object_id
AND SIC.column_id = SC.column_id
WHERE SIC.object_id = SI.object_id
AND SIC.index_id = SI.index_id
AND SIC.is_included_column = 0
ORDER BY SIC.key_ordinal
FOR XML PATH('')
)
,@WithSQL =' WITH (PAD_INDEX = ' + CASE SI.is_padded WHEN 1 THEN 'ON' ELSE 'OFF' END + ',' + CHAR(13) +
' IGNORE_DUP_KEY = ' + CASE SI.ignore_dup_key WHEN 1 THEN 'ON' ELSE 'OFF' END + ',' + CHAR(13) +
' ALLOW_ROW_LOCKS = ' + CASE SI.allow_row_locks WHEN 1 THEN 'ON' ELSE 'OFF' END + ',' + CHAR(13) +
' ALLOW_PAGE_LOCKS = ' + CASE SI.allow_page_locks WHEN 1 THEN 'ON' ELSE 'OFF' END + ',' + CHAR(13) +
CASE SI.type WHEN 2 THEN 'SORT_IN_TEMPDB = OFF,DROP_EXISTING = OFF,' ELSE '' END +
CASE WHEN SI.fill_factor > 0 THEN ' FILLFACTOR = ' + CONVERT(VARCHAR(3),SI.fill_factor) + ',' ELSE '' END +
' STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, ONLINE = OFF) ON ' + QUOTEname(SFG.name)
,@IncludeSQL = ( SELECT QUOTEname(SC.name) + ','
FROM sys.index_columns SIC
JOIN sys.columns SC
ON SIC.object_id = SC.object_id
AND SIC.column_id = SC.column_id
WHERE SIC.object_id = SI.object_id
AND SIC.index_id = SI.index_id
AND SIC.is_included_column = 1
ORDER BY SIC.key_ordinal
FOR XML PATH('')
)
,@WhereSQL = SI.filter_definition
FROM sys.indexes SI
JOIN sys.filegroups SFG
ON SI.data_space_id =SFG.data_space_id
WHERE object_id = @TableID
AND index_id = @IndexID
SELECT @IndexColsSQL = '(' + SUBSTRING(@IndexColsSQL,1,LEN(@IndexColsSQL)-1) + ')'
IF LTRIM(RTRIM(@IncludeSQL)) <> ''
SELECT @IncludeSQL = ' INCLUDE (' + SUBSTRING(@IncludeSQL,1,LEN(@IncludeSQL)-1) + ')'
IF LTRIM(RTRIM(@WhereSQL)) <> ''
SELECT @WhereSQL = ' WHERE (' + @WhereSQL + ')'
END
SELECT @CreateSQL = @CreateSQL
+ @IndexColsSQL + CASE WHEN @IndexColsSQL <> '' THEN CHAR(13) ELSE '' END
+ ISNULL(@IncludeSQL,'') + CASE WHEN @IncludeSQL <> '' THEN CHAR(13) ELSE '' END
+ ISNULL(@WhereSQL,'') + CASE WHEN @WhereSQL <> '' THEN CHAR(13) ELSE '' END
+ @WithSQL
UPDATE [dbo].[#CollationIDXTable]
SET CreateScript = @CreateSQL
,DropScript = @DropSQL
,ExistsScript = @ExistsSQL
WHERE Schema_ID = @SchemaID
AND Object_ID = @TableID
AND Index_ID = @IndexID
END
INSERT INTO #ChangeCollationObjectsBackupTbl
(SchemaID, TableID, ObjectName, ObjectType, CreateScript, DropScript, ExistsScript, Processed)
SELECT Schema_ID,Object_ID,Indexname,'Index-'
+ CASE IndexType
WHEN 1 THEN 'Clustered'
WHEN 2 THEN 'NonClustered'
ELSE ''
END
,CreateScript, DropScript, ExistsScript, 0
FROM [#CollationIDXTable]
-----------------------------------------------------------------------------------------------------------------------------
-- Backup Check Constraints
-----------------------------------------------------------------------------------------------------------------------------
INSERT INTO #ChangeCollationObjectsBackupTbl
(SchemaID, TableID, ObjectName, ObjectType, CreateScript, DropScript, ExistsScript, Processed)
SELECT CCC.SchemaID
,CCC.TableID
,SCC.name
,'ComputedColumn'
,'ALTER TABLE ' + QUOTEname(CCC.SchemaName) + '.' + QUOTEname(CCC.TableName) +
' ADD CONSTRAINT ' + QUOTEname(SCC.name) + ' CHECK ' + SCC.definition
,'ALTER TABLE ' + QUOTEname(CCC.SchemaName) + '.' + QUOTEname(CCC.TableName) +
' DROP CONSTRAINT ' + QUOTEname(SCC.name)
,'EXISTS (SELECT 1
FROM sys.check_constraints SCC
JOIN sys.tables ST
ON ST.object_id = SCC.Parent_object_id
JOIN sys.schemas SCH
ON ST.schema_id = SCH.schema_id
WHERE SCC.name =''' + SCC.name + '''
AND ST.name =''' + CCC.TableName + '''
AND SCH.name =''' + CCC.SchemaName + ''')'
,0
FROM sys.check_constraints SCC
JOIN #ChangeCollationTables CCC
ON SCC.parent_object_id = CCC.TableID
JOIN sys.tables ST
ON ST.object_id = CCC.TableID
AND ST.schema_id = CCC.SchemaID
-----------------------------------------------------------------------------------------------------------------------------
-- Backup Foreignkey Constraints
-----------------------------------------------------------------------------------------------------------------------------
INSERT INTO #ChangeCollationObjectsBackupTbl
(SchemaID, TableID, ObjectName, ObjectType, CreateScript, DropScript, ExistsScript, Processed)
SELECT STP.schema_id
,STP.object_id
,SF.name
,'Foreign Key'
,' ALTER TABLE ' + QUOTEname(SCHEMA_name(STP.schema_id)) + '.' + QUOTEname(STP.name)
+' WITH CHECK ADD CONSTRAINT ' + QUOTEname(SF.name) + ' FOREIGN KEY(' +
STUFF
(
(
SELECT ',' + QUOTEname(SC.name)
FROM sys.foreign_key_columns SFC
JOIN sys.columns SC
ON SC.object_id = SFC.parent_object_id
AND SC.column_id = SFC.parent_column_id
WHERE SFC.constraint_object_id = SF.object_id
ORDER BY SC.column_id
FOR XML PATH ('')
),1,1,'')
+ ') REFERENCES ' + QUOTEname(SCHEMA_name(STC.schema_id)) + '.' + QUOTEname(STC.name)
+ ' (' +
+ STUFF
(
(
SELECT ',' + QUOTEname(SC.name)
FROM sys.foreign_key_columns SFC
JOIN sys.columns SC
ON SC.object_id = SFC.referenced_object_id
AND SC.column_id = SFC.referenced_column_id
WHERE SFC.constraint_object_id = SF.object_id
ORDER BY SC.column_id
FOR XML PATH ('')
),1,1,'')
+ ')'
,'ALTER TABLE ' + QUOTEname(SCHEMA_name(STP.schema_id)) + '.' + QUOTEname(STP.name) + ' DROP CONSTRAINT [' + SF.name + ']'
,' EXISTS ( SELECT 1 FROM sys.FOREIGN_KEYS WHERE name =''' + SF.name + ''' and parent_object_id = ' + CONVERT(varchar(50),SF.parent_object_id) + ')'
,0
FROM sys.foreign_keys SF
JOIN sys.tables STP
ON STP.object_id = SF.parent_object_id
JOIN sys.tables STC
ON STC.object_id = SF.referenced_object_id
WHERE EXISTS (
SELECT 1
FROM sys.foreign_key_columns SFCIn
JOIN #ChangeCollationColumns CCC
ON SFCIn.parent_object_id = CCC.TableID
AND SFCIn.parent_column_id = CCC.ColumnID
WHERE SFCIn.constraint_object_id = SF.object_id
)
UNION
SELECT STP.schema_id
,STP.object_id
,SF.name
,'Foreign Key'
,' ALTER TABLE ' + QUOTEname(SCHEMA_name(STC.schema_id)) + '.' + QUOTEname(STC.name)
+' WITH CHECK ADD CONSTRAINT ' + QUOTEname(SF.name) + ' FOREIGN KEY(' +
STUFF
(
(
SELECT ',' + QUOTEname(SC.name)
FROM sys.foreign_key_columns SFC
JOIN sys.columns SC
ON SC.object_id = SFC.parent_object_id
AND SC.column_id = SFC.parent_column_id
WHERE SFC.constraint_object_id = SF.object_id
ORDER BY SC.column_id
FOR XML PATH ('')
),1,1,'')
+ ') REFERENCES ' + QUOTEname(SCHEMA_name(STP.schema_id)) + '.' + QUOTEname(STP.name)
+ ' (' +
+ STUFF
(
(
SELECT ',' + QUOTEname(SC.name)
FROM sys.foreign_key_columns SFC
JOIN sys.columns SC
ON SC.object_id = SFC.referenced_object_id
AND SC.column_id = SFC.referenced_column_id
WHERE SFC.constraint_object_id = SF.object_id
ORDER BY SC.column_id
FOR XML PATH ('')
),1,1,'')
+ ')'
,'ALTER TABLE ' + QUOTEname(SCHEMA_name(STC.schema_id)) + '.' + QUOTEname(STC.name) + ' DROP CONSTRAINT [' + SF.name + ']'
,' EXISTS ( SELECT 1 FROM sys.FOREIGN_KEYS WHERE name =''' + SF.name + ''' and parent_object_id = ' + CONVERT(varchar(50),SF.parent_object_id) + ')'
,0
FROM sys.foreign_keys SF
JOIN sys.tables STP
ON STP.object_id = SF.referenced_object_id
JOIN sys.tables STC
ON STC.object_id = SF.parent_object_id
WHERE EXISTS (
SELECT 1
FROM sys.foreign_key_columns SFCIn
JOIN #ChangeCollationColumns CCC
ON SFCIn.referenced_object_id = CCC.TableID
AND SFCIn.referenced_column_id = CCC.ColumnID
WHERE SFCIn.constraint_object_id = SF.object_id
)
-----------------------------------------------------------------------------------------------------------------------------
-- Loop through Tables to change Collation
-----------------------------------------------------------------------------------------------------------------------------
DECLARE @BackupID int
DECLARE @ObjectType VARCHAR(50)
DECLARE @ObjectName sysname
-----------------------------------------------------------------------------------------------------------------------------
-- Inner Loop -- Drop the objects
-----------------------------------------------------------------------------------------------------------------------------
UPDATE [dbo].[#ChangeCollationTables]
SET Processed = 0
WHILE EXISTS(SELECT 1
FROM #ChangeCollationTables
WHERE ISNULL(Processed,0) = 0
)
BEGIN
SELECT @sql = ''
SELECT TOP 1 @SchemaID = SchemaID
,@TableID = TableID
,@TableName = TableName
,@SchemaName = SchemaName
--,@sql = 'IF ' + ExistsScript + CHAR(13) + DropScript + CHAR(13)
FROM [dbo].[#ChangeCollationTables]
WHERE ISNULL(Processed,0) = 0
ORDER BY RunRank DESC, SchemaID ASC,TableID ASC
UPDATE #ChangeCollationObjectsBackupTbl
SET Processed = 0
WHERE SchemaID = @SchemaID
AND TableID = @TableID
WHILE EXISTS(SELECT 1
FROM #ChangeCollationObjectsBackupTbl
WHERE ISNULL(Processed,0) = 0
AND SchemaID = @SchemaID
AND TableID = @TableID
)
BEGIN
SELECT @sql = ''
SELECT TOP 1 @BackupID = BackupID
,@ObjectName = ObjectName
,@ObjectType = ObjectType
,@sql = 'IF ' + ExistsScript + CHAR(13) + DropScript + CHAR(13)
FROM #ChangeCollationObjectsBackupTbl
WHERE ISNULL(Processed,0) = 0
AND SchemaID = @SchemaID
AND TableID = @TableID
ORDER BY BackupID DESC
IF @GenerateScriptsOnly = 1
BEGIN
PRINT @sql
END
ELSE
BEGIN
PRINT @sql
EXEC (@sql)
END
UPDATE #ChangeCollationObjectsBackupTbl
SET Processed = 1
WHERE SchemaID = @SchemaID
AND TableID = @TableID
AND BackupID = @BackupID
END
UPDATE [dbo].[#ChangeCollationTables]
SET Processed = 1
WHERE SchemaID = @SchemaID
AND TableID = @TableID
END
-----------------------------------------------------------------------------------------------------------------------------
-- Apply the collation changes
-----------------------------------------------------------------------------------------------------------------------------
UPDATE [dbo].[#ChangeCollationTables]
SET Processed = 0
WHILE EXISTS(SELECT 1
FROM #ChangeCollationTables
WHERE ISNULL(Processed,0) = 0
)
BEGIN
SELECT @sql = ''
SELECT TOP 1 @SchemaID = SchemaID
,@TableID = TableID
,@TableName = TableName
,@SchemaName = SchemaName
--,@sql = 'IF ' + ExistsScript + CHAR(13) + DropScript + CHAR(13)
FROM [dbo].[#ChangeCollationTables]
WHERE ISNULL(Processed,0) = 0
ORDER BY RunRank DESC, SchemaID ASC,TableID ASC
SELECT @sql = ''
SELECT @sql = @sql + AlterScript + CHAR(13)
FROM #ChangeCollationColumns
WHERE SchemaID = @SchemaID
AND TableID = @TableID
IF @GenerateScriptsOnly = 1
BEGIN
PRINT @sql
END
ELSE
BEGIN
PRINT @sql
EXEC (@sql)
END
UPDATE [dbo].[#ChangeCollationTables]
SET Processed = 1
WHERE SchemaID = @SchemaID
AND TableID = @TableID
END
-----------------------------------------------------------------------------------------------------------------------------
-- Inner Loop -- ReApply the objects
-----------------------------------------------------------------------------------------------------------------------------
UPDATE [dbo].[#ChangeCollationTables]
SET Processed = 0
WHILE EXISTS(SELECT 1
FROM #ChangeCollationTables
WHERE ISNULL(Processed,0) = 0
)
BEGIN
SELECT @sql = ''
SELECT TOP 1 @SchemaID = SchemaID
,@TableID = TableID
,@TableName = TableName
,@SchemaName = SchemaName
--,@sql = 'IF ' + ExistsScript + CHAR(13) + DropScript + CHAR(13)
FROM [dbo].[#ChangeCollationTables]
WHERE ISNULL(Processed,0) = 0
ORDER BY RunRank asc, SchemaID ASC,TableID ASC
UPDATE #ChangeCollationObjectsBackupTbl
SET Processed = 0
WHERE SchemaID = @SchemaID
AND TableID = @TableID
WHILE EXISTS(SELECT 1
FROM #ChangeCollationObjectsBackupTbl
WHERE ISNULL(Processed,0) = 0
AND SchemaID = @SchemaID
AND TableID = @TableID
AND CreateScript IS NOT NULL
)
BEGIN
SELECT @sql = ''
SELECT TOP 1 @BackupID = BackupID
,@ObjectName = ObjectName
,@ObjectType = ObjectType
,@sql = CASE ObjectType
WHEN 'View' THEN CreateScript + CHAR(13)
ELSE 'IF NOT ' + ExistsScript + CHAR(13) + CreateScript + CHAR(13)
END
FROM #ChangeCollationObjectsBackupTbl
WHERE ISNULL(Processed,0) = 0
AND SchemaID = @SchemaID
AND TableID = @TableID
AND CreateScript IS NOT NULL
ORDER BY BackupID ASC
IF @GenerateScriptsOnly = 1
BEGIN
PRINT @sql
END
ELSE
BEGIN
PRINT @sql
EXEC (@sql)
END
UPDATE #ChangeCollationObjectsBackupTbl
SET Processed = 1
WHERE SchemaID = @SchemaID
AND TableID = @TableID
AND BackupID = @BackupID
END
UPDATE [dbo].[#ChangeCollationTables]
SET Processed = 1
WHERE SchemaID = @SchemaID
AND TableID = @TableID
END
GO
Update – Junho de 2023
Como o processo acima pode causar dúvidas em como ser aplicado, decidimos demonstrar o print do passo a passo a ser executado.
Obs Importante: Faça um backup antes de alterar as informações da base
Obs Importante 2: Certifique-se de ninguém estar usando a base
- Clique com o botão direito na base, e depois em Propriedades
- Confirme o nome da collation que esta usando
- Abra a query nessa base de dados, selecione tudo e execute (mesmo com erros a direita por causa das tabelas temporárias que serão criadas)
- Abra uma nova aba, e execute a procedure
- Feche todas as abas de query dessa base
- Agora clique com o botão direito na base, e vá em Propriedades novamente
- Vá em Opções e mude a Collation
- Agora é só confirmar e aguardar encerrar (dependendo do tamanho da base pode demorar)
Bom pessoal, por hoje é só.
Abraços e até a próxima.





Esta dando erro.
Msg 207, Level 16, State 1, Procedure ChangeCollation_SP, Line 504
Invalid column name ‘filter_definition’.
Boa noite Brother.
Que estranho.
Qual é a versão do SQL Server?
Um grande abraço.
Olá, Dan. Estou precisando fazer justamente isso em minha base do protheus. Teria o contato de alguém para fazer esse trabalho para mim?
Estou em processo de migrar o dicionário pro banco, mas gera um erro e o suporte informou ser por conta do collation.
Bom dia Paulo, tudo bem?
Nós da Atilio Sistemas ( https://atiliosistemas.com/ ) fazemos esse tipo de trabalho, mas sempre com agendas marcadas com antecedência.
Se você quiser entrar em contato e solicitar um orçamento, fique à vontade.
Ou se for o caso, se quiser fazer por ai mesmo, seriam apenas dois passos:
1. Copiar todo o código acima e colar no seu SQL Server (talvez apenas mudando a linha 10, colocando o nome da collation atual sua)
2. Execute o código acima para ele criar a procedure no seu ambiente
3. Ai você executa a procedure ChangeCollation_SP
Um grande abraço.
Dan, o código não está rodando. O sql acusa erro em várias linhas. Como faço para solicitar um orçamento com vocês? Tenho urgência.
Boa tarde Paulo.
Te mandamos por eMail o procedimento de como rodar. Assim que possível veja e nos dê um feedback se deu tudo certo.
Abraços.
Ao executar deu erro pra mim:
Msg 530, Nível 16, Estado 1, Procedimento ChangeCollation_SP, Linha 175 [Linha de Início do Lote 0]
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.
Boa tarde Kadu, tudo joia?
Que estranho, pode ser alguma configuração. Em todo o caso, pesquisei no Google por “The maximum recursion 100 has been exhausted before statement completion sql server” e achei esses dois links que podem te ajudar:
https://learn.microsoft.com/en-us/answers/questions/105463/the-maximum-recursion-100-has-been-exhausted-befor
https://stackoverflow.com/questions/9650045/the-maximum-recursion-100-has-been-exhausted-before-statement-completion
Abraços.
Bom dia, Dan.
Preciso trocar a collation do banco do Protheus. Num banco de teste, cópia do de produção, criei a stored procedure com sucesso e rodei a mesma logo em seguida, mas o resultado não foi o esperado.
O SQL Server 2016 informa que a consulta foi concluída com erros. Tentei localizar o erro, mas sem sucesso, pois não tem nenhum erro registrado. Após quase 7 horas rodando, o resultado foi um conjunto de vários comandos!
Tem vários desses (mais de 270 mil linhas), considerando dropar índices e estatísticas, caso eles existam:
IF EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[TDQ010]’) AND name = N’TDQ010_UNQ’)
DROP INDEX [TDQ010_UNQ] ON [dbo].[TDQ010] WITH ( ONLINE = OFF )
Depois vem um bloco de quase 80 mil linhas procurando alterar a collation das colunas das diversas tabelas do banco:
ALTER TABLE [dbo].[TDQ010] ALTER COLUMN [TDQ_FILIAL] varchar(4) COLLATE Latin1_General_BIN NOT NULL
Aqui notei uma coisa estranha, algumas das linhas vem com a estrutura apenas ALTER TABLE [dbo].[nome_da_tabela] ALTER, o que não faz sentido.
Por fim, vem outro bloco, de cerca de 116 mil linhas, recriando os índices:
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[TDQ010]’) AND name = N’TDQ0101′)
CREATE NONCLUSTERED INDEX [TDQ0101] ON [dbo].[TDQ010](TDQ_FILIAL ASC ,TDQ_CODPLA ASC ,TDQ_CODEQP ASC ,R_E_C_N_O_ ASC ,D_E_L_E_T_ ASC )
WITH (PAD_INDEX = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON,
SORT_IN_TEMPDB = OFF,DROP_EXISTING = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, ONLINE = OFF) ON [PRIMARY]
E finalizando, vem o Horário de conclusão: 2024-04-14T22:28:58.5959883-03:00
Eu deveria agora tentar rodar os comandos gerados?
De acordo com os prints aqui nesse artigo, a própria stored procedure é que devia fazer isso.
Bom dia Roberto, tudo joia?
Que estranho, não peguei erros assim. Pelo log que você enviou, são só dessa tabela TDQ010 que acontece isso?
Se sim, o que você poderia fazer é:
a. Via APSDU, faça um backup da tabela TDQ
b. Via APSDU, faça um drop table na tabela TDQ
c. Rode o processamento da collation
d. Faça o sistema recriar a TDQ (execute um DbSelectArea ou ChkFile na TDQ)
e. Rode um append dos dados que foram feitos backup
Um grande abraço.
Bom dia, Dan. Sim, tudo joia.
Não é só com a TDQ010 não! São com diversas tabelas. Essa é apenas a primeira de cada bloco que informa sobre diversas tabelas, como disse no post anterior.
Boa tarde Roberto, opa que bom que esta tudo joia.
Faça o teste com a TDQ e veja se ela some da lista (após fazer o que comentamos do backup e drop)
Se sim, então era alguma sujeira na tabela, ai então faça uma user function em advpl que faz o seguinte
A. Da um select em todas as tabelas q tem dados ou monta um array com as tabelas que estão com problema
B. Vem percorrendo todas essas tabelas
C. Nesse laço de repetição você vai
C1. Fazer o bkp da tabela para uma nova, pode usar por exemplo a FWDbCreate
C2. Ai você vai replicar os dados dessa tabela pra copia (pode usar um reclock mesmo com todos os dados ou tentar outra forma)
C3. Ai você vai dar um drop na tabela
C4. Ai vai recriar a tabela com checkFile
C5. Então volte os dados do backup
Um grande abraço.
Bom dia, Dan.
Estava pendente dar um retorno sobre isso.
Depois dessa simulação em abril/2024, em bases de teste, hoje fiz em produção. O mesmo erro se manifestou, mas conforme eu já tinha descoberto na fase de testes, basta rodar novamente a stored procedure.
Minha base do Protheus tem 5.214 tabelas e 106.075 campos. Nas bases de teste do TSS, por exemplo, muito menores (apenas 95 campos distribuídos em 1.665 tabelas), o problema não ocorria. A stored rodou e converteu todos os campos.
O que ocorreu é que nessas bases com muitos campos, o buffer acaba e os comandos alter table são gerados incompletos para várias colunas.
Lembra que comentei isso?
“Aqui notei uma coisa estranha, algumas das linhas vem com a estrutura apenas ALTER TABLE [dbo].[nome_da_tabela] ALTER, o que não faz sentido.”
Ele não consegue completar a linha de comando devido a falta de buffer.
Então, depois da primeira execução, simplesmente rodei a ChangeCollation_SP novamente. Como ela pula os campos que já estão na collation order destino, a tarefa foi completada com sucesso para as colunas restantes.
Depois foi só conferir que deu tudo certo.
Bom dia Roberto, tudo joia?
Ah entendi, então era devido ao buffer em memória.
Supondo então que, num cenário onde o buffer seria pequeno, em uma base com 300 campos, e o buffer conseguisse 100 por vezes, teria que rodar o script umas 3 vezes para acertar todos os campos.
Muito obrigado pelo feedback.
Tenha uma ótima e abençoada semana.
Um grande abraço.