How to refer to previous row in SQL 2005

In this post I will describe simple mechanism allowing to refer back to previous row in SQL Server 2005/2008 database table.  

For the purpose of this article let’s create a table dbo.AgentLog:  

create table dbo.AgentLog (
	 AgentID	int
	,LogInTime	datetime
	,LogOutTime	datetime
	)
go

insert dbo.AgentLog
select 1, '2010-09-08 09:40:00.000', '2010-09-08 10:14:00.000'
union all
select 1, '2010-09-08 10:35:00.000', '2010-09-08 12:04:00.000'
union all select 1, '2010-09-08 13:11:00.000', '2010-09-08 14:24:00.000'
union all select 1, '2010-09-08 14:35:00.000', '2010-09-08 16:08:00.000'
union all select 2, '2010-09-08 09:00:00.000', '2010-09-08 09:23:00.000'
union all select 2, '2010-09-08 10:12:00.000', '2010-09-08 12:05:00.000'
union all select 2, '2010-09-08 13:12:00.000', '2010-09-08 15:07:00.000'
union all select 2, '2010-09-08 15:10:00.000', '2010-09-08 16:25:00.000'
go

Our table will look like this:  

AgentID LogInTime LogOutTime
----------- ----------------------- -----------------------
1           2010-09-08 09:40:00.000 2010-09-08 10:14:00.000
1           2010-09-08 10:35:00.000 2010-09-08 12:04:00.000
1           2010-09-08 13:11:00.000 2010-09-08 14:24:00.000
1           2010-09-08 14:35:00.000 2010-09-08 16:08:00.000
2           2010-09-08 09:00:00.000 2010-09-08 09:23:00.000
2           2010-09-08 10:12:00.000 2010-09-08 12:05:00.000
2           2010-09-08 13:12:00.000 2010-09-08 15:07:00.000
2           2010-09-08 15:10:00.000 2010-09-08 16:25:00.000

The task is to for each agent list all breaks the person made and its length. To do this I need to somehow refer back to previous row to calculate difference in minutes between login and previous logout.  

To accomplish the task I will use two features new to SQL Server 2005 (when compared to SQL Server 2000). These are CTE (Common Table Expression) and Row_Number() function.  

First one in essence can be thought as variable table of results from SELECT statement. This data set only exists in current scope.  

Row_Number() is a SQL Server built-in function that adds extra identity column to data set retrieved with SELECT statement.  

SELECT
     Row_Number() over (partition by AgentID order by LogInTime) AS RowID
    ,*
FROM dbo.AgentLog

Row_Number() takes partition by argument, which tells function that you want unique row numbers against specific column. If this isn’t provided, SQL Server will just number all rows on one-by-one basis. Combining CTE and Row_Number() we can now get the following: 

 ;with AgentLogCTE as
(select Row_Number() over (partition by AgentID order by LogInTime) as RowID, * from dbo.AgentLog )

This CTE then contains following information:

RowID                AgentID     LogInTime               LogOutTime
-------------------- ----------- ----------------------- -----------------------
1                    1           2010-09-08 09:40:00.000 2010-09-08 10:14:00.000
2                    1           2010-09-08 10:35:00.000 2010-09-08 12:04:00.000
3                    1           2010-09-08 13:11:00.000 2010-09-08 14:24:00.000
4                    1           2010-09-08 14:35:00.000 2010-09-08 16:08:00.000
1                    2           2010-09-08 09:00:00.000 2010-09-08 09:23:00.000
2                    2           2010-09-08 10:12:00.000 2010-09-08 12:05:00.000
3                    2           2010-09-08 13:12:00.000 2010-09-08 15:07:00.000
4                    2           2010-09-08 15:10:00.000 2010-09-08 16:25:00.000

Now what we need to do is just join this table to itself in some clever way, linking current row to previous one:

select
    t1.AgentID
   ,t2.LogOutTime as PrevLogout
   ,t1.LogInTime as Login
   ,convert(varchar, t1.LogInTime - t2.LogOutTime, 108) as BreakTime
from AgentLogCTE t1
   join AgentLogCTE t2
      on t2.RowID = (t1.RowID-1) and t1.AgentID = t2.AgentID
go

And this is it! Resultset looks like the following:

AgentID     PrevLogout              Login                   BreakTime
----------- ----------------------- ----------------------- ------------------------------
1           2010-09-08 10:14:00.000 2010-09-08 10:35:00.000 00:21:00
1           2010-09-08 12:04:00.000 2010-09-08 13:11:00.000 01:07:00
1           2010-09-08 14:24:00.000 2010-09-08 14:35:00.000 00:11:00
2           2010-09-08 09:23:00.000 2010-09-08 10:12:00.000 00:49:00
2           2010-09-08 12:05:00.000 2010-09-08 13:12:00.000 01:07:00
2           2010-09-08 15:07:00.000 2010-09-08 15:10:00.000 00:03:00

Whole SQL script explained in this article can be obtained from here.

Cascading dropdowns with just a bit of Ajax in ASP.NET 2.0

In this sample I am going to go through very common idea of cascading dropdowns. Very basically speaking it is all about having set of dropdowns where next dropdown is populated on the basis of value selected from previous one.

Let’s start with very simple xml document:

<?xml version="1.0" encoding="utf-8" ?>
<People>
    <Person ID="1" Name="Laith Francis" ParentID="0" />
    <Person ID="2" Name="Hammett Swanson" ParentID="0" />
    <Person ID="3" Name="Macon Hayden" ParentID="0" />
    <Person ID="4" Name="Kennan Mooney" ParentID="0" />
    <Person ID="5" Name="Jin Hutchinson" ParentID="4" />
    <Person ID="6" Name="Emery Schwartz" ParentID="1" />
    <Person ID="7" Name="Melvin Garrett" ParentID="2" />
    <Person ID="8" Name="Giacomo Lamb" ParentID="4" />
    <Person ID="9" Name="Stephen Harding" ParentID="3" />
    <Person ID="10" Name="Ian Ward" ParentID="1" />
    <Person ID="11" Name="Raphael Walters" ParentID="2" />
    <Person ID="12" Name="Lee Gould" ParentID="1" />
    <Person ID="13" Name="Carter Carpenter" ParentID="4" />
    <Person ID="14" Name="Kareem Shepard" ParentID="14" />
    <Person ID="15" Name="Zahir Montgomery" ParentID="12" />
    <Person ID="16" Name="Zachery Mcmahon" ParentID="14" />
    <Person ID="17" Name="Hu Dillon" ParentID="12" />
    <Person ID="18" Name="Allistair Bradford" ParentID="13" />
    <Person ID="19" Name="Vincent Bryan" ParentID="11" />
    <Person ID="20" Name="Vance Santos" ParentID="12" />
</People>

Now let’s create very simple .aspx page, that has three dropdowns. First one we will populate using standard ASP.NET binding method so we will use DropDownList control in this instance. Because we do not populate other dropdowns straight away, nothing stops us from using html select tags. The markup can be like this:

<form id="form1" runat="server">
<div>
    Boss:
    <asp:DropDownList DataSourceID="xmlBosses"
        DataTextField="Name"
        DataValueField="ID"
        ID="ddlBosses" runat="server"></asp:DropDownList>
    <asp:XmlDataSource DataFile="~/App_Data/data.xml"
        ID="xmlBosses" runat="server" XPath="/People/Person[@ParentID=0]">
    </asp:XmlDataSource>
    <br />
    Manager:
    <select id="ddlManagers"></select>
    <br />
    Workers:
    <select id="ddlWorkers"></select>
</div>
</form>

Having this markup done we now need a mechanism to populate ddlManagers when ddlBosses changes. Same action needs to happen to ddlWorkers when ddlManagers is changed. To accomplish that we need make Ajax request to some data provider. In our instance, because application is created in ASP.NET 2.0, I opted for web service, (.Net 3.5 with MVC with it’s JsonResult class and Json() method is much more friendly on this front thought).

Firstly we need web service. We want to give it a boss Id and get list of all managers that report to this boss. Web service can look like this one:

public class People : System.Web.Services.WebService {
    [WebMethod]
    [ScriptMethod(ResponseFormat = ResponseFormat.Xml)]
    public List GetData(int ParentID) { 
        List<int> result = new List<int>(); 

        XmlDocument doc = new XmlDocument(); 
        doc.Load(Server.MapPath("~/App_Data/data.xml"));
        XmlNodeList nodes = doc.SelectNodes("/People/Person[@ParentID="
            + ParentID.ToString() + "]");

        foreach (XmlNode node in nodes) {
            Person p = new Person();
            p.ID = Convert.ToInt32(node.Attributes["ID"].Value);
            p.Name = node.Attributes["Name"].Value;
            p.ParentID = Convert.ToInt32(node.Attributes["ParentID"].Value);
            result.Add(p);
        } 
        return result;
    }

    [Serializable]
    public class Person {
       private int _id;
       private string _name;
       private int _parentId;

       public int ID {
           get { return _id; }
           set { _id = value; }
       }

       public string Name {
           get { return _name; }
           set { _name = value; }
       }

       public int ParentID {
           get { return _parentId; }
           set { _parentId = value; }
       }
    }
}

Having all of that we are now ready for very final stage – building Ajax call to execute web service and process returned data. The only thing we need is a bit of jQuery code, that will do it all:

$(function() {
    // for each dropdown bind change event
    $('select').change(function(e) {
        // capture which dropdown caused request
        if(!e) var e = window.event;
        var srcId = e.srcElement.id;

        // create ajax request
        $.ajax({
            url: 'WebServices/People.asmx/GetData', //call to created web method
            data: { ParentID: $(this).val() },  //web method expects ParentID
            contentType: 'application/xml; charset=utf-8',
            dataType: 'xml',
            error: function(xhr, status, e) { ... },
            success: function(data) {
                // if success get dropdown to be updated
                var select = null;
                switch(srcId) {
                    case 'ddlBosses':
                        select = $('select#ddlManagers');
                    break;
                    case 'ddlManagers':
                        select = $('select#ddlWorkers');
                    break;
                    default:                         
                    break;
                };

                // remove items from dropdown
                $(select).empty();

                // loop through Ajax request results
                $('Person', data).each(function() {
                    // for each result create option element
                    var option = document.createElement('option');
                    $(option).text($('Name', this).text()).val($('ID', this).text());
                    // and append it to dropdown
                    $(select).append(option);
                });
            }
        });
    });
});

And this is it!

I hope you will enjoy it!

How to create page with header and iframe covering rest of the page

A few times I needed to have a page where I have an iframe (I know, I know – it is bad design, but sometimes you need to sacrifice!) with header to the top. I tried many different CSS hacks, but could not get it right. Then I came around pageY(), tiny function created by John Resig (jQuery author), which helps to resolve this particular problem brilliantly:

    function ResizeIFrame() {
        var buffer = 40; //scroll bar buffer
        var height = document.documentElement.clientHeight -
            pageY(document.getElementById('ifrm')) + buffer;
        height = (height < 0) ? 0 : height;
        $('iframe#ifrm').css('height', height + 'px');
    }

    function pageY(elem) {
        return elem.offsetParent ? (elem.offsetTop + pageY(elem.offsetParent)) :
            elem.offsetTop;
    }

Where ‘ifrm’ is ID of iframe.

The only thing we need now is handing load and resize events:

    window.onload = window.onresize = ResizeIFrame;

This could not be simpler!

How to drop all schema objects in MS SQL 2005

I needed to test my SQL implementation script that was a part of big SQL Server Data Warehouse release. For anybody who have ever done large SQL implementation fundamental question is always “Have I scripted everything that is needed?”. So how to check if your implementation script contains all objects you require? Here is a few steps that helped me:

  1. Collate all individual scripts into one or more implementation script (I recommend having  one script per development, but it always depends on circumstances).
  2. Order your scripts so you know the sequence of running them. This is specifically important if there is dependency between objects created by different scripts.
  3. Create backout script for each implementation one and order them as well.
  4. Drop all objects that come into release. “How to drop all schema objects in one go?” – this is where it turns out to be priceless to have DBA in your team:
declare @schema varchar(200)
select @schema = 'MySchema'

select
'DROP ' + case
    when o.xtype = 'U' then 'TABLE'
    when o.xtype = 'V' then 'VIEW'
    when o.xtype = 'P' then 'PROCEDURE'
    when o.xtype = 'FN' then 'FUNCTION'
    end + ' ' + s.name + '.' + o.name as SQL
from
    sys.sysobjects as o
    join sys.schemas as s on o.uid = s.schema_id
where
    s.name = @schema and
    o.xtype in ('U','V','P','FN')
union all
select
'DROP SCHEMA ' + @schema

This will give you DROP statement for each table, view, stored procedure and user defined function in your schema, as well as schema itself. Just grab whichever statement you need and execute it!

Passing list as parameter from AJAX request to MVC controller

I came across this recently when needed to pass array of selected values into MVC controller during Ajax request. Scenario was that on the page I had set of checkboxes and button. User can then make multiple choice selection and submit form using button. Page could look like this:

<ul id="sampleList">
    <li><input type="checkbox" value="1" checked="checked" />text 1</li>
    <li><input type="checkbox" value="2" checked="checked" />test 2</li>
    <li><input type="checkbox" value="3" checked="checked" />test 3</li>
</ul>
<input type="button" id="submitButton" value="submit" />
<span id="spanResults"></span>

Also in the application I have, let’s say, “TestController”. Within this controller I have “GetValue” method, that I want to concatenate input data into a string:

public JsonResult GetValue (List<string> id)
{
return Json(id);
}

Then jQuery code to collect values of checked checkboxes and make Ajaxcall would be as following:

var items = new Array();
$('#sampleList li input:checkbox:checked').each(function() {
    items.push($(this).val());
});
$('#submitButton').click(function() {
    $.ajax({
        type: 'post',
        url: '/Test/GetValues',
        data: { id: items },
        contentType: 'application/json; charset=utf-8',
        dataType: 'json',
        success: function(data) {
            $('#spanResults').text(data.join(','));
        },
        error: function(e) {
            alert(e.message);
        }
});

This should hopefully put some light on “how on the earth do I pass list as parameters via Ajax to MVC”. Exactly the same mechanics applies to passing complex types and multiple parameters – just wrapp multiple parameters in complext type and you are done!

« Previous PageNext Page »