Getting averages by hour/day - sql-server

I have the following query where I'm trying to get the average counts by hour by day. I'm getting the totals without a problem, but I can't seem to get the average.
Can anyone see what I'm doing wrong with the below query, and also how I could get the results in the below format?
SELECT
[Day],
[Hour],
[DayN],
Totals,
AVG(Totals) AS [Avg]
FROM
(
SELECT
[Day] = DATENAME(WEEKDAY, StartDate),
[DayN] = DATEPART(WEEKDAY, StartDate),
[Hour] = DATEPART(HOUR,StartDate),
Totals = COUNT(*)
from
Visit
where
StartDate >= '01 Jan 2019'
GROUP BY
DATENAME(WEEKDAY,StartDate),
DATEPART(WEEKDAY,StartDate),
DATEPART(HOUR,StartDate)
) AS q
GROUP BY [Day], [Hour], Totals, [DayN]
ORDER BY DayN;
Sample Data: (more at https://justpaste.it/65w8z )
CREATE TABLE [dbo].[Visit](
[VisitID] [int] NOT NULL,
[StartDate] [datetime] NULL
)
GO
INSERT [dbo].[Visit] ([VisitID], [StartDate]) VALUES (30513, CAST(N'2019-01-01T00:06:28.480' AS DateTime))
GO
INSERT [dbo].[Visit] ([VisitID], [StartDate]) VALUES (30514, CAST(N'2019-01-01T00:07:23.637' AS DateTime))
GO
INSERT [dbo].[Visit] ([VisitID], [StartDate]) VALUES (30515, CAST(N'2019-01-01T00:14:44.840' AS DateTime))
GO
INSERT [dbo].[Visit] ([VisitID], [StartDate]) VALUES (30516, CAST(N'2019-01-01T00:16:05.030' AS DateTime))
GO
INSERT [dbo].[Visit] ([VisitID], [StartDate]) VALUES (30517, CAST(N'2019-01-01T00:18:23.787' AS DateTime))
GO
INSERT [dbo].[Visit] ([VisitID], [StartDate]) VALUES (30518, CAST(N'2019-01-01T00:20:33.073' AS DateTime))
GO
INSERT [dbo].[Visit] ([VisitID], [StartDate]) VALUES (30519, CAST(N'2019-01-01T00:20:42.450' AS DateTime))
GO
INSERT [dbo].[Visit] ([VisitID], [StartDate]) VALUES (30520, CAST(N'2019-01-01T00:25:03.303' AS DateTime))
GO

Try This you need to get avg separately please run this
SELECT [Day], [Hour], [DayN], Totals, (
SELECT avg(totals)
FROM
( SELECT [Day] = DATENAME(WEEKDAY, StartDate),
[DayN] = DATEPART(WEEKDAY, StartDate),
[Hour] = DATEPART(HOUR,StartDate),
Totals = COUNT(*)
FROM Visit
WHERE StartDate >= '01 Jan 2019'
GROUP BY DATENAME(WEEKDAY,StartDate),
DATEPART(WEEKDAY,StartDate),
DATEPART(HOUR,StartDate) ) AS qq
WHERE q.[day]=qq.[day]) [Avg]
FROM
( SELECT [Day] = DATENAME(WEEKDAY, StartDate),
[DayN] = DATEPART(WEEKDAY, StartDate),
[Hour] = DATEPART(HOUR,StartDate),
Totals = COUNT(*)
FROM Visit
WHERE StartDate >= '01 Jan 2019'
GROUP BY DATENAME(WEEKDAY,StartDate),
DATEPART(WEEKDAY,StartDate),
DATEPART(HOUR,StartDate) ) AS q
ORDER BY DayN;

try this :
SELECT
[Day],
[Hour],
[DayN],
Totals,
AVG(Totals) AS [Avg],
A
FROM
(
SELECT
[Day] = DATENAME(WEEKDAY, StartDate),
[DayN] = DATEPART(WEEKDAY, StartDate),
[Hour] = DATEPART(HOUR,StartDate),
Totals = COUNT(*),
COUNT(1)/Count(distinct DATEPART(HOUR, StartDate)) A
from
Visit
where
StartDate >= '01 Jan 2019'
GROUP BY
DATENAME(WEEKDAY,StartDate),
DATEPART(WEEKDAY,StartDate),
DATEPART(HOUR,StartDate)
) AS q
GROUP BY [Day], [Hour], Totals, [DayN],A
ORDER BY DayN;
do you need AVG(Totals) AS [Avg] anymore? if you don't you can delete 2 last rows

Related

Filling table with weeks of the year skips first week of 2021

So, I am using the following snippet on a procedure, which fills me a temporary table with the first day of each year's week, the week number and month name.
However, when I reach week 53, of 2020, it jumps to week 2 of 2021. This happens because the first of january is in the so called week 53 (which is correct), but it should also be creating another row with the first week of january 2021 (even with the sunday as being in 2020, as it should).
Snippet:
SET DATEFIRST 7
DECLARE #tblSundays TABLE (
[year] INT
,[month] INT
,[week] INT
,[date] DATETIME
)
DECLARE #DateFrom DATETIME = '2020-12-12'
,#DateTo DATETIME = '2021-06-06';
--select #DateFrom,#DateTo;
WITH CTE (dt)
AS (
SELECT #DateFrom
UNION ALL
SELECT DATEADD(d, 1, dt)
FROM CTE
WHERE dt < #DateTo
)
INSERT INTO #tblSundays
SELECT datepart(year, dt)
,datepart(month, dt)
,datepart(week, dt)
,dt
FROM CTE
WHERE datepart("dw", dt) = 1
OPTION (MAXRECURSION 1000)
;
select * from #tblSundays
Is there any way that I can do this within this snippet, or should I create a manual verification?
Thanks!
You should do a couple of little tricks to set week 1 to the first Sunday of the year (tested for the next 30 years):
set datefirst 7
declare #tblSundays table ([year] int, [month] int, [week] int, [date] date)
declare #DateFrom datetime = '20201212', #DateTo datetime = '20510112';
-- Get the next closest Sunday
select #DateFrom = dateAdd(wk, dateDiff(wk, 0, #datefrom - 1 ), 0) + 6
with CTE (dt) as (
select #DateFrom
union all
select dateAdd(dd, 7, dt) from CTE where dt < #DateTo
)
insert into #tblSundays
select datePart(yy, dt),
datePart(mm, dt),
datePart(wk, dt - (1 + (datePart(dy, dt) + 5) % 7) % 7),
dt
from CTE
-- where datepart(dw, dt) = 1
option (maxrecursion 1600);
select *, datePart(wk, [date]) as [standard_wk]
from #tblSundays
I don't see how you could have those two condition on that same snippet, but you could add a second query there to complete what you are trying to do.
Something like this, maybe:
SET datefirst 7
DECLARE #tblSundays TABLE
(
[year] INT,
[month] INT,
[week] INT,
[date] DATETIME
)
DECLARE #DateFrom DATETIME = '2020-12-12',
#DateTo DATETIME = '2021-06-06';
--select #DateFrom,#DateTo;
WITH cte (dt)
AS (SELECT #DateFrom
UNION ALL
SELECT Dateadd(d, 1, dt)
FROM cte
WHERE dt < #DateTo)
INSERT INTO #tblSundays
SELECT Datepart(year, dt),
Datepart(month, dt),
Datepart(week, dt),
dt
FROM cte
WHERE Datepart("dw", dt) = 1
OPTION (maxrecursion 1000);
--second new query
WITH cte (dt)
AS (SELECT #DateFrom
UNION ALL
SELECT Dateadd(d, 1, dt)
FROM cte
WHERE dt < #DateTo)
INSERT INTO #tblSundays
SELECT Datepart(year, dt),
Datepart(month, dt),
Datepart(week, dt),
dt
FROM cte
WHERE Datepart("dw", dt) <> 1
AND Datepart(day, dt) = 1
AND Datepart(week, dt) NOT IN (SELECT week
FROM #tblSundays)
OPTION (maxrecursion 1000);
SELECT *
FROM #tblSundays
ORDER BY year,
month,
week
Would that be OK?

TSQL: Continuous period for whole year / each month

I try to find all Cust who have membership for at least for one day in each month during 2018.
I came up with solution checking their membership at the beginning / middle / end end of each month like in snippet below, but trying to find more intelligent solution.
I know that I can use tally table for each of 365 days to check this but probably there is more elegant solution ? I'm bit new to SQL, I think I'm missing something in GROUPing area.
In the code snippet shown below, both Cust have at least one day membership.
Desired output:
CustID
------
1
22
Code:
with data as
(
select *
from (values (1, 1, '2017-12-11', '2018-01-16'), (1, 22, '2018-01-28', '2018-03-9' ), (1, 333, '2018-03-1', '2018-12-31') , -- island
(22, 1, '2017-12-31', '2018-01-11'), (22, 2, '2017-2-11', '2019-12-31')) as t (CustID, ContractID, StartDD, EndDD) ---
)
select
isdate(startDD), isdate(EndDD)
from
data
), gaps as
(
select
*,
datediff(day, lag(EndDD, 1, StartDD) over (partition by CustID order by StartDD), StartDD) as BreakDD -- negative is island
from
data
)
select
*,
datepart(month,StartDD) mmS , datepart(month,EndDD) mmE
from
gaps
-- and was active any 1+ day during each of the 12 months in 2018 ????
where
1 = 1
/* and (cast('1/1/2018' as date) between StartDD and EndDD
or cast('1/15/2018' as date) between StartDD and EndDD
or cast('1/31/2018' as date) between StartDD and EndDD)
---- etc.. for each month
and ( cast('12/1/2018' as date) between StartDD and EndDD
or cast('12/15/2018' as date) between StartDD and EndDD
or cast('12/31/2018' as date) between StartDD and EndDD
)
*/
--select CustID, max(BreakDD) Max_Days
--from gaps
--group by CustID
Try this answer.
First create a function to return all the month and year between the given dates.
Function:
--SELECT * FROM dbo.Fn_GetMonthYear('2017-12-11','2018-01-16')
ALTER FUNCTION dbo.Fn_GetMonthYear(#StartDate DATETIME,#EndDate DATETIME)
RETURNS TABLE
AS
RETURN(
SELECT DATEPART(MONTH, DATEADD(MONTH, x.number, #StartDate)) AS [Month]
,DATEPART(YEAR, DATEADD(MONTH, x.number, #StartDate)) AS [Year]
FROM master.dbo.spt_values x
WHERE x.type = 'P'
AND x.number <= DATEDIFF(MONTH, #StartDate, #EndDate)
)
Table Schema:
CREATE TABLE #t(CustID INT, ContractID INT, StartDD date, EndDD date)
INSERT INTO #t values (1, 1, '2017-12-11', '2018-01-16'), (1, 22, '2018-01-28', '2018-03-9' ), (1, 333, '2018-03-1', '2018-12-31') , -- island
(22, 1, '2017-12-31', '2018-01-11'), (22, 2, '2017-2-11', '2019-12-31')
Here is the T-SQL Query for your requirement.
SELECT CustID
,COUNT(DISTINCT [Month]) NoOfMonths
FROM(
SELECT *
FROM #t t
CROSS APPLY dbo.Fn_GetMonthYear(StartDD,EndDD)
)D
WHERE [Year] = 2018
GROUP BY CustID
HAVING COUNT(DISTINCT [Month])=12
Result:
CustID NoOfMonths
1 12
22 12
find all Cust who have membership for at least for one day in each
month during 2018
I think this mean that data must be present between '2018-01-01' and '2018-12-31' for each custid.
CREATE TABLE #t(CustID INT, ContractID INT, StartDD date, EndDD date)
INSERT INTO #t values (1, 1, '2017-12-11', '2018-01-16'), (1, 22, '2018-01-28', '2018-03-9' ), (1, 333, '2018-03-1', '2018-12-31') , -- island
(22, 1, '2017-12-31', '2018-01-11'), (22, 2, '2017-2-11', '2019-12-31')
declare #From Datetime='2018-01-01'
declare #To datetime='2018-12-31'
;with CTE as
(
select CustID,min(StartDD)StartDD
,max(EndDD)EndDD
from #t
group by CustID
)
select CustID,StartDD
,EndDD
from CTE
where StartDD<=#From and EndDD>=#To
This script is not tested across all sample data.
But logic is clear.So it can be corrected accordingly.
So tell for what sample data it is not working.

SQL Server Date Calculation

I want to get the based on current date.
If current date is:
01st to 14th of any month, it needs to return 15th of that month
15th to 31st of any month, it needs to return last day of that month
For example:
Current_Date Exp_date
-------------------------
01-08-2019 15-08-2019
10-08-2019 15-08-2019
14-08-2019 15-08-2019
15-08-2019 31-08-2019
20-08-2019 31-08-2019
25-08-2019 31-08-2019
31-08-2019 31-08-2019
I want as much as simplified form.
We can achieve it with simple logic as below .
If You are using Sql Server version 2012 and higher versions we've EOMONTH() Function to give EndOfMonth Date .
Sample Data:
CREATE TABLE #YourTable (CurrentDate DATETIME)
INSERT INTO #YourTable (CurrentDate)SELECT '08-01-2019'
INSERT INTO #YourTable (CurrentDate) SELECT '08-10-2019'
INSERT INTO #YourTable (CurrentDate) SELECT '08-14-2019'
INSERT INTO #YourTable (CurrentDate) SELECT '08-15-2019'
INSERT INTO #YourTable (CurrentDate) SELECT '08-20-2019'
INSERT INTO #YourTable (CurrentDate) SELECT '08-25-2019'
INSERT INTO #YourTable (CurrentDate) SELECT '08-31-2019'
Query:
SELECT DATEPART(DD,CurrentDate),
case when DATEPART(DD,CurrentDate)<15 THEN DATEADD(dd,-day(CurrentDate)+15,CurrentDate)
when DATEPART(DD,CurrentDate)>14 THEN EOMONTH(CurrentDate) END AS Exp_Date
FROM #YourTable
You may try this.
select current_date,
case when datepart(day, current_date) > 14
then
DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, current_date) + 1, 0))
else
DATEADD(D, 15, DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, current_date) , 0)))
end as Exp_date
from yourtable
Try this:replace hardCode date With your date
SELECT CONCAT(CONCAT(CONCAT (CONCAT(CASE WHEN DAY('2017/08/25') < 14 THEN 15 else 31 end , '-'),
CASE WHEN DATEPART(month, '2017/08/25') < 10 THEN Concat('0',DATEPART(month, '2017/08/25')) else DATEPART(month, '2017/08/25') end),'-'), cast(DATEPART(year, '2017/08/25') as nvarchar(4)))
SELECT [Current_date],Exp_date,
CASE WHEN 14 BETWEEN DATEPART(DAY,[Current_date]) AND DATEPART(DAY,Exp_date)
THEN CAST(DATEADD(DAY,15-DATEPART(DAY,[Current_date]),[Current_date]) AS DATE)
ELSE CAST(DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,[Current_date])+1,0)) AS DATE)
END
FROM MOnthData
Check this..
select case when datepart(dd, dt) between 1 and 14 then right('0'+ cast(month(dt) as varchar(2)), 2) + '-15-' + cast(year(dt) as varchar(4)) else eomonth(dt) end from test

Aggregate over the column that is not in group by list

For example I have the following table:
declare #table table(val int, dt datetime)
insert into #table values
(10, '2018-3-20 16:00'),
(12, '2018-3-20 14:00'),
(14, '2018-3-20 12:00'),
(16, '2018-3-20 10:00'),
(10, '2018-3-19 14:00'),
(12, '2018-3-19 12:00'),
(14, '2018-3-19 10:00'),
(10, '2018-3-18 12:00'),
(12, '2018-3-18 10:00')
I try to aggregate using the column in group by, it is okay:
select day, MAX(val) as max_by_value from
(
select DATEPART(DAY, dt) as day, val from #table
) q
group by day
It returns:
day max_by_value
18 12
19 14
20 16
Now I need max value by time of the day, so I need 10 as result for each day.
I try to use over but it say Column '#table.dt' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
select DATEPART(DAY, dt), MAX(val) as max_by_value
,ROW_NUMBER() over (partition by DATEPART(DAY, dt) order by dt desc) as max_by_date
from #table
group by DATEPART(DAY, dt)
I understand why I get this error but don't understand how to fix my issue. Could you please help to find some way to fill [max_by_date] column?
As result I expect the following output:
day max_by_value max_by_time
18 12 10
19 14 10
20 16 10
Starting from 2012 version, you can use the First_value window function:
SELECT DISTINCT DATEPART(DAY, dt),
MAX(val) OVER (partition by DATEPART(DAY, dt)) as max_by_value,
FIRST_VALUE(val) OVER (partition by DATEPART(DAY, dt) order by dt desc) as max_by_date
FROM #table
Note: I've used the OVER clause for the MAX function instead of using group by.
With 2008 version, you can use a subquery instead:
SELECT DISTINCT DATEPART(DAY, dt),
MAX(val) OVER (partition by DATEPART(DAY, dt)) as max_by_value,
(
SELECT TOP 1 val
FROM #table as t1
WHERE DATEPART(DAY, t1.dt) = DATEPART(DAY, t0.dt)
ORDER BY dt DESC
) as max_by_date
FROM #table as t0

Select date and time range when date and time are in two seperate columns

I'm working on a database where the date and time is in two seperate columns and I'm tasked to show records between two dates and times intervals.
I have made the following query, but that gives me only the records from 10 to 12 on both dates. I'm missing the records from after 12 to 10 the next day.
How can I accomplish this?
SELECT
[guid],
[date],
[time],
[pos]
FROM
SomeTable
WHERE
[guid] = '0Q8m48D_uHua6P0'
AND [date] >= '2017-09-12'
AND time >= '10:00'
AND [date] <= '2017-09-13'
AND time <= '12:00';
Have a clause for beginning and end date exceptions:
SELECT [guid], [date], [time], [pos]
FROM [table]
WHERE [guid] = '0Q8m48D_uHua6P0'
and [date] >= '2017-09-12' AND [time] >= iif([date] = '2017-09-12', '10:00', [time])
AND [date] <= '2017-09-13' AND [time] <= iif([date] = '2017-09-13', '12:00', [time]);
You could of course combine them and then use BETWEEN to filter, like this:
SELECT
[guid],
[date],
[time],
[pos]
FROM
SomeTable
WHERE [guid] = '0Q8m48D_uHua6P0'
AND (DATEADD(day, DATEDIFF(day,'19000101', [date]), CAST([time] AS DATETIME2(7)))) BETWEEN '2017-09-12 12:00:00.000' and '2017-09-12 12:00:00.000'
..but this is a bad idea, since you have a function on your conditions, which will might make the query optimizer ignore any indexes you might have in place (since such a query is not sargable)
So, I would create a new persisted computed column on your table called [Datetime] or anything that makes sense to you and then give it a default value as:
DATEADD(day, DATEDIFF(day,'19000101', [date]), CAST([time] AS DATETIME2(7))))
Then you would be able to write the query as follows:
SELECT
[guid],
[date],
[time],
[pos]
FROM
SomeTable
WHERE [guid] = '0Q8m48D_uHua6P0'
AND [Datetime] BETWEEN '2017-09-12 12:00:00.000' and '2017-09-12 12:00:00.000'
You can compute a column as DATETIME and then filter on it.
;with cte as (
SELECT
[guid],
[date],
[time],
[pos],
DT = cast([date] as datetime) + cast([time] as datetime)
FROM
SomeTable
WHERE
[guid] = '0Q8m48D_uHua6P0'
)
select *
from cte
where DT between '20170912 10:00' and '20170913 12:00'
Here is one way of doing this
SELECT
[guid],
[date],
[time],
[pos]
FROM
SomeTable
WHERE
[guid] = '0Q8m48D_uHua6P0'
AND
CAST (
CONVERT(Varchar(10), [date], 112) + ' ' +
CONVERT(Varchar(8), [time]) AS DateTime) Between '2017-09-12 10:00'
AND '2017-09-13 12:00'

Resources