I have a database which current collation is French_CI_AS, which means that searches are case insensitive, but accent sensitive. I had the impression that changing the collation of the database to French_CI_AI would solve my problem so I did it. However, I still can't make a simple search work Accent-Insensitive. I check and the table collation is French_CI_AI, which is probably because it was changed along with the database.
Is there some way to make the search possible with Accent insensitive on all database ? Or is there any way to make a single seach that way ? I only have one or two Stored procedures that would need that so I could go that way to.
In case it makes any differences, the datatype I look for are all nvarchar(n)s.
Thanks
You can make the search use whatever collation you want, e.g.
WHERE column COLLATE FRENCH_CI_AI LIKE '%something%' COLLATE FRENCH_CI_AI
However I suspect it will work better if you actually fix the column (which didn't happen when you changed the database collation). Leaving out any constraints and other dependencies, the short answer of how to fix this:
ALTER TABLE dbo.foo ADD newcol NVARCHAR(32) COLLATE FRENCH_CI_AI;
UPDATE dbo.foo SET newcol = oldcol;
ALTER TABLE dbo.foo DROP COLUMN oldcol;
EXEC sp_rename N'dbo.foo.newcol', N'oldcol', 'COLUMN';
First, chaging the database collation does not impact existing column in table but will affect newly created table and column. It's the default collation to use when creating new object in the database.
So, what you have to do is to find all existing column and updating current collation.
You can do it with a sql cursor and query all table and column and update collation for specific type or column name.
By example :
1) you change the collation for future object
USE master;
GO
ALTER DATABASE databasename COLLATE French_CI_AI ;
GO
2) change manually the column collation for specific column
ALTER TABLE tablenameX ALTER COLUMN LastName varchar(100) COLLATE French_CI_AI NULL
ALTER TABLE tablenameY ALTER COLUMN FirstName varchar(100) COLLATE French_CI_AI NULL
3) generate a sql script with cursor
You need to decide what column to change collation. It can be by type, name or specific table.
You can start by looking wich type of caracter type you have and replace them in the example, see the column cursor.
SELECT distinct DATA_TYPE from information_schema.columns
you can refer to this post :
http://www.codeproject.com/Articles/302405/The-Easy-way-of-changing-Collation-of-all-Database
declare #CollationName as nvarchar = 'Latin1_General_CI_AS'
declare #tablename as nvarchar(100) = ''
declare #sqltext as nvarchar(100) = ''
declare #columnname as nvarchar(200) = ''
declare #DataType as nvarchar(100) = ''
declare #CharacterMaxLen as int = 0
declare #IsNullable as bit = 0
DECLARE MyTableCursor Cursor
FOR
SELECT * from information_schema.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'
OPEN MyTableCursor
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE MyColumnCursor Cursor
FOR
SELECT COLUMN_NAME,DATA_TYPE, CHARACTER_MAXIMUM_LENGTH,
IS_NULLABLE from information_schema.columns
WHERE table_name = #TableName AND (Data_Type LIKE '%char%'
OR Data_Type LIKE '%text%') AND COLLATION_NAME <> #CollationName
ORDER BY ordinal_position
Open MyColumnCursor
FETCH NEXT FROM MyColumnCursor INTO #ColumnName, #DataType,
#CharacterMaxLen, #IsNullable
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQLText = 'ALTER TABLE ' + #TableName + ' ALTER COLUMN [' + #ColumnName + '] ' +
#DataType + '(' + CASE WHEN #CharacterMaxLen = -1 THEN 'MAX' ELSE #CharacterMaxLen END +
') COLLATE ' + #CollationName + ' ' +
CASE WHEN #IsNullable = 'NO' THEN 'NOT NULL' ELSE 'NULL' END
PRINT #SQLText
FETCH NEXT FROM MyColumnCursor INTO #ColumnName, #DataType,
#CharacterMaxLen, #IsNullable
END
CLOSE MyColumnCursor
DEALLOCATE MyColumnCursor
FETCH NEXT FROM MyTableCursor INTO #TableName
END
CLOSE MyTableCursor
DEALLOCATE MyTableCursor
Related
I created few user defined types in my database as below
CREATE TYPE [dbo].[StringID] FROM [nvarchar](20) NOT NULL
and assigned them to various tables. The tables in my database are in various schemas (not only dbo)
But I realized I need bigger field, and I need to alter, e.g increase from [nvarchar](20) to [nvarchar](50), but there is no ALTER TYPE statement.
I need a script that uses a temp table/cursor whatever and saves all the tables and fields where my type is used. Then change existing fields to base type - e.g. from CustID [StringID] to CustID [nvarchar(20)].
Drop the user type and recreate it with new type - e.g. nvarchar(50)
and finally set back fields to user type
I do not have rules defined on types, so don't have to drop rules and re-add them.
Any help is appreciated.
This is what I normally use, albeit a bit manual:
/* Add a 'temporary' UDDT with the new definition */
exec sp_addtype t_myudt_tmp, 'numeric(18,5)', NULL
/* Build a command to alter all the existing columns - cut and
** paste the output, then run it */
select 'alter table dbo.' + TABLE_NAME +
' alter column ' + COLUMN_NAME + ' t_myudt_tmp'
from INFORMATION_SCHEMA.COLUMNS
where DOMAIN_NAME = 't_myudt'
/* Remove the old UDDT */
exec sp_droptype t_mydut
/* Rename the 'temporary' UDDT to the correct name */
exec sp_rename 't_myudt_tmp', 't_myudt', 'USERDATATYPE'
We are using the following procedure, it allows us to re-create a type from scratch, which is "a start". It renames the existing type, creates the type, recompiles stored procs and then drops the old type. This takes care of scenarios where simply dropping the old type-definition fails due to references to that type.
Usage Example:
exec RECREATE_TYPE #schema='dbo', #typ_nme='typ_foo', #sql='AS TABLE([bar] varchar(10) NOT NULL)'
Code:
CREATE PROCEDURE [dbo].[RECREATE_TYPE]
#schema VARCHAR(100), -- the schema name for the existing type
#typ_nme VARCHAR(128), -- the type-name (without schema name)
#sql VARCHAR(MAX) -- the SQL to create a type WITHOUT the "CREATE TYPE schema.typename" part
AS DECLARE
#scid BIGINT,
#typ_id BIGINT,
#temp_nme VARCHAR(1000),
#msg VARCHAR(200)
BEGIN
-- find the existing type by schema and name
SELECT #scid = [SCHEMA_ID] FROM sys.schemas WHERE UPPER(name) = UPPER(#schema);
IF (#scid IS NULL) BEGIN
SET #msg = 'Schema ''' + #schema + ''' not found.';
RAISERROR (#msg, 1, 0);
END;
SELECT #typ_id = system_type_id FROM sys.types WHERE UPPER(name) = UPPER(#typ_nme);
SET #temp_nme = #typ_nme + '_rcrt'; -- temporary name for the existing type
-- if the type-to-be-recreated actually exists, then rename it (give it a temporary name)
-- if it doesn't exist, then that's OK, too.
IF (#typ_id IS NOT NULL) BEGIN
exec sp_rename #objname=#typ_nme, #newname= #temp_nme, #objtype='USERDATATYPE'
END;
-- now create the new type
SET #sql = 'CREATE TYPE ' + #schema + '.' + #typ_nme + ' ' + #sql;
exec sp_sqlexec #sql;
-- if we are RE-creating a type (as opposed to just creating a brand-spanking-new type)...
IF (#typ_id IS NOT NULL) BEGIN
exec recompile_prog; -- then recompile all stored procs (that may have used the type)
exec sp_droptype #typename=#temp_nme; -- and drop the temporary type which is now no longer referenced
END;
END
GO
CREATE PROCEDURE [dbo].[recompile_prog]
AS
BEGIN
SET NOCOUNT ON;
DECLARE #v TABLE (RecID INT IDENTITY(1,1), spname sysname)
-- retrieve the list of stored procedures
INSERT INTO
#v(spname)
SELECT
'[' + s.[name] + '].[' + items.name + ']'
FROM
(SELECT sp.name, sp.schema_id, sp.is_ms_shipped FROM sys.procedures sp UNION SELECT so.name, so.SCHEMA_ID, so.is_ms_shipped FROM sys.objects so WHERE so.type_desc LIKE '%FUNCTION%') items
INNER JOIN sys.schemas s ON s.schema_id = items.schema_id
WHERE is_ms_shipped = 0;
-- counter variables
DECLARE #cnt INT, #Tot INT;
SELECT #cnt = 1;
SELECT #Tot = COUNT(*) FROM #v;
DECLARE #spname sysname
-- start the loop
WHILE #Cnt <= #Tot BEGIN
SELECT #spname = spname
FROM #v
WHERE RecID = #Cnt;
--PRINT 'refreshing...' + #spname
BEGIN TRY -- refresh the stored procedure
EXEC sp_refreshsqlmodule #spname
END TRY
BEGIN CATCH
PRINT 'Validation failed for : ' + #spname + ', Error:' + ERROR_MESSAGE();
END CATCH
SET #Cnt = #cnt + 1;
END;
END
there's a good example of a more comprehensive script here
It's worth noting that this script will include views if you have any. I ran it and instead of exec'ing inline generated a script as the output which I then tweaked and ran.
Also, if you have functions/sprocs using the user defeined types you'll need to drop those before running your script.
Lesson Learned: in future, don't bother with UDTs they're more hassle than they're worth.
SET NOCOUNT ON
DECLARE #udt VARCHAR(150)
DECLARE #udtschema VARCHAR(150)
DECLARE #newudtschema VARCHAR(150)
DECLARE #newudtDataType VARCHAR(150)
DECLARE #newudtDataSize smallint
DECLARE #OtherParameter VARCHAR(50)
SET #udt = 'Name' -- Existing UDDT
SET #udtschema = 'dbo' -- Schema of the UDDT
SET #newudtDataType = 'varchar' -- Data type for te new UDDT
SET #newudtDataSize = 500 -- Lenght of the new UDDT
SET #newudtschema = 'dbo' -- Schema of the new UDDT
SET #OtherParameter = ' NULL' -- Other parameters like NULL , NOT NULL
DECLARE #Datatype VARCHAR(50),
#Datasize SMALLINT
DECLARE #varcharDataType VARCHAR(50)
DECLARE #Schemaname VARCHAR(50),
#TableName VARCHAR(50),
#FiledName VARCHAR(50)
CREATE TABLE #udtflds
(
Schemaname VARCHAR(50),
TableName VARCHAR(50),
FiledName VARCHAR(50)
)
SELECT TOP 1
#Datatype = Data_type,
#Datasize = character_maximum_length
FROM INFORMATION_SCHEMA.COLUMNS
WHERE Domain_name = #udt
AND Domain_schema = #udtschema
SET #varcharDataType = #Datatype
IF #DataType Like '%char%'
AND #Datasize IS NOT NULL
AND ( #newudtDataType <> 'varchar(max)'
OR #newudtDataType <> 'nvarchar(max)'
)
BEGIN
SET #varcharDataType = #varcharDataType + '('
+ CAST(#Datasize AS VARCHAR(50)) + ')'
END
INSERT INTO #udtflds
SELECT TABLE_SCHEMA,
TABLE_NAME,
Column_Name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE Domain_name = #udt
AND Domain_schema = #udtschema
DECLARE #exec VARCHAR(500)
DECLARE alter_cursor CURSOR
FOR SELECT Schemaname,
TableName,
FiledName
FROM #udtflds
OPEN alter_cursor
FETCH NEXT FROM alter_cursor INTO #Schemaname, #TableName, #FiledName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #exec = 'Alter Table ' + #Schemaname + '.' + #TableName
+ ' ALTER COLUMN ' + #FiledName + ' ' + #varcharDataType
EXECUTE ( #exec
)
FETCH NEXT FROM alter_cursor INTO #Schemaname, #TableName, #FiledName
END
CLOSE alter_cursor
SET #exec = 'DROP TYPE [' + #udtschema + '].[' + #udt + ']'
EXEC ( #exec
)
SET #varcharDataType = #newudtDataType
IF #newudtDataType Like '%char%'
AND #newudtDataSize IS NOT NULL
AND ( #newudtDataType <> 'varchar(max)'
OR #newudtDataType <> 'nvarchar(max)'
)
BEGIN
SET #varcharDataType = #varcharDataType + '('
+ CAST(#newudtDataSize AS VARCHAR(50)) + ')'
END
SET #exec = 'CREATE TYPE [' + #newudtschema + '].[' + #udt + '] FROM '
+ #varcharDataType + ' ' + #OtherParameter
EXEC ( #exec
)
OPEN alter_cursor
FETCH NEXT FROM alter_cursor INTO #Schemaname, #TableName, #FiledName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #exec = 'Alter Table ' + #Schemaname + '.' + #TableName
+ ' ALTER COLUMN ' + #FiledName + ' ' + '[' + #newudtschema
+ '].[' + #udt + ']'
EXECUTE ( #exec
)
FETCH NEXT FROM alter_cursor INTO #Schemaname, #TableName, #FiledName
END
CLOSE alter_cursor
DEALLOCATE alter_cursor
SELECT *
FROM #udtflds
DROP TABLE #udtflds
1: http://www.sql-server-performance.com/2008/how-to-alter-a-uddt/ has replaced http://www.sql-server-performance.com/faq/How_to_alter_a%20_UDDT_p1.aspx
The simplest way to do this is through Visual Studio's object explorer, which is also supported in the Community edition.
Once you have made a connection to SQL server, browse to the type, right click and select View Code, make your changes to the schema of the user defined type and click update. Visual Studio should show you all of the dependencies for that object and generate scripts to update the type and recompile dependencies.
As devio says there is no way to simply edit a UDT if it's in use.
A work-round through SMS that worked for me was to generate a create script and make the appropriate changes; rename the existing UDT; run the create script; recompile the related sprocs and drop the renamed version.
The solutions provided here can only be applied if the user defined types are used in table definitions only, and if the UDT columns are not indexed.
Some developers also have SP's and functions using UDT parameters, which is not covered either. (see comments on Robin's link and in the Connect entry)
The Connect entry from 2007 has finally been closed after 3 years:
Thank you for submitting this
suggestion, but given its priority
relative to the many other items in
our queue, it is unlikely that we will
actually complete it. As such, we are
closing this suggestion as “won’t
fix”.
I tried to solve a similiar problem ALTERing XML SCHEMA COLLECTIONS, and the steps seem to mostly apply to ALTER TYPE, too:
To drop a UDT, the following steps are necessary:
If a table column references the UDT, it has to be converted to the underlying type
If the table column has a default constraint, drop the default constraint
If a procedure or function has UDT parameters, the procedure or function has to be dropped
If there is an index on a UDT column, the index has to be dropped
If the index is a primary key, all foreign keys have to be dropped
If there are computed columns based on a UDT column, the computed columns have to be dropped
If there are indexes on these computed columns, the indexes have to be dropped
If there are schema-bound views, functions, or procedures based on tables containing UDT columns, these objects have to be dropped
I ran into this issue with custom types in stored procedures, and solved it with the script below. I didn't fully understand the scripts above, and I follow the rule of "if you don't know what it does, don't do it".
In a nutshell, I rename the old type, and create a new one with the original type name. Then, I tell SQL Server to refresh its details about each stored procedure using the custom type. You have to do this, as everything is still "compiled" with reference to the old type, even with the rename. In this case, the type I needed to change was "PrizeType". I hope this helps. I'm looking for feedback, too, so I learn :)
Note that you may need to go to Programmability > Types > [Appropriate User Type] and delete the object. I found that DROP TYPE doesn't appear to always drop the type even after using the statement.
/* Rename the UDDT you want to replace to another name */
exec sp_rename 'PrizeType', 'PrizeTypeOld', 'USERDATATYPE';
/* Add the updated UDDT with the new definition */
CREATE TYPE [dbo].[PrizeType] AS TABLE(
[Type] [nvarchar](50) NOT NULL,
[Description] [nvarchar](max) NOT NULL,
[ImageUrl] [varchar](max) NULL
);
/* We need to force stored procedures to refresh with the new type... let's take care of that. */
/* Get a cursor over a list of all the stored procedures that may use this and refresh them */
declare sprocs cursor
local static read_only forward_only
for
select specific_name from information_schema.routines where routine_type = 'PROCEDURE'
declare #sprocName varchar(max)
open sprocs
fetch next from sprocs into #sprocName
while ##fetch_status = 0
begin
print 'Updating ' + #sprocName;
exec sp_refreshsqlmodule #sprocName
fetch next from sprocs into #sprocName
end
close sprocs
deallocate sprocs
/* Drop the old type, now that everything's been re-assigned; must do this last */
drop type PrizeTypeOld;
New answer to an old question:
Visual Studio Database Projects handle the drop and recreate process when you deploy changes. It will drop stored procs that use UDDTs and then recreate them after dropping and recreating the data type.
1.Rename the old UDT,
2.Execute query ,
3.Drop the old UDT.
Simple DROP TYPE first then CREATE TYPE again with corrections/alterations?
There is a simple test to see if it is defined before you drop it ... much like a table, proc or function -- if I wasn't at work I would look what that is?
(I only skimmed above too ... if I read it wrong I apologise in advance! ;)
I have a dataset with numerous columns that are completely null, I am attempting to create a proc which will delte a column if it is checked to have all nulls, but I am getting numerous errors, the most recent one is that it runs without errors but does not delete the column when no non null cells are found.
Any pointers would be massively appreciated, thank you.
create or alter proc delete_nulls
(#columnname varchar(max))
as
begin;
set #columnname = trim(#columnname)
if (select count(#columnname) from dbo.dataset1 where #columnname is not null) < 0
alter table dbo.dataset1
drop column [#columnname]
end;
First, column names are of type sysname. Second, you will need to use dynamic SQL. Something along these lines. This still won't work as written because you will need to make the "IF" statement dynamic as well. That logic you may want to pull outside the stored procedure. And I agree with the comments above that this is just a start. You want more logic to make sure the table and column actually exist.
create or alter proc delete_nulls
(#columnname sysname, #tablename sysname)
as
begin;
DECLARE #my_string NVARCHAR(1000)
set #columnname = trim(#columnname)
if (select count(#columnname) from dbo.dataset1 where #columnname is not null) < 0
BEGIN
SET #my_string = N'
alter table ' + #tablename + '
drop column '+ #columnname
EXEC sp_executesql #stmt = #my_string
END
end;
I want to modify the following query:
UPDATE wp_posts
SET post_content =
REPLACE(post_content, 'http://oldlink.com', 'http://newlink.com');
To be something that goes through all tables, columns and values. Something similar to this (but this doesn't work):
UPDATE * SET *= REPLACE(*, 'http://oldlink.com', 'http://newlink.com');
I want to replace every instance of my old link to my new link in my database. Is there any way to do this?
UPDATE
Sorry, I forgot to mention, it's MySQL. I'm currently going through all the answers and I'll be back and let you know what worked. Thanks everyone!
UPDATE 2
Hi guys (and girls), I decided to just dump the database and do manual search and replace (with TextWrangler). As this isn't (currently) a large DB, it's probably the easiest way.
Here is one that works on SQL Server -- does something like this work for you?
Search and Replace SQL Server data in all columns, of all tables
Here's a stored procedure named, SearchAndReplace, that searches
through all the character columns of all tables in the current
database, and replaces the given string with another user provided
string.
This mssql script will whine a bit if there is a computed column in a table, but it will still execute:
DECLARE #searchvalue varchar(100)
DECLARE #newvalue varchar(100)
SET nocount off
SET #searchvalue = 'http://oldlink.com'
SET #newvalue = 'http://newlink.com'
SELECT * into #t FROM
(
SELECT 'update [' + a.TABLE_name + '] SET ['+ column_name+ ']=''' + #newvalue + '''
where [' +a.column_name+
']='''+#searchvalue +'''' sqlstring
FROM INFORMATION_SCHEMA.COLUMNS a
join
INFORMATION_SCHEMA.TABLES b
ON a.TABLE_name = b.TABLE_name
and b.TABLE_type = 'base table'
WHERE data_type in ('varchar', 'char', 'nvarchar')
and character_maximum_length >= len(#newvalue)
) a
DECLARE #sqlstring as nvarchar(500)
DECLARE SqlCursor CURSOR FAST_FORWARD FOR
SELECT sqlstring FROM #t
OPEN SqlCursor
FETCH NEXT FROM SqlCursor
INTO #sqlstring
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC(#sqlstring)
FETCH NEXT FROM SqlCursor
INTO #sqlstring
END
CLOSE SqlCursor
DEALLOCATE SqlCursor
DROP TABLE #t
You didn't mention the database. I currently use Sybase ASA, where you cannot do this literally but it can be done by checking the column names from a join of systable and syscolumn and then using execute immediate
I'm trying to write a stored procedure to copy a subset of data from one set of tables to an identical set of tables in a different database. The "source" database needs to be a parameter to the stored procedure.
I've struggled with this for two days now, and I thought I had a good solution:
Validate that the schemas are the same.
Create temporary "rmt" synonyms for the source tables using dynamic SQL.
Copy the data using INSERT INTO A SELECT * FROM rmtA WHERE <criteria>
Delete the synonyms.
This works pretty well for most tables, but for tables that contain an identity column, I'm forced not only to SET IDENTITY_INSERT ON & OFF, but even worse, I can't use SELECT *; I have to specify all the columns explicitly. This will be a nightmare if I add or delete columns later.
I've gotta get something out the door, so I'm going with this solution for now, but I'd like to think that there's a better solution out there somewhere.
Help?
It sounds like you're using dynamic SQL in your stored procedure, so you're ready to dynamically create your list of columns in the SELECT clause.
You can select from sys.columns to get the list of columns, and learn if the table has an identity column. Here's a query that shows the information you need to create the list of columns.
SELECT c.name, is_identity
FORM sys.columns c
WHERE object_id = object_id('MyTable')
In short, if is_identity is 1 for at least one column, you'll need to include the SET IDENTITY_INSERT. And, you would exclude any columns from the SELECT clause where is_identity = 1.
And, this approach will adapt to new columns you add to the tables.
Here's an example
DECLARE #TableName varchar(128) = 'MyTableName'
DECLARE #ColumnName varchar(128)
DECLARE #IsIdentity bit
DECLARE #TableHasIdentity bit = 0
DECLARE #sql varchar(2000) = 'SELECT '
-- create cursor to step through list of columns
DECLARE MyCurs CURSOR FOR
SELECT c.name, is_identity
FROM sys.columns c
WHERE object_id = object_id(#TableName)
ORDER BY column_id
-- open cursor and get first row
OPEN MyCurs
FETCH NEXT FROM MyCurs INTO #ColumnName, #IsIdentity
-- process each column in the table
WHILE ##FETCH_STATUS = 0
BEGIN
IF #IsIdentity = 0
-- add column to the SELECT clause
SET #sql = #sql + #ColumnName + ', '
ELSE
-- indicate that table has identity column
SET #TableHasIdentity = 1
-- get next column
FETCH NEXT FROM MyCurs INTO #ColumnName, #IsIdentity
END
-- cursor cleanup
CLOSE MyCurs
DEALLOCATE MyCurs
-- add FROM clause
SET #sql = LEFT(#sql, LEN(#sql)-1) + CHAR(13) + CHAR(10) + 'FROM ' + #TableName
-- add SET IDENTITY if necessary
IF #TableHasIdentity = 1
SET #sql = 'SET IDENTITY_INSERT ' + #TableName + ' ON' + CHAR(13) + CHAR (10)
+ #sql + CHAR(13) + CHAR (10)
+ 'SET IDENTITY_INSERT ' + #TableName + ' OFF'
PRINT #sql
I created few user defined types in my database as below
CREATE TYPE [dbo].[StringID] FROM [nvarchar](20) NOT NULL
and assigned them to various tables. The tables in my database are in various schemas (not only dbo)
But I realized I need bigger field, and I need to alter, e.g increase from [nvarchar](20) to [nvarchar](50), but there is no ALTER TYPE statement.
I need a script that uses a temp table/cursor whatever and saves all the tables and fields where my type is used. Then change existing fields to base type - e.g. from CustID [StringID] to CustID [nvarchar(20)].
Drop the user type and recreate it with new type - e.g. nvarchar(50)
and finally set back fields to user type
I do not have rules defined on types, so don't have to drop rules and re-add them.
Any help is appreciated.
This is what I normally use, albeit a bit manual:
/* Add a 'temporary' UDDT with the new definition */
exec sp_addtype t_myudt_tmp, 'numeric(18,5)', NULL
/* Build a command to alter all the existing columns - cut and
** paste the output, then run it */
select 'alter table dbo.' + TABLE_NAME +
' alter column ' + COLUMN_NAME + ' t_myudt_tmp'
from INFORMATION_SCHEMA.COLUMNS
where DOMAIN_NAME = 't_myudt'
/* Remove the old UDDT */
exec sp_droptype t_mydut
/* Rename the 'temporary' UDDT to the correct name */
exec sp_rename 't_myudt_tmp', 't_myudt', 'USERDATATYPE'
We are using the following procedure, it allows us to re-create a type from scratch, which is "a start". It renames the existing type, creates the type, recompiles stored procs and then drops the old type. This takes care of scenarios where simply dropping the old type-definition fails due to references to that type.
Usage Example:
exec RECREATE_TYPE #schema='dbo', #typ_nme='typ_foo', #sql='AS TABLE([bar] varchar(10) NOT NULL)'
Code:
CREATE PROCEDURE [dbo].[RECREATE_TYPE]
#schema VARCHAR(100), -- the schema name for the existing type
#typ_nme VARCHAR(128), -- the type-name (without schema name)
#sql VARCHAR(MAX) -- the SQL to create a type WITHOUT the "CREATE TYPE schema.typename" part
AS DECLARE
#scid BIGINT,
#typ_id BIGINT,
#temp_nme VARCHAR(1000),
#msg VARCHAR(200)
BEGIN
-- find the existing type by schema and name
SELECT #scid = [SCHEMA_ID] FROM sys.schemas WHERE UPPER(name) = UPPER(#schema);
IF (#scid IS NULL) BEGIN
SET #msg = 'Schema ''' + #schema + ''' not found.';
RAISERROR (#msg, 1, 0);
END;
SELECT #typ_id = system_type_id FROM sys.types WHERE UPPER(name) = UPPER(#typ_nme);
SET #temp_nme = #typ_nme + '_rcrt'; -- temporary name for the existing type
-- if the type-to-be-recreated actually exists, then rename it (give it a temporary name)
-- if it doesn't exist, then that's OK, too.
IF (#typ_id IS NOT NULL) BEGIN
exec sp_rename #objname=#typ_nme, #newname= #temp_nme, #objtype='USERDATATYPE'
END;
-- now create the new type
SET #sql = 'CREATE TYPE ' + #schema + '.' + #typ_nme + ' ' + #sql;
exec sp_sqlexec #sql;
-- if we are RE-creating a type (as opposed to just creating a brand-spanking-new type)...
IF (#typ_id IS NOT NULL) BEGIN
exec recompile_prog; -- then recompile all stored procs (that may have used the type)
exec sp_droptype #typename=#temp_nme; -- and drop the temporary type which is now no longer referenced
END;
END
GO
CREATE PROCEDURE [dbo].[recompile_prog]
AS
BEGIN
SET NOCOUNT ON;
DECLARE #v TABLE (RecID INT IDENTITY(1,1), spname sysname)
-- retrieve the list of stored procedures
INSERT INTO
#v(spname)
SELECT
'[' + s.[name] + '].[' + items.name + ']'
FROM
(SELECT sp.name, sp.schema_id, sp.is_ms_shipped FROM sys.procedures sp UNION SELECT so.name, so.SCHEMA_ID, so.is_ms_shipped FROM sys.objects so WHERE so.type_desc LIKE '%FUNCTION%') items
INNER JOIN sys.schemas s ON s.schema_id = items.schema_id
WHERE is_ms_shipped = 0;
-- counter variables
DECLARE #cnt INT, #Tot INT;
SELECT #cnt = 1;
SELECT #Tot = COUNT(*) FROM #v;
DECLARE #spname sysname
-- start the loop
WHILE #Cnt <= #Tot BEGIN
SELECT #spname = spname
FROM #v
WHERE RecID = #Cnt;
--PRINT 'refreshing...' + #spname
BEGIN TRY -- refresh the stored procedure
EXEC sp_refreshsqlmodule #spname
END TRY
BEGIN CATCH
PRINT 'Validation failed for : ' + #spname + ', Error:' + ERROR_MESSAGE();
END CATCH
SET #Cnt = #cnt + 1;
END;
END
there's a good example of a more comprehensive script here
It's worth noting that this script will include views if you have any. I ran it and instead of exec'ing inline generated a script as the output which I then tweaked and ran.
Also, if you have functions/sprocs using the user defeined types you'll need to drop those before running your script.
Lesson Learned: in future, don't bother with UDTs they're more hassle than they're worth.
SET NOCOUNT ON
DECLARE #udt VARCHAR(150)
DECLARE #udtschema VARCHAR(150)
DECLARE #newudtschema VARCHAR(150)
DECLARE #newudtDataType VARCHAR(150)
DECLARE #newudtDataSize smallint
DECLARE #OtherParameter VARCHAR(50)
SET #udt = 'Name' -- Existing UDDT
SET #udtschema = 'dbo' -- Schema of the UDDT
SET #newudtDataType = 'varchar' -- Data type for te new UDDT
SET #newudtDataSize = 500 -- Lenght of the new UDDT
SET #newudtschema = 'dbo' -- Schema of the new UDDT
SET #OtherParameter = ' NULL' -- Other parameters like NULL , NOT NULL
DECLARE #Datatype VARCHAR(50),
#Datasize SMALLINT
DECLARE #varcharDataType VARCHAR(50)
DECLARE #Schemaname VARCHAR(50),
#TableName VARCHAR(50),
#FiledName VARCHAR(50)
CREATE TABLE #udtflds
(
Schemaname VARCHAR(50),
TableName VARCHAR(50),
FiledName VARCHAR(50)
)
SELECT TOP 1
#Datatype = Data_type,
#Datasize = character_maximum_length
FROM INFORMATION_SCHEMA.COLUMNS
WHERE Domain_name = #udt
AND Domain_schema = #udtschema
SET #varcharDataType = #Datatype
IF #DataType Like '%char%'
AND #Datasize IS NOT NULL
AND ( #newudtDataType <> 'varchar(max)'
OR #newudtDataType <> 'nvarchar(max)'
)
BEGIN
SET #varcharDataType = #varcharDataType + '('
+ CAST(#Datasize AS VARCHAR(50)) + ')'
END
INSERT INTO #udtflds
SELECT TABLE_SCHEMA,
TABLE_NAME,
Column_Name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE Domain_name = #udt
AND Domain_schema = #udtschema
DECLARE #exec VARCHAR(500)
DECLARE alter_cursor CURSOR
FOR SELECT Schemaname,
TableName,
FiledName
FROM #udtflds
OPEN alter_cursor
FETCH NEXT FROM alter_cursor INTO #Schemaname, #TableName, #FiledName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #exec = 'Alter Table ' + #Schemaname + '.' + #TableName
+ ' ALTER COLUMN ' + #FiledName + ' ' + #varcharDataType
EXECUTE ( #exec
)
FETCH NEXT FROM alter_cursor INTO #Schemaname, #TableName, #FiledName
END
CLOSE alter_cursor
SET #exec = 'DROP TYPE [' + #udtschema + '].[' + #udt + ']'
EXEC ( #exec
)
SET #varcharDataType = #newudtDataType
IF #newudtDataType Like '%char%'
AND #newudtDataSize IS NOT NULL
AND ( #newudtDataType <> 'varchar(max)'
OR #newudtDataType <> 'nvarchar(max)'
)
BEGIN
SET #varcharDataType = #varcharDataType + '('
+ CAST(#newudtDataSize AS VARCHAR(50)) + ')'
END
SET #exec = 'CREATE TYPE [' + #newudtschema + '].[' + #udt + '] FROM '
+ #varcharDataType + ' ' + #OtherParameter
EXEC ( #exec
)
OPEN alter_cursor
FETCH NEXT FROM alter_cursor INTO #Schemaname, #TableName, #FiledName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #exec = 'Alter Table ' + #Schemaname + '.' + #TableName
+ ' ALTER COLUMN ' + #FiledName + ' ' + '[' + #newudtschema
+ '].[' + #udt + ']'
EXECUTE ( #exec
)
FETCH NEXT FROM alter_cursor INTO #Schemaname, #TableName, #FiledName
END
CLOSE alter_cursor
DEALLOCATE alter_cursor
SELECT *
FROM #udtflds
DROP TABLE #udtflds
1: http://www.sql-server-performance.com/2008/how-to-alter-a-uddt/ has replaced http://www.sql-server-performance.com/faq/How_to_alter_a%20_UDDT_p1.aspx
The simplest way to do this is through Visual Studio's object explorer, which is also supported in the Community edition.
Once you have made a connection to SQL server, browse to the type, right click and select View Code, make your changes to the schema of the user defined type and click update. Visual Studio should show you all of the dependencies for that object and generate scripts to update the type and recompile dependencies.
As devio says there is no way to simply edit a UDT if it's in use.
A work-round through SMS that worked for me was to generate a create script and make the appropriate changes; rename the existing UDT; run the create script; recompile the related sprocs and drop the renamed version.
The solutions provided here can only be applied if the user defined types are used in table definitions only, and if the UDT columns are not indexed.
Some developers also have SP's and functions using UDT parameters, which is not covered either. (see comments on Robin's link and in the Connect entry)
The Connect entry from 2007 has finally been closed after 3 years:
Thank you for submitting this
suggestion, but given its priority
relative to the many other items in
our queue, it is unlikely that we will
actually complete it. As such, we are
closing this suggestion as “won’t
fix”.
I tried to solve a similiar problem ALTERing XML SCHEMA COLLECTIONS, and the steps seem to mostly apply to ALTER TYPE, too:
To drop a UDT, the following steps are necessary:
If a table column references the UDT, it has to be converted to the underlying type
If the table column has a default constraint, drop the default constraint
If a procedure or function has UDT parameters, the procedure or function has to be dropped
If there is an index on a UDT column, the index has to be dropped
If the index is a primary key, all foreign keys have to be dropped
If there are computed columns based on a UDT column, the computed columns have to be dropped
If there are indexes on these computed columns, the indexes have to be dropped
If there are schema-bound views, functions, or procedures based on tables containing UDT columns, these objects have to be dropped
I ran into this issue with custom types in stored procedures, and solved it with the script below. I didn't fully understand the scripts above, and I follow the rule of "if you don't know what it does, don't do it".
In a nutshell, I rename the old type, and create a new one with the original type name. Then, I tell SQL Server to refresh its details about each stored procedure using the custom type. You have to do this, as everything is still "compiled" with reference to the old type, even with the rename. In this case, the type I needed to change was "PrizeType". I hope this helps. I'm looking for feedback, too, so I learn :)
Note that you may need to go to Programmability > Types > [Appropriate User Type] and delete the object. I found that DROP TYPE doesn't appear to always drop the type even after using the statement.
/* Rename the UDDT you want to replace to another name */
exec sp_rename 'PrizeType', 'PrizeTypeOld', 'USERDATATYPE';
/* Add the updated UDDT with the new definition */
CREATE TYPE [dbo].[PrizeType] AS TABLE(
[Type] [nvarchar](50) NOT NULL,
[Description] [nvarchar](max) NOT NULL,
[ImageUrl] [varchar](max) NULL
);
/* We need to force stored procedures to refresh with the new type... let's take care of that. */
/* Get a cursor over a list of all the stored procedures that may use this and refresh them */
declare sprocs cursor
local static read_only forward_only
for
select specific_name from information_schema.routines where routine_type = 'PROCEDURE'
declare #sprocName varchar(max)
open sprocs
fetch next from sprocs into #sprocName
while ##fetch_status = 0
begin
print 'Updating ' + #sprocName;
exec sp_refreshsqlmodule #sprocName
fetch next from sprocs into #sprocName
end
close sprocs
deallocate sprocs
/* Drop the old type, now that everything's been re-assigned; must do this last */
drop type PrizeTypeOld;
New answer to an old question:
Visual Studio Database Projects handle the drop and recreate process when you deploy changes. It will drop stored procs that use UDDTs and then recreate them after dropping and recreating the data type.
1.Rename the old UDT,
2.Execute query ,
3.Drop the old UDT.
Simple DROP TYPE first then CREATE TYPE again with corrections/alterations?
There is a simple test to see if it is defined before you drop it ... much like a table, proc or function -- if I wasn't at work I would look what that is?
(I only skimmed above too ... if I read it wrong I apologise in advance! ;)