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/

Apr 21, 2009

Google Adsense Privacy Policy Generator - Create a Privacy Policy Adsense

Google Adsense Privacy Policy Generator - Create a Privacy Policy Adsense

Privacy Policy

Privacy Policy for www.tselvaa.blogspot.com 

If you require any more information or have any questions about our privacy policy, please feel free to contact us by email at tselvaa9@google.com. 

At www.tselvaa.blogspot.com, the privacy of our visitors is of extreme importance to us. This privacy policy document outlines the types of personal information is received and collected by www.tselvaa.blogspot.com and how it is used. 

Log Files
Like many other Web sites, www.tselvaa.blogspot.com makes use of log files. The information inside the log files includes internet protocol ( IP ) addresses, type of browser, Internet Service Provider ( ISP ), date/time stamp, referring/exit pages, and number of clicks to analyze trends, administer the site, track user’s movement around the site, and gather demographic information. IP addresses, and other such information are not linked to any information that is personally identifiable. 

Cookies and Web Beacons 
www.tselvaa.blogspot.com does not use cookies. 

DoubleClick DART Cookie 
.:: Google, as a third party vendor, uses cookies to serve ads on www.tselvaa.blogspot.com.
.:: Google's use of the DART cookie enables it to serve ads to users based on their visit to www.tselvaa.blogspot.com and other sites on the Internet. 
.:: Users may opt out of the use of the DART cookie by visiting the Google ad and content network privacy policy at the following URL - http://www.google.com/privacy_ads.html 

Some of our advertising partners may use cookies and web beacons on our site. Our advertising partners include ....
Google Adsense


These third-party ad servers or ad networks use technology to the advertisements and links that appear on www.tselvaa.blogspot.com send directly to your browsers. They automatically receive your IP address when this occurs. Other technologies ( such as cookies, JavaScript, or Web Beacons ) may also be used by the third-party ad networks to measure the effectiveness of their advertisements and / or to personalize the advertising content that you see. 

www.tselvaa.blogspot.com has no access to or control over these cookies that are used by third-party advertisers. 

You should consult the respective privacy policies of these third-party ad servers for more detailed information on their practices as well as for instructions about how to opt-out of certain practices. www.tselvaa.blogspot.com's privacy policy does not apply to, and we cannot control the activities of, such other advertisers or web sites. 

If you wish to disable cookies, you may do so through your individual browser options. More detailed information about cookie management with specific web browsers can be found at the browsers' respective websites. 

Blogger Buster: Complete List of Blogger Widgets

Blogger Buster: Complete List of Blogger Widgets