How can I use a declared variable as the target of INTO? - sql

The #BACKUP_TABLE_NAME variable can be used when querying INFORMATION_SCHEMA.TABLES.
But, I want to use the fully qualified #FQP_BACKUP_TABLE variable as the target of the INTO clause.
How can I use a variable on the INTO clause?
USE THE_DATABASE;
-- Real table = 'THE_TABLE'
-- Backup table = 'BACKUP_TABLE'
DECLARE #BACKUP_TABLE_NAME VARCHAR(64) = 'BACKUP_TABLE';
DECLARE #FQP_BACKUP_TABLE VARCHAR(128) = 'THE_BACKUP_DATABASE.dbo.' + #BACKUP_TABLE_NAME
IF EXISTS (SELECT 1
FROM THE_BACKUP_DATABASE.INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = #BACKUP_TABLE_NAME)
BEGIN
-- Backup table already exists. Do not overwrite it.
PRINT 'ERROR: Backup table ' + #BACKUP_TABLE_NAME + ' already exists.'
RAISERROR('ERROR: Cannot overwrite existing backup table.', 18, -1)
END
ELSE
BEGIN
-- Backup table does not exist. Create it.
SELECT *
INTO THE_BACKUP_DATABASE.dbo.BACKUP_TABLE
--INTO #FQP_BACKUP_TABLE -- NEVER WORKS
FROM THE_DATABASE.dbo.THE_TABLE;
END;

You could wrap the final insert in a Dynamic SQL statement.
Example
...
BEGIN
Declare #SQL varchar(max) = '
SELECT *
INTO '+ #FQP_BACKUP_TABLE +'
FROM THE_DATABASE.dbo.THE_TABLE;'
Exec(#SQL)
END;

Related

How to alter user defined data types in SQL Server tables and SP [duplicate]

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! ;)

How to check existence of tables in different databases in a proc - SQLServer

I am working on a proc which is present in one database, it picks table from other source database (passed as parameter) and insert missing values to other destination database (again passed as parameter).
My insert query is a dynamic query and before executing it, I want to check existence of both the source and destination tables. I don't want to execute dynamic queries again just to check existence of tables as they are not advisable to use in bulk,.
Is there any way I can achieve something like below
USE #DbName
GO
IF EXISTS (
SELECT 1
FROM sys.tables
WHERE NAME = #table
AND type = 'U'
)
BEGIN
code here...
END
or
IF EXISTS (
SELECT 1
FROM #fulltableName -- where variable consists 'dbname.sys.tables'
WHERE NAME = #table
AND type = 'U'
)
BEGIN
code here...
END
with the help of only variables and without executing dynamic queries.
Check if the OBJECT_ID returns a value. If you are checking for tables, use U as 2nd parameter.
IF OBJECT_ID('DatabaseName.dbo.TableName', 'U') IS NOT NULL
BEGIN
-- Table Exists
END
With variables:
DECLARE #DatabaseName VARCHAR(100) = 'MyCustomDatabase'
DECLARE #SchemaName VARCHAR(100) = 'dbo'
DECLARE #TableName VARCHAR(100) = 'Countries'
IF OBJECT_ID(QUOTENAME(#DatabaseName) + '.' + QUOTENAME(#SchemaName) + '.' + QUOTENAME(#TableName), 'U') IS NOT NULL
BEGIN
-- Do stuff
END
Make sure to execute with a login with enough privileges.

SQL Server - Select columns that meet certain conditions?

My COLUMNS can contain only three values or var chars - economy, basic, luxury. I want to select a ROW and display only those COLUMNS which contain luxury. The problem is that there are many such columns - about 50. I don't want to type the names of all those columns in my select query. Is there a shorter and simpler alternative to this ? Which query should I use ?
I am thinking of something like this (this is a FAKE query) -
#declare Column_Name varchar(30)
select Column_Name where Column_Value = 'luxury'
from ATable
where rowId = 'row 5';
Table structure -
rowId | Column1 | Column2 | Column3.....
I've created a stored procedure for you.
This procedure examines the MSSQL meta to build a dynamic SQL string that returns a result containing column names N and their values V, and the corresponding row key K from which that value was retrieved, for a specified table.
When this is executed, the results stored in a global temporary table called ##ColumnsByValue, which can then be queried directly.
Create the GetColumnsByValue stored procedure, by executing this script:
-- =============================================
-- Author: Ben Roberts (sepster#internode.on.net)
-- Create date: 22 Mar 2013
-- Description: Returns the names of columns that contain the specified value, for a given row
-- =============================================
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF OBJECT_ID ( 'dbo.GetColumnsByValue', 'P' ) IS NOT NULL
DROP PROCEDURE dbo.GetColumnsByValue;
GO
CREATE PROCEDURE dbo.GetColumnsByValue
-- Add the parameters for the stored procedure here
#idColumn sysname,
#valueToFind nvarchar(255),
#dbName sysname,
#tableName sysname,
#schemaName sysname,
#debugMode int = 0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #SQL nvarchar(max);
DECLARE #SQLUnion nvarchar(max);
DECLARE #colName sysname;
DECLARE #dbContext nvarchar(256);
DECLARE #Union nvarchar(10);
SELECT #dbContext = #dbName + '.' + #schemaName + '.sp_executeSQL';
SELECT #SQLUnion = '';
SELECT #Union = '';
IF OBJECT_ID ( 'tempdb..##GetColumnsByValueIgnoreList') IS NULL -- no columns to ingore have been specified, need to create an empty list.
BEGIN
CREATE TABLE ##GetColumnsByValueIgnoreList (column_name nvarchar(255));
END
DECLARE DBcursor CURSOR FOR
SELECT
COLUMN_NAME
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = #tableName
AND
TABLE_SCHEMA = #schemaName;
OPEN DBcursor;
FETCH DBcursor INTO #colName;
WHILE (##FETCH_STATUS = 0)
BEGIN
IF (
#colName != #idColumn
AND
#colName NOT IN (SELECT column_name FROM ##GetColumnsByValueIgnoreList)
)
BEGIN
SELECT #SQL = 'SELECT '+#idColumn+' as K, '''+#colName+''' as N, ' +#colName+ ' as V FROM ' + #dbName + '.' + #schemaName + '.' + #tableName;
--PRINT #SQL;
SELECT #SQLUnion = #SQL + #Union + #SQLUnion;
SELECT #Union = ' UNION ';
END
FETCH DBcursor INTO #colName;
END; -- while
CLOSE DBcursor; DEALLOCATE DBcursor;
IF (#debugMode != 0)
BEGIN
PRINT #SQLUnion;
PRINT #dbContext;
END
ELSE
BEGIN
-- Delete the temp table if it has already been created.
IF OBJECT_ID ('tempdb..##ColumnsByValue') IS NOT NULL
BEGIN
DROP TABLE ##ColumnsByValue
END
-- Create a new temp table
CREATE TABLE ##ColumnsByValue (
K nvarchar(255), -- Key
N nvarchar(255), -- Column Name
V nvarchar(255) -- Column Value
)
-- Populate it with the results from our dynamically generated SQL.
INSERT INTO ##ColumnsByValue EXEC #dbContext #SQLUnion;
END
END
GO
The SP takes several inputs as parameters, these are explained in the following code.
Note also I've provided a mechanism to add an "ignore list" as an input:
This allows you to list any column names that should not be included
in the results.
You do NOT need to add the columnn that you're using as your key, ie the row_id from your example structure.
You MUST include other columns that are not varchar as
these will cause an error (as the SP just does a varchar comparison
on all columns it looks at).
This is done via a temp table that you must create/populate
Your example table structure suggests
the table contains only columns of interest, so this may not apply to
you.
I've included example code for how to do this (but only do this if you need to):
IF OBJECT_ID ( 'tempdb..##GetColumnsByValueIgnoreList') IS NOT NULL
BEGIN
DROP TABLE ##GetColumnsByValueIgnoreList;
END
CREATE TABLE ##GetColumnsByValueIgnoreList (column_name nvarchar(255));
INSERT INTO ##GetColumnsByValueIgnoreList VALUES ('a_column');
INSERT INTO ##GetColumnsByValueIgnoreList VALUES ('another_column');
INSERT INTO ##GetColumnsByValueIgnoreList VALUES ('yet_another_column');
Now, to fire off the procedure that build your temp table of results, use the following code (and modify as appropriate, of course).
-- Build the ##ColumnsByValue table
EXEC dbo.GetColumnsByValue
#idColumn = 'row_id', -- The name of the column that contains your row ID (eg probably your PK column)
#dbName = 'your_db_name',
#tableName = 'your_table_name',
#schemaName = 'dbo',
#debugMode = 0 -- Set this to 1 if you just want a print out of the SQL used to build the temp table, to 0 if you want the temp table populated
This leaves you with ##ColumnsByValue, on which you can perform whatever search you need, eg:
select * from ##ColumnsByValue WHERE v = 'luxury' and k = 5 --some_row_id
You'd need to re-execute the stored procedure (and if relevant, create/modify the ignore list table prior to it) for each table you want to examine.
A concern with this approach is the nvarchar length might get exceeded in your case. You'd prob. need to use different datatype, reduce the column name lengths etc. Or break it up into sub-steps and union the results together to get the resultset you're after.
Another concern I have is that this is complete overkill for your particular scenario, where a one-off script-to-query-window will give you the basis of what you need, then some clever text editing in eg Notepad++ will get you all the way there... and hence this problem will likely (and quite reasonably) put you off doing it this way! But it is a good general-case question, and so deserves an answer for anyone interested in future ;-)

Trying to update all string values of 'NULL' to NULL in sql server

I have a database table that was imported from a csv file which had NULL in it, so that when it was imported instead of fields being NULL they contain the string value 'NULL'.
The CSV file is to big to open in a text editor to edit out all of the NULL so I am trying to create a SQL query to update each column of the table.
So far I have this
Alter PROCEDURE [dbo].[sp_fixnulls]
#column nvarchar(100)
AS
BEGIN
SET NOCOUNT ON;
UPDATE my_table
SET #column=''
WHERE #column = 'NULL'
END
---------------
sp_fixnulls (SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME ='my_table')
but this is not working. I get the error message
Msg 201, Level 16, State 4, Procedure sp_fixnulls, Line 0
Procedure or function 'sp_fixnulls' expects parameter '#column', which was not supplied.
#column is a variable. It cannot dynamically swap out its contents into SQL. You need to use TSQL to accomplish this, whereby you generate the SQL into a string then execute it. Such as:
DECLARE #sql VARCHAR(MAX)
SET #sql = '
UPDATE my_table
SET ' + #column + '=''''
WHERE ' + #column + ' = ''NULL''
'
EXEC (#sql) -- don't forget the parentheses
A small note that my code sets the column to empty string, which is NOT the same as NULL. I went with your existing example. If you want NULL, then
SET ' + #column + '= NULL
'Msg 201, Level 16, State 4, Procedure sp_fixnulls, Line 0 Procedure or function 'sp_fixnulls' expects parameter '#column', which was not supplied.'
The error you're getting is because you're trying to pass a select statement as a parameter to a stored procedure. If you were to pass just the name of one column to the stored procedure, you would get past that error.
Then you wouldn't get another error, but you wouldn't get the result you want. If you fix your stored procedure as Eli recommended (with the addition of parentheses around #sql in the EXEC statement, as I commented), it will work.
Then you'll need to wrap your stored procedure with a cursor (which most people don't recommend, but works when needed). Alternatively, you can just select all of the column names and generate a bunch of execute statements, and then run the statements.
SELECT 'EXECUTE sp_fixnulls N''' + column_name + ''';'
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'my_table';
This will generate statements like the following (assuming the table has columns id, name, and date).
EXECUTE sp_fixnulls N'id';
EXECUTE sp_fixnulls N'name';
EXECUTE sp_fixnulls N'date';
Another option is to forgo the stored procedure altogether.
SELECT 'UPDATE my_table SET ' + column_name
+ ' = NULL WHERE ' + column_name + ' = ''NULL'';'
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'my_table';
This will generate statements like the following.
UPDATE my_table SET id = NULL WHERE id = 'NULL';
UPDATE my_table SET name = NULL WHERE name = 'NULL';
UPDATE my_table SET date = NULL WHERE date = 'NULL';

Alter user defined type in SQL Server

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! ;)