SQL Server - script to update database columns from varchar to nvarchar if not already nvarchar

StackOverflow https://stackoverflow.com/questions/179987

  •  05-07-2019
  •  | 
  •  

Question

I am in a situation where I must update an existing database structure from varchar to nvarchar using a script. Since this script is run everytime a configuration application is run, I would rather determine if a column has already been changed to nvarchar and not perform an alter on the table. The databases which I must support are SQL Server 2000, 2005 and 2008.

Was it helpful?

Solution

The following query should get you what you need:

IF EXISTS 
  (SELECT *
   FROM sysobjects syo
   JOIN syscolumns syc ON
     syc.id = syo.id
   JOIN systypes syt ON
     syt.xtype = syc.xtype
   WHERE 
     syt.name = 'nvarchar' AND
     syo.name = 'MY TABLE NAME' AND
     syc.name = 'MY COLUMN NAME')
BEGIN
   ALTER ...
END

OTHER TIPS

You can run the following script which will give you a set of ALTER commands:

SELECT 'ALTER TABLE ' + isnull(schema_name(syo.id), 'dbo') + '.' +  syo.name 
    + ' ALTER COLUMN ' + syc.name + ' NVARCHAR(' + case syc.length when -1 then 'MAX' 
        ELSE convert(nvarchar(10),syc.length) end + ');'
   FROM sysobjects syo
   JOIN syscolumns syc ON
     syc.id = syo.id
   JOIN systypes syt ON
     syt.xtype = syc.xtype
   WHERE 
     syt.name = 'varchar' 
    and syo.xtype='U'

There are, however, a couple of quick caveats for you.

  1. This will only do tables. You'll want to scan all of your sprocs and functions to make sure they are changed to NVARCHAR as well.
  2. If you have a VARCHAR > 4000 you will need to modify it to be NVARCHAR(MAX)

But those should be easily doable with this template.

If you want this to run automagically you can set it in a WHILE clause.

The issue with Josef's answer is that it would change NOT NULL fields to NULL after executing the queries. The following manipulation fixes it:

SELECT cmd = 'alter table [' + c.table_schema + '].[' + c.table_name 
 + '] alter column [' + c.column_name + '] nvarchar('
 +CASE WHEN CHARACTER_MAXIMUM_LENGTH<=4000
       THEN CAST(CHARACTER_MAXIMUM_LENGTH as varchar(10)) ELSE 'max' END+')' 
 + CASE WHEN IS_NULLABLE='NO' THEN ' NOT NULL' ELSE '' END,*
FROM information_schema.columns c
WHERE c.data_type='varchar' 
ORDER BY CHARACTER_MAXIMUM_LENGTH desc

Credits to Igor's answer

Fixed the space issue and added schema

SELECT 'ALTER TABLE [' + isnull(schema_name(syo.object_id), sysc.name) + '].[' +  syo.name 
    + '] ALTER COLUMN ' + syc.name + ' NVARCHAR(' + case syc.max_length when -1 then 'MAX' 
        ELSE convert(nvarchar(10),syc.max_length) end + ');'
   FROM sys.objects syo
   JOIN sys.columns syc ON
     syc.object_id= syo.object_id
   JOIN sys.types syt ON
     syt.system_type_id = syc.system_type_id
    JOIN sys.schemas sysc ON
    syo.schema_id=sysc.schema_id
   WHERE 
     syt.name = 'varchar' 
    and syo.type='U'

Further updated to fix MAX being replaced with -1.

SELECT cmd = 'ALTER TABLE [' + c.table_schema + '].[' + c.table_name 
 + '] ALTER COLUMN [' + c.column_name + '] NVARCHAR('
 +CASE WHEN CHARACTER_MAXIMUM_LENGTH<=4000 THEN 
 CASE WHEN CHARACTER_MAXIMUM_LENGTH = -1 THEN
 'MAX' ELSE CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR(10)) END ELSE 'MAX' END+')' 
 + CASE WHEN IS_NULLABLE='NO' THEN ' NOT NULL' ELSE '' END,*
FROM information_schema.columns c
WHERE c.data_type='VARCHAR' 
ORDER BY CHARACTER_MAXIMUM_LENGTH DESC

Credit to Nezam's Answer

And another one to manage default values:

SELECT cmd = 
CASE WHEN name IS NOT NULL THEN
    'ALTER TABLE ' + c.table_name + ' DROP CONSTRAINT ' + d.name + '; ' +
    'ALTER TABLE [' + c.table_schema + '].[' + c.table_name + '] ALTER COLUMN [' + c.column_name + '] ' + 
    'NVARCHAR(' +
    CASE WHEN CHARACTER_MAXIMUM_LENGTH <= 4000 THEN 
        CASE WHEN CHARACTER_MAXIMUM_LENGTH = -1 THEN
            'MAX' 
        ELSE 
            CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR(10)) 
        END 
     ELSE 
        'MAX' 
    END 
    + ')' +
    CASE WHEN IS_NULLABLE='NO' THEN ' NOT NULL' ELSE '' END + '; ' + 
    'ALTER TABLE '+ c.table_name + ' ADD CONSTRAINT ' + d.name +' DEFAULT '+ c.column_default + ' FOR ' + c.column_name + ';'
ELSE
    'ALTER TABLE [' + c.table_schema + '].[' + c.table_name + '] ALTER COLUMN [' + c.column_name + '] ' +
    'NVARCHAR(' +
    CASE WHEN CHARACTER_MAXIMUM_LENGTH<=4000 THEN
        CASE WHEN CHARACTER_MAXIMUM_LENGTH = -1 THEN
            'MAX' 
        ELSE 
            CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR(10)) 
        END 
    ELSE 
        'MAX' 
    END
    + ')' +
    CASE WHEN IS_NULLABLE='NO' THEN ' NOT NULL' ELSE '' END 
END,d.name, c.*
FROM information_schema.columns c
LEFT OUTER JOIN sys.default_constraints d ON d.parent_object_id = object_id(c.table_name)
AND d.parent_column_id = columnproperty(object_id(c.table_name), c.column_name, 'ColumnId')
WHERE c.data_type='VARCHAR' 
ORDER BY CHARACTER_MAXIMUM_LENGTH DESC
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top