91

How can I alter a user-defined table type in SQL Server ?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
yogi
  • 19,175
  • 13
  • 62
  • 92
  • 2
    possible duplicate of [How to ALTER the Table Value Parameter](http://stackoverflow.com/questions/1734541/how-to-alter-the-table-value-parameter) – Aaron Bertrand Jul 10 '12 at 12:19
  • 1
    Possible duplicate of [How to ALTER the Table Value Parameter](https://stackoverflow.com/questions/1734541/how-to-alter-the-table-value-parameter) – KyleMit Aug 14 '19 at 20:17

9 Answers9

86

As of my knowledge it is impossible to alter/modify a table type.You can create the type with a different name and then drop the old type and modify it to the new name

Credits to jkrajes

As per msdn, it is like 'The user-defined table type definition cannot be modified after it is created'.

Manivannan Nagarajan
  • 1,019
  • 1
  • 9
  • 14
  • 2
    I use a series of generated drop/create scripts of all objects depending on the user defined type and of the user defined type itself . – Ronnie Overby Jul 17 '13 at 16:59
  • 6
    How to end up with 20,000+ lines scripts in sql? drop/creating all dependencies of table types you need to alter. :P – Spacemonkey May 22 '15 at 19:18
  • 25
    This is terrible (Microsoft), now i have to modify ALL sp's whenever i make a change to these tables, then delete, recreate and go back and update ANY references to them. Poorly thought out.. I know this is a rant but what were they thinking?? – schmoopy May 18 '16 at 21:16
  • 1
    Also it appears that you are not even allowed to change the owner of a user defined table type either. Dropping the table type and creating it again with a new owner name seems to be the only way change the owner name in case you need to modify this. sp_changeobjectowner doesn't work on user defined Table Types... – Jeff May 11 '18 at 05:33
45

This is kind of a hack, but does seem to work. Below are the steps and an example of modifying a table type. One note is the sp_refreshsqlmodule will fail if the change you made to the table type is a breaking change to that object, typically a procedure.

  1. Use sp_rename to rename the table type, I typically just add z to the beginning of the name.
  2. Create a new table type with the original name and any modification you need to make to the table type.
  3. Step through each dependency and run sp_refreshsqlmodule on it.
  4. Drop the renamed table type.

EXEC sys.sp_rename 'dbo.MyTableType', 'zMyTableType';
GO
CREATE TYPE dbo.MyTableType AS TABLE(
    Id INT NOT NULL,
    Name VARCHAR(255) NOT NULL
);
GO
DECLARE @Name NVARCHAR(776);

DECLARE REF_CURSOR CURSOR FOR
SELECT referencing_schema_name + '.' + referencing_entity_name
FROM sys.dm_sql_referencing_entities('dbo.MyTableType', 'TYPE');

OPEN REF_CURSOR;

FETCH NEXT FROM REF_CURSOR INTO @Name;
WHILE (@@FETCH_STATUS = 0)
BEGIN
    EXEC sys.sp_refreshsqlmodule @name = @Name;
    FETCH NEXT FROM REF_CURSOR INTO @Name;
END;

CLOSE REF_CURSOR;
DEALLOCATE REF_CURSOR;
GO
DROP TYPE dbo.zMyTableType;
GO

WARNING:

This can be destructive to your database, so you'll want to test this on a development environment first.

Nick O
  • 3,716
  • 6
  • 38
  • 50
  • remark on sp_refreshsqlmodule at MSDN link: https://msdn.microsoft.com/en-us/library/bb326754.aspx is quite threatening. But does this SP also updates the the text of referenced objects or just updates there compiled form. I mean if I'll run SP_HELPTEXT on one of my referenced SP then will I get the new Type name there or not? – yogi May 23 '15 at 06:52
  • This will not change the table type in your stored proc to the z table type. All it does it refreshes the schema as it is, but fixes the reference to your table type. If you don't run `sp_refreshsqlmodle` on your store proc after recreating the table type, the stored proc will fail saying that the table type has changed. I do recommend running this on a development version of you DB before running it in production. – Nick O May 29 '15 at 18:53
  • 1
    This answer did not work for me. As soon as sp_rename is executed, parameter type on SP is updated to refer to new UDTT. Then later on the renamed UDTT cannot be dropped. sp_rename enforces dependencies. This answer assumes that sp_rename will not update parameter type on referencing SPs. – murtazat Nov 10 '15 at 00:11
  • `sp_rename` on user defined datat type throw me an error: `Either the parameter @objname is ambiguous or the claimed @objtype ((null)) is wrong.` what could be wrong ? – Muflix Dec 22 '16 at 22:51
  • 1
    @Muflix, you may need to pass in the object type like this `EXEC sys.sp_rename 'dbo.MyTableType', 'zMyTableType', 'OBJECT';`. Also be sure you are including the schema for the first parameter and not in the second. – Nick O Jan 12 '17 at 19:43
  • @norlando when i added the schema info, it works, thanks. – Muflix Jan 15 '17 at 23:03
18

Here are simple steps that minimize tedium and don't require error-prone semi-automated scripts or pricey tools.

Keep in mind that you can generate DROP/CREATE statements for multiple objects from the Object Explorer Details window (when generated this way, DROP and CREATE scripts are grouped, which makes it easy to insert logic between Drop and Create actions):

Drop and Create To

  1. Back up you database in case anything goes wrong!
  2. Automatically generate the DROP/CREATE statements for all dependencies (or generate for all "Programmability" objects to eliminate the tedium of finding dependencies).
  3. Between the DROP and CREATE [dependencies] statements (after all DROP, before all CREATE), insert generated DROP/CREATE [table type] statements, making the changes you need with CREATE TYPE.
  4. Run the script, which drops all dependencies/UDTTs and then recreates [UDTTs with alterations]/dependencies.

If you have smaller projects where it might make sense to change the infrastructure architecture, consider eliminating user-defined table types. Entity Framework and similar tools allow you to move most, if not all, of your data logic to your code base where it's easier to maintain.

To generate the DROP/CREATE statements for multiple objects, you can right-click your Database > Tasks > Generate Scripts... (as shown in the screenshot below). Notice:

  1. DROP statements are before CREATE statements
  2. DROP statements are in dependency order (i.e. reverse of CREATE)
  3. CREATE statements are in dependency order

screenshot showing how to generate drop create statements for multiple objects

Nate Anderson
  • 18,334
  • 18
  • 100
  • 135
lightmotive
  • 520
  • 5
  • 17
  • 2
    I took your advice and considered migrating hundreds of thousands of lines of code to use Entity Framework so we could avoid having to drop and recreate a user-defined table type. But, after a second I decided dropping and recreating the table type was an easier path to go down for now. – PapillonUK Feb 08 '17 at 16:34
  • 2
    @PapillonUK, I'm with you. I have a larger project, so I'm still making the same decision as you (and the method above is so fast that I'll likely continue with that for the foreseeable future). I mentioned moving away from UDTTs for those who have smaller projects where it might make sense to change the architecture now to simplify future changes, so I've updated my answer accordingly. Thanks. – lightmotive Feb 09 '17 at 17:34
11

Simon Zeinstra has found the solution!

But, I used Visual Studio community 2015 and I didn't even have to use schema compare.

Using SQL Server Object Explorer, I found my user-defined table type in the DB. I right-mouse clicked on the table-type and selected . This opened a code tab in the IDE with the TSQL code visible and editable. I simply changed the definition (in my case just increased the size of an nvarchar field) and clicked the Update Database button in the top-left of the tab.

Hey Presto! - a quick check in SSMS and the udtt definition has been modified.

Brilliant - thanks Simon.

BioEcoSS
  • 129
  • 1
  • 5
9

If you can use a Database project in Visual Studio, you can make your changes in the project and use schema compare to synchronize the changes to your database.

This way, dropping and recreating the dependent objects is handled by the change script.

Simon Zeinstra
  • 795
  • 8
  • 19
5

You should drop the old table type and create a new one. However if it has any dependencies (any stored procedures using it) you won't be able to drop it. I've posted another answer on how to automate the process of temporary dropping all stored procedures, modifying the table table and then restoring the stored procedures.

Community
  • 1
  • 1
BornToCode
  • 9,495
  • 9
  • 66
  • 83
  • Isn't it a given that it would have dependencies, otherwise why would you have it created to begin with? – LarryBud Jan 09 '20 at 13:46
  • @LarryBud - One simple example could be if somebody just created it and didn't have a chance yet to use it in any stored procedure etc. – BornToCode Mar 08 '20 at 18:03
4

Just had to do this alter user defined table type in one of my projects. Here are the steps I employed:

  1. Find all the SP using the user defined table type.
  2. Save a create script for all the SP(s) found.
  3. Drop the SP(s).
  4. Save a create script for the user defined table you wish to alter. 4.5 Add the additional column or changes you need to the user defined table type.
  5. Drop the user defined table type.
  6. Run the create script for the user defined table type.
  7. Run the create script for the SP(s).
  8. Then start modifying the SP(s) accordingly.
J G
  • 41
  • 1
3

you cant ALTER/MODIFY your TYPE. You have to drop the existing and re-create it with correct name/datatype or add a new column/s

2

I created two stored procedures for this. The first one

create_or_alter_udt_preprocess takes the udt name as input, drops all the stored procs/functions that use the udt, drops the udt, and return a sql script to recreate all the procedures/functions.

The second one create_or_alter_udt_postprocess takes the script outputted from the first proc and executes it.

With the two procs, changing an udt can be done by:

  1. call create_or_alter_udt_preprocess;
  2. create the udt with a new definition;
  3. call create_or_alter_udt_postprocess;

Use a transaction to avoid losing the original procs in case of errors.

create or ALTER   proc create_or_alter_udt_postprocess(@udt_postprocess_data xml)
as 
begin
    if @udt_postprocess_data is null 
        return;

    declare @obj_cursor cursor 
    set @obj_cursor = cursor fast_forward for 
    select n.c.value('.', 'nvarchar(max)') as definition
    from @udt_postprocess_data.nodes('/Objects/definition') as n(c)

    open @obj_cursor;

    declare @definition nvarchar(max);
    fetch next from @obj_cursor into @definition;
    while (@@fetch_status = 0)
    begin
        exec sp_executesql @stmt= @definition
        fetch next from @obj_cursor into @definition
    end

    CLOSE @obj_cursor;
    DEALLOCATE @obj_cursor; 
end

Create or ALTER   proc create_or_alter_udt_preprocess(@udt nvarchar(200), @udt_postprocess_data xml out) 
AS
    BEGIN
        set @udt_postprocess_data = null;
        if TYPE_ID(@udt) is null
            return;

        declare @drop_scripts nvarchar(max);
        SELECT @drop_scripts =  (
        (select N';'+ drop_script
            from 
        (
        SELECT 
            drop_script = N'drop ' + case sys.objects.type when 'P' then N'proc ' else N'function' end
                + sys.objects.name + N';' + + nchar(10) + nchar(13)
        FROM sys.sql_expression_dependencies d
        JOIN sys.sql_modules m ON m.object_id = d.referencing_id
        JOIN sys.objects ON sys.objects.object_id = m.object_id
        WHERE referenced_id = TYPE_ID(@udt)
        ) dependencies
        FOR XML PATH (''), type
        ).value('.', 'nvarchar(max)')
         ) ;
    
        declare @postprocess_data xml;

        set @udt_postprocess_data =
        (SELECT 
            definition
        FROM sys.sql_expression_dependencies d
        JOIN sys.sql_modules m ON m.object_id = d.referencing_id
        JOIN sys.objects ON sys.objects.object_id = m.object_id
        WHERE referenced_id = TYPE_ID(@udt)
        FOR XML PATH (''), root('Objects'));
        
        exec sp_executesql @stmt= @drop_scripts;
        exec sp_droptype @udt;
  END

Example usage:

begin tran
declare @udt_postprocess_data xml;

exec create_or_alter_udt_preprocess @udt= 'test_list', @udt_postprocess_data = @udt_postprocess_data out;
CREATE TYPE test_list AS TABLE(
    test_name nvarchar(50) NULL
);

exec create_or_alter_udt_postprocess @udt_postprocess_data = @udt_postprocess_data;

commit;

Code to set up the example usage:

CREATE TABLE [dbo].[test_table](
    [test_id] [int] IDENTITY(1,1) NOT NULL, [test_name] [varchar](20) NULL
) ON [USERDATA]
GO


CREATE TYPE test_list AS TABLE(test_name nvarchar(20) NULL)
GO

create proc add_tests(
@test_list test_list readonly)
as 
begin
    SET NOCOUNT ON;
    insert into test_table(test_name)
    select test_name
    from @test_list;
end;

create proc add_tests2(
@test_list test_list readonly)
as 
begin
    SET NOCOUNT ON;
    insert into test_table(test_name)
    select test_name
    from @test_list;
end;
SeanLi
  • 21
  • 3