Showing posts with label datetime function. Show all posts
Showing posts with label datetime function. Show all posts

Apr 29, 2009

Missing Date Ranges- the SQL

Here is the reader's problem:

'There is a table, where two of the columns are DateFrom and DateTo. Both columns contain date and time values. How does one find the missing date ranges or, in other words, all the date ranges that are not covered by any of the entries in the table'.
At first glance, the reader's problem and the one on my article look similar; they both involve finding missing, or unused, date ranges. However, they are not the same. In the article, a hypothetical application registers the moments (time) of events and stores them in a database (table). In the reader's problem, some imaginary application records the beginning and the end of the processes, representing them as continuous date/time ranges.
The second task may become more complicated, if the processes can overlap each other in time.
The following diagram illustrates the reader's problem (see Fig.1):
Fig.1 The reader's problem

As shown on the diagram, there is only one missing date range: from Jan 24 to Jan 31.

Without the process that started on Jan 01 and completed on Jan 22, shown in green in the diagram, there would be three missing date ranges:

Jan 04 - Jan 04;
Jan 11 - Jan 19;
Jan 24 - Jan 31;
Notice, that a special case, when the process started, but did not finish yet, can be treated as a process with the end time equal to the current time.

For example, if the process started on June 10 and still running on June 14 (when you execute the query), you can consider that process as a process that started on June 10 and finished on June 14. In other words, dates in the range from June 10 to June 14 are covered (exist).

Solution with Auxiliary Table

Before we explore the fist solution, let us create and load sample table (see Listing1):

IF EXISTS(SELECT * FROM sysobjects
WHERE ID = (OBJECT_ID('dateRanges')) AND xtype = 'U')
DROP TABLE dateRanges;
GO
CREATE TABLE dateRanges(dateFrom DATETIME, dateTo DATETIME)
GO

INSERT INTO dateRanges VALUES('Jan 01, 2000','Jan 22, 2000')
INSERT INTO dateRanges VALUES('Jan 01, 2000','Jan 03, 2000')
INSERT INTO dateRanges VALUES('Jan 05, 2000','Jan 10, 2000')
INSERT INTO dateRanges VALUES('Jan 20, 2000','Jan 23, 2000')
INSERT INTO dateRanges VALUES('Feb 01, 2000','Feb 06, 2000')
INSERT INTO dateRanges VALUES('Feb 02, 2000','Feb 05, 2000')
INSERT INTO dateRanges VALUES('Mar 01, 2000','Mar 02, 2000')
INSERT INTO dateRanges VALUES('Mar 20, 2000','Mar 23, 2000')
INSERT INTO dateRanges VALUES('Mar 25, 2000','Mar 28, 2000')
INSERT INTO dateRanges VALUES('Mar 29, 2000','Apr 03, 2000')
INSERT INTO dateRanges VALUES('Apr 01, 2000','Apr 05, 2000')
INSERT INTO dateRanges VALUES('Apr 20, 2000','Apr 23, 2000')
INSERT INTO dateRanges VALUES('Apr 25, 2000','Apr 28, 2000')
INSERT INTO dateRanges VALUES('May 19, 2000','May 23, 2000')
INSERT INTO dateRanges VALUES('May 20, 2000','May 23, 2000')
INSERT INTO dateRanges VALUES('May 24, 2000','May 25, 2000')
INSERT INTO dateRanges VALUES('May 28, 2000','May 29, 2000')
INSERT INTO dateRanges VALUES('May 28, 2000','Jun 10, 2000')
INSERT INTO dateRanges VALUES('May 29, 2000','Jun 10, 2000')
INSERT INTO dateRanges VALUES('Jun 05, 2000','Jun 15, 2000')
INSERT INTO dateRanges VALUES('Jun 20, 2000','Jun 28, 2000')
INSERT INTO dateRanges VALUES('Jul 02, 2000','Jul 28, 2000')

Listing1. Create and load sample table













Fig.2 The Test data

Then, using an auxiliary table you can easily find missing dates (see Listing2):

SET NOCOUNT ON;

DECLARE @minDateFrom DATETIME, @maxDateTo DATETIME;
SELECT @minDateFrom = MIN(dateFrom),
@maxDateTo = MAX(dateTo)
FROM dateRanges;

-- create auxiliary table dateSequence
IF EXISTS(SELECT * FROM sysobjects
WHERE ID = (OBJECT_ID('dateSequence')) AND xtype = 'U')
DROP TABLE dateSequence;
CREATE TABLE dateSequence(dt DATETIME NOT NULL PRIMARY KEY);

-- load table dateSequence
WITH dates AS
(
SELECT @minDateFrom AS initDate
UNION ALL
SELECT DATEADD(dd, 1, initDate) FROM dates WHERE initDate <@maxDateTo ) INSERT INTO dateSequence SELECT initDate FROM dates OPTION (MAXRECURSION 0); -- get missing dates SELECT CONVERT(VARCHAR(20),dt, 107) AS missingDates FROM dateSequence t1 WHERE NOT EXISTS (SELECT * FROM dateRanges t2 WHERE dt BETWEEN t2.dateFrom AND t2.dateTo) Results: missingDates -------------------- Jan 24, 2000 Jan 25, 2000 Jan 26, 2000 Jan 27, 2000 Jan 28, 2000 Jan 29, 2000 Jan 30, 2000 Jan 31, 2000 Feb 07, 2000 Feb 08, 2000 . . . . . . . Feb 28, 2000 Feb 29, 2000 Mar 03, 2000 Mar 04, 2000 . . . . . . . Mar 18, 2000 Mar 19, 2000 Mar 24, 2000 Apr 06, 2000 Apr 07, 2000 . . . . . . . Apr 18, 2000 Apr 19, 2000 Apr 24, 2000 Apr 29, 2000 Apr 30, 2000 May 01, 2000 May 02, 2000 . . . . . . . May 17, 2000 May 18, 2000 May 26, 2000 May 27, 2000 Jun 16, 2000 Jun 17, 2000 Jun 18, 2000 Jun 19, 2000 Jun 29, 2000 Jun 30, 2000 Jul 01, 2000 Listing2. Find missing dates, using an auxiliary table The same approach can be used, if you need to find missing hours, minutes or seconds. For example, for missing minutes, you need to make a small change in DATEADD() function in common table expression (see Listing3): SET NOCOUNT ON; DECLARE @minDateFrom DATETIME, @maxDateTo DATETIME; SELECT @minDateFrom = MIN(dateFrom), @maxDateTo = MAX(dateTo) FROM dateRanges; -- create table dateSequence IF EXISTS(SELECT * FROM sysobjects WHERE ID = (OBJECT_ID('dateSequence')) AND xtype = 'U') DROP TABLE dateSequence; CREATE TABLE dateSequence(dt DATETIME NOT NULL PRIMARY KEY); -- load table dateSequence WITH dates AS ( SELECT @minDateFrom AS initDate UNION ALL SELECT DATEADD(mi, 1, initDate) FROM dates WHERE initDate <@maxDateTo ) INSERT INTO dateSequence SELECT initDate FROM dates OPTION (MAXRECURSION 0); -- get missing dates SELECT dt AS missingDates FROM dateSequence t1 WHERE NOT EXISTS (SELECT * FROM dateRanges t2 WHERE dt BETWEEN t2.dateFrom AND t2.dateTo) Listing3. Solution for missing minutes with auxiliary table However, the solutions, as shown in Listing2 and Listing3, are not the answers for the reader's problem. This will require finding the missing, or unused, date ranges, but not missing, or unused, dates. One possible way to solve that problem is to transform already found missing dates into missing date ranges. In order to test that solution, you will need to: 1. Create and load an auxiliary table dateSequence as shown in the Listing2 2. Run following query (see Listing 4) WITH datesCTE(missingDates) AS (SELECT missingDates FROM (SELECT dt AS missingDates FROM dateSequence t1 WHERE NOT EXISTS (SELECT * FROM dateRanges t2 WHERE dt BETWEEN t2.dateFrom AND t2.dateTo)) tbl1 ) SELECT CONVERT(VARCHAR(20), t1.missingDates, 107) missingFrom, CONVERT(VARCHAR(20), MIN(t2.missingDates), 107) missingTo FROM (SELECT missingDates FROM datesCTE tbl1 WHERE NOT EXISTS(SELECT * FROM datesCTE tbl2 WHERE DATEDIFF(dd, tbl2.missingDates, tbl1.missingDates) = 1)) t1 INNER JOIN (SELECT missingDates FROM datesCTE tbl1 WHERE NOT EXISTS(SELECT * FROM datesCTE tbl2 WHERE DATEDIFF(dd, tbl1.missingDates, tbl2.missingDates) = 1)) t2 ON DATEDIFF(dd, t1.missingDates, t2.missingDates) >= 0
GROUP BY t1.missingDates
ORDER BY t1.missingDates

Results:

missingFrom missingTo
------------ ------------
Jan 24, 2000 Jan 31, 2000
Feb 07, 2000 Feb 29, 2000
Mar 03, 2000 Mar 19, 2000
Mar 24, 2000 Mar 24, 2000
Apr 06, 2000 Apr 19, 2000
Apr 24, 2000 Apr 24, 2000
Apr 29, 2000 May 18, 2000
May 26, 2000 May 27, 2000
Jun 16, 2000 Jun 19, 2000
Jun 29, 2000 Jul 01, 2000

Listing4. Find missing date ranges, using an auxiliary table

There is, however, at least one way to solve the reader's problem in one query and without an auxiliary table.
Original post: http://www.simple-talk.com/sql/t-sql-programming/missing-date-ranges--the-sequel/

SET DATEFIRST (Transact-SQL)

SET DATEFIRST (Transact-SQL)

Comparison of Dates in SQL

Dealing with dates has always been a problem with SQL Server queries. Small things lead to critical errors, which are very difficult to detect. One of the common usages of dates is in 'where' clause of the query to filter out the records based on one or more datetime fields. Keeping few things in mind while writing such SQL queries will help you to write an effective query which does not miss any desired records. This article gives you an idea about writing such queries involving date comparisons.
If the manager says, "I want all the employees who joined in year 2009 until 06-Feb-2009." In SQL, this is a very simple query - Date of Joining >= (greater than equal to) 01-01-2009 and <= (less than equal to) 02-06-2009. To some of you this may sound okay, and some may not be sure if it is correct. But this query, which seems to be simple, may not return all employees having date of joining in the period given above. The employee having date of joining as '2009-02-06 12:11:46.273' in the database will not be returned by this query (Date of Joining >= 01-01-2009 and <= 02-06-2009). This is due to the reason that '02-06-2009' in the above query is taken by SQL as '02-06-2009 00:00:00.000' and the date of joining '2009-02-06 12:11:46.273' is greater than this value (not less or equal). This will get more clear with other examples in this article.
When a developer writes a query involving date comparison, it is common practice to include just the date portion in the query (time portion not included). I encountered a similar issue with the reports being generated for my .NET application. As an example: a report was required to show all employees who entered their timesheet entries today. Whenever an employee enters his or her timesheet, a record is created in a database. The employees who entered their time today are fetched based on datetime field that indicates the time of entry.

For the above example, the following table shows the records created in the database:

EmployeeId EnteredHour EnteredDate
1 8 2009-03-19 12:11:46.273
2 8 2009-03-19 14:12:20.893
3 8 2009-03-19 15:12:25.563
4 8 2009-03-19 15:12:35.707
Now I run the following query to fetch the EmployeeIDs of employee who entered their time today (2009-03-19).

SELECT * FROM EmployeeTimeEntry WHERE EnteredDate = GETDATE()

Surprisingly, the above query does not return any records. This is due to the fact that for the above 'WHERE' clause to be satisfied, the time portion of the column 'EnteredDate' should also match with the datetime returned by the GETDATE() function. The solution to this problem is to use the DATEDIFF function as shown in the query below:

SELECT * FROM EmployeeTimeEntry WHERE DATEDIFF(DAY,EnteredDate,GETDATE())= 0

The use of 'DAY' as first argument of the DATEDIFF function ignores the time portion of field 'EnteredDate' and just calculates the difference in days between 'EnteredDate' and date returned by GETDATE function. Since the date portion of both 'EnteredDate' and date returned by GETDATE function is same, I get the desired result.

Source:
http://www.sqlservercentral.com/articles/T-SQL/65806/

Comparison of Dates in SQL

Dealing with dates has always been a problem with SQL Server queries. Small things lead to critical errors, which are very difficult to detect. One of the common usages of dates is in 'where' clause of the query to filter out the records based on one or more datetime fields. Keeping few things in mind while writing such SQL queries will help you to write an effective query which does not miss any desired records. This article gives you an idea about writing such queries involving date comparisons.
If the manager says, "I want all the employees who joined in year 2009 until 06-Feb-2009." In SQL, this is a very simple query - Date of Joining >= (greater than equal to) 01-01-2009 and <= (less than equal to) 02-06-2009. To some of you this may sound okay, and some may not be sure if it is correct. But this query, which seems to be simple, may not return all employees having date of joining in the period given above. The employee having date of joining as '2009-02-06 12:11:46.273' in the database will not be returned by this query (Date of Joining >= 01-01-2009 and <= 02-06-2009). This is due to the reason that '02-06-2009' in the above query is taken by SQL as '02-06-2009 00:00:00.000' and the date of joining '2009-02-06 12:11:46.273' is greater than this value (not less or equal). This will get more clear with other examples in this article.
When a developer writes a query involving date comparison, it is common practice to include just the date portion in the query (time portion not included). I encountered a similar issue with the reports being generated for my .NET application. As an example: a report was required to show all employees who entered their timesheet entries today. Whenever an employee enters his or her timesheet, a record is created in a database. The employees who entered their time today are fetched based on datetime field that indicates the time of entry.

For the above example, the following table shows the records created in the database:

EmployeeId EnteredHour EnteredDate
1 8 2009-03-19 12:11:46.273
2 8 2009-03-19 14:12:20.893
3 8 2009-03-19 15:12:25.563
4 8 2009-03-19 15:12:35.707
Now I run the following query to fetch the EmployeeIDs of employee who entered their time today (2009-03-19).

SELECT * FROM EmployeeTimeEntry WHERE EnteredDate = GETDATE()

Surprisingly, the above query does not return any records. This is due to the fact that for the above 'WHERE' clause to be satisfied, the time portion of the column 'EnteredDate' should also match with the datetime returned by the GETDATE() function. The solution to this problem is to use the DATEDIFF function as shown in the query below:

SELECT * FROM EmployeeTimeEntry WHERE DATEDIFF(DAY,EnteredDate,GETDATE())= 0

The use of 'DAY' as first argument of the DATEDIFF function ignores the time portion of field 'EnteredDate' and just calculates the difference in days between 'EnteredDate' and date returned by GETDATE function. Since the date portion of both 'EnteredDate' and date returned by GETDATE function is same, I get the desired result.

Source:
http://www.sqlservercentral.com/articles/T-SQL/65806/

Mar 4, 2009

How to get the name of the day of date

That should be DATENAME(dw,'3 sep 2007')

But I prefer using fully qulified names DATENAME(weekday,'3 sep 2007')

Jan 21, 2009

How to Combine Date & Time in SQL

/*
=================================================================
== PROCEDURE: COMBINE_DTM
==
== PURPOSE: Combines a date and time into a datetime result
==
== CREATED: Friday, October 3, 2008 @ 1:24:00 AM
==
== TYPE: APP
=================================================================*/
CREATE FUNCTION [dbo].[COMBINE_DTM] (

@inDATE_DTM AS datetime,

@inTIME_DTM AS datetime

) RETURNS datetime
AS
BEGIN
RETURN
CAST((ROUND(CAST(@inDATE_DTM AS float), 0, 1) ) AS float) +
(CAST(@inTIME_DTM AS float) - (ROUND(CAST(@inTIME_DTM AS float), 0, 1)) + .00000001)
END
use this links to original links

http://social.msdn.microsoft.com/forums/en-US/sqlintegrationservices/thread/ee161d81-8f04-4328-b2a8-a32f95c30b29/
or
http://blogs.msdn.com/patrickgallucci/archive/2007/11/18/sql-server-function-to-merge-a-date-with-a-time.aspx

Dec 17, 2008

How to get week name in sql:sql/sql2005

CREATE FUNCTION dbo.DayOfWeek_fn(@dtDate DATETIME)
RETURNS VARCHAR(10)
AS
BEGIN
DECLARE @rtDayofWeek VARCHAR(10)
DECLARE @weekDay INT
-- Here I have subtracted 7 For keeping Sunday as the First day
-- like wise for Monday we need to subtract 2 and so on
SET @weekDay = ((DATEPART(dw,@dtDate)+@@DATEFIRST-7)%7)
SELECT @rtDayofWeek = CASE @weekDay
WHEN 1 THEN 'Sunday'
WHEN 2 THEN 'Monday'
WHEN 3 THEN 'Tuesday'
WHEN 4 THEN 'Wednesday'
WHEN 5 THEN 'Thursday'
WHEN 6 THEN 'Friday'
WHEN 7 THEN 'Saturday'
END
RETURN (@rtDayofWeek)
END
GO