To calculate average of date differences in a column in sql server - sql-server

I need to calculate average of date differences excluding weekends across rows in a column. The query gives me wrong results. But, when i calculate it myself, i get different results. the query is something like -
select zone, avg(datediff(dd,startdate,enddate)-datediff((ww,startdate,enddate)*2)) from table where startdate >'1/1/2013' group by zone
I don't get correct results. Please guide me.

try this code:
select zone , avg(NumOfDays) from
(select zone, (datediff(dd,startdate,enddate)-datediff((ww,startdate,enddate)*2)) as NumOfDays from table where startdate >'1/1/2013')
group by zone

I don't know how your data looks like and what you challenge exactly is but I think your problem is "excluding weekend"? Maybe this helps you?
First create a function that counts every weekend day between your period for filtering it later:
CREATE FUNCTION fnc_GetWeekendDays(#dFrom DATETIME, #dTo DATETIME)
RETURNS INT AS
BEGIN
Declare #weekendDays int
Set #weekendDays = 0
While #dFrom <= #dTo
Begin
If ((datepart(dw, #dFrom) = 1) OR (datepart(dw, #dFrom) = 7))
Set #weekendDays = #weekendDays + 1
Set #dFrom = DateAdd(d, 1, #dFrom)
End
Return (#weekendDays)
END
After this, write your query:
Select
zone,
avg(cast((datediff(SECOND,StartTime,EndTime) - dbo.fnc_GetWeekendDays(StartTime, EndTime)*86400) as float))
from
(
Select zone,
CASE
WHEN (datepart(dw, startdate) = 1) THEN DATEADD(Day, DATEDIFF(Day, 0, startdate), 1)
WHEN (datepart(dw, startdate) = 7) THEN DATEADD(Day, DATEDIFF(Day, 0, startdate), 2)
ELSE startdate END as StartTime,
CASE
WHEN (datepart(dw, enddate) = 1) THEN DATEADD(Day, DATEDIFF(Day, 0, enddate), -2)
WHEN (datepart(dw, enddate) = 7) THEN DATEADD(Day, DATEDIFF(Day, 0, enddate), -1)
ELSE enddateEND as EndTime
from table
) subquery
where StartTime < EndTime and startdate >'2013-01-01 00:00:00.000'
Group by zone
You will get your average time in seconds, if you want another format you have to calculate it...

Related

How to get Satuday's date of previous month in SQL Server? Can someone help me with this query?

Suppose now we are in September, I want output of the last Saturday date in the previous month, August, where 28-08-2021 falls under last Saturday of previous month in SQL Server
..fiddle..
select *, datename(weekday, pmlsat), dateadd(week, 1, pmlsat)
from
(
select _date,
--last saturday of previous month
dateadd(day, -datepart(weekday, dateadd(day, ##datefirst, eomonth(_date, -1)))%7, eomonth(_date, -1)) as pmlsat
from
(
select top(100) dateadd(month, row_number() over(order by ##spid), '20141215') as _date
from sys.all_objects
) as d
) as p
order by _date;
DECLARE #date1 DATETIME
SET #date1='2021-8-31'
WHILE Day(#date1) >= 1
BEGIN
IF (SELECT Datename(weekday, #date1)) = 'Saturday'
BREAK
SET #date1=Dateadd(dd, -1, #date1)
CONTINUE
END
SELECT Datename(weekday, #date1) AS 'Datename',
(SELECT CONVERT(NVARCHAR(20), #date1, 23)) AS 'DATE'
First, let's talk about how to get the beginning of this month. There are a multiple ways, I find DATEFROMPARTS() the most intuitive (see Simplify Date Period Calculations in SQL Server):
DECLARE #FirstOfMonth date = DATEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), 1);
SELECT #FirstOfMonth;
-- result:
-- 2021-09-01
Now, the last Saturday in the previous month must be between 1 and 7 days before the first of this month. So we can generate a sequence of 7 consecutive numbers, and subtract those days from the first of the month, like this:
DECLARE #FirstOfMonth date = DATEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), 1);
SELECT #FirstOfMonth;
;WITH n(n) AS
(
SELECT 1 UNION ALL
SELECT n + 1 FROM n WHERE n < 7
)
SELECT d = DATEADD(DAY, -n, #FirstOfMonth) FROM n;
/* result:
2021-08-31
2021-08-30
2021-08-29
2021-08-28
2021-08-27
2021-08-26
2021-08-25 */
To determine what a Saturday is, you either need to rely on DATEPART(WEEKDAY, date) - which in turn is affected by SET DATEFIRST, or you need to rely on DATENAME(WEEKDAY, date) - which in turn is affected by SET LANGUAGE. I will err toward language being more stable (English), so:
DECLARE #FirstOfMonth date = DATEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), 1);
SELECT #FirstOfMonth;
;WITH n(n) AS
(
SELECT 1 UNION ALL
SELECT n + 1 FROM n WHERE n < 7
),
d(d) AS
(
SELECT DATEADD(DAY, -n, #FirstOfMonth)
FROM n
)
SELECT LastMonthLastSaturday = d
FROM d
WHERE DATENAME(WEEKDAY, d) = 'Saturday';
-- result:
-- 2021-08-28
But that is a subjective call - if you can't rely on one of those, get a calendar table, then it's simply something like:
SELECT LastMonthLastSaturday = MAX(TheDate)
FROM dbo.Calendar
WHERE TheDayOfWeekName = 'Saturday'
AND TheDate < DATEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), 1);

How to get date from day name?

How can I get the date of specific day ? Like if I have Thursday or month number ?
If I give 12 for instance I want to get the date of 12th day of this month. Or if I give 'Sun' or 'Sat' is it possible to get the dates of these days ?
DATEFROMPARTS function can construct a date from day, month and year.
DATEPARTS does the opposite - gives you the day, month, year, hour, etc. of a date. Or you can use functions like YEAR, MONTH and DAY.
You can deconstruct the value returned by GETDATE function and construct whatever date you want. Here is for example how to get the date for 12th day of the current month:
select DATEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), 12)
Converting 'Sun' or 'Sat' to date is a bit more difficult. First, they aren't quite deterministic. If today is Friday, "Sunday this week" means "next Sunday" in some parts of the world and "last Sunday" in others. You should implement your own logic based on the value returned by DATEPART(dw, GETDATE()) (which will give you the day of the week).
To find the weekday of the current month
DECLARE #daynumber INT = 12
SELECT datename(weekday, dateadd(d, #daynumber - 1, getdate()))
To find the dates of the current month of a given weekday
DECLARE #dayname char(3) = 'sat'
;WITH CTE as
(
SELECt TOP
(datediff(D, eomonth(getdate(), -1),eomonth(getdate())))
dateadd(d,row_number()over(ORDER BY 1/0),
eomonth(getdate(),-1))date
FROM
(values(1),(2),(3),(4),(5),(6))x(x),
(values(1),(2),(3),(4),(5),(6))y(x)
)
SELECT day(date) monthday, date
FROM CTE
WHERE left(datename(weekday, date),3) = #dayname
select sysdatetime(); --2018-12-13 16:29:56.0560574
---If I give 12 for instance I want to get the date of 12th day of this month.
declare #numDate int = 12;
select dateadd(m, datediff(m,0,getdate()),#numDate - 1 ); --2018-12-12 00:00:00.000
--Or if I give 'Sun' or 'Sat' is it possible to get the dates of these days ?
declare #text nvarchar(20) = 'Sunday';
declare #dateStart date = dateadd(month, datediff(month, 0, sysdatetime()), 0),
#days int =( select (DAY(dateadd(dd,-1,DATEADD(m,1,cast(2018 as varchar(4)) + '-' + cast(12 as varchar(2)) +'-01')))));
declare #dateEnd date = DATEADD(day,#days-1,#dateStart);
;WITH CTE (Dates,EndDate) AS
(
SELECT #dateStart AS Dates,#dateEnd AS EndDate
UNION ALL
SELECT DATEADD(day,1,Dates),EndDate
FROM CTE
WHERE DATEADD(day,1,Dates) <= EndDate
)
SELECT CTE.Dates, DATENAME(DW, CTE.Dates)
FROM CTE
where DATENAME(DW, CTE.Dates) = #text;
Result:
Dates,Day
2018/12/2,Sunday
2018/12/9,Sunday
2018/12/16,Sunday
2018/12/23,Sunday
2018/12/30,Sunday
-- Here is how to get week day name to week day number
DECLARE #T TABLE (Dow INT, NameOfDay VARCHAR(15), ShortName CHAR(3));
WITH Days AS
(
SELECT TOP 7
ROW_NUMBER() OVER(PARTITION BY object_id ORDER BY object_id) AS RowNo
FROM
sys.all_columns
)
INSERT INTO #T
SELECT
RowNo,
DATENAME(WEEKDAY, RowNo - 1),
LEFT(DATENAME(WEEKDAY, RowNo - 1), 3)
FROM
Days
SELECT
*
FROM
#T;
-- Here is how to get start of period
SELECT
DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), 0) AS StartOfDay,
DATEADD(WEEK, DATEDIFF(WEEK, 0, GETDATE()), 0) AS StartOfWeek,
DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0) AS StartOfMonth,
DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), 0) AS StartOfYear;
-- An example
WITH
StartPeriods AS
(
SELECT DATEADD(WEEK, DATEDIFF(WEEK, 0, GETDATE()), 0) AS StartOfWeek
),
SelectedDay AS
(
SELECT
Dow - 1 AS Dow,
(SELECT StartOfWeek FROM StartPeriods) AS StartOfWeek
FROM
#T
WHERE
ShortName = 'Wed'
)
SELECT
DATEADD(DAY, Dow, StartOfWeek)
FROM
SelectedDay;

Performance tuning for SQL Query

Hi here I am attaching my sample table structure which I want to use in my project
CREATE TABLE TESTSALESVOLUMETABLE
(
ID INT IDENTITY(1,1),
AMOUNT DECIMAL(18,2),
CREDITEDDATE DATETIME
)
and the queries I used like this
DECLARE #CURRENTDATE AS DATETIME = GETDATE()
DECLARE #PSV AS INT = 0
DECLARE #TOTAL AS INT = 0
IF (DATEPART(DAY, #CURRENTDATE) <= 15)
BEGIN
SELECT #PSV = (
SELECT Sum(AMOUNT)
FROM TESTSALESVOLUMETABLE
WHERE DATEPART(DAY, CREDITEDDATE) <= 15
AND MONTH(CREDITEDDATE) = MONTH(#CURRENTDATE)
AND YEAR(CREDITEDDATE) = YEAR(#CURRENTDATE)
)
END
ELSE
BEGIN
SELECT #PSV = (
SELECT Sum(AMOUNT)
FROM TESTSALESVOLUMETABLE
WHERE DATEPART(DAY, CREDITEDDATE) > 15
AND MONTH(CREDITEDDATE) = MONTH(#CURRENTDATE)
AND YEAR(CREDITEDDATE) = YEAR(#CURRENTDATE)
)
END
SELECT #total = (
SELECT Sum(Amount)
FROM TESTSALESVOLUMETABLE
)
SELECT #PSV 'PSV',
#total 'TOTAL'
Is there any way to increase the performance of this query
First, you don't need a subquery for setting the variable. Second, the use of functions on columns usually prevents the use of indexes. So, I would recommend something like this:
SELECT #PSV = Sum(AMOUNT)
FROM TESTSALESVOLUMETABLE
WHERE CREDITEDDATE >= DATEADD(DAY, 1 - DAY(GETDATE()), CAST(GETDATE() as DATE)) AND
CREDITEDDATE < DATEADD(DAY, 16 - DAY(GETDATE()), CAST(GETDATE() as DATE));
Then, you want an index on TESTSALESVOLUMETABLE(CREDTEDDATE, AMOUNT).
Following the guidelines from: Bad habits to kick : mis-handling date / range queries - Aaron Bertrand - 2009-10-16
First, we want to get rid of:
where datepart(day, crediteddate) <= 15
and month(crediteddate)=month(#currentdate)
and year(crediteddate)=year(#currentdate)
because:
[...] you've effectively eliminated the possibility of SQL Server taking advantage of an index. Since you've forced it to build a nonsargable condition, this means it will have to convert every single value in the table to compare it to the [value] you've presented on the right hand side [...]
Second, we want to make sure to avoid using between with datetimes because it can return unwanted rows or miss wanted rows, even when using something like between ... and dateadd(second, -1, #thrudate) or even between ... and 'yyyy-mm-ddT23:59:59.997'. (See Aaron Bertrand's article for more examples on this).
So the best way to do this would be to say:
If today is the 15th or earlier, get rows >= the 1st of this month and < the 16th of this month
If today is the 16th or later, get rows >= the 16th of this month and < the 1st of next month
Also, as Gordon Linoff mentioned, you will benefit from an index on testsalesvolumetable(crediteddate, amount). But Gordon's formulas always return the 1st and 16th of the current month.
Instead of breaking the procedure into two queries depending on the current day, we can calculate those from and thru dates and just use one query.
Here is example code both with and without using variables for the from and thru dates, along with a quick calendar test to check the resulting ranges.
rextester link for test setup: http://rextester.com/YVLI65217
create table testsalesvolumetable (crediteddate datetime not null, amount int not null)
insert into testsalesvolumetable values
('20161201',1) ,('20161202',1) ,('20161203',1) ,('20161204',1) ,('20161205',1)
,('20161206',1) ,('20161207',1) ,('20161208',1) ,('20161209',1) ,('20161210',1)
,('20161211',1) ,('20161212',1) ,('20161213',1) ,('20161214',1) ,('20161215',1)
,('20161216',1) ,('20161217',1) ,('20161218',1) ,('20161219',1) ,('20161220',1)
,('20161221',1) ,('20161222',1) ,('20161223',1) ,('20161224',1) ,('20161225',1)
,('20161226',1) ,('20161227',1) ,('20161228',1) ,('20161229',1) ,('20161230',1)
,('20161231',1) ,('20170101',1)
/* ----- without variables */
declare #psv int;
select #psv = Sum(amount)
from testsalesvolumetable
where crediteddate >= dateadd(day, (1- (day(convert(date,getdate()))/16)) - (day(convert(date,getdate()))%16), convert(date,getdate()))
and crediteddate < case
when day(convert(date,getdate()))>15
then dateadd(month, datediff(month, -1, convert(date,getdate())), 0)
else dateadd(day,15,dateadd(month, datediff(month, 0, convert(date,getdate())), 0))
end;
select psv=#psv;
--*/
/* ----- with variables */
--declare #psv int;
declare #currentdate date;
/* change to date datatype to get rid of time portion*/
set #currentdate = getdate();
--set #currentdate = '20161212'
declare #fromdatetime datetime;
declare #thrudatetime datetime;
set #fromdatetime = dateadd(day, (1- (day(#currentdate)/16)) - (day(#currentdate)%16), #currentdate);
set #thrudatetime = case
when day(#currentdate)>15
then dateadd(month, datediff(month, -1, #currentdate), 0)
else dateadd(day,15,dateadd(month, datediff(month, 0, #currentdate), 0))
end;
select #psv = sum(amount)
from testsalesvolumetable
where crediteddate >= #fromdatetime
and crediteddate < #thrudatetime;
--/*
select
psv=#psv
, CurrentDate =convert(varchar(10),#currentdate ,121)
, FromDateTime=convert(varchar(10),#fromdatetime,121)
, ThruDateTime=convert(varchar(10),#thrudatetime,121);
--*/
Rextester link for the calendar test: http://rextester.com/ESZRH30262
--/* ----- Calendar Test */
;with n as (
select n from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) t(n)
)
, cal as (
select DateValue=convert(datetime,dateadd(day, row_number() over (order by (select 1)) -1, '20160101'))
from n as a
cross join n as b
cross join n as c
cross join n as d
)
select
--DateValue=convert(varchar(10),DateValue,121)
minDate =convert(varchar(10),min(DateValue),121)
, maxDate =convert(varchar(10),max(DateValue),121)
, FromDatetime=convert(varchar(10),dateadd(day, (1- (day(DateValue)/16)) - (day(DateValue)%16), DateValue),121)
, ThruDatetime=convert(varchar(10),case
when day(DateValue)>15
then dateadd(m, datediff(m, -1, DateValue), 0)
else convert(varchar(10),dateadd(day, 16 - day(DateValue), DateValue),121)
end,121)
, GordonFrom = convert(varchar(10),dateadd(day, 1 - day(DateValue), cast(DateValue as date)),121)
, GordonThru = convert(varchar(10),dateadd(day, 16 - day(DateValue), cast(DateValue as date)),121)
from cal
where datevalue >= '20160101'
and datevalue < '20170101'
--/*
group by
convert(varchar(10),dateadd(day, (1- (day(DateValue)/16)) - (day(DateValue)%16), DateValue),121)
, convert(varchar(10),case
when day(DateValue)>15
then dateadd(m, datediff(m, -1, DateValue), 0)
else convert(varchar(10),dateadd(day, 16 - day(DateValue), DateValue),121)
end,121)
, convert(varchar(10),dateadd(day, 1 - day(DateValue), cast(DateValue as date)),121)
, convert(varchar(10),dateadd(day, 16 - day(DateValue), cast(DateValue as date)),121)
order by FromDateTime
I thing this will work fine
DECLARE #PSV AS INT = 0
DECLARE #TOTAL AS INT = 0
IF (DATEPART(DAY,GETDATE()) <= 15)
BEGIN
SELECT #PSV = Sum(AMOUNT)
FROM TESTSALESVOLUMETABLE
WHERE CREDITEDDATE >= DATEADD(DAY, 1 - DAY(GETDATE()), CAST(GETDATE() as DATE)) AND
CREDITEDDATE < DATEADD(DAY, 16 - DAY(GETDATE()), CAST(GETDATE() as DATE));
END
ELSE
BEGIN
SELECT #PSV = Sum(AMOUNT)
FROM TESTSALESVOLUMETABLE
WHERE CREDITEDDATE >= DATEADD(DAY, 16 - DAY(GETDATE()), CAST(GETDATE() as DATE)) AND
CREDITEDDATE < DATEADD(DAY, 31 - DAY(GETDATE()), CAST(GETDATE() as DATE));
END
SELECT #total = (
SELECT Sum(Amount)
FROM TESTSALESVOLUMETABLE
)
SELECT #PSV 'PSV',
#total 'TOTAL'

Where Statement with Case based on Current Time

I'm looking to create a Case statement so that,
If the current time is before 11AM, I want the information from yesterday as well as today.
If the time is after 11AM, I only want the information from today.
Here's what I have right now
FROM [EDC].[dbo].[DIM_DefectData] with (NoLock)
Where
Case
When datepart(hh, GetDate()) < 11 then
[InitiateDt] > DATEADD(day, DATEDIFF(day, 0, GETDATE()),-1)
Else
[InitiateDt] > DATEADD(day, DATEDIFF(day, 0, GETDATE()),0)
End
And ....(additional requirements which are working)
If I understand your problem correctly, the following is you want:
FROM [EDC].[dbo].[DIM_DefectData] WITH (NOLOCK)
WHERE [InitiateDt] > (
CASE
WHEN DATEPART(HH, GETDATE()) < 11
THEN DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), -1)
ELSE DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), 0)
END )
I placed the [InitiateDt] near by the WHERE clause.
try this
select * from yourTable
where [InitiateDt] > Case When datepart(hh,getdate()) <11
Then dateadd(day,datediff(day,0,getdate()),-1)
Else dateadd(day,datediff(day,0,getdate()),0)
END
Try this:
DECLARE #t table(d datetime)
insert #t values
('2016-08-30 09:00'),
('2016-08-30 10:00'),
('2016-08-31 10:00'),
('2016-08-30 11:00'),
('2016-08-30 11:08')
if(DATEPART(HH, GETDATE()) < 11)
SELECT CAST(d AS TIME) FROM #t
WHERE CAST(d AS TIME) <= CAST('11:00' AS TIME)
ELSE
SELECT CAST(d AS TIME) FROM #t
WHERE CAST(d AS TIME) > CAST('11:00' AS TIME)
The above returns all today's data as well yesterday before 11:00AM.

SQL Server - Date String

Can you please help me create an sql statement that will get the previous date and concatenate that date in this format:
'2016-05-05 00:00:00'
I already know the sql function to get the previous date but I don't know yet on how to get the time in the expected format. My query is something like this
Select *
from table
where
transaction_date between '2016-05-05 00:00:00' and '2016-05-05 23:59:59'
So I need 00:00:00 and 23:59:59 to be concatenated in the date.
You don't need the times at all. Just don't use BETWEEN and use >= and < for the next day, like this:
SELECT *
FROM table
WHERE transaction_date >= '2016-05-05'
AND transaction_date < '2016-05-06'
Or if you only have the single date value:
DECLARE #date DATETIME = '2016-05-05'
SELECT *
FROM table
WHERE transaction_date >= #date
AND transaction_date < DATEADD(DAY, 1, #date)
The added benefit of doing it this way is that you also don't miss out on times that occur in the last second of the day as your original query does. For example 2016-05-05 23:59:59.001
Edit: From the comments below
To get yesterdays date, get today's date and subtract a day from it:
DECLARE #date DATETIME = DATEADD(DAY, -1, CAST(GETDATE() AS DATE))
Now use the logic as mentioned above.
This function:
DATEADD(dd, DATEDIFF(dd, 0, GetDate()), 0)
...will turn 2016-05-05 18:18:231 into 2016-05-05 00:00:000. From there it is easy to get full date boundaries by adding and subtracting days:
DECLARE #Date DATETIME
,#YesterdayStartDate DATETIME
,#TodayStartDate DATETIME
SET #Date = GetDate()
SET #TodayStartDate = DATEADD(dd, DATEDIFF(dd, 0, #date), 0)
SET #YesterdayStartDate = DATEADD(dd, -1, #TodayStartDate)
SELECT #Date, #YesterdayStartDate, #TodayStartDate
SELECT *
FROM Table
WHERE
transaction_date >= #YesterdayStartDate AND
transaction_date < #TodayStartDate
This will give you the result with transaction_date on yesterday.
SELECT
*
FROM
TABLE
WHERE
transaction_date BETWEEN dateadd(DAY, datediff(DAY, 1, GETDATE()), 0) AND dateadd(DAY, 1, dateadd(DAY, datediff(DAY, 1, GETDATE()), 0))
Compare date with out time part . ie below code.
Select * from table
where CONVERT(VARCHAR(8), transaction_date , 112) = CONVERT(VARCHAR(8), getdate(), 112)

Resources