Jacob's profileThe Internet Ramblings o...PhotosBlogLists Tools Help

The Internet Ramblings of Jacob

A man trapped in his own basement...

Jacob Sanford

Occupation
Location
Below are the books I have either authored or co-authored. Hopefully, this list will keep growing. :o)
Professional SharePoint 2007 Design
Professional ASP.NET 2.0 Design: CSS, Themes, and Master Pages (Programmer to Programmer)
January 05

August 18th

well, it's official; as of 8/18, there should be a new little jacob (or jaqueline i guess) running around. we went to our first OB appointment today and they predict 8/18 for the new one. and since we had a c-section before, we MUST have one this time. so it will be scheduled. so we should know pretty solidly when the day will be as we get closer.

so save up now and start knitting your booties. you have 7 months. Smile

oh, and HAPPY BIRTHDAY MATT!!!
August 15

Table versus Scalar UDF's in SQL 2005

I have been working on a fairly large scale financial reporting system for one of our clients and, in this regard, have begun getting really deep into Reporting Services. One of the decisions we made early in the game was to create an all-encompassing data view that would be the datasource for all of the reports we generate. This view joined probably 10-15 tables to report out a lot of different information needed by the reports, all of which worked fine. The problem came in when I incorporated User Defined Functions (UDFs) into the data view.

I used probably 6 different UDFs to return everything from the Federal Fiscal Year (FFY) of a service line item to the authorized amount (and subsequent disbursed amount) for the same line item. The subset of data we had included around 35,000 records and we pulled by FFY. For FFY 2008, which had approximately 3500 records, the data pull took well over 2 minutes and invariably crashed our reports on the server.

I began looking into how to optimize this view because, obviously, if the report is crashing, nobody is signing off and nobody is getting paid.

The problem is that we were using scalar UDFs, meaning UDFs that returned a single value. On its surface, this may not seem like a huge deal but, the way they are used, each returned record called the UDF. So, with 5 UDFs for each record and 3500 records returned for a FFY, the server hit was noticeable. A lot.

So what made a lot more sense was to create a sort of in-memory table with the information I needed and join to it in the view and reference the fields that way.

To make more sense, I'll show you the code I created for the FFY lookup. Remember, before, I called a scalar UDF to get the FFY for each row ... 3500 times (or more).

So the first step was to create the in-line table UDF. Here is my function (I'll explain it after the code):

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:        Jacob J. Sanford
-- ALTER date:    August 14, 2008
-- Description:    Returns the FFY for all Service Line Items
-- =============================================
CREATEFUNCTION [dbo].[irss_udf_FederalFiscalYear_TABLE_GET]()
RETURNS @ffytable TABLE (ServiceLineItemID INT PRIMARY KEY, FFY INT) WITH SCHEMABINDING
AS
BEGIN

    DECLARE @ServiceLineItemID INT, @dtStart DATETIME, @dtEnd DATETIME
    DECLARE @strFFY INT, @intStartMonth INT, @intEndMonth INT
    DECLARE @strStartFFY VARCHAR(MAX), @strEndFFY VARCHAR(MAX)

    DECLARE SLI CURSOR FOR
    SELECT
        DISTINCT
         SLI.ServiceLineItemID
        ,(SELECT TOP 1 StartDate from dbo.ServiceLineItem WHERE ServiceLineItemID = SLI.ServiceLineItemID AND AuthorizationDate IS NOT NULL ORDER BY TrackID DESC)
        ,(SELECT TOP 1 EndDate from dbo.ServiceLineItem WHERE ServiceLineItemID = SLI.ServiceLineItemID AND AuthorizationDate IS NOT NULL ORDER BY TrackID DESC)
    FROM [dbo].[ServiceLineItem] SLI

    OPEN SLI

    FETCH NEXT FROM SLI INTO @ServiceLineItemID, @dtStart, @dtEnd

    WHILE (@@FETCH_STATUS = 0)
    BEGIN
        SET @intStartMonth = MONTH(@dtStart)
        SET @intEndMonth = MONTH(@dtEnd)

        IF(@intStartMonth < 10)
            SET @strStartFFY = YEAR(@dtStart)
        ELSE
            SET @strStartFFY = YEAR(@dtStart) + 1

        IF(@intEndMonth < 10)
            SET @strEndFFY = YEAR(@dtEnd)
        ELSE
            SET @strEndFFY = YEAR(@dtEnd) + 1

        IF(@strStartFFY = @strEndFFY)
            SET @strFFY = @strStartFFY
        ELSE
            BEGIN
                -- Get the start and end dates of the second federal fiscal year:
                DECLARE @dtEndFFY_begin DATETIME--, @dtEndFFY_end DATETIME
                SET @dtEndFFY_begin = DATEADD(YEAR, -1, CONVERT(DATETIME, ('10/1/' + @strEndFFY)))

                --Get the # of Days in each fiscal year
                DECLARE @intDaysInFFY1 INT, @intDaysInFFY2 INT
                SET @intDaysInFFY1 = DATEDIFF(DAY, @dtStart, @dtEndFFY_begin)
                SET @intDaysInFFY2 = DATEDIFF(DAY, @dtEndFFY_begin, @dtEnd)

                IF(@intDaysInFFY1 >= @intDaysInFFY2)
                    SET @strFFY = @strStartFFY
                ELSE
                    SET @strFFY = @strEndFFY
            END

        INSERT INTO @ffytable(ServiceLineItemID, FFY) VALUES (@ServiceLineItemID, @strFFY)

        FETCH NEXT FROM SLI INTO @ServiceLineItemID, @dtStart, @dtEnd
    END

    CLOSE SLI
    DEALLOCATE SLI

    RETURN

END

There are a couple of things to notice about this. First, you set the UDF up differently than a scalar UDF in a couple of ways. For instance, you have to return a TABLE variable . In my example, I am defining the table at this step and creating a primary key in order to create an index (so that it would hopefully pull even faster). I am also SCHEMABINDING to hopefully make it pull that much faster. This is essentially defining a TABLE variable that I can then insert into (notice the bold INSERT command at the bottom). The business logic of the UDF isn't really that important, but I left it in so you could get an idea of what I was trying to do. Plus, if you work with people that use Federal Fiscal Years, this could be useful to you.

Once I have the UDF defined, I can join to it just like any other table:

SELECT
     DISTINCT
     SLI.TrackID
    ,SLI.ServiceLineItemID
    ,FFYTABLE.FFY
FROM
    dbo.ServiceLineItem SLI
    LEFT JOIN dbo.irss_udf_FederalFiscalYear_TABLE_GET() FFYTABLE ON SLI.ServiceLineItemID = FFYTABLE.ServiceLineItemID

I went through and eliminated external calls where unnecessary (i.e., where i could just recreated the functionality within the SELECT of the view) and used the table UDFs to replace the scalar UDFs in all other situations. When I did this, the select for my 3500 records went from almost 3 minutes to 37 seconds. In other words, I saw around a 75-80% performance boost.

I think scalar UDFs might be fine for small datasets and probably have their place. But, if you are dealing with large amounts of data, I would probably opt for table UDFs instead.

June 09

SharePoint Developers Rejoice!

Just got back from TechEd Developers and the Tweener event. Man, what a good time. Lots of discussions with a lot of people and some new information that will make my life easier as a SharePoint developer. So, of course, I'll share.Smile

COOL THING #1:

The first thing, and this gave me goosebumps, is that you can now install WSS 3.0 SP1 ON YOUR VISTA BOX!!! That is just crazy. Here is a screenshot after my successful implementation:

image

Notice the "jacob-laptop" URL? That is awesome.

You can find the full instructions on how to do this by using this link:

http://community.bamboosolutions.com/blogs/bambooteamblog/archive/2008/05/21/how-to-install-windows-sharepoint-services-3-0-sp1-on-vista-x64-x86.aspx

One thing that I ran into that wasn't really stated in the above instructions is the problem that occurs if you have Vista's User Account Control (UAC) turned on. I, like a good computer user, had it turned on. There are probably more than a few developers that turned it off as soon as they got their Vista laptops. But, well, I'm a goodie two-shoes. Anyway, if you have the UAC turned on, you will think everything works but then run into a problem once you try to set up your sites. Primarily, you can't set up a new web application, which means you can't add a new site collection, which means you can't create any sites. In other words, all you'll have set up is Central Administration. I tried launching IE as an administrator and that wouldn't work. I created new administrator accounts and tried using those accounts and that wouldn't work. I couldn't get anything to work. But, as soon as I switched off the UAC, it worked.

So, if you have this problem, too, go into your Control Panel and click on User Accounts. At the bottom of the dialogue box that comes up, you will see a link to "Turn User Account Control on or off". Click there, change the checkbox, and click OK. You should be fine after that. Smile

*DISCLAIMER*  the tool provided by bamboo solutions is basically a registry hack (i think) and is not supported by Microsoft. Obviously, when you move code you develop against this installation of WSS up against a real server, you will want to test it again. But this is pretty neato and will especially allow presenters to have a much faster installation of WSS to work with in their presentations (that is what I used it for). 

COOL THING #2:

The other cool thing to happen last week is the release of the Visual Studio 2008 Extensions for SharePoint 2007 (version 1.2):

http://www.microsoft.com/downloads/details.aspx?FamilyID=7bf65b28-06e2-4e87-9bad-086e32185e68&displaylang=en

You can now develop SharePoint Web Parts (and other custom SharePoint applications) in Visual  Studio 2008 (instead of just in VS05).

With my laptop running WSS 3.0 and the new extensions installed, a friend created a running web part for SharePoint in 2 minutes while I was talking to the crowd at the Tweener presentation. I didn't even know he had done it. I just turned abound and BAM a Web Part.

If you are a SharePoint Developer, you probably have goosebumps by now, too. And are probably already downloading these tools for your own development. Wink

May 22

New MCTS in town

Just got back from taking (and passing) the 70-528 exam. This one, as I predicted, was MUCH easier for me than the 536. It seemed like half the questions were about Master Pages and Themes, both of which I wrote about extensively in my first book. There was even a question about control adapters, which I wrote about in both of my books.
 
It was funny, I think I found a coding error in one of the questions. I'm pretty sure the solution would not compile. I know I answered the question right, but the answer provided just won't compile. I tested the bad line in VS08 when I got home just to verify. I left a comment on that question saying pretty much that and made a suggestion how to fix it. Want to feel like a badass? Fix errors on certification exam questions. haha
 
Anyway, I am now MCTS: .NET Framework 2.0 Web Applications.
 
I think I will take the MCPD next week. I want to study over the stuff this weekend and make a decision then. If its along the same track as this test, I think I can pass it. If not, I may hold off. We'll see.
May 20

The Next Generation of Geek

My baby loves the computer books...

Happy to be a geek like daddy

Apparently, he likes InfoPath

 
Photo 1 of 6