I have the following that selects from a log and groups down to minute (excluding seconds and milisec):
SELECT DATEPART(YEAR, [Date]) AS YEAR, DATEPART(MONTH, [Date]) AS MONTH,
DATEPART(DAY, [Date]) AS DAY, DATEPART(HOUR, [Date]) AS HOUR,
DATEPART(MINUTE, [Date]) AS MIN, COUNT(*) AS COUNT
FROM [database].[dbo].[errorlog]
GROUP BY DATEPART(YEAR, [Date]), DATEPART(MONTH, [Date]), DATEPART(DAY, [Date]),
DATEPART(HOUR, [Date]), DATEPART(MINUTE, [Date])
ORDER BY DATEPART(YEAR, [Date]) DESC, DATEPART(MONTH, [Date]) DESC,
DATEPART(DAY, [Date]) DESC, DATEPART(HOUR, [Date]) DESC,
DATEPART(MINUTE, [Date]) DESC;
But as you can see thats a lot of fuzz just for getting a count, so I wonder if there is a better way to group it so I get grouped down to minutes in respect to year, month, day and hour?
This should would work:
select CAST([Date] AS smalldatetime) as time_stamp, count(*) as count
FROM [database].[dbo].[errorlog]
group by CAST([Date] AS smalldatetime)
order by CAST([Date] AS smalldatetime) desc;
Update after comments on this answer:
select dateadd(second,-datepart(ss,[Date]),[Date]) as time_stamp, count(*) as count
FROM [database].[dbo].[errorlog]
group by dateadd(second,-datepart(ss,[Date]),[Date])
order by dateadd(second,-datepart(ss,[Date]),[Date]) desc ;
The first solution rounds up the timestamp to the nearest minute. I realised that this is not exactly what the OP wanted.
So, the second solution just substracts the seconds part from the timestamp and leaves the timestamp with seconds as zero(Assuming [Date] does not have fractional seconds)
DATEADD(minute,DATEDIFF(minute,'20010101',[Date]),'20010101')
Should round all Date column values down to the nearest minute. So:
SELECT DATEADD(minute,DATEDIFF(minute,'20010101',[Date]),'20010101'),
COUNT(*) AS COUNT
FROM [database].[dbo].[errorlog]
GROUP BY DATEADD(minute,DATEDIFF(minute,'20010101',[Date]),'20010101')
ORDER BY DATEADD(minute,DATEDIFF(minute,'20010101',[Date]),'20010101') DESC;
(You could move this expression into a subquery if you want to further reduce the repetition)
You could do something like this to get
declare #now datetime
set #now = GETDATE()
select dateadd(minute, mm, #now) as date, c from (
select DATEDIFF(minute, #now, [Date]) as mm, COUNT(1) as c
from [database].[dbo].[errorlog]
group by DATEDIFF(minute, #now, [Date])
) t
Related
SELECT count(*), sum(price)
FROM Orders
where creationDate > '2020-10-01' and creationDate < '2020-10-02'
this query gives total no of orders and the sum of price for 1st Oct.
I want the results for every day from Oct'1st to Till Date. On the particular day how many orders and the sum of their price.
Use group by:
select convert(date, creationDate) creationDay, count(*) cnt, sum(price) total_price
from Orders
where creationDate >= '20201001' and creationDate < dateadd(day, 1, convert(date, getdate()))
group by convert(date, creationDate)
order by creationDay
If you want a running count and sum, then use window functions:
select convert(date, creationDate) creationDay,
sum(count(*)) over(order by convert(date, creationDate)) running_cnt,
sum(sum(price)) over(order by convert(date, creationDate)) total_running_price
from Orders
where creationDate >= '20201001' and creationDate < dateadd(day, 1, convert(date, getdate()))
group by convert(date, creationDate)
order by creationDay
I can get the top 10 percent for the previous month using
select top 10 percent ID, Ref, Entered_Date, [Type], CREATEDBY, Office, Created_Date, Amt from Tbl
where DATEPART(m, Entered_Date) = DATEPART(m, DATEADD(m, -1, getdate())) AND DATEPART(yyyy, Entered_Date) = DATEPART(yyyy, DATEADD(m, -1, getdate())) and
CreatedBy ='User1')
order by amt DESC
I am having to do this many times for each user using a union, how can I do this on one query? When I add the other users CreatedBy in('User1','User2') it doesn't work. I had a look at row over partition but cant figure it out. I'm using SSMS 2017.
Updated with below
Select * From(
select ID, Ref, Entered_Date, [Type], CREATEDBY, Office, Created_Date, Amt
NTILE (10) OVER ( PARTITION BY CREATEDBY ORDER BY Amt desc) AS PercentageNo
from Tbl
where DATEPART(m, Entered_Date) = DATEPART(m, DATEADD(m, -1, getdate())) AND DATEPART(yyyy, Entered_Date) = DATEPART(yyyy, DATEADD(m, -1, getdate()))
/*Entered_Date between DATEADD(m, -2, getdate()) and DATEADD(m, -1, getdate()) */ )as SubQuery
where PercentageNo=1 order By Amt
you can use GROUP BY for select percentage of each user as this:
select top 10 percent ID, Ref, Entered_Date, [Type], CREATEDBY, Office, Created_Date, Amt
FROM Tbl
where DATEPART(m, Entered_Date) = DATEPART(m, DATEADD(m, -1, getdate()))
AND DATEPART(yyyy, Entered_Date) = DATEPART(yyyy, DATEADD(m, -1, getdate()))
AND CreatedBy IN ('User1','User2')
GROUP BY ID, Ref, Entered_Date, [Type], CREATEDBY, Office, Created_Date, Amt
order by amt DESC
you can use row_number() for this reason
If you want the top 10% for each user, I would recommend using logic such as this:
select ID, Ref, Entered_Date, [Type], CREATEDBY, Office, Created_Date, Amt
from (select t.*,
row_number() over (partition by user order by amt desc) as seqnum,
count(*) over (partition by user) as cnt
from Tbl t
where Entered_Date >= dateadd(month, -1, dateadd(day, 1 - day(getdate()), convert(date, getdate()))) and
Entered_Date < dateadd(day, 1 - day(getdate()), convert(date, getdate()))
) t
where seqnum <= cnt / 10;
order by user, amt desc;
Note the change to the date arithmetic. The functions are all applied only on getdate(). This allows indexes to be used to winnow down the data.
The key for solving your problem is using window functions with the partition by user construct to do separate counts and enumerations for each user.
This I now woking as expected thanks to Gordon Linoff Re Dates and Caius Jard for pointing me in to Ntile
Select * From(
select ID, Ref, Entered_Date, [Type], CREATEDBY, Office, Created_Date, Amt
NTILE (10) OVER ( PARTITION BY CREATEDBY ORDER BY Amt desc) AS PercentageNo
from Tbl
where Entered_Date >= dateadd(month, -1, dateadd(day, 1 - day(getdate()), convert(date, getdate()))) and Entered_Date < dateadd(day, 1 - day(getdate()), convert(date, getdate())) as SubQuery where PercentageNo=1 order By Amt
I'm trying to get a total count for each day between 07:00 and 19:00 for the last 7 days. The below query only displays the count for the date 7 days back and not each individual day. Any help would be greatly appreciated. Thanks!
DECLARE #Date AS DATETIME = DATEADD(HOUR, 7, CAST(CAST(DATEADD(DAY, -7, GETDATE()) AS DATE) AS DATETIME))
DECLARE #Date2 AS DATETIME = DATEADD(HOUR, 19, CAST(CAST(DATEADD(DAY, -7, GETDATE()) AS DATE) AS DATETIME))
SELECT CONVERT(NVARCHAR(20), DATE, 120) AS Report_Date, COUNT(DISTINCT GUID) AS ROW_COUNT
FROM TABLE WITH (NOLOCK)
WHERE DATEADD(MINUTE, +270, DATE) >= #Date
AND DATEADD(MINUTE, +270, DATE) < #Date2
GROUP BY CONVERT(NVARCHAR(20), DATE, 120)
As you need past past 7days so use getdate()- 7
SELECT CAST(DATE as DATE) AS Report_Date,
COUNT(DISTINCT GUID) AS ROW_COUNT
FROM t
WHERE DATEPART(HOUR, DATE) >= 7 AND
DATEPART(HOUR, DATE) < 19
and CAST(DATE as DATE)>=getdate()-7 and CAST(DATE as DATE)<=getdate()
GROUP BY CAST(DATE as DATE)
ORDER BY CAST(DATE as DTE)
Don't convert date columns to dates. Use date functions. I don't understand why you are adding 270 minutes to the date.
I would go for a more direct answer to your question:
SELECT CAST(DATE as DATE) AS Report_Date,
COUNT(DISTINCT GUID) AS ROW_COUNT
FROM TABLE
WHERE DATEPART(HOUR, DATE) >= 7 AND
DATEPART(HOUR, DATE) < 19
GROUP BY CAST(DATE as DATE)
ORDER BY CAST(DATE as DTE)
;With T AS
(
SELECT CAST(DATE as DATE) AS Report_Date,COUNT(DISTINCT GUID) AS ROW_COUNT
FROM tbl
WHERE
DATEPART(HOUR, DATE) >= 7 AND DATEPART(HOUR, DATE) < 19
)
SELECT Report_date,Row_Count From T GROUP BY Report_date
ORDER BY Report_date
I've got a table with a column indicating the date and time each row was inserted into the table. I'm trying to get a statistics for the average and peak rates of insertions:
Peak insertions per minute
Peak insertions per second
Average insertions per minute
Average insertions per second
I can envisage a solution using a GROUP BY to put the data into "buckets" (one for each interval) and then average the count of items in each, however it seems a very clunky solution.
Is there a more elegant T-SQL solution to this problem?
Grouping Sets are the way to go, they're are intended for this very application of grouping by multiple sets of grouping attributes (grouping sets) in one query, and should result in better execution plans i.e. better performance:
-- if you weren't grouping by minutes and seconds this would
-- probably look more 'elegant'
SELECT
GROUPING_ID(
YEAR(orderdate),
MONTH(orderdate),
DAY(orderdate),
DATEPART(hour, orderdate),
DATEPART(MINUTE, orderdate),
DATEPART(SECOND, orderdate)) AS grp_id,
MAX([Insertions]) AS max_insertions,
AVG([Average]) AS avg_insertions,
YEAR(orderdate) AS order_year,
MONTH(orderdate) AS order_month,
DAY(orderdate) AS order_day,
DATEPART(HOUR, orderdate) AS order_hour,
DATEPART(MINUTE, orderdate) AS order_minute,
DATEPART(SECOND, orderdate) AS order_second -- this will be null if the grouping set is minute
FROM Sales.Orders
GROUP BY
GROUPING SETS
(
(
-- grouping set 1: order second
YEAR(orderdate),
MONTH(orderdate),
DAY(orderdate),
DATEPART(hour, orderdate),
DATEPART(MINUTE, orderdate),
DATEPART(SECOND, orderdate)
),
(
-- grouping set 2: order minute
YEAR(orderdate),
MONTH(orderdate),
DAY(orderdate),
DATEPART(hour, orderdate),
DATEPART(MINUTE, orderdate)
)
);
GROUP BY is the way to go.
I would just make a CTE for each time interval you want, and select the max for each one:
;WITH CTEMinute AS
(
SELECT YEAR(datefield) yr,
MONTH(datefield) mo,
DAY(datefield) d,
DATEPART(hour, datefield) hr,
DATEPART(minute, datefield) Mint,
COUNT(*) as 'Inserts'
FROM MyTable
GROUP BY YEAR(datefield),
MONTH(datefield),
DAY(datefield),
DATEPART(hour, datefield),
DATEPART(minute, datefield)
)
,CTESecond AS
(
SELECT YEAR(datefield) yr,
MONTH(datefield) mo,
DAY(datefield) d,
DATEPART(hour, datefield) hr,
DATEPART(minute, datefield) Mint,
DATEPART(second, datefield) sec,
COUNT(*) as 'Inserts'
FROM MyTable
GROUP BY YEAR(datefield),
MONTH(datefield),
DAY(datefield),
DATEPART(hour, datefield),
DATEPART(minute, datefield),
DATEPART(second, datefield)
)
Then you can just select from those CTEs to get max/min/avg values per time unit.
If you want it to be more elegant you can potentially just make on CTE for as fine granularity as you are likely to want (i.e. milliseconds or whatever), and then you can SELECT/GROUP BY that.
The issue with doing that is CTEs don't really perform that well since they are basically disposable views with no indexes or anything, so aggregating a CTE within another query will quickly bog down.
Expanding on J Coopers answer, I think the Rollup Feature might be what you're after.
SELECT
MAX([Insertions]) AS max_insertions,
AVG([Average]) AS avg_insertions,
YEAR(orderdate), AS YEAR
MONTH(orderdate), AS MONTH
DAY(orderdate), AS DAY
DATEPART(hour, orderdate), AS HOUR
DATEPART(MINUTE, orderdate), AS MINUTE
DATEPART(SECOND, orderdate) AS SECOND
FROM Sales.Orders
GROUP BY ROLLUP(
YEAR(orderdate),
MONTH(orderdate),
DAY(orderdate),
DATEPART(hour, orderdate),
DATEPART(MINUTE, orderdate),
DATEPART(SECOND, orderdate)
)
Given the following table, how does one calculate the hourly mode, or value with the highest frequency by hour?
CREATE TABLE Values
(
ValueID int NOT NULL,
Value int NOT NULL,
LogTime datetime NOT NULL
)
So far, I've come up with the following query.
SELECT count(*) AS Frequency,
DatePart(yy, LogTime) as [Year],
DatePart(mm, LogTime) as [Month],
DatePart(dd, LogTime) as [Day],
DatePart(hh, LogTime) as [Hour]
FROM Values
GROUP BY
Value,
DatePart(yy, LogTime),
DatePart(mm, LogTime),
DatePart(dd, LogTime),
DatePart(hh, LogTime)
However, this yields the frequency of each distinct value by hour. How do I add a constraint to only return the value with the maximum frequency by hour?
Thanks
The following query may look odd... but it works and it gives you what you want. This query will give you the value that had the highest frequency in a particular "hour" (slice of time).
I am NOT dividing into Year, Month, Day, etc... only hour (as you requested) even though you had those other fields in your example query.
I chose to do "MAX(Value)" below, because the case can come up where more than one "value" tied for first place with the highest frequency by hour. You can choose to do MIN, or MAX or some other 'tiebreaker' if you want.
WITH GroupedValues (Value, Frequency, Hour) AS
(SELECT
Value,
COUNT(*) AS Frequency,
DATEPART(hh, LogTime) AS Hour
FROM
dbo.MyValues
GROUP BY
Value,
DATEPART(hh, LogTime))
SELECT
MAX(Value) AS Value,
a.Hour
FROM
GroupedValues a INNER JOIN
(SELECT MAX(Frequency) AS MaxFrequency,
Hour FROM GroupedValues GROUP BY Hour) b
ON a.Frequency = b.MaxFrequency AND a.Hour = b.Hour
GROUP BY
a.Hour
Nest the aggregates...
SELECT
MAX(Frequency) AS [Mode],
[Year],[Month],[Day],[Hour]
FROM
(SELECT
COUNT(*) AS Frequency,
DatePart(yy, LogTime) as [Year],
DatePart(mm, LogTime) as [Month],
DatePart(dd, LogTime) as [Day],
DatePart(hh, LogTime) as [Hour]
FROM
Values
GROUP BY
Value,
DatePart(yy, LogTime),
DatePart(mm, LogTime),
DatePart(dd, LogTime),
DatePart(hh, LogTime)
) foo
GROUP By
[Year],[Month],[Day],[Hour]