Login or Sign Up to become a member!

EXPERTS, INFORMATION, IDEAS & KNOWLEDGE

Social bookmarker Add this

Your profile

Search

November 2008
Mon Tue Wed Thu Fri Sat Sun
 << <   > >>
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30

XML Feeds

Tags: dates

All the LessThanDot Journals

ISO Week In SQL Server

by SQLDenis


Permalink 22 Sep 2008 10:03 , Categories: Data Modelling & Design Tags: dates, functions, how to, iso, iso week, sql server 2008, tip

ISO Week in SQL Server

First let’s take a look at what ISO week is, from WikiPedia:
Week date representations are in the format as shown below.
YYYY-Www or YYYYWww
YYYY-Www-D or YYYYWwwD

[YYYY] indicates the so-called ISO year which is slightly different than the calendar year (see below). [Www] is the week number prefixed by the letter ‘W’, from W01 through W53. [D] is the weekday number, from 1 through 7, beginning with Monday and ending with Sunday. This form is popular in the manufacturing industries.
There are mutually equivalent definitions for week 01:

  • the week with the year’s first Thursday in it,
  • the week with 4 January in it,
  • the first week with the majority (four or more) of its days in the starting year, and
  • the week starting with the Monday in the period 29 December – 4 January.

If 1 January is on a Monday, Tuesday, Wednesday or Thursday, it is in week 01. If 1 January is on a Friday, Saturday or Sunday, it is in week 52 or 53 of the previous year.
The week number can be described by counting the Thursdays: week 12 contains the 12th Thursday of the year.
The ISO year starts at the first day (Monday) of week 01 and ends at the Sunday before the new ISO year (hence without overlap or gap). It consists of 52 or 53 full weeks. The ISO year number deviates from the number of the calendar year (Gregorian year) on a Friday, Saturday, and Sunday, or a Saturday and Sunday, or just a Sunday, at the start of the calendar year (which are at the end of the previous ISO year) and a Monday, Tuesday and Wednesday, or a Monday and Tuesday, or just a Monday, at the end of the calendar year (which are in week 01 of the next ISO year). For Thursdays, the ISO year number is always equal to the calendar year number.
Examples:

  • 2008-12-29 is written “2009-W01-1″
  • 2010-01-03 is written “2009-W53-7″

You can read more about ISO week here: http://en.wikipedia.org/wiki/ISO_week_date

Sometimes you need to show the ISO week in SQL server but there was no built in way to calculate it until SQL server 2008 was released. In SQL Server 2000/2005 you could use the user defined function ISOweek which was in the SQL Server books on line.
Here is what the function looks like

  1. CREATE FUNCTION ISOweek  (@DATE DATETIME)
  2. RETURNS INT
  3. AS
  4. BEGIN
  5.    DECLARE @ISOweek INT
  6.    SET @ISOweek= DATEPART(wk,@DATE)+1
  7.       -DATEPART(wk,CAST(DATEPART(yy,@DATE) AS CHAR(4))+‘0104′)
  8. –Special cases: Jan 1-3 may belong to the previous year
  9.    IF (@ISOweek=0)
  10.       SET @ISOweek=dbo.ISOweek(CAST(DATEPART(yy,@DATE)-1
  11.          AS CHAR(4))+‘12′+ CAST(24+DATEPART(DAY,@DATE) AS CHAR(2)))+1
  12. –Special case: Dec 29-31 may belong to the next year
  13.    IF ((DATEPART(mm,@DATE)=12) AND
  14.       ((DATEPART(dd,@DATE)-DATEPART(dw,@DATE))>= 28))
  15.       SET @ISOweek=1
  16.    RETURN(@ISOweek)
  17. END
  18. GO

Now run the following query on SQL server 2000 and up

  1. SELECT dbo.ISOweek(‘20071231′),DATEPART(wk,‘20071231′)


If you are running SQL server 2008 then you can use DATEPART and the datepart argument isowk. Run the select statement below to see the result

  1. SELECT DATEPART(isowk,‘20071231′),DATEPART(wk,‘20071231′)

As you can see here also SQL Server’s wk part returns 53 while isowk returns 1

I have also added parts of this to the wiki here: ISO Week In SQL Server

4 comments »Send a trackback » 992 views

Only In A Database Can You Get 1000% + Improvement By Changing A Few Lines Of Code

by SQLDenis


Permalink 17 Aug 2008 07:55 , Categories: Data Modelling & Design Tags: database, dates, indexing, performance tuning, rdbms, sql, t-sql, temporal data

Take a look at this query.

  1. SELECT * FROM
  2.  
  3. (
  4.  
  5. SELECT customer_id, ‘MTD’ AS record_type, COUNT(*), SUM(), AVG()
  6.  
  7. FROM payment_table
  8.  
  9.  
  10. WHERE YEAR(payment_dt) = YEAR(GETDATE())
  11.  
  12. and MONTH(payment_dt) = MONTH(GETDATE())
  13.  
  14. GROUP BY customer_id) MTD_payments
  15.  
  16. UNION ALL
  17.  
  18. (
  19.  
  20. SELECT customer_id, ‘YTD’ AS record_type, COUNT(*), SUM(), AVG()
  21.  
  22. FROM payment_table
  23.  
  24. WHERE
  25.  
  26. WHERE YEAR(payment_dt) = YEAR(GETDATE())
  27.  
  28. GROUP BY customer_id) YTD_payments
  29.  
  30. UNION ALL
  31.  
  32. (
  33.  
  34. SELECT customer_id, ‘LTD’ AS record_type, COUNT(*), SUM(), AVG()
  35.  
  36. FROM payment_table) LTD_payments
  37.  
  38. ) payments_report
  39.  
  40. ORDER BY customer_id, record_type

Can you see the problem?
A person had this query, it would run for over 24 hours. Wow, that is pretty bad, I don’t think I had ever written something that ran over an hour, and the ones I did were mostly defragmentation and update statistics jobs.

The problem is that the following piece of code

  1. WHERE YEAR(payment_dt) = YEAR(GETDATE())
  2. and MONTH(payment_dt) = MONTH(GETDATE())

is not sargable. First what does it mean to be sargable? A query is said to be sargable if the DBMS engine can take advantage of an index to speed up the execution of the query (using index seeks, not covering indexes). The term is derived from a contraction of Search ARGument Able.

This query is not sargable because there is a function on the column, whenever you use a function on the column you will not get an index seek but an index scan. The difference between an index seek and an index scan can be explained like this: when searching for something in a book, you go to the index in the back find the page number and go to the page, that is an index seek. When looking for something in a book you go from page one until the last page, read all the words on all the ages and get what you need, that was an index scan. Do you see how much more expensive in terms of performance that was?

Let’s get back to the query, what can we do to make this piece of code use an index seek?

  1. WHERE YEAR(payment_dt) = YEAR(GETDATE())
  2. and MONTH(payment_dt) = MONTH(GETDATE())

You would change it to this:

  1. WHERE payment_dt >= DATEADD(mm, DATEDIFF(mm, 0, GETDATE())+0, 0)
  2. and payment_dt < DATEADD(mm, DATEDIFF(mm, 0, GETDATE())+1, 0)

You can see the complete question on the MSDN forum site here:
http://forums.microsoft.com/msdn/ShowPost.aspx?PostID=3746751&SiteID=1

The Person said that his query went from over 24 hours to 36 seconds. Wow!! That is very significant. hardware cannot help you out if you have bad queries like that.

The same exact day I answered a very similar question, take a look here: http://forums.microsoft.com/msdn/ShowPost.aspx?PostID=3752248&SiteID=1

The person had this

  1. AND DATEDIFF(d, ‘08/10/2008′, DateCreated) >= 0
  2. AND DATEDIFF(d, DateCreated, ‘08/15/2008′) >= 0

I told him to change it to this

  1. AND DateCreated >= ‘08/10/2008′
  2. and DateCreated <= ‘08/15/2008′

And that solved that query. If you are interested in some more performance, I have written some Query Optimization items on the LessThanDot Wiki. Below are some direct links

Case Sensitive Search
No Functions on Left Side of Operator
Query Optimizations With Dates
Optimization: Set Nocount On
No Math In Where Clause
Don't Use (select *), but List Columns

If you are interested in some blogposts about dates, take a look at these two which I wrote earlier
How Are Dates Stored In SQL Server?
Do You Know How Between Works With Dates?

16 comments »1 trackback » 5851 views