User-defined types for T-SQL
This topic provides reference information about user-defined types and table-valued parameters in Microsoft SQL Server and their compatibility with Amazon Aurora MySQL. It explains the differences in feature support between SQL Server and Aurora MySQL, highlighting that Aurora MySQL does not currently support user-defined types or table-valued parameters.
Feature compatibility | AWS SCT / AWS DMS automation level | AWS SCT action code index | Key differences |
---|---|---|---|
|
|
Replace scalar UDT with base types. Rewrite stored procedures that use table-type input parameters to use strings with CSV, XML, or JSON, or to process row-by-row. For more information, see Stored Procedures. |
SQL Server Usage
SQL Server user-defined types provide a mechanism for encapsulating custom data types and for adding NULL constraints.
SQL Server also supports table-valued user-defined types, which you can use to pass a set of values to a stored procedure.
User defined types can also be associated to CLR code assemblies. Beginning with SQL Server 2014, memory-optimized types support memory optimized tables and code.
Note
If your code uses custom rules bound to data types, Microsoft recommends discontinuing use of this deprecated feature.
All user-defined types are based on an existing system data types. They allow developers to reuse the definition, making the code and schema more readable.
Syntax
The simplified syntax for the CREATE TYPE
statement.
CREATE TYPE <type name> {
FROM <base type> [ NULL | NOT NULL ] | AS TABLE (<Table Definition>)}
Examples
User-defined types
Create a ZipCodeScalar
user-defined type.
CREATE TYPE ZipCode FROM CHAR(5) NOT NULL
Use the ZipCodetype
in a table.
CREATE TABLE UserLocations (UserID INT NOT NULL PRIMARY KEY, ZipCode ZipCode);
INSERT INTO [UserLocations] ([UserID],[ZipCode]) VALUES (1, '94324'); INSERT INTO [UserLocations] ([UserID],[ZipCode]) VALUES (2, NULL);
For the preceding example, the following error message appears. It indicates that NULL values for ZipCodeare
aren’t allowed.
Msg 515, Level 16, State 2, Line 78 Cannot insert the value NULL into column 'ZipCode', table 'tempdb.dbo.UserLocations'; column doesn't allow nulls. INSERT fails. The statement has been terminated.
Table-valued types
The following example demonstrates how to create and use a table valued types to pass a set of values to a stored procedure.
Create the OrderItems
table.
CREATE TABLE OrderItems ( OrderID INT NOT NULL, Item VARCHAR(20) NOT NULL, Quantity SMALLINT NOT NULL, PRIMARY KEY(OrderID, Item) );
Create a table valued type for the OrderItems
table.
CREATE TYPE OrderItems AS TABLE ( OrderID INT NOT NULL, Item VARCHAR(20) NOT NULL, Quantity SMALLINT NOT NULL, PRIMARY KEY(OrderID, Item) );
Create the InsertOrderItems
procedure. Note that the entire set of rows from the table valued parameter is handled with one statement.
CREATE PROCEDURE InsertOrderItems @OrderItems AS OrderItems READONLY AS BEGIN INSERT INTO OrderItems(OrderID, Item, Quantity) SELECT OrderID, Item, Quantity FROM @OrderItems; END
Instantiate the OrderItems
type, insert the values, and pass it to a stored procedure.
DECLARE @OrderItems AS OrderItems;
INSERT INTO @OrderItems ([OrderID], [Item], [Quantity]) VALUES (1, 'M8 Bolt', 100), (1, 'M8 Nut', 100), (1, M8 Washer, 200); EXECUTE [InsertOrderItems] @OrderItems = @OrderItems; (3 rows affected)
Select all rows from the OrderItems
table.
SELECT * FROM OrderItems; OrderID Item Quantity 1 M8 Bolt 100 1 M8 Nut 100 1 M8 Washer 200
For more information, see CREATE TYPE (Transact-SQL)
MySQL Usage
Amazon Aurora MySQL-Compatible Edition (Aurora MySQL) 5.7 doesn’t support user defined types and user defined table valued parameters.
The current documentation doesn’t indicate these features will be supported in Aurora MySQL version 8.
Migration Considerations
For scalar user-defined types, replace the type name with base type and optional NULL constraints.
For table-valued user-defined types used as stored procedure parameters, the workaround is more complicated.
Common solutions include using either temporary tables to hold the data or passing large string parameters containing the data in CSV, XML, JSON (or any other convenient format) and then writing code to parse these values in a stored procedure. Alternatively, if the logic doesn’t require access to the entire set of changes, and for small data sets, it is easier to call the stored procedure in a loop and pass the columns as standard parameters, row by row.
Memory-optimized engines aren’t yet supported in Aurora MySQL. You must convert memory optimized tables to disk based tables.
Examples
Replacing a user-defined type
Replace the ZipCode
user-defined type with a base type.
CREATE TABLE UserLocations ( UserID INT NOT NULL PRIMARY KEY, /*ZipCode*/ CHAR(5) NOT NULL );
Replacing a table-valued stored procedure parameter
The following steps describe how to replace a table-valued parameter with a source table and a LOOP
cursor.
Create an OrderItems
table.
CREATE TABLE OrderItems ( OrderID INT NOT NULL, Item VARCHAR(20) NOT NULL, Quantity SMALLINT NOT NULL, PRIMARY KEY(OrderID, Item) );
Create and populate the SourceTable
.
CREATE TABLE SourceTable ( OrderID INT, Item VARCHAR(20), Quantity SMALLINT, PRIMARY KEY (OrderID, Item) );
INSERT INTO SourceTable (OrderID, Item, Quantity) VALUES (1, 'M8 Bolt', 100), (2, 'M8 Nut', 100), (3, 'M8 Washer', 200);
Create a procedure to loop through the SourceTable
and insert rows.
Note
There are syntax differences from T-SQL for both the CREATE PROCEDURE
and the CURSOR
declaration and use. For more information, see Stored Procedures and Cursors.
CREATE PROCEDURE LoopItems() BEGIN DECLARE done INT DEFAULT FALSE; DECLARE var_OrderID INT; DECLARE var_Item VARCHAR(20); DECLARE var_Quantity SMALLINT; DECLARE ItemCursor CURSOR FOR SELECT OrderID, Item, Quantity FROM SourceTable; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; OPEN ItemCursor; CursorStart: LOOP FETCH NEXT FROM ItemCursor INTO var_OrderID, var_Item, var_Quantity; IF Done THEN LEAVE CursorStart; END IF; INSERT INTO OrderItems (OrderID, Item, Quantity) VALUES (var_OrderID, var_Item, var_Quantity); END LOOP; CLOSE ItemCursor; END;
Call the stored procedure.
CALL LoopItems();
Select all rows from the OrderItems
table.
SELECT * FROM OrderItems; OrderID Item Quantity 1 M8 Bolt 100 2 M8 Nut 100 3 M8 Washer 200
Summary
SQL Server | Aurora MySQL | Comments |
---|---|---|
Table-valued parameters |
Not supported |
Use either temporary tables, or CSV, XML, JSON string parameters and parse the data. Alternatively, rewrite the stored procedure to accept the data one row at a time and process the data in a loop. |
Memory-optimized table-valued user-defined types |
Not supported |
Not supported. |
For more information, see Cursors