Group SUM data in 24 hour chunks - sql

I have data in Microsoft SQL Server 2008 that I need to SUM. The catch is I need to group the sums by a 24 hour period. The 24 hour period is from 3:00pm one day to 3:00pm the next day.
For Example
DateExited, Value
1/1/2012 15:00, 5
1/1/2012 15:04, 6
1/1/2012 17:00, 7
1/2/2012 00:00, -5
1/2/2012 09:00, 10
1/2/2012 15:00, 31
The sum of that should be 54. I have the following query but that groups everything from midnight to midnight instead of 3:00 pm to 3:00 pm
SELECT dateadd(day,datediff(day,0, dateadd(hh, 0, DateExited) ),0) As SummaryDate, SUM(Value) as s1
FROM Test
where DateExited BETWEEN dateadd(year,datediff(year,0,GETDATE()),0) AND GetDate()
GROUP BY dateadd(day,datediff(day,0, dateadd(hh, 0, DateExited) ),0)
ORDER BY SummaryDate

You could add minus 15 hours, and then cast the result to date:
select cast(dateadd(hour,-15,'2012-05-06 14:30') as date) -- 2012-05-05
select cast(dateadd(hour,-15,'2012-05-06 15:30') as date) -- 2012-05-06
Giving you a group by like:
group by cast(dateadd(hour,-15,'2012-05-06 03:30') as date)

Subtract 15 hours from your date/time value should give you the results you are looking for.
Also, with SQL 2008, you can convert datetimes to dates instead of adding days to 0.
SELECT CONVERT(DATE, DATEADD(hour, -15, DateExited)) As SummaryDate, SUM(Value) as s1
FROM Test
WHERE DATEADD(hour, -15, DateExited) BETWEEN #StartDate AND #EndDate
GROUP BY CONVERT(DATE, DATEADD(hour, -15, DateExited))
ORDER BY SummaryDate

The easiest way is to cast to date:
SELECT cast(SummaryDate as date), SUM(Value) as s1
FROM Test
where DateExited BETWEEN dateadd(year,datediff(year,0,GETDATE()),0) AND GetDate()
GROUP BY cast(SummaryDate as date)
ORDER BY 1

You could create a function to return the needed value given an date input parameter:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION GetValueSum
(
#StartDate DATETIME
)
RETURNS INT
AS
BEGIN
DECLARE #ValueSum INT
DECLARE #StartDateTime DATETIME
DECLARE #EndDateTime DATETIME
-- Set the starting time to 3 PM on the datetime sent in:
SET #StartDateTime = DATEADD(hh, 15, CAST(CAST(#StartDate AS DATETIME) AS DATETIME))
-- Set the end time to one day later, minus 3 milliseconds (this smallest unit sql server can use)
SET #EndDateTime = DATEADD(ms, -3, DATEADD(dd, 1, #StartDateTime))
SELECT #ValueSum = SUM(Value)
FROM dbo.test
WHERE DateExited BETWEEN #StartDateTime AND #EndDateTime
RETURN #ValueSum
END
GO

Related

Today Production - SQL Date Calculation case when

I have an issue regarding date calculations.
I have a datetime column called CreatedLocalTime date with this format: 2015-11-15 19:48:50.000
I need to retrieve a new column called Prod_Date with:
if “CreatedLocalTime” between
(CreatedLocalTime 7 AM)
& (CreatedLocalTime+1 7 AM)
return CreatedLocalTime date with DD/MM/YYYY format
On other words, today production = sum of yesterday from 7 AM till today at 7 AM.
Any help using case?
For day 7AM to day+1 7AM, you can try:
SELECT CAST(CreatedLocalTime as date)
...
FROM ...
WHERE ...
CreatedLocalTime >= DATEADD(hour, 7, CAST(CAST(CreatedLocalTime as date) as datetime))
AND
CreatedLocalTime < DATEADD(hour, 31, CAST(CAST(CreatedLocalTime as date) as datetime))
...
For previous day 7AM to day 7AM, replace 7 by -14 and 31 by 7.
Another way..
SELECT CASE WHEN CreatedLocalTime BETWEEN DATEADD(HOUR, 7,
CAST(CAST (CreatedLocalTime AS DATE) AS DATETIME))
AND DATEADD(HOUR, 31,
CAST(CAST (CreatedLocalTime AS DATE) AS DATETIME))
THEN REPLACE(CONVERT(NVARCHAR, CreatedLocalTime, 103), ' ', '/')
END AS CreatedLocalTime
You can write the else part for this, if needed
It looks like you want somthing along the lines of
DECLARE #StartDateTime Datetime
DECLARE #EndDateTime Datetime
SET #EndDateTime = DATEADD(hour, 7,convert(datetime,convert(date,getdate())) )
SET #StartDateTime = DATEADD(day, -1, #EndDateTime )
--Print out the variables for demonstration purposes
PRINT '#StartDateTime = '+CONVERT(nchar(19), #StartDateTime,120)
PRINT '#EndDateTime = '+CONVERT(nchar(19), #EndDateTime,120)
SELECT SUM (Production) AS Prod_Date FROM YourSchema.YourTable WHERE CreatedLocalTime >= #StartDateTime AND CreatedLocalTime < #EndDateTime
But you could also look at it as all the times which after you remove 7 hours from them are yesterday
SELECT SUM (Production) AS Prod_Date
FROM YourSchema.YourTable
WHERE DATEDIFF(day,DATEADD(hour, -7, CreatedLocalTime ))) = 1
The First version will be more efficient as the query will only have to do the date arithmetic once at the start while the second involved executing DATEDIFF and DATEADD for every record. This will be slower on large amounts of data.
The Gold plated solution would be to add a computed column to your table
ALTER TABLE YourSchema.YourTable ADD EffectiveDate AS CONVERT(date, DATEDIFF(day,DATEADD(hour, -7, CreatedLocalTime ))))
And then an index on that column
CREATE INDEX IX_YourTable_EffectiveDate ON YourSchema.YourTable (EffectiveDate )
So you can write
DECLARE #YesterDay date = DATEADD(day,-1, getdate())
SELECT SUM (Production) AS Prod_Date
FROM YourSchema.YourTable
WHERE EffectiveDate = #YesterDay

SQL Insert Dates every 7 days for 10 years

I am trying to script a query that inserts a date into a table every 7 days for the next 10 years. This will prevent me from having to have to type these dates by hand.
Is there a way to specify a start date and add 7 days to that date on each insert until the end date is reached?
Attached is my query. not sure where to being on this one. Any help is most appreciated.
declare #startDate date
declare #endDate date
set #startDate='2015-01-03'
set #endDate='2015-01-04'
INSERT INTO TimePeriod (YearsA)
VALUES ('2015-01-03'),
('2015-01010'),
(etc.)
('2025-01-04)
The below query will give you weekend dates for till 2042-05-17 years.
SELECT DISTINCT DATEADD(DAY, - DATEPART(WEEKDAY, DayNumber), CAST(DayNumber AS DATE))
FROM(
SELECT TOP (10000)
DATEADD(DAY
,ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1
, CAST(YEAR(GETDATE()) AS VARCHAR(4)) + '0101' ) DayNumber
From master..spt_values x Cross Join master..spt_values y
)x
ORDER BY DATEADD(DAY, - DATEPART(WEEKDAY, DayNumber), CAST(DayNumber AS DATE))
Result
2015-01-03
2015-01-10
2015-01-17
2015-01-24
2015-01-31
2015-02-07
2015-02-14
2015-02-21
2015-02-28
You can use recursive CTE to get all the dates:
try;
declare #startDate date
declare #endDate date
set #startDate='2015-03-01' -- YYYY-MM-DD format
set #endDate='2015-04-01'
;with all_date as (
select #startDate Dates
union all
select DATEADD(day, 7, Dates)
from all_date
where Dates < #endDate
)
INSERT INTO TimePeriod (YearsA)
select Dates from all_date

select query using getdate() to after 15 days in sql server?

name amount date
---------------------------------------
xxx 1000 2014-04-20 12:53:23.983
yyy 1500 2014-04-25 12:53:23.983
My output like this:
name amount date
--------------------------------------
xxx 1000 2014-04-20 12:53:23.983
My query:
alter proc K_VM_GetTaxdetails
as
begin
select name, amount, date
from K_VM_TaxDetails
where DATEADD(day, -15, GETDATE()) = date
end
I have tried like this but I am not getting required output.
If I have a date = 2014-04-20 12:53:23.983 in my table, I want to display all data before 15 days from that date.
How can I write in where condition?
This displays all rows in the last 15 days:
declare #now = select cast(floor(cast(getdate() as float)) as datetime); -- truncate time from datetime
select name, amount, date from K_VM_TaxDetails
where date >= dateadd(day, -15, #now);
This displays all rows for single day 15 days ago:
declare #now = select cast(floor(cast(getdate() as float)) as datetime); -- truncate time from datetime
select name,amount,date from K_VM_TaxDetails
where date >= dateadd(day, -15, #now) and
date < dateadd(day, -14, #now);
This will give you all data from the day 15 days ago
alter proc K_VM_GetTaxdetails
as
begin
declare #d datetime = dateadd(day, datediff(day, -15, getdate()), 0)
select name, amount, date
from K_VM_TaxDetails
where date >= #d -- retrieve from
and date < dateadd(day, 1, #d) -- retrieve to
end
Shows dates at least 15 days older than the current date:
select name,amount,date from K_VM_TaxDetails
where date <= DATEADD(day, -15, GETDATE())
I suggest to compare date with only date part and not time as including time in date comparison sometimes provided incorrect result.
Below query removes time part and compare only date.
select name, amount, date
from K_VM_TaxDetails
where (convert(date,[date]) >= DATEADD(day, -15, convert(date,GETDATE())) and convert(date,[date]) <= convert(date,GETDATE()))
alter proc K_VM_GetTaxdetails
as
begin
select name, amount, date
from K_VM_TaxDetails
where DATEADD(day, -15, SYSDATETIME()) = date
end

tsql: How to retrieve the last date of each month between given date range

I have two date for example 08/08/2013 and 11/11/2013 and I need last date of each month starting from August to November in a table so that i can iterate over the table to pick those dates individually.
I know how to pick last date for any month but i am stucked with a date range.
kindly help, it will be highly appreciated.
Note : I am using Sql 2008 and date rang could be 1 month , 2 month or 6 month or a year or max too..
You can use CTE for getting all last days of the month within the defined range
Declare #Start datetime
Declare #End datetime
Select #Start = '20130808'
Select #End = '20131111'
;With CTE as
(
Select #Start as Date,Case When DatePart(mm,#Start)<>DatePart(mm,#Start+1) then 1 else 0 end as [Last]
UNION ALL
Select Date+1,Case When DatePart(mm,Date+1)<>DatePart(mm,Date+2) then 1 else 0 end from CTE
Where Date<#End
)
Select * from CTE
where [Last]=1 OPTION ( MAXRECURSION 0 )
DECLARE #tmpTable table (LastDates DATE);
DECLARE #startDate DATE = '01/01/2012'; --1 Jan 2012
DECLARE #endDate DATE = '05/31/2012'; --31 May 2012
DECLARE #tmpEndDate DATE;
SET #startDate = DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,#startDate)+1,1));
SET #tmpEndDate = DATEADD(DAY, 1, #endDate);
WHILE (#startDate <= #tmpEndDate)
BEGIN
INSERT INTO #tmpTable (LastDates) values (DATEADD(DAY, -1, #startDate));
SET #startDate = DATEADD(MONTH, 1, #startDate);
END
SELECT [LastDates] FROM #tmpTable;
Output:
Example: 1
#startDate DATE = '01/01/2012'; --1 Jan 2012
#endDate DATE = '05/31/2012'; --31 May 2012
LastDates
----------
2012-01-31
2012-02-29
2012-03-31
2012-04-30
2012-05-31
Example: 2
#startDate DATE = '11/01/2011'; --1 Nov 2011
#endDate DATE = '03/13/2012'; --13 Mar 2012
LastDates
----------
2011-11-30
2011-12-31
2012-01-31
2012-02-29
I've created a table variable, filled it with all days between #startDate and #endDate and searched for max date in the month.
declare #tmpTable table (dates date)
declare #startDate date = '08/08/2013'
declare #endDate date = '11/11/2013'
while #startDate <= #endDate
begin
insert into #tmpTable (dates) values (#startDate)
set #startDate = DATEADD(DAY, 1, #startDate)
end
select max(dates) as [Last day] from #tmpTable as o
group by datepart(YEAR, dates), datepart(MONTH, dates)
Results:
Last day
2013-08-31
2013-09-30
2013-10-31
2013-11-11
To also get last day of November this can be used before loop:
set #endDate = DATEADD(day, -1, DATEADD(month, DATEDIFF(month, 0, #endDate) + 1, 0))
Following script demonstrates the script to find last day of previous, current and next month.
----Last Day of Previous Month
SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE()),0))
LastDay_PreviousMonth
----Last Day of Current Month
SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+1,0))
LastDay_CurrentMonth
----Last Day of Next Month
SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+2,0))
LastDay_NextMonth
If you want to find last day of month of any day specified use following script.
--Last Day of Any Month and Year
DECLARE #dtDate DATETIME
SET #dtDate = '8/18/2007'
SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,#dtDate)+1,0))
LastDay_AnyMonth
ResultSet:
LastDay_AnyMonth
Source - SQL Server Central.
You can use a recursive CTE to do this, note the MAXRECURSION OPTION prevents an infinite loop:
DECLARE #StartDate DATE = '2013-08-08'
DECLARE #EndDate DATE = '2013-11-11'
;WITH dateCTE
AS
(
SELECT CAST(DATEADD(M, 1,DATEADD(d, DAY(#StartDate) * -1, #StartDate)) AS DATE) EndOFMonth
UNION ALL
SELECT CAST(DATEADD(M, 2,DATEADD(d, DAY(EndOFMonth) * -1, EndOFMonth)) AS DATE)
FROM dateCTE
WHERE EndOFMonth < DATEADD(d, DAY(#EndDate) * -1, #EndDate)
)
SELECT *
FROM dateCTE
OPTION (MAXRECURSION 30);
This returns
EndOFMonth
----------
2013-08-31
2013-09-30
2013-10-31
try this
the last row(where) is optional for date filtering
declare #table table
(
thisdate date
)
insert into #table values ('12/01/2013'),('05/06/2013'),('04/29/2013'),('02/20/2013')
select *,DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,thisdate)+1,0))
LastDay from #table
where thisdate between 'givendate' and 'givendate'
The Example Below is for all dates
thisdate lastday
2013-12-01 2013-12-31 23:59:59.000
2013-05-06 2013-05-31 23:59:59.000
2013-04-29 2013-04-30 23:59:59.000
2013-02-20 2013-02-28 23:59:59.000
The following CTE gives you the last day of every month from February 1900 until the middle of the 26th century (on my machine):
;with LastDaysOfMonths as (
select DATEADD(month,
ROW_NUMBER() OVER (ORDER BY so.object_id),
'19000131') as Dt
from sys.objects so,sys.objects so1
)
select * from LastDaysOfMonths
It should be easy enough to use it as part of a larger query or to filter it down to just the dates you want. You can adjust the range of years as needed by changing the constant 19000131. The only important thing to do is make sure that you use a month that has 31 days in it and always have the constant be for day 31.
No need to use a common table expression or anything like that - this simple query will do it:
SELECT DATEADD(d, -1, DATEADD(mm, DATEDIFF(m, 0, DATEADD(m, number, '2013-08-08')) + 1, 0)) AS EndOfMonth
FROM master.dbo.spt_values
WHERE 'P' = type
AND DATEADD(m, number, '2013-08-08') < '2013-11-11';
Although the question is about the last day which #bummi has already answered.
But here is the solution for the first date which might be helpful for someone.
Get the first dates of all the months in-between the #FromDate and #ToDate.
DECLARE #FromDate DATETIME = '2019-08-13'
DECLARE #ToDate DATETIME = '2019-11-25'
;WITH CTE
AS
(
SELECT DATEADD(DAY, -(DAY(#FromDate) - 1), #FromDate) AS FirstDateOfMonth
UNION ALL
SELECT DATEADD(MONTH, 1, FirstDateOfMonth)
FROM CTE
WHERE FirstDateOfMonth < DATEADD(DAY, -(DAY(#ToDate) - 1), #ToDate)
)
SELECT * FROM CTE
Here is the result
--Result
2019-08-01 00:00:00.000
2019-09-01 00:00:00.000
2019-10-01 00:00:00.000
2019-11-01 00:00:00.000

SQL Query errors doesn't return values for Fridays

I have this sql stored procedure which is supposed to look at a particular table and return job numbers based on a date column. This works great except for when its run Saturday morning (and should return all job numbers with a date of Friday, but returns no rows). Any suggestions? Is there some logic problem here I'm not seeing? How might I track this down?
Stored Procedure
ALTER Procedure [dbo].[JC_GetJobsClosedYesterday]
As
SELECT [JobNumber]
FROM [NCLGS].[dbo].[JobClosedDate]
Where LastInvoiceDate between dbo.ufn_StartOfDay (DATEADD(d, -1, GETDATE())) AND dbo.ufn_StartOfDay (GETDATE())
order by JobNumber desc
And the start of day function.
ALTER function [dbo].[ufn_StartOfDay] ( #inDate datetime )
RETURNS DateTime AS
BEGIN
DECLARE #Now datetime
set #Now = #inDate
DECLARE #DayStart datetime
set #DayStart = #Now
set #DayStart = DATEADD (ms, -DATEPART(ms,#Now),#DayStart)
set #DayStart = DATEADD (s, -DATEPART(s,#Now),#DayStart)
set #DayStart = DATEADD (mi, -DATEPART(mi,#Now),#DayStart)
set #DayStart = DATEADD (hh, -DATEPART(hh,#Now),#DayStart)
return #DayStart
END
EDIT: I'm not having trouble with my date conversion (unless it doesn't know how to handle Fridays). I need help with the returning no rows part.
SAMPLE DATA:
JobNumber LastInvoiceDate DayOfWeek
112117 2011-06-13 00:00:00.000 Monday
112089 2011-06-10 00:00:00.000 Friday
112090 2011-06-10 00:00:00.000 Friday
112068 2011-06-10 00:00:00.000 Friday
112082 2011-06-10 00:00:00.000 Friday
UPDATE: Now I'm really confused. This "no data on fridays" thing has been happening (happened again last friday), but I still can't figure it out. Is it possible that GETDATE() isn't returning what I think its returning? Because when I try the following modifications based on #Thomas's suggestion, both methods get data, but the report that generated based on this code last sat has no data.
DECLARE #date datetime
--SET #date = '2011-06-21 13:42:27.257'
SET #date = '2011-06-11 03:42:27.257'
--Original Code
SELECT [JobNumber]
FROM [NCLGS].[dbo].[JobClosedDate]
Where LastInvoiceDate between dbo.ufn_StartOfDay (DATEADD(d, -1, #date)) AND dbo.ufn_StartOfDay (#date)
order by JobNumber desc
--Returns 21 records
--Modified based on #Thomas suggestion
Select [JobNumber]
From [NCLGS].[dbo].[JobClosedDate]
Where LastInvoiceDate >= DateAdd( d, DateDiff( d, 0, #date ) - 1, 0 )
And LastInvoiceDate < DateAdd( d, DateDiff( d, 0, #date ), 0 )
Order By JobNumber Desc
--Returns 21 records
Instead of DATEADD(d, -1, GETDATE()) you should use an expression that returns the beginning of the previous day. You could use your dbo.ufn_StartOfDay() function for that, but there's simpler way to do the same:
Select #DayStart = DateAdd( d, DateDiff( d, 0, #inDate ), 0 )
which means: increase the nil timestamp by the whole number of days between the nil timestamp and the given one.
I would also suggest using that expression instead of the already present call to the function as well, so your query would be:
Select [JobNumber]
From [NCLGS].[dbo].[JobClosedDate]
Where LastInvoiceDate Between DateAdd( d, DateDiff( d, 0, GetDate() ) - 1, 0 )
And DateAdd( d, DateDiff( d, 0, GetDate() ), 0 )
Order By JobNumber Desc
Addition
You need to be clearer about where exactly the problem is. Here is a sample query I created which tests every date from Thursday, June 9 through Saturday June 18. On which date did you expect to get values but did not or visa versa:
With SampleData As
(
Select 112117 As JobNumber, '2011-06-13 00:00:00.000' As LastInvoiceDate, 'Monday' As DayOfWeek
Union All Select 112089, '2011-06-10 00:00:00.000', 'Friday'
Union All Select 112090, '2011-06-10 00:00:00.000', 'Friday'
Union All Select 112068, '2011-06-10 00:00:00.000', 'Friday'
Union All Select 112082, '2011-06-10 00:00:00.000', 'Friday'
)
, TestDates As
(
Select Cast('20110609' As datetime) As Date
Union All
Select DateAdd(d,1,Date)
From TestDates
Where Date <= '20110617'
)
Select TD.Date, DateName(dw,TD.Date), Count(SD.JobNumber)
From TestDates As TD
Left Join SampleData As SD
On SD.LastInvoiceDate Between DateAdd( d, DateDiff( d, 0, TD.Date ) - 1, 0 )
And DateAdd( d, DateDiff( d, 0, TD.Date ), 0 )
Group By TD.Date
Update
In looking at your comments and the code, I think the problem is in your use of Between. Col Between DateA And DateB translates to Col >= DateA And Col <= DateB. I.e., it is inclusive of both end points. Instead, you need to exclude the final end point:
Select [JobNumber]
From [NCLGS].[dbo].[JobClosedDate]
Where LastInvoiceDate >= DateAdd( d, DateDiff( d, 0, GetDate() ) - 1, 0 )
And LastInvoiceDate < DateAdd( d, DateDiff( d, 0, GetDate() ), 0 )
Order By JobNumber Desc
This will give you all job numbers that on the previous date. I.e, if today is Friday, June 10, 2011, it will give you all LastInvoiceDate values from 2011-06-09 midnight through 2011-06-09 23:59:59.
See Floor a date in SQL server
To strip the time portion use:
SELECT CAST(FLOOR(CAST(CURRENT_TIMESTAMP AS float)) AS DATETIME)
This should be the fastest way. Thus
SELECT [JobNumber]
FROM [NCLGS].[dbo].[JobClosedDate]
WHERE LastInvoiceDate between
CAST((FLOOR(CAST(GETDATE() float))-1.0) AS DATETIME) AND
CAST(FLOOR(CAST(GETDATE() AS float)) AS DATETIME)
ORDER BY JobNumber DESC
The procedure which updates the jobclosed table runs Monday, Tuesday, Wednesday, Thursday, & Friday mornings. It doesn't run on Saturdays, so the records aren't inserted until monday, at which time they won't be retrieved by my stored procedure. I will schedule the update job to run on Saturday's also.
I added the following before the select statement:
if datepart(dw, GETDATE()) = 7 OR datepart(dw, GETDATE()) = 1
BEGIN
Exec dbo.NCL_MaintainJobClosedDateTable
--Select 'True'
END
Which will force an update on Saturday and Sunday mornings.