Pages

Thursday, May 28, 2015

How to compare approximate dates with SQL

Let say you have to write a code for the periodic payment application, and in your database you store the payment start date. After that you want to check if the next payment is within +/- 1 day of the current date - do something. And obviously, you font want to write a lot of code to do that :)

The first idea I had was to create a whole bunch of unions for each day and then check if current date is one of those, but later I shrinked that to 3 conditions connected with 'or' (here is NHibernate function for that)

var settlementDateDifferenceFromNow = Projections.SqlFunction(
 new SQLFunctionTemplate(
  NHibernateUtil.Int32,
  @"
  CASE WHEN
  ABS(DATEDIFF(DAY, GETDATE(), DATEADD(MONTH, DATEDIFF(MONTH, ?1, GETDATE()) - 1, ?1))) <= 1 OR
  ABS(DATEDIFF(DAY, GETDATE(), DATEADD(MONTH, DATEDIFF(MONTH, ?1, GETDATE()), ?1))) <= 1 OR
  ABS(DATEDIFF(DAY, GETDATE(), DATEADD(MONTH, DATEDIFF(MONTH, ?1, GETDATE()) + 1, ?1))) <= 1
  THEN 1
  ELSE 0
  END
  "),
 NHibernateUtil.Int32,
 Projections.Property<Contract>(x => x.SettlementDate_Actual)
);

I am not sure about 'or' conjunction tho... would it cause performance problems?

Monday, May 11, 2015

Nhibernate paging for time consuming operations

Imagine, you have to implement batching for a time consuming operation using NHibernate. I would write it like this:

var items = new List<Item>();
for (var page = 0; page == 0 || items.Any(); page++)
{
    items = GetListOfItems(page);
    foreach (var items in items)

    {
        DoStuff(item);
    }
}

public void GetListOfItems(int page)
{
    return CurrentSession.QueryOver<Item>()
        .Select(Projections.Id())
        .Take(PageSize)
        .Skip(PageSize * page)
        .List();
}

But what I found is that if the DuStuff is time consuming then it is possible to have duplicated items from the query. Thatis because of NHibernate generated query:

SELECT TOP (@p0) y0_
FROM (
  SELECT this_.Id AS y0_
  ,ROW_NUMBER() OVER (
    ORDER BY CURRENT_TIMESTAMP
  ) AS __hibernate_sort_row
  FROM contact.sfContact this_
) AS query
WHERE query.__hibernate_sort_row > @p1
ORDER BY query.__hibernate_sort_row;

That order by CURRENT_TIMESTAMP... If you want consistent results you would have to apply specific ordering:

public void GetListOfItems(int page)
{
    return CurrentSession.QueryOver<Item>()
        .Select(Projections.Id())
        .OrderBy(i => i.Id).Asc
        .Take(PageSize)
        .Skip(PageSize * page)
        .List();
}