Sunday, October 4, 2009

Using Table Value Parameters in SQL Server 2008

Table-valued parameter is another exciting new feature in SQL Server 2008. Essentially, TVP solves the problem of inserting multiple rows into the database. Various solutions have been implemented to achieve this, ranging from round trips for every row to comma separated values. With TVP, you can insert multiple rows with a single round trip to the server without having the additional overheads of composition and de-composition of values.

TVP is not entirely new to SQL Server. SQL Server 2000 introduced the table variables which can be used to store a set of records. Where TVP builds on it, is the ability to pass them as input parameters to stored procedures or functions. This could not be done with table variables.

Let us look at some of the features of TVP before we see how they can be used to insert data.

  • TVPs can participate in Set based operations.
  • They are strongly typed
  • You cannot perform DML operations on TVPs. They can be passed as only READ-ONLY parameters to stored procedures\functions
  • Remember that TVPs are materialized in the TempDB. Essentially, it means that if you insert more rows into your TVP, the size of TempDB is what gets affected.

Having seen what TVP is, let us now move onto build an example which will actually use TVP. Here, I will show you a very common scenario that we use for multiple entries. We have an order and each order is comprised of multiple products. I have created the necessary tables using the scripts below

   1: Create table dbo.Products



   2: (



   3: ProductId int IDENTITY(1,1) PRIMARY KEY,



   4: ProductName varchar(250),



   5: ItemRate decimal



   6: )



   7:  



   8: Create table dbo.Orders



   9: (



  10:     OrderId int IDENTITY(1,1) Primary key,



  11:     OrderDate datetime,



  12:     OrderStatus int



  13: )



  14:  



  15: Create Table dbo.OrderDetails



  16: (



  17:     OrderId int Foreign Key references Orders(OrderId),



  18:     ProductId int Foreign Key references Products(ProductId),



  19:     Quantity int,



  20:     Amount decimal



  21: )




I have also inserted data for the tables above. The next thing to do is to create a user defined type that will insert data into the OrderDetails table.





   1: Create type dbo.OrderParam as Table



   2: (ProductId int, Quantity int, Amount decimal)








Now that the type has been created, let me go ahead and create a stored procedure that takes the just created type as an input parameter.





   1: Create proc dbo.InsertOrders



   2: @OrderItems OrderParam ReadOnly



   3: as



   4: begin



   5:     Declare @iOrderId int



   6:     Insert into Orders values (GETDATE(),0)



   7:     Select @iOrderId=SCOPE_IDENTITY()



   8:     Insert into OrderDetails(OrderId, ProductId, Quantity, Amount)



   9:         Select @iOrderId, ProductId, Quantity, Amount from @OrderItems



  10: end




Notice the fact that the TVPs have been declared read only. Also pay attention to the second insert statement. The values are inserted from the input parameter using a single select statement. The only remaining thing to do is to call this stored procedure from the front end.



For this purpose, I have created a data grid where users select the orders and save it into the database. Below is the code that goes into the Save Click routine.





   1: private void SaveButton_Click(object sender, EventArgs e)



   2:         {



   3:             //create a new datatable to hold the grid data



   4:             DataTable OrderParam = new DataTable();



   5:             //the columns are the same as the TVP type



   6:             OrderParam.Columns.Add("ProductId", typeof(int));



   7:             OrderParam.Columns.Add("Quantity", typeof(int));



   8:             OrderParam.Columns.Add("Amount", typeof(decimal));



   9:  



  10:  



  11:             //loop through the grid to load data into the datatable



  12:             foreach (DataGridViewRow item in grdOrders.Rows)



  13:             {



  14:                 DataGridViewComboBoxCell prodCell = (DataGridViewComboBoxCell) item.Cells[0];



  15:                 if (prodCell.Value != null)



  16:                 {



  17:                     DataRow prdRow = OrderParam.NewRow();



  18:                     prdRow[0] = prodCell.Value;



  19:                     prdRow[1] = item.Cells[1].Value;



  20:                     prdRow[2] = item.Cells[2].Value;



  21:                     OrderParam.Rows.Add(prdRow);



  22:                 }



  23:             }



  24:             //connect to the database to insert the values.



  25:             var connString = ConfigurationManager.ConnectionStrings["DatabaseConn"].ConnectionString;



  26:             SqlConnection dbConn = new SqlConnection(connString);



  27:             dbConn.Open();



  28:             using(SqlCommand cmd = new SqlCommand("InsertOrders", dbConn))



  29:             {



  30:               cmd.CommandType = CommandType.StoredProcedure;



  31:               cmd.Parameters.AddWithValue("OrderItems", OrderParam);



  32:               cmd.ExecuteNonQuery();



  33:             }



  34:             dbConn.Close();



  35:         }




The only thing worth noticing is that I have used a datatable which is quite logical because TVPs are tables anyways. As you can see, the savings in the number of round trips to the database is enormous. The USP of the TVPs lie in the fact that they are not hard to implement at all.  Have fun!!!

Friday, October 2, 2009

Time well SpEnt

Technorati Tags: ,,

When a relatively unheard of theatre group comes forward to do a quiz, you are not sure what to expect. But ASAP did themselves only favours, be it the quiz or the short sketch they showcased just before the finals. The only disappointing thing for the evening was the number of teams participating. The quiz master put the number at 100 which seemed a bit low given that Chennai usually attracts a lot of participation.

The quiz kicked off with 40 question prelims which seems to be the standard nowadays. It fittingly started with a question on the Bapu.

There were questions on Anjali Tendulkar, the Olympics and Quick Gun Murugan. The toughest one seemed to be on the longest winning streak in sports history. I don’t think any of the 100 odd teams got this right. 8 teams qualified for the finals at the end of a low scoring prelims with  “When I was in London” comprising of Srinivasan and Rajagopal coming out tops.

The questions in the finals were engrossing bringing out some brilliant answers from the teams. The quality of quizzing was again top-class. JK and Sriram were quick off the blocks but the team of V.V. Ramanan and Ramkumar eventually caught up leading the quiz into the last round. It came down to the last question to decide the winners and “When I was in London” bagged it with a brilliant answer on the word “FAIL”.

Overall a very enjoyable evening. If anything, the finals just seemed to be one round too long but I dont think the ardent quizzers of Chennai would complain given the good quizzing that was in offing. Mention should also go to the quiz master Vinod and his colleague Ganesh for the research and the quality of questions.

I hope that this now goes on to become an annual event and one more annual quiz gets added to the Chennai quizzing calendar. Way to go ASAP!!!

Saturday, September 12, 2009

CLR Integration Changes in SQL Server 2008

CLR Integration was first introduced in SQL Server 2005. Though there was a lot of excitement when the feature was  introduced, it has since fizzled off. Personally, I think CLR Integration is one of the most underused features of SQL Server. For some reason, we have not been able to move away from extended procedures or UDFs to writing CLR Integrated code.

In SQL Server 2008, there are two noticeable introductions to CLR Introduction. One is the support for LINQ and the other is the support for Nullable types.

To enable CLR support in SQL Server, you first have to switch it on. This is done by running the sp_configure with clr enabled = 1.  One thing to keep in mind is when you are using nullable types, you cannot use the automatic deployment option within Visual Studio 2008. You will have to register your assemblies manually. I will briefly show you how it is done.

Once you have turned the support for CLR on, the next step is to create your assembly. For this, I have a stored procedure written in Visual Studio. The code for the same is as below

 

   1: using System;



   2: using System.Data;



   3: using System.Data.SqlClient;



   4: using System.Data.SqlTypes;



   5: using Microsoft.SqlServer.Server;



   6:  



   7:  



   8: public partial class StoredProcedures



   9: {



  10:     [Microsoft.SqlServer.Server.SqlProcedure]



  11:     public static void SearchEmployee(Int64? iEmployeeId, out Int32? iVacationhours)



  12:     {



  13:         iVacationhours = null;



  14:         if (iEmployeeId !=null)



  15:         {



  16:             //open the sql connection with the current context



  17:             using (SqlConnection connection = new SqlConnection("context connection=true"))



  18:             {



  19:                 //open the connection



  20:                 connection.Open();



  21:                 //build the query and execute it               



  22:                 string query = "Select VacationHours from HumanResources.Employee where EmployeeId = " + iEmployeeId.ToString();



  23:                 SqlCommand sCommand = new SqlCommand(query, connection);



  24:                 SqlDataReader vacationReader = sCommand.ExecuteReader();



  25:                 using (vacationReader)



  26:                 {



  27:                     vacationReader.Read();



  28:                     iVacationhours = vacationReader.GetInt32(0);



  29:                 }



  30:             }            



  31:         }



  32:         



  33:  



  34:     }



  35: };






The code just returns the vacation hours for a given employee. The point worth noting is that both input and output parameters are nullable types. The next step is to add the assembly to the SQL Server database.



The syntax to do that is



   1: create assembly NullableTypes from




   2:  'C:\Users\Administrator\Documents\Visual Studio 2008\Projects\NullableTypesExample\NullableTypesExample\bin\Debug\NullableTypesExample.dll'


   3:  Go




Once the assembly is registered, you can now proceed to create your stored procedure pointing it to the managed code by executing the statement below




   1:  Create Proc dbo.SearchEmployee(@a bigint, @b smallint output)


   2:  as


   3:  EXTERNAL NAME NullableTypes.[StoredProcedures].SearchEmployee


   4:  GO




That is it!!! You are now all set to execute the stored procedure and check the results. For your reference, I am also giving below the statements to execute the procedure



 




   1:  declare @outvalue smallint


   2:  set @outvalue = 0


   3:  exec dbo.SearchEmployee 1,@outvalue output


   4:  select @outvalue






If I change the input value to null, null will be returned as output exhibiting support for Nullable Types.