What do you do, when you want a data type that should represent more than one
interrelated values? Would you do this with two separate data types? Not a good
idea! Is it possible that you can do this with just one single data type? Yes,
you can. We have been provided a “proper” way to sort this out: The
User-Defined Type in SQL Server 2005. Well, you had this in SQL Server 2000,
didn’t you? Yes, but in this case it is really enhanced, or in other words, it
is greatly empowered.
The integration of the .NET CLR (Common Language Runtime) into SQL Server 2005
is the man of many parts, that allows us to create our own data type by using
our favorite manage code, like C#.NET. This article speaks about how to create
a user-defined type and how can it be useful in our day-to-day operations. This
article does not contain much detail about user-defined types, but the
manipulation of them. The reason that made me write this kind of article is,
being a developer, I always love to have hands-on-experience and the concept of
this can be found in many forms.
What is the UDT that we are going to create?
As I really wanted to put this in a more practical manner, I went through some
of my applications that were done using SQL Server 2000 in order to find a
proper kind of value to make a UDT. Finally, I found one in one of the
Sales-Automation applications of mine. One of the processes that is involved in
this operation is to have a count on both physical and system stock on a daily
basis of items. The physical stock and system stock are stored separately in
two different columns. Why do we not have a single column for this purpose?
This thought came up as soon as I saw my own design. So, this little scenario
is going to be used for our first UDT, though a little simple.
Naming the new UDT as “ItemStock” is fine I think. The “ItemStock” will have
two parts: Physical Stock and System Stock. Having a third part, something like
Unit-Of-Measure will give our UDT more flexibility, but I will not go ahead
with it, just to keep things simple. Remember the KISS principle?
What is the first step?
The first step of the procedure is to have an assembly that contains the
implementation of the UDT. Since my favorite CLR-supported language is C#.NET,
we’ll create the assembly with C#.NET. All you have to do is, open Visual
Studio 2005 and create a new project of the type “Database”. Once created, add
a new item which should be a “User-Defined Type”. This leads to the creation of
a skeleton of the type we need. Here is the cleaned-up code:
[Serializable] [Microsoft.SqlServer.Server.SqlUserDefinedType(Format.Native)] public struct ItemStock : INullable { public override string ToString() { return ""; } public bool IsNull { get { return m_Null; } } public static ItemStock Null { get { ItemStock h = new ItemStock (); h.m_Null = true; return h; } } public static ItemStock Parse(SqlString s) { if (s.IsNull) return Null; ItemStock u = new ItemStock (); return u; } }
I’ll give you simple description on some important parts of this code snippet.
• [Serializable]
This allows the UDT to be serialized.
• [Microsoft.SqlServer.Server.SqlUserDefinedType(Format.Native)]
This is a required attribute for UDTs. The required property “Format” determines the serialization format. With the version of Beta 2 April CTP, there are three elements under the Format enumerator: Native, Unknown and UserDefined. This attribute can be specified with some more named-parameters like IsByteOrdered, IsFixedLength, MaxByteSize, Name and ValidationMethodName.
• Format.Native
This is the native SQL Server binary serialization which is the fastest. This can only be applied to fixed-length, value-type data types. The named-parameter “MaxByteSize” cannot be specified with this type.
• Format.UserDefined
This is basically used for reference types. If this is specified, the serialization have to be implemented by ourselves. This can be done by implementing IBinarySerialize interface.
• INullable
Implementing INullable interface makes the UDT null aware. The public property “IsNull” of the INullable must be implemented too.
• Static Null method
This method must be implemented that returns the type of UDT itself.
• Static Parse method
This static method makes UDT convert-possible from a string value.
• ToString()
This Overrides the UDT’s virtual ToString() method. The convention of the UDT back to the string is done through this.
Well, I think that I have covered some basic, important areas of the UDT. Now,
have a look on our UDT: The ItemStock.
[Serializable] [Microsoft.SqlServer.Server.SqlUserDefinedType(Format.Native)] public struct ItemStock : INullable { //these three variables hold the value of physical stock, system stock, //and the indicator whether the value of UDT is null respectively. private int _physicalStock; private int _systemStock; private bool _isNull; //The INullable readonly property. public bool IsNull { get { return _isNull; } } //This method returns null-valued UDT. public static ItemStock Null { get { ItemStock itemStock = new ItemStock(); itemStock._isNull = true; return itemStock; } } //Coversion from string to UDT. //If the value is null, return null-valued UDT. //If not, validate and create a new ItemStock UDT. public static ItemStock Parse(SqlString value) { if (value.IsNull) return ItemStock.Null; ItemStock itemStock = new ItemStock(); try { string[] values = value.ToString().Split('|'); itemStock._physicalStock = Convert.ToInt32(values[0]); itemStock._systemStock = Convert.ToInt32(values[1]); itemStock._isNull = false; return itemStock; } catch (Exception exception) { throw exception; } } //String representation of the UDT. public override string ToString() { if (this.IsNull) return "NULL"; else return this._physicalStock + " | " + this._systemStock; } //Returns the physical stock. This is a read-only property public int PhysicalStock { get { return this._physicalStock; } } //Returns the system stock. This is a read-only property public int SystemStock { get { return this._systemStock; } } //Returns the difference between physical and system stock. This is a read-only property public int Difference { get { return _physicalStock - _physicalStock;}} }
Well, the code is very simple. You can clearly see, we have implemented all
what we discussed so far. In addition to that, three new properties have been
implemented. You will see the usability of it soon.
The Second Step
Now it is time to register the assembly we created in the SQL Server 2005. Open
the MS SQL Server Management Studio and Click on “New Query”. Set the database
you want to add the UDT and place the code below on it.
CREATE ASSEMBLY SQLServerUDTs FROM '{FilePath}\SQLServerUDTs.dll'
Here is the Last step
Once you run the second step, the last step of the procedure is, create a data
type base on the UDT we created. See the code below.
CREATE TYPE ItemStock EXTERNAL NAME SQLServerUDTs.ItemStock
The above code creates a UDT named “ItemStock” that points to the assembly
registered. The “SQLServerUDTs.ItemStock” represents the namespace and the UDT
name in the assembly.
Time to use it
We have done! To see the usability of the newly created UDT, let’s create a
simple table that contains the UDT and insert some records.
CREATE TABLE Items (Id int IDENTITY(1,1) PRIMARY KEY, [Name] varchar(100) NOT NULL, InStock ItemStock NULL) INSERT INTO Items ([Name], InStock) VALUES ('Dining Table 1', '0|0') INSERT INTO Items ([Name], InStock) VALUES ('Dining Table 2', NULL) INSERT INTO Items ([Name], InStock) VALUES ('Dining Table 3', NULL) INSERT INTO Items ([Name], InStock) VALUES ('Dining Table 4', NULL) --this will cause to an exception INSERT INTO Items ([Name], InStock) VALUES ('Dining Table 4', 'p|s')
Ok, now table is ready with data. Let’s see how we it help us in day-to-day
operations.
-- Select all columns from the tables. -- See the way we have to access UDT properties SELECT Id, [Name], InStock.ToString(), InStock.PhysicalStock, InStock.SystemStock FROM Items -- Updating records UPDATE Items SET InStock = '1860|1860' WHERE Id = 3 UPDATE Items SET InStock = '138|156' WHERE Id = 4 -- applying for conditions SELECT Id, InStock.Difference FROM Items WHERE InStock.PhysicalStock < InStock.SystemStock
Well, you can see now how the new stuff that comes with SQL Server 2005, make
our life easier. Especially the MS .Net Framework integration, which is the
most intriguing part for me. You can see some of the experiments I have done
with this and more are being done.
As I have stated above, this article started you off on some basic UDT stuff.
Well, in my next article, I will be covering more on this and I will highly
appreciate if you can give some comments on this. I know definitely, it will
help me out with next part on UDT.