May 2007 Entries

Just got back from the Business & Decision 2007 Seminar in Napitia, southern Italy. Was a good event with plenty of networking and fun, but thanks to the Industrial action by Alitalia we got back 36 hours late :)

...and tomorrow I'm off to South Africa due to some unfortunate and unforseen circumstances. I should buy shares in Shell, the amount of fuel I'm burning atm!


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

BCS Oxon WebComp LogoEntries for the BCS Oxfordshire Schools web competition are now closed! Judging is taking place ready for the prize giving on 20th June at the Shrivenham campus of Cranfield.

The competition was sponsored, amongst others, by Business & Decision and Microsoft (thanks Mark for you help!).


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

One of my pet hates is SQL Server cursors. Yes, I know that they are part of the product for a reason, and I know that they are very handy. So why do I hate them, and what can be done to avoid them? Are they a necessary evil? I developed a technique to avoid them a few years ago, and some others people have since designed some techniques as well, such as on this post. These other techniques are great, but my style of doing things has the one advantage that it can be repeated and nested in complex ways because of the use of variable tables... as you'll see below.

Well, lets first create some test data. I used a server with both SQL2000 and SQL2005 server on to do some tests just to show this technique works on both. I created an empty database with an empty table, and then added 1,000 random numbers between the value of 1-100 into the table. The script I used to create the table is below:

CREATE TABLE [dbo].[test] (
 [testid] [int] IDENTITY (1, 1) NOT NULL ,
 [randnum] [int] NOT NULL
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[test] WITH NOCHECK ADD
 CONSTRAINT [PK_test] PRIMARY KEY  CLUSTERED
 (
  [testid]
 )  ON [PRIMARY]
GO

I then constructed two very simple queries which took a number as a parameter and returned the number of columns matching that number. Yes, I know that I could have done a single SELECT to get that, but that's not the point of the exercise :) Where this technique IS essential is where you find yourself doing complex nested group-bys and start to think about pivots and the like (and especially where there's no reliable numeric ID column) Basically, where row-by-row operations are needed.

Firstly, the cursor script I used looked like this:

DECLARE @iCheckNumber int,
 @iCurrentNumber int,
 @iNumberCount int

SELECT @iCheckNumber = 35,
 @iNumberCount = 0

DECLARE testnum CURSOR FOR

SELECT randnum FROM test

OPEN testnum
 
FETCH testnum INTO @iCurrentNumber

WHILE @@FETCH_STATUS = 0
   BEGIN
 FETCH testnum INTO @iCurrentNumber

 IF @iCurrentNumber = @iCheckNumber
 BEGIN
  SELECT @iNumberCount = @iNumberCount + 1
 END
   END

CLOSE testnum

DEALLOCATE testnum

SELECT @iCurrentNumber

As you can see, it's a pretty standard cursor script. I ran it from Query Analyzer (2000) or Management Studio (2005) and it ran fine and returned the results I wanted. So how do I do the same kind of thing without cursors and without creating umpteen temporary tables? Well, I can use the T-SQL support for loops, and variable tables, as below:

DECLARE @iCheckNumber int,
  @iCurrentNumber int,
  @iNumberCount int,
  @iCurrentRow int

SELECT @iCheckNumber = 35,
  @iNumberCount = 0

DECLARE @temptest AS TABLE
 (
  iRowID int identity(1,1),
  randnum int 
 )

INSERT INTO @temptest (randnum)
SELECT randnum FROM test

SELECT @iCurrentRow = 1

WHILE @iCurrentRow < (SELECT COUNT(*) FROM test)
BEGIN
 IF (SELECT randnum FROM @temptest WHERE iRowID = @iCurrentRow) = @iCheckNumber
 BEGIN
  SELECT @iNumberCount = @iNumberCount + 1
 END

 SELECT @iCurrentRow = @iCurrentRow + 1

print @iCurrentRow
END

Note that I'm creating an identity column on my temporary table. This makes it ideal for writing queries where your data has no SEQUENTIAL numeric identity column already. One common use for this is with paging. Imagine you have a table with an ID and Name column. You want to pull back your names 500 at a time. You can get the first 500 no problem, just do a SELECT TOP 500. When you come to get 500-1000 you have problems - and that's where a cursor is normally used.

With my technique, you can put all your unique IDs in the temporary table, and then just iterate through the row numbers you want.

Note that the example I used here probably performs better as a cursor because it is so simple, but that's not a true reflection of cursors. They perform badly, even in 2005, and the more complex they get then, in my experience, the more useful it is to avoid
 

 


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist

We've been using TableAdapters created using the XSD designer in VS2005 lately, and have found they have a couple of gotchas.

For example, one limitation of a TableAdapter is the lack of support for SQL stored proc return values when using a non-scalar stored proc call - or at least, lack of any useful access to them. You'd expect that maybe the return value would be the return from the auto-genned function, but you'd be wrong. The return is actually the number of rows affected, and that in itself is useless if you're turning row count off in the stored proc (as many do).

So maybe the ReturnValue is a property on the TableAdapter? Again, no... you can use 'out' parameters fine (just pass the variable as ref to the auto-genned function), but thats not very nice. I don't want to throw away the return value concept and use 'out' params just because the TableAdapter isn't clever enough.

So, what to do? There's some good blog posts around on the technique, but most of them only work really well when you have just one or two stored procs and table adapters (see this blog post for an example. In the solutions we're building, we often have lots. And lots. It's a heady mixture of designer work and cut-paste fest. Ugly maybe, but it does allow flexibility. This meant we had to make some kind of vaguely generic function to get the ReturnValue available to our UI code layer.

To achieve this, we made use of the TableAdapter partial classes that the XSD creates for us. Let's say that we have a set of stored procs to do with orders. You might have:

AddOrder
UpdateOrder
DeleteOrder
GetOrder
GetOrders

You'd probably only be bothered with the return value from the stored procs that actually wrote date - the top 3. Lets say they are returning an error code, like 0 for success, 1 if they can't find the specified record, 2 if it will break a constraint etc. What you can then do is create your TableAdapter queries for each function - lets assume you called them all the same thing as their underlying stored procs. Let's say our TableAdapter is called OrderTableAdapter.

We then need to do our clever jiggery pokery. Just outside our partial class for OrderTableAdapter, we put an enumeration, like so:

    public enum OrderCommandType
    {
        Add,
        Update,
        Delete,
        Get,
        GetAll
    }

We do this so that it's a cleaner solution in our data layer, where we'll have a manager class (I hate lobbing strings around where an enum would be better). Following this enum declaration, we have the main worker function which is a method called 'ReturnValue' which takes one parameter - the OrderCommandType. For simplicity, I've written comments inline in the method below.

  partial class OrderTableAdapter
    {
        public int ReturnValue(OrderCommandType Command)
        {
            // get the return value for the appropriate command
            switch
(Command)
            {
                case OrderCommandType.Add:

                    // the first param is the SQL command collection of the TableAdapter, the second param is the stored proc name that we want the return value from
                    return TableAdapterHelper.GetReturnValue(this.CommandCollection, "AddOrder");

                case OrderCommandType.Update:

                    return TableAdapterHelper.GetReturnValue(this.CommandCollection, "UpdateOrder");

                case OrderCommandType.Delete:

                    return TableAdapterHelper.GetReturnValue(this.CommandCollection, "DeleteOrder");

                default:

                    // for cleanliness, throw an error if someone tries to get the ReturnValue from one of the method types that we have decided shouldn't return a value
                    throw new Exception("OrderCommandType requested does not support return values");
            }
        }
    }

You'll see I refer to a TableAdapterHelper - this is important as it's the bit that actually digs out the ReturnValue itself...

    public static class TableAdapterHelper
    {
        public static int GetReturnValue(SqlCommand[] aCommands, string sCommandText)
        {
            // now lets find that commmand

            // iterate through all command objects in the command collection
            foreach (SqlCommand oCommand in aCommands)
            {
                // see if the stored proc name matches
                if (oCommand.CommandText.Equals(string.Format("dbo.{0}", sCommandText)))
                {
                    if (oCommand.Parameters != null)
                    {
                        // loop thru all params
                        foreach (SqlParameter oParam in oCommand.Parameters)
                        {
                           
// find the param with a direction of ReturnValue (should only ever be one)
                            if (oParam.Direction == System.Data.ParameterDirection.ReturnValue)
                            {
                                if (oParam.Value == DBNull.Value || oParam.Value == null) return 0; // assume dbnull is success

                                return (int)oParam.Value;
                            }
                        }
                    }
                }
            }

            throw new Exception("Could not locate Return Value");
        }
    }

As you'll see, my technique works on the basis that the stored proc names are known and unique across commands in a single TableAdapter, which should normally be the case. You might find it ugly that the stored proc names are embedded in code - but they're in the XSD anyway so it's still containing in one place.

So the final part of the jigsaw is the actual call itself, and the getting of the ReturnValue. Here is an example of how to make the call. Note that I've made OrderID an out param. I like to throw an error if the SQL error code is non-zero.

OrderTableAdapter ta = new OrderTableAdapter();

int
iRowsAffected = ta.AddOrder(ref OrderID, OrderCode, CustomerID, OrderLineID);

int iErrorCode = ta.ReturnValue(OrderCommandType.Add);

if(iErrorCode != 0)
{
  // throw an exception here
}

You can also combine this technique with 'out' param error messages and the like to make a very powerful below-data layer in the stored procs.

 


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist