I need to change the collation on a restored database to match the server and the tempdb database. I understand that I can use ALTER DATABASE and ALTER TABLE to change collertion. But will it only affect new data added to the tables? Do i have to go down the road with
Script needed to re-create your user databases and all the objects in them
Export all your data using a tool such as the bcp Utility
Create a new database
Script the tables in right collection and import all data.
It's a Microsoft Sql Server 2008
I used this script:
DECLARE #collation NVARCHAR(64)
SET #collation = 'Latin1_General_CI_AS'
SELECT
'ALTER TABLE [' + TABLE_SCHEMA + '].[' + TABLE_NAME + '] '
+ 'ALTER COLUMN [' + COLUMN_NAME + '] '
+ DATA_TYPE + '(' + CASE CHARACTER_MAXIMUM_LENGTH
WHEN -1 THEN 'MAX'
ELSE CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR) END + ') '
+ 'COLLATE ' + #collation + ' '
+ CASE WHEN IS_NULLABLE = 'NO' THEN 'NOT NULL' ELSE 'NULL' END
FROM INFORMATION_SCHEMA.columns
WHERE COLLATION_NAME IS NOT NULL
AND TABLE_NAME IN ( SELECT TABLE_NAME from information_schema.tables WHERE table_type = 'BASE TABLE' )
AND COLLATION_NAME <> #collation
Also check Set or Change the Database Collation
Related
We have a database that we technically have admin access to on a database server. I can access the server and database on SSMS just fine, but can't actually RDP into that server as its controlled by the DBA team, which is expected.
I'm tasked to optimize the database, as currently we have a ridiculous amount of views that are just pivot and unpivot images of tables we implemented as part of a POC design last summer.
Since we are satisfied with the POC phase, I'm trying to reduce these views and so before I do that I need to export the data to csv file.
I am using the following command (based on this suggestion) in SSMS to do so:
EXEC xp_cmdshell 'bcp "select * from view1" queryout "\\fin-nas.dev.domain.com\abc01\CSV_Files\file1.csv" -c -t, -U "user123" -P "Password123" -S "dbserver.dev.domain.com" -d "Database1"';
I am running into the following issue:
SQLState = S1000, NativeError = 0
Error = [Microsoft][ODBC Driver 13 for SQL Server]Unable to open BCP host data-file
After some research, I realized that either:
file1.csv has to be created first in the directory specified (\\fin-nas.dev.domain.com\abc01\CSV_Files) which I did create
or this is basically an access issue, so from this suggestion here, I ran
xp_cmdshell 'whoami';
and that returned what seems to be the account associated with the DBA login...
domain\dom_sasql$
Does this mean I'm completely unable to export the data and only the DBA can do so?
Normally what you do is map a network drive, after that it's the same as normal.
The bellow script does such a task by exporting all data associated with a given file-group
you can change the query to export a single statement but this script was used for regular export and import between domains that did not trust each other where an FTP service was transporting the data between domains.
We could not do all data as it were "golden" records that only where allowed to be exported
/*******************************************************************
this script will generate thebcp out commands for all data from the
users current connected database. The this script will only work if
both databases have the same ddl version, meaning same tables, same
columns same data definitions.
*******************************************************************/
SET NOCOUNT ON
GO
DECLARE #Path nvarchar(2000) = 'f:\export\' -- storage location for bcp dump (needs to have lots of space!)
, #Batchsize nvarchar(40) = '1000000' -- COMMIT EVERY n RECORDS
, #Xmlformat bit = 0 -- 1 for yes to xml format, 0 for not xml
, #SourceServerinstance nvarchar(200) = 'localhost'-- SQL Server \ Instance name
, #Security nvarchar(800) = ' -T ' -- options are -T (trusted), -Uloginid -Ploginpassword
, #GenerateDump bit = 0 -- 0 for storing data to disk, not 1 for loading from disk
, #FileGroup sysname = 'Data'; -- Table filegroup that we are intrested in
--> set output to text and execute the query, then copy the generated commands, validate and execucte them
--------------------------------Do not edit below this line-----------------------------------------------------------------
DECLARE #filter TABLE(TABLE_NAME sysname)
INSERT INTO #filter (TABLE_NAME)
SELECT o.name
FROM sys.indexes as i
JOIN sys.objects as o on o.object_id = i.object_id
WHERE i.data_space_id = FILEGROUP_ID(#FileGroup)
AND i.type_desc ='CLUSTERED'
and o.name not like 'sys%'
order by 1
if(#GenerateDump=0)
begin
--BCP-OUT TABLES
SELECT 'bcp "' + QUOTENAME( TABLE_CATALOG ) + '.' + QUOTENAME( TABLE_SCHEMA )
+ '.' + QUOTENAME( TABLE_NAME ) + '" out "' + #path + '' + TABLE_NAME + '.dat" -q -b"'
+ #batchsize + '" -e"' + #path + 'Error_' + TABLE_NAME + '.err" -n -CRAW -o"' + #path + ''
+ TABLE_NAME + '.out" -S"' + #SourceServerinstance + '" ' + #security + ''
FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME IN (SELECT TABLE_NAME FROM #filter)
if(#Xmlformat=0)
begin
print 'REM CREATE NON-XML FORMAT FILE '
SELECT 'bcp "' + QUOTENAME( TABLE_CATALOG ) + '.' + QUOTENAME( TABLE_SCHEMA ) + '.'+
QUOTENAME( TABLE_NAME ) + '" format nul -n -CRAW -f "' + #path + ''
+ TABLE_NAME + '.fmt" -S"' + #SourceServerinstance + '" ' + #security + ''
FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME IN (SELECT TABLE_NAME FROM #filter)
end
else
begin
PRINT 'REM XML FORMAT FILE'
SELECT 'bcp "' +QUOTENAME( TABLE_CATALOG ) + '.' + QUOTENAME( TABLE_SCHEMA )
+ '.' + QUOTENAME( TABLE_NAME ) + '" format nul -x -n -CRAW -f "'
+ #path + '' + TABLE_NAME + '.xml" -S"' + #SourceServerinstance + '" ' + #security + ''
FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME IN (SELECT TABLE_NAME FROM #filter)
end
end
else
begin
print '--Make sure you backup your database first'
--GENERATE CONSTRAINT NO CHECK
PRINT '--NO CHECK CONSTRAINTS'
SELECT 'ALTER TABLE ' + QUOTENAME(TABLE_SCHEMA)+'.'+QUOTENAME( TABLE_NAME ) + ' NOCHECK CONSTRAINT ' + QUOTENAME( CONSTRAINT_NAME )
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME IN (SELECT TABLE_NAME FROM #filter)
PRINT '--DISABLE TRIGGERS'
SELECT 'ALTER TABLE ' + QUOTENAME(TABLE_SCHEMA)+'.'+QUOTENAME( TABLE_NAME ) + ' DISABLE TRIGGER ALL'
FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME IN (SELECT TABLE_NAME FROM #filter)
--TRUNCATE TABLE
SELECT 'TRUNCATE TABLE ' +QUOTENAME(TABLE_SCHEMA)+'.'+QUOTENAME( TABLE_NAME ) + '
GO '
FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME IN (SELECT TABLE_NAME FROM #filter)
--BULK INSERT
SELECT DISTINCT 'BULK INSERT ' + QUOTENAME(TABLE_CATALOG) + '.'
+ QUOTENAME( TABLE_SCHEMA ) + '.' + QUOTENAME( TABLE_NAME ) + '
FROM ''' + #path + '' + TABLE_NAME + '.Dat''
WITH (FORMATFILE = ''' + #path + '' + TABLE_NAME + '.FMT'',
BATCHSIZE = ' + #batchsize + ',
ERRORFILE = ''' + #path + 'BI_' + TABLE_NAME + '.ERR'',
TABLOCK);
GO '
FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME IN (SELECT TABLE_NAME FROM #filter)
--GENERATE CONSTRAINT CHECK CONSTRAINT TO VERIFY DATA AFTER LOAD
PRINT '--CHECK CONSTRAINT'
SELECT 'ALTER TABLE ' + QUOTENAME(TABLE_SCHEMA)+'.'+QUOTENAME( TABLE_NAME ) + ' CHECK CONSTRAINT '
FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME IN (SELECT TABLE_NAME FROM #filter)
SELECT 'ALTER TABLE ' + QUOTENAME(TABLE_SCHEMA)+'.'+ QUOTENAME( TABLE_NAME ) + ' ENABLE TRIGGER ALL'
FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME IN (SELECT TABLE_NAME FROM #filter)
end
Running bcp on local computer connecting to the SQL server over the network works if you dont have access. This is because xp_cmdshell is trying to push from the SQL Server to an external folder. Just connect from your client machine using BCP and pull the data locally.
Open CMD and run the bcp command.
Example (with spaced out columns):
bcp "select * from (select 'Style Code' as [Style Code], 'MY Code' as [MY Code] union select [Style Code], [MY Code] from UnPivoted_Table) q order by case [Style Code] when 'Style Code' then 0 ELSE 1 END" queryout "\\server.domain.com\CSV_Files\file1.csv" -c -t, -T -S "server1.db.com" -d "DB1"
Is there a possibility to alter a column from "allows null" to "does not allow null" without knowledge of the actual data type of the column?
I think no, so I have made as the basic skeleton code for my stored procedure:
SELECT t.name,c.max_length FROM sys.types t
LEFT JOIN sys.columns c ON(t.system_type_id = c.system_type_id)
WHERE object_id=OBJECT_ID(#TableName) AND c.name=#FieldName;
and
EXEC('UPDATE ' + #TableName + ' SET ' + #FieldName + ' = ' + #DefaultValue + ' WHERE ' + #FieldName + ' IS NULL');
EXEC('ALTER TABLE ' + #TableName + ' ALTER COLUMN ' + #FieldName + ' NOT NULL');
I guess now I only have to get the return values from the first query back into the second. I can't get my head around how to get the values into a variable and then access them again. Ideas?
Since the INFORMATION_SCHEMA has all required information and is part of a SQL standard, it might be better to use that in this case (however, SQL Server's ALTER TABLE ALTER COLUMN is non-standard anyway so it might not matter as much).
Either way, you should also be checking for whether there's character length and/or numeric precision being specified, and make sure you're altering the table in the correct schema (and not getting dbo.TableName instead of customschema.TableName). You could try something like this (I used INFORMATION_SCHEMA here but you could easily refactor this to use the sys.columns view):
DECLARE #retVal VARCHAR(500);
SELECT #retVal =
CASE WHEN CHARACTER_MAXIMUM_LENGTH > 0
THEN CONCAT(DATA_TYPE, '(', CHARACTER_MAXIMUM_LENGTH ,')')
WHEN CHARACTER_MAXIMUM_LENGTH = -1 AND DATA_TYPE <> 'xml'
THEN CONCAT(DATA_TYPE, '(MAX)')
WHEN DATA_TYPE IN ('numeric', 'decimal')
THEN CONCAT(DATA_TYPE, '(', NUMERIC_PRECISION,',', NUMERIC_SCALE,')')
ELSE DATA_TYPE
END
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = #schemaName
AND TABLE_NAME = #tableName
AND COLUMN_NAME = #columnName
#retVal will now capture datatypes like int, varchar(100), varbinary(MAX), or decimal(10,2) correctly.
And then build up a dynamic SQL Query like this:
DECLARE #sql VARCHAR(MAX);
SET #sql = 'ALTER TABLE ' + #schemaName + '.' + #tableName + ' ALTER COLUMN ' + #columnName + ' ' + #retVal + ' NOT NULL;'
EXEC(#sql);
You select values into variables like this:
SELECT #Var1=t.name,#Var2=c.max_length FROM sys.types t
LEFT JOIN sys.columns c ON(t.system_type_id = c.system_type_id)
WHERE object_id=OBJECT_ID(#TableName) AND c.name=#FieldName;
This of course assumes that you have already declared Var1 & Var2, and that your query will only return one row.
I am trying to run a query to drop server logins coming from windows and recreate them. I run this query to drop logins and it works fine
USE master
SELECT
'IF EXISTS (SELECT * FROM sys.server_principals WHERE name = ' +
'''' + [name] + '''' + ')' +
' BEGIN DROP LOGIN ' + QUOTENAME([name]) + ' END; ' +
'CREATE LOGIN ' + QUOTENAME([name]) + ' FROM WINDOWS; '
FROM sys.server_principals
WHERE type_desc IN ('WINDOWS_GROUP' , 'WINDOWS_LOGIN')
But the following query
USE master
SELECT
'ALTER LOGIN ' + QUOTENAME([name]) + ' WITH DEFAULT_DATABASE = ' +
spr.default_database_name
+ 'GRANT ' + spe.permission_name + ' to ' + QUOTENAME([name])
+ CASE WHEN spr.default_database_name IS NOT NULL
THEN ' WITH DEFAULT DATABASE = ' + spr.default_database_name
ELSE
''
END
FROM sys.server_principals spr INNER JOIN
sys.server_permissions spe ON spr.principal_id = spe.grantee_principal_id
WHERE spr.type_desc IN ('WINDOWS_GROUP', 'WINDOWS_LOGIN')
is returning this error
Cannot resolve collation conflict for column 1 in SELECT statement.
So when I investigated the issue it is coming from the sysname column default_database_name
The query ran when I used spr.default_database_name COLLATE Latin1_General_CI_AI
My three parts question is: Did I get the problem because of a row or because the column data type is sysname?. Is COLLATE Latin1_General_CI_AI the correct syntax for this scenario?.
when I run this query
SELECT name, description
FROM sys.fn_helpcollations()
The results are showing many collations for different languages. So how did I get this error for a completely English database.
Edit:
I ran these two queries to check the collation property for database and server
SELECT DATABASEPROPERTYEX(N'master', N'Collation')
SELECT SERVERPROPERTY(N'Collation')
and the result in both of them was
SQL_Latin1_General_CP1_CI_AS
It may help to bring the default collate for database and server instance, changing the query as shown below:
SELECT
'ALTER LOGIN ' + QUOTENAME([name]) + ' WITH DEFAULT_DATABASE = ' +
spr.default_database_name COLLATE database_default
+ 'GRANT ' + spe.permission_name + ' to ' + QUOTENAME([name])
+ CASE WHEN spr.default_database_name IS NOT NULL
THEN ' WITH DEFAULT DATABASE = ' + spr.default_database_name COLLATE database_default
ELSE
''
END
FROM sys.server_principals spr INNER JOIN
sys.server_permissions spe ON spr.principal_id = spe.grantee_principal_id
WHERE spr.type_desc IN ('WINDOWS_GROUP', 'WINDOWS_LOGIN')
It looks like someone at MS has fixed the collation on sys.server_permissions.state_desc as Latin1_General_CI_AS_KS_WS
I've tried on a number of instances (SQL 2016) and found the same problem. Putting "collate database_default" works.
Solution for your question: Specify collation for two columns where you CONCAT
USE master
GO
SELECT
'ALTER LOGIN ' + QUOTENAME([name]) COLLATE SQL_Latin1_General_CP1_CI_AS +
' WITH DEFAULT_DATABASE = ' +
spr.default_database_name COLLATE SQL_Latin1_General_CP1_CI_AS
+ 'GRANT ' + spe.permission_name + ' to ' + QUOTENAME([name])
+ CASE WHEN spr.default_database_name IS NOT NULL
THEN ' WITH DEFAULT DATABASE = ' + spr.default_database_name
ELSE
''
END
FROM sys.server_principals spr INNER JOIN
sys.server_permissions spe ON spr.principal_id = spe.grantee_principal_id
WHERE spr.type_desc IN ('WINDOWS_GROUP', 'WINDOWS_LOGIN')
I need to copy several tables definition from one database to other database. I have found a way to generate create statement for every individual table that I need. However I cannot execute every 'create statement' that is generated. I expect that following script will recreate all table that match criteria in NEWDB database. However it didn't. It always recreate only one table instead of all tables that I need. I am not sql expert and I believe I am missing something here.
declare #sql varchar(8000)
select #sql='create table [NEWDB].[' + OBJECT_SCHEMA_NAME(so.id) + '].[' + so.name + '] (' + o.list + ')' + CASE WHEN tc.Constraint_Name IS NULL THEN '' ELSE 'ALTER TABLE ' + so.Name + ' ADD CONSTRAINT ' + tc.Constraint_Name + ' PRIMARY KEY ' + ' (' + LEFT(j.List, Len(j.List)-1) + ')' END
from sysobjects so
cross apply
(SELECT
' ['+column_name+'] ' +
data_type + case data_type
when 'sql_variant' then ''
when 'text' then ''
when 'ntext' then ''
when 'xml' then ''
when 'decimal' then '(' + cast(numeric_precision as varchar) + ', ' + cast(numeric_scale as varchar) + ')'
else coalesce('('+case when character_maximum_length = -1 then 'MAX' else cast(character_maximum_length as varchar) end +')','') end + ' ' +
case when exists (
select id from syscolumns
where object_name(id)=so.name
and name=column_name
and columnproperty(id,name,'IsIdentity') = 1
) then
'IDENTITY(' +
cast(ident_seed(so.name) as varchar) + ',' +
cast(ident_incr(so.name) as varchar) + ')'
else ''
end + ' ' +
(case when IS_NULLABLE = 'No' then 'NOT ' else '' end ) + 'NULL ' +
case when information_schema.columns.COLUMN_DEFAULT IS NOT NULL THEN 'DEFAULT '+ information_schema.columns.COLUMN_DEFAULT ELSE '' END + ', '
from information_schema.columns where table_name = so.name
order by ordinal_position
FOR XML PATH('')) o (list)
left join
information_schema.table_constraints tc
on tc.Table_name = so.Name
AND tc.Constraint_Type = 'PRIMARY KEY'
cross apply
(select '[' + Column_Name + '], '
FROM information_schema.key_column_usage kcu
WHERE kcu.Constraint_Name = tc.Constraint_Name
ORDER BY
ORDINAL_POSITION
FOR XML PATH('')) j (list)
where xtype = 'U'
AND name NOT IN ('dtproperties')
-- criteria
AND name like 'AUD_%'
order by OBJECT_SCHEMA_NAME(so.id),so.name
-- this execute one table only regardless how many table in actual result set
exec (#sql)
SSMS supports scripting all schema (and optionally data as INSERTS) in 2k8:
Right click on a database and select Tasks->Generate Scripts...
Select All tables OR Pick the tables you require, Click Next.
....
See answer here: to "Script all data from SQL Server database"
That's a lot easier than trying to roll your own.
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.
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.
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.
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
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
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