SELECT all date interval from timestamp to timestamp - sql

Is there a way to select all days between two timestamps.
Say I have 2 timestamps 2/1/2015 and 2/28/2015
Is there a way to SELECT all days between the too
Output:
days
--------
2/1/2015 |
2/2/2015 |
2/3/2015 |
...
2/26/2015 |
2/27/2015 |
2/28/2015 |
Edit:
I am trying to count some records that were created each day between to dates, The table only has a timecreated field with the time stamp of when they were created, what I need to do is generate a table that has all days between 2 dates and a 0 count if no records were created that day or the X for the number of records created.

You can use generate_series to generate all dates between your two dates, and do a join with your table using the timestamp column:
SELECT a_date, count(days) FROM
generate_series('2015-01-01'::timestamp, '2015-03-31', '1 day') a_date
left outer join
my_table
on my_table.days = a_date
group by a_date

Sure. How about:
SELECT * FROM table WHERE days>='2015-02-01' AND days<='2015-02-28'

You can use count() on the column indicating that there's a record or not. Say your column is called "Record" :
SELECT count(record) as NbRecord FROM table WHERE days>='2015-02-01' AND days<='2015-02-28'

If you want a table that actually contains the dates, this is one way to do it:
--gives a table which contains every date between start & end (inclusive)
declare #startdate datetime = '05/01/2015';
declare #enddate datetime = '05/20/2015';
declare #result table(d datetime);
declare #diff int = datediff(d,#startdate,#enddate);
declare #i int = 0;
while(#i<=#diff) begin
insert into #result(d) values (#startdate + #i);
set #i = #i + 1;
end;
select * from #result

Related

SQL date difference ignoring years

I have two date columns and I would like to calculate their differences in Months/Days and exclude years.
Assuming the first date is 10/30/2017, comparing it to the current date, its difference should one. If the current date is 10/30/2018, the difference should also be one.
A few examples:
Schedule Date:10/30/2017 Current Date 10/29/2017 Diff 1
Schedule Date:10/30/2017 Current Date 11/30/2017 Diff 30
Schedule Date:10/30/2017 Current Date 10/29/2018 Diff 1
Schedule Date:10/30/2017 Current Date 11/30/2018 Diff 30
Schedule Date:10/30/2017 Current Date 10/29/2019 Diff 1
Schedule Date:10/30/2017 Current Date 11/30/2019 Diff 30
Try This
SELECT ABS(365 * DATEDIFF(year, '10/30/2017', '11/30/2018')
- DATEDIFF(day, '10/30/2017', '11/30/2018')) AS DateDiff;
Note that the difference between 10/30/ and 11/30/ can't be 30 days as you have shown.It is 31 days.
You can use Common Table Expression to get your results like below :
DECLARE #Schedule_Date datetime = '10/30/2017'
DECLARE #Current_Date datetime = '11/30/2019'
;WITH usingCTE AS
(
SELECT CAST(STUFF(CONVERT(varchar, #Schedule_Date, 102), 1, 4, CAST(YEAR(#Current_Date) AS varchar)) AS datetime) AS Schedule_Date
)
SELECT abs(DATEDIFF(day, #Current_Date, Schedule_Date)) FROM usingCTE
Another approach of the query :
DECLARE #Schedule_Date datetime = '10/30/2017'
DECLARE #Current_Date datetime = '11/30/2017'
SELECT ABS(DATEDIFF(day,
REPLACE(#Schedule_Date, DATEPART(year, #Schedule_Date),
DATEPART(year, #Current_Date)), -- replace the year with current year
#Current_Date))
This might work for you, first adjust the date to be in the same year, then calculate the number of days different between the two dates:
DECLARE #ScheduleDate DATE = '2017-10-30';
DECLARE #Dates TABLE (CurrentDate DATE);
INSERT INTO #Dates VALUES ('2017-10-29'),('2017-11-30'),('2018-10-29'),('2018-11-30'),('2019-10-29'),('2019-11-30');
SELECT #ScheduleDate ScheduleDate, *
FROM #Dates a
CROSS APPLY (SELECT AdjustedDate=DATEADD(YEAR, YEAR(#ScheduleDate) - YEAR(a.CurrentDate), a.CurrentDate)) b
CROSS APPLY (SELECT Diff=ABS(DATEDIFF(DAY, #ScheduleDate, b.AdjustedDate))) c

Finding the next working day from SQL table

I am dealing with the holiday table of a application and I have to find the next working days based on the holiday list in that table .
If the input is a working day, we expect a blank/NULL to be returned, but if it is a holiday, we expect the next working day to be returned.
My holiday table contains below sample data.
First date column is for startdate and second one is for enddate. Instead of using startdate and enddate for two consecutive holidays. Client have created two separate rows.
Now I have to write a select query which will give the next working days based on that sample data.
Suppose if I am passing '2016-04-20 00:00:00.000' as the conditional date then the query should return '2016-04-22 00:00:00.000' as the working date and there are consecutive two holidays.
2016 2016-04-20 00:00:00.000 2016-04-20 00:00:00.000 Test
2016 2016-04-21 00:00:00.000 2016-04-21 00:00:00.000 Test2
2016 2016-04-28 00:00:00.000 2016-04-28 00:00:00.000 Test3
You can try this:
--create table holidays(y int, ds datetime, de datetime, hname varchar(10));
--insert into holidays values
--(2016,'2016-04-20 00:00:00.000','2016-04-20 00:00:00.000','Test'),
--(2016,'2016-04-21 00:00:00.000','2016-04-21 00:00:00.000','Test2'),
--(2016,'2016-04-28 00:00:00.000','2016-04-28 00:00:00.000','Test3'),
--(2016,'2016-04-22 00:00:00.000','2016-04-22 00:00:00.000','Test4')
CREATE FUNCTION dbo.getNextDate(#dateToCheck datetime) RETURNS Datetime
AS
BEGIN
RETURN(
select top 1 dateadd(d,1,de) from
(select y,
MIN(ds) as ds,
MAX(ds) as de
from
(
Select
*,
ROW_NUMBER() OVER(ORDER BY ds asc) as ranking
from holidays
) t
group by y,(CAST(ds AS INT)-Ranking)
)t
where #dateToCheck BETWEEN ds AND de
)
END
Upon testing:
SELECT dbo.getNextDate('2016-04-23 00:00:00.000')-- returns NULL
SELECT dbo.getNextDate('2016-04-21 00:00:00.000')-- returns 2016-04-23 00:00:00.000
SQL demo link
Let me know if the below mentioned code works.This code first makes a calendar table and then excludes Saturday and Sunday from the dates.
declare #mn date = (select min(yourdate) from table)
select top 1 a.caldate
from
(
select dateadd(dd,row_number() over (order by (select 1)) - 1,#mn) as caldate
from sys.all_objects
) as a
where a.caldate not in (select cast(yourdate as date) as yourdate from TableA) and datename(dw,a.caldate) not in ('Saturday','Sunday') and a.caldate >= '2016-04-20'
Suposing a holidays table with this structure:
CREATE TABLE holidays (
[year] int,
[ds] datetime,
[de] datetime,
[description] nvarchar(50)
)
You can create a function that iterates through dates until it finds the correct one
CREATE FUNCTION dbo.getNextDate(#dateToCheck datetime) RETURNS Datetime
AS
BEGIN
DECLARE #tempDate datetime
SET #tempDate=DATEADD(day,1,#dateToCheck)
WHILE EXISTS(SELECT * FROM holidays WHERE #tempDate BETWEEN ds AND de)
BEGIN
SET #tempDate=DATEADD(day,1,#tempDate)
END
RETURN #tempDate
END
This is a very rudimentary first aproximation but it should work.

Query to aggregate totals between dates

I have data of the following format:
Date Value
08/28 100
09/01 1
09/01 5
09/10 2
I would like my output to be:
Date Value
08/28 100
08/29 100
08/30 100
08/31 100
09/01 106
09/02 106
.
.
.
09/10 108
I'm just getting started with SQL, so any help would be appreciated. What I have right now is below, but that's not really close to what I seek:
SELECT Date, COUNT(DISTINCT(Service)) AS Value
FROM [Directory]
WHERE Date <= #myDate
GROUP BY Date ORDER BY Date
First, you can use a sub query to get the aggregate values
SELECT Date, (SELECT SUM(Value) FROM Directory d WHERE d.Date <= Directory.Date)
FROM [Directory]
WHERE Date <= #myDate
ORDER BY Date
Which would give you something that looks like this:
Date Value
08/28 100
09/01 101
09/01 106
09/10 108
Then you can add a Date table as sgeddes suggested. This article explains if fairly well: http://michaelmorley.name/how-to/create-date-dimension-table-in-sql-server
Then you can modify your query like so
SELECT DateTable.Date, (SELECT SUM(Value) FROM Directory d WHERE d.Date <= Directory.Date)
FROM [Directory] LEFT OUTER JOIN DateTable on Directory.Date = DateTable.Date
WHERE DateTable.Date <= #myDate
ORDER BY DateTable.Date
To get the data format you're looking for.
Based on sgeddes suggestion:
SELECT a.Date, COUNT(DISTINCT(d.Service)) AS Value
FROM [Directory] d
LEFT OUTER JOIN [Date Table] a on d.Date = a.Date
WHERE Date <= #myDate
GROUP BY Date
ORDER BY Date
Use following script in sqlserver :
BEGIN
--If exist then drop temp tables
DROP TABLE #YOURTABLE;
DROP TABLE #TEST1;
DECLARE #MINDATE DATETIME;
DECLARE #MAXDATE DATETIME;
CREATE TABLE #YOURTABLE(
CDATE DATE,
VALUE INT
);
INSERT INTO #YOURTABLE VALUES ('08/28/2014',100),('09/01/2014',1),('09/01/2014',5),('09/10/2014',100);
--select start date and end date from your table
SELECT #MINDATE=MIN(CDATE),#MAXDATE=MAX(CDATE) FROM #YOURTABLE;
CREATE TABLE #TEST1(
CDATE DATE,
VALUE INT
);
;WITH CALENDAR
AS (
SELECT #MINDATE CDATE
UNION ALL
SELECT CDATE + 1
FROM CALENDAR
WHERE CDATE + 1 <= #MAXDATE
)
-- insert all dates with 0 value in temp table
INSERT INTO #TEST1 SELECT CDATE,0 FROM CALENDAR;
--delete dates which are already there in your table
DELETE FROM #TEST1 WHERE CDATE IN (SELECT CDATE FROM #YOURTABLE)
-- insert all dates with values from your table to temporary table which holds dates which are not in your table
INSERT INTO #TEST1 SELECT * FROM #YOURTABLE;
SELECT T1.CDATE,(SELECT SUM(VALUE) FROM #TEST1 T2 WHERE T2.CDATE<=T1.CDATE) FROM #TEST1 T1
END

Grouping by contiguous dates, ignoring weekends in SQL

I'm attempting to group contiguous date ranges to show the minimum and maximum date for each range. So far I've used a solution similar to this one: http://www.sqlservercentral.com/articles/T-SQL/71550/ however I'm on SQL 2000 so I had to make some changes. This is my procedure so far:
create table #tmp
(
date smalldatetime,
rownum int identity
)
insert into #tmp
select distinct date from testDates order by date
select
min(date) as dateRangeStart,
max(date) as dateRangeEnd,
count(*) as dates,
dateadd(dd,-1*rownum, date) as GroupID
from #tmp
group by dateadd(dd,-1*rownum, date)
drop table #tmp
It works exactly how I want except for one issue: weekends. My data sets have no records for weekend dates, which means any group found is at most 5 days. For instance, in the results below, I would like the last 3 groups to show up as a single record, with a dateRangeStart of 10/6 and a dateRangeEnd of 10/20:
Is there some way I can set this up to ignore a break in the date range if that break is just a weekend?
Thanks for the help.
EDITED
I didn't like my previous idea very much. Here's a better one, I think:
Based on the first and the last dates from the set of those to be grouped, prepare the list of all the intermediate weekend dates.
Insert the working dates together with weekend dates, ordered, so they would all be assigned rownum values according to their normal order.
Use your method of finding contiguous ranges with the following modifications:
1) when calculating dateRangeStart, if it's a weekend date, pick the nearest following weekday;
2) accordingly for dateRangeEnd, if it's a weekend date, pick the nearest preceding weekday;
3) when counting dates for the group, pick only weekdays.
Select from the resulting set only those rows where dates > 0, thus eliminating the groups formed only of the weekends.
And here's an implementation of the method, where it is assumed, that a week starts on Sunday (DATEPART returns 1) and weekend days are Sunday and Saturday:
DECLARE #tmp TABLE (date smalldatetime, rownum int IDENTITY);
DECLARE #weekends TABLE (date smalldatetime);
DECLARE #minDate smalldatetime, #maxDate smalldatetime, #date smalldatetime;
/* #1 */
SELECT #minDate = MIN(date), #maxDate = MAX(date)
FROM testDates;
SET #date = #minDate - DATEPART(dw, #minDate) + 7;
WHILE #date < #maxDate BEGIN
INSERT INTO #weekends
SELECT #date UNION ALL
SELECT #date + 1;
SET #date = #date + 7;
END;
/* #2 */
INSERT INTO #tmp
SELECT date FROM testDates
UNION
SELECT date FROM #weekends
ORDER BY date;
/* #3 & #4 */
SELECT *
FROM (
SELECT
MIN(date + CASE DATEPART(dw, date) WHEN 1 THEN 1 WHEN 7 THEN 2 ELSE 0 END)
AS dateRangeStart,
MAX(date - CASE DATEPART(dw, date) WHEN 1 THEN 2 WHEN 7 THEN 1 ELSE 0 END)
AS dateRangeEnd,
COUNT(CASE WHEN DATEPART(dw, date) NOT IN (1, 7) THEN date END) AS dates,
DATEADD(d, -rownum, date) AS GroupID
FROM #tmp
GROUP BY DATEADD(d, -rownum, date)
) s
WHERE dates > 0;

TSQL Selecting 2 & 3 week intervals

I am attempting to populate a table based on 2 and 3 week intervals for a semi-monthly pay period in TSQL. The table should populate,
2 week date
2 week date
3 week date
2 week date
2 week date
3 week date
..based on the first date I supply, subsequently adding 2 or 3 weeks to the last date supplied. I should be able to supply a start date and end date. It may be that it's just early in the morning, but can't think of an elegant way to accomplish this task. Any pointers?
Thanks!
George
WITH dates (d, n) AS
(
SELECT #mydate, 1
UNION ALL
SELECT DATEADD(week, CASE n % 3 WHEN 0 THEN 3 ELSE 2 END, d), n + 1
FROM dates
WHERE d < #enddate
)
INSERT
INTO mytable
SELECT d
FROM dates
OPTION (MAXRECURSION 0)
Horrid brute force approach - because the 2,2,3 is difficult to loop just adding it regardless into the temp table and then filtering at the end incase a couple extra entries go in - not the most efficient but if you are needing to just get a range one off then it works.
So the caveat here is: ok for one off, I wouldn't use in production :)
declare #start datetime
declare #end datetime
declare #calculated datetime
set #start = '20010101'
set #end = '20011231'
set #calculated = #start
Create Table #Dates (PayDate datetime)
while #calculated <= #end
begin
set #calculated = DateAdd(wk,2,#calculated)
insert into #Dates(paydate) values (#calculated)
set #calculated = DateAdd(wk,2,#calculated)
insert into #Dates(paydate) values (#calculated)
set #calculated = DateAdd(wk,3,#calculated)
insert into #Dates(paydate) values (#calculated)
end
select * from #Dates where paydate >= #start and paydate <= #end
drop table #dates
So you have a 7-week cycle -- figure out which 7-week period you're in from some known starting point and then which week of this group of 7 you are.