What is collation and how it changes in SQL Server

What is collation and how it changes in SQL Server
What is collation and how it changes in SQL Server

With one sentence collation is the way characters are encoded in a database. That is, you define whether it will be case sensitive  (uppercase, lowercase), what language the varchar and text fields are in.

Fields like nvarchar which are in Unicode format are not affected by the encoding of the characters, but it affects the sort since depending on the collation there may be a different order of letters or be affected by the case sensitive.

The collation on system databases and on  user databases could be different from each other since SQL Server 2000 and later.

How can we see what collation all databases have with query

select name,databasepropertyex(name,'COLLATION'),databasepropertyex(name,'Recovery'),databasepropertyex(name,'STATUS') from master.dbo.sysdatabases
What is collation and how it changes in SQL Server

The collation at master database defines the collation in the rest of the system bases as well. This may affect system functions such as database login and sort on tempdb.

The collation on  user databases affects as we mentioned first of all what language the non-unicode characters and there is a basic problem when we decide to change it. That whatever data was passed up to the moment of the change will remain with the collation they had.

Even if we have the wrong collation in one base, we can read the field with a different collation, e.g. on a base that is in  SQL_Latin1_General_CP1253_CI_AI  with the following query we can read the Greek characters:

select cast (Onoma as varchar(100)) collate Greek_CI_AS
from Pelatis

To change the collation in the database

To change the collation to user databases the following steps should be taken so that we do not have the problem we mentioned above and that not all records in the database have the same collation (it is good to have done them backup before we start):

1 – We export the generate script of the base (its structure):

Right click on the database, Tasks, Generate scripts… , Script entire database and all database objects

2 – With the following script that I have made, generate the statements so that we can do bcp out and bcp in the whole base. We execute it and keep both columns.

declare 
@user varchar(50),
@pass varchar(50),
@pathout varchar(200),
@pathin varchar(200)

set @user = 'username'
set @pass = 'password'
set @pathout = 'c:\backups\'
set @pathin = 'c:\backups\'

SELECT 
'bcp '           --bcp
+  QUOTENAME(DB_NAME())+ '.'               
+  QUOTENAME(SCHEMA_NAME(SCHEMA_ID))+ '.'  -- 
+  QUOTENAME(name)                         
+ ' out ' +@pathout                        
+  REPLACE(SCHEMA_NAME(schema_id),' ','') + '_' 
+  REPLACE(name,' ','')                    
+ '.txt -S'
+ REPLACE(REPLACE(QUOTENAME(@@servername),'[',''),']','')  --server name
+' -U'+@user+' -P'+@pass+' '  --username  pass
+'-n -t[cdel] -r[rdel]'  ,  
AS  BCP_OUT,

'bcp '           --bcp
+  QUOTENAME(DB_NAME())+ '.'               
+  QUOTENAME(SCHEMA_NAME(SCHEMA_ID))+ '.' 
+  QUOTENAME(name)                         
+ ' in '+@pathin                          
+  REPLACE(SCHEMA_NAME(schema_id),' ','') + '_' 
+  REPLACE(name,' ','')                   
+ '.txt -S'
+ REPLACE(REPLACE(QUOTENAME(@@servername),'[',''),']','')  --server name
+' -U'+@user+' -P'+@pass+' '  --username  pass
+'-w -n'
+' -e'+@pathin +'logbcpin.txt'
+' -t[cdel] -r[rdel]'   ,  
AS  BCP_IN
from sys.tables

3 – We execute it bcp out in command prompt from step 2.

4 – Turn off the base.

drop database [AdventureWorks2017 ]

5 – We run the script that creates the database from step 1.

6 – We change the collation in the base.

ALTER DATABASE AdventureWorks2017 GREEK_CI_AI ;GO

7 – We execute it bcp in in command prompt from step 2. (after first disabling the constraints)

EXEC sp_msforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT all"

8 – When the bcp in is completed, enable the constraints.

EXEC sp_msforeachtable "ALTER TABLE ? CHECK CONSTRAINT all"

To change the collation on the system bases

To change the collation in the system bases it is necessary rebuild the master and this results in:

  • May all be lost jobs from msdb
  • May all be lost logins kai oi clock
  • Let them be lost dblinks
  • To lose all the rights that have been passed to the roles such as e.g. to the public
  • To be done detach All the user databases
  • Let them all perish the parameters of the instance (which we had in sp_configure) such as max_memory, max_dop etc.

The procedure for changing the collation in systemic ones is as follows:

1 – Backup of the master, msdb, model databases (with the corresponding modification of the paths in the following script that I have made)

DECLARE @date CHAR(8)
SET @date = (SELECT CONVERT(char(8), GETDATE(), 112))

DECLARE @backup_loc VARCHAR(125)  
SET @backup_loc = 'X:\backups\'  -- Σε ποιά τοποθεσία θα παρθούν τα backups

DECLARE @rest_dbf VARCHAR(125)
SET @rest_dbf = 'D:\Databases\'   -- Σε ποιά τοποθεσία θα γίνουν restore τα datafiles

DECLARE @rest_log VARCHAR(125)
SET @rest_log = 'L:\Logfiles\';  -- Σε ποια τοποθεσία θα γίνουν restore τα logfiles


WITH CTE ( DatabaseName, Npath )
          AS ( SELECT DISTINCT
                        DB_NAME(database_id) ,
                        STUFF((SELECT   ' ' + CHAR(13)+', MOVE ''' + name + ''''
                                        + CASE Type
                                            WHEN 0 THEN ' TO ' +@rest_dbf
                                            ELSE ' TO ' +@rest_log
                                          END
                                        + REVERSE(LEFT(REVERSE(physical_name),
                                                       CHARINDEX('\',
                                                              REVERSE(physical_name),
                                                              1) - 1)) + ''''
                               FROM     sys.master_files sm1
                               WHERE    sm1.database_id = sm2.database_ID
                        FOR   XML PATH('') ,
                                  TYPE).value('.', 'varchar(max)'), 1, 1, '') AS Npath
               FROM     sys.master_files sm2
  )
--select * from CTE
SELECT
    'BACKUP DATABASE ' + name + ' TO DISK = ''' + @backup_loc + '' + name + '_' + @date + '.bak'' WITH COMPRESSION, COPY_ONLY, STATS=5' as Backup_Commands_onSource,
    'RESTORE DATABASE '+ name + ' FROM DISK = ''' + @backup_loc + '' + name + '_' + @date + '.bak'' WITH RECOVERY, REPLACE, STATS=5 ' + CTE.Npath as Restore_Commands_onTarget
FROM sys.databases d
    INNER JOIN CTE ON d.name = cte.databasename
WHERE d.name in('master','model','msdb') --DBs
GO

2 – Backup of jobs (press F7 in SSMS to open the object explorer details, select all jobs and right click, script job as, create to, file.. as in the image below):

What is collation and how it changes in SQL Server

3 – Backup logins, users, roles with Greg Ryan's query:

--Script All Logins / Users / and Roles

/****************************************************************
This Script Generates A script to Create all Logins, Server Roles
, DB Users and DB roles on a SQL Server

Greg Ryan

10/31/2013
****************************************************************/
SET NOCOUNT ON

DECLARE
        @sql nvarchar(max)
,       @Line int = 1
,       @max int = 0
,       @@CurDB nvarchar(100) = ''

CREATE TABLE #SQL
       (
        Idx int IDENTITY
       ,xSQL nvarchar(max)
       )

INSERT INTO #SQL
        ( xSQL
        )
        SELECT
                'IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE name = N'''
                + QUOTENAME(name) + ''')
' + '	CREATE LOGIN ' + QUOTENAME(name) + ' WITH PASSWORD='
                + sys.fn_varbintohexstr(password_hash) + ' HASHED, SID='
                + sys.fn_varbintohexstr(sid) + ', ' + 'DEFAULT_DATABASE='
                + QUOTENAME(COALESCE(default_database_name , 'master'))
                + ', DEFAULT_LANGUAGE='
                + QUOTENAME(COALESCE(default_language_name , 'us_english'))
                + ', CHECK_EXPIRATION=' + CASE is_expiration_checked
                                            WHEN 1 THEN 'ON'
                                            ELSE 'OFF'
                                          END + ', CHECK_POLICY='
                + CASE is_policy_checked
                    WHEN 1 THEN 'ON'
                    ELSE 'OFF'
                  END + '
Go

'
            FROM
                sys.sql_logins
            WHERE
                name <> 'sa'

INSERT INTO #SQL
        ( xSQL
        )
        SELECT
                'IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE name = N'''
                + QUOTENAME(name) + ''')
' + '	CREATE LOGIN ' + QUOTENAME(name) + ' FROM WINDOWS WITH '
                + 'DEFAULT_DATABASE='
                + QUOTENAME(COALESCE(default_database_name , 'master'))
                + ', DEFAULT_LANGUAGE='
                + QUOTENAME(COALESCE(default_language_name , 'us_english'))
                + ';
Go

'
            FROM
                sys.server_principals
            WHERE
                type IN ( 'U' , 'G' )
                AND name NOT IN ( 'BUILTIN\Administrators' ,
                                  'NT AUTHORITY\SYSTEM' );
                                  
PRINT '/*****************************************************************************************/'
PRINT '/*************************************** Create Logins ***********************************/'
PRINT '/*****************************************************************************************/'
SELECT
        @Max = MAX(idx)
    FROM
        #SQL 
WHILE @Line <= @max
      BEGIN



            SELECT
                    @sql = xSql
                FROM
                    #SQL AS s
                WHERE
                    idx = @Line
            PRINT @sql

            SET @line = @line + 1
        
      END
DROP TABLE #SQL

CREATE TABLE #SQL2
       (
        Idx int IDENTITY
       ,xSQL nvarchar(max)
       )

INSERT INTO #SQL2
        ( xSQL
        )
        SELECT
                'EXEC sp_addsrvrolemember ' + QUOTENAME(L.name) + ', '
                + QUOTENAME(R.name) + ';
GO

'
            FROM
                sys.server_principals L
            JOIN sys.server_role_members RM
            ON  L.principal_id = RM.member_principal_id
            JOIN sys.server_principals R
            ON  RM.role_principal_id = R.principal_id
            WHERE
                L.type IN ( 'U' , 'G' , 'S' )
                AND L.name NOT IN ( 'BUILTIN\Administrators' ,
                                    'NT AUTHORITY\SYSTEM' , 'sa' );


PRINT '/*****************************************************************************************/'
PRINT '/******************************Add Server Role Members     *******************************/'
PRINT '/*****************************************************************************************/'
SELECT
        @Max = MAX(idx)
    FROM
        #SQL2 
SET @line = 1
WHILE @Line <= @max
      BEGIN



            SELECT
                    @sql = xSql
                FROM
                    #SQL2 AS s
                WHERE
                    idx = @Line
            PRINT @sql

            SET @line = @line + 1
        
      END
DROP TABLE #SQL2

PRINT '/*****************************************************************************************/'
PRINT '/*****************Add User and Roles membership to Indivdual Databases********************/'
PRINT '/*****************************************************************************************/'


--Drop Table #Db
CREATE TABLE #Db
       (
        idx int IDENTITY
       ,DBName nvarchar(100)
       );



INSERT INTO #Db
        SELECT
                name
            FROM
                master.dbo.sysdatabases
            WHERE
                name NOT IN ( 'Master' , 'Model' , 'msdb' , 'tempdb' )
            ORDER BY
                name;


SELECT
        @Max = MAX(idx)
    FROM
        #Db
SET @line = 1
--Select * from #Db


--Exec sp_executesql @SQL

WHILE @line <= @Max
      BEGIN
            SELECT
                    @@CurDB = DBName
                FROM
                    #Db
                WHERE
                    idx = @line

            SET @SQL = 'Use ' + @@CurDB + '

Declare  @@Script NVarChar(4000) = ''''
DECLARE cur CURSOR FOR

Select  ''Use ' + @@CurDB + ';
Go
IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N'''''' +
                mp.[name] + '''''')
CREATE USER ['' + mp.[name] + ''] FOR LOGIN ['' +mp.[name] + ''] WITH DEFAULT_SCHEMA=[dbo]; ''+ CHAR(13)+CHAR(10) +
''GO'' + CHAR(13)+CHAR(10) +

''EXEC sp_addrolemember N'''''' + rp.name + '''''', N''''['' + mp.[name] + '']''''; 
Go''  
FROM sys.database_role_members a
INNER JOIN sys.database_principals rp ON rp.principal_id = a.role_principal_id
INNER JOIN sys.database_principals AS mp ON mp.principal_id = a.member_principal_id


OPEN cur

FETCH NEXT FROM cur INTO @@Script;
WHILE @@FETCH_STATUS = 0
BEGIN   
PRINT @@Script
FETCH NEXT FROM cur INTO @@Script;
END

CLOSE cur;
DEALLOCATE cur;';
--Print @SQL
Exec sp_executesql @SQL;
--Set @@Script = ''
            SET @Line = @Line + 1

      END

DROP TABLE #Db

4 – Backup db links, certificates (If they exist). It can be easily done with the SSMS GUI.

What is collation and how it changes in SQL Server

5 – We do detach bases with the following script we can automate it:

USE [master];
GO
DECLARE @database NVARCHAR(200) ,
@cmd NVARCHAR(1000) ,
@detach_cmd NVARCHAR(4000) ,
@attach_cmd NVARCHAR(4000) ,
@file NVARCHAR(1000) ,
@i INT ,
@whichdb INT=0,
@dbname nvarchar(200)='%%',
@Drive_Now nvarchar(10)='',
@Drive_New nvarchar(10)='';
create table #tempattach(attach_script varchar(max));
create table #tempdetach(detach_script varchar(max));

--set 4 for only userdbs, 0 for all dbs
set @whichdb = 4

--if u want to attach db on new drive with same path uncomment with the correct drive letters
--set @Drive_Now = 'C:\' set @Drive_New = 'Z:\'

--uncomment if u pick only one DBname
--set @dbname = '%iDBA%'

DECLARE dbname_cur CURSOR STATIC LOCAL FORWARD_ONLY
FOR
SELECT RTRIM(LTRIM([name]))
FROM sys.databases
WHERE database_id > @whichdb
and name like @dbname;

OPEN dbname_cur

FETCH NEXT FROM dbname_cur INTO @database

WHILE @@FETCH_STATUS = 0
BEGIN
SELECT @i = 1;    
SET @attach_cmd = 
        'EXEC sp_attach_db @dbname = ''' + @database + '''' + CHAR(10);
  -- Change skip checks to false if you want to update statistics before you detach.
    SET @detach_cmd =
        'EXEC sp_detach_db @dbname = ''' + @database
        + ''' , @skipchecks = ''true'';' + CHAR(10);

  -- Get a list of files for the database
    DECLARE dbfiles_cur CURSOR STATIC LOCAL FORWARD_ONLY
    FOR
        SELECT  physical_name
        FROM    sys.master_files
        WHERE   database_id = DB_ID(@database)
        ORDER BY [file_id];

    OPEN dbfiles_cur

    FETCH NEXT FROM dbfiles_cur INTO @file

    WHILE @@FETCH_STATUS = 0 
        BEGIN
            SET @attach_cmd = @attach_cmd + '    ,@filename'
                + CAST(@i AS NVARCHAR(10)) + ' = ''' + @file + ''''
                + CHAR(10);
            SET @i = @i + 1;

            FETCH NEXT FROM dbfiles_cur INTO @file
        END

    CLOSE dbfiles_cur;

    DEALLOCATE dbfiles_cur; 
   insert into #tempattach values(REPLACE(@attach_cmd,@Drive_Now,@Drive_New));
   insert into #tempdetach values(@detach_cmd);

    FETCH NEXT FROM dbname_cur INTO @database
END
CLOSE dbname_cur;

DEALLOCATE dbname_cur;

select * from #tempdetach
select * from #tempattach

drop table #tempattach
drop table #tempdetach

6 – Double click on the SQL Server ISO whose instance it is, e.g. 2016

7 – In the command prompt as an administrator, cd to the drive where the ISO was mounted and run the following command, after first putting the correct instance name, windows authentication, and the collation we want to go to:

 F:\>cd F:

Setup /QUIET /ACTION=REBUILDDATABASE /INSTANCENAME=MSSQLSERVER /SQLSYSADMINACCOUNTS=INSTANCENAME\SA /SAPWD=KWDIKOS /SQLCOLLATION=SQL_Latin1_General_CP1253_CI_AI

8 – The change is complete, so we have to go through the jobs, logins, parameters, etc. again.

9 – Attach the user databases. (from the results of step 5)

Sources:

Share it

4 thoughts on “Τι είναι το collation και πως αλλάζει στον SQL Server

  1. We must be more careful in what we recommend because we may be led into irreversible mistakes. The database structure as a whole is not done with what you suggest (SCRIPT DATABASE AS—>File). This makes the base's characteristics script and not its objects (Tables etc). The result is that when someone goes to pass the data, the corresponding tables do not exist!!! So if someone hadn't backed up the entire database then they will have to rebuild its structure from scratch by hand!!!

    1. By mistake I had mentioned the Script Database option instead of Generate Scripts which exports the entire database schema along with the schema of all tables so the steps mentioned apply normally.

Leave a reply