Total number of Events on every date - sql

I am stuck developing a query.
I have a table, structured like this:
[EventId] [Description] [EventName] [ValidFrom] [ValidTo] [Approved]
1 Sample1 1stEvent 2013-01-27 2013-05-10 1
2 Sample2 2stEvent 2013-04-07 2013-06-15 1
3 Sample3 3stEvent 2013-04-07 2013-06-15 1
4 Sample4 4stEvent 2013-03-02 2013-05-29 1
5 Sample5 5stEvent 2013-05-17 2013-07-10 1
6 Sample6 6stEvent 2013-03-20 2013-05-11 1
What i want is the total number of events for every date within a date range, inclusive.
Select distinct
Convert(varchar,ValidFrom,101)as [Date],
case
when count(EventID)>1 then Convert(nvarchar, count(EventID)) +' Events'
else Convert(nvarchar, count(EventID)) + ' Event'
end as CountOf,
Row_Number()
over (Order By Convert(varchar,ValidFrom,101)) as RowNumber
from [Table]
where Approved=1
group by Convert(varchar,ValidFrom,101)
This is the query I have come up with until now, but this shows the total number of events on a particular date without including the events which were continued as per the dates between valid from and valid to dates.

This code sample is not complete - you need to enter the fields you need to display and aggregate upon. It sounds like you're looking for a result that's between two dates, and you don't have that in your query. I'm not sure I comletely understand your question.
DECLARE #pStartDate DATE
DECLARE #pEndDate DATE
SET #pStartDate = [enter your start date for the date range]
SET #pEndDate = [enter your end date for the date range]
SELECT
COUNT(EventId),
ValidFrom,
ValidTo
FROM [Table]
WHERE
ValidFrom >= #pStartDate
AND ValidTo <= #pEndDate
AND Approved = 1
GROUP BY
ValidFrom,
ValidTo

This will do:
declare #dateFrom date
declare #dateTo date
SET #dateFrom = '20130101'
SET #dateTo = '20130501'
;with cte as(Select #dateFrom AS EveryDay
UNION ALL
Select dateadd(dd, 1, EveryDay) FROM cte WHERE EveryDay < #dateTo)
SELECT
EveryDay,
COUNT(DISTINCT [EventName]) AS NoEvents
from cte LEFT JOIN Table1 ON ValidFrom <= EveryDay AND ValidTo >= EveryDay
GROUP BY EveryDay
OPTION (MAXRECURSION 0)
SQL Fiddle

With the above Query i have mentioned gives the result like this :-
Date CountOf RowNumber
01/27/2013 2 Events 1
03/02/2013 1 Event 2
04/07/2013 2 Events 3
05/17/2013 1 Event 4
As you can see i am getting total events only on the base of particular date which is not desired.
i want to show the result as like in valid from =01/27/2013 in valid to 2013-05-10 which means in the case of Date =2013-03-02 the events should be incremented by one or the number of events which are active on that date,so the result for 2013-03-02 should be 6 events, i wish i can present a better picture but because of the complexity it made me blank :(

SELECT t1.EventId,t1.Description,t1.EventName,t1.ValidFrom,t1.ValidTo,t1.Approved,SUM(TotalEvent) FROM
(SELECT EventId,Description,EventName,ValidFrom,ValidTo,Approved,COUNT(EventId) as Total
FROM TABLE
WHERE Approved='1' AND ValidFrom >=(DATE) AND ValidTO<=(DATE)
GROUP BY EventId,Description,EventName,ValidFrom,ValidTo,Approved) AS t1
LEFT JOIN
(SELECT EventId, COUNT(EventId) as TotalEvent
FROM TABLE
WHERE ValidFrom >=(DATE) AND ValidTO<=(DATE)) AS t2
ON t1.EventId=t2.EventId
Group by t1.EventId,t1.Description,t1.EventName,t1.ValidFrom,t1.ValidTo,t1.Approved
Hope this help you.

Related

Date closest to current date in SQL (prior dates or future dates)

I am running into a bit of an issue with finding out how to look for dates closest to the current date. I looked at this among others:
Get closest date to current date sql
I also looked here: https://bytes.com/topic/sql-server/answers/79502-get-closest-date
I am using MS SQL 2012 and there have been many questions asked about this, so I apologize for bringing it back up. I can't seem to get my query to work.
Basically I have a table as follows:
ITEMNMBR | MINDATE | MAXDATE | CLOSESTDATE
------------------------------------------------
123456 | 2017-10-15 | 2017-11-04 | NULL
654321 | 2017-09-29 | 2017-12-08 | NULL
The current date would be today, '2017-10-03'. We would find for item number '123456' the closest date of purchase is 2017-10-15. For item number '654321' the closest date of purchase is 2017-09-29 as that happened much more recently than how long it will take for the next purchase to take effect (hence I am looking at an absolute value of the difference between the dates). You'll have to forgive me, but the query I am including doesn't include the "CLOSESTDATE" column. I've included it there to let you know that any of my calculations have rendered that column NULL. So here's what I have:
--Lines commented below are not used in the current iteration of the query
--DECLARE #dt DATETIME
--SET #dt = GETDATE()
SELECT
I.ITEMNMBR,
MIN(PDATE1) AS MINDATE,
MAX(PDATE1) AS MAXDATE
FROM dbo.IV00101 I
LEFT OUTER JOIN
(SELECT P.[Item Number],
P.[Req Date] AS PDATE1
FROM dbo.Purchases P
WHERE ((P.[Document Status] = 'Open') AND
(P.[POStat] <> 'Closed') AND
(P.[POStat] <> 'Received')) AND P.[Req Date] >= DATEADD(d, -15,
DATEDIFF(d, 0, GETDATE()))
) AS P ON P.[Item Number]= I.ITEMNMBR
WHERE P.[Item Number] = '123456'
GROUP BY
I.ITEMNMBR
ORDER BY MINDATE DESC
When I run this query, I get the table I outlined previously, minus the "CLOSESTDATE" column. The "CLOSESTDATE" column is what I want to use to display what date of purchase is closest to TODAY. Basically, if a date of purchase happened three days ago and the next date of purchase is a month out, then I want to show the date of purchase from three days ago. Also, the query can be written without using a subquery, but I was using other calculations within the subquery prior to reverting back to the rather simplistic original query. Thus, the query can be written like this:
SELECT
I.ITEMNMBR,
MIN(P.[Req Date]) AS MINDATE,
MAX(P.[Req Date]) AS MAXDATE
FROM dbo.IV00101 I
LEFT OUTER JOIN
Purchases P ON P.[Item Number] = I.ITEMNMBR
WHERE P.[Item Number] = '123456' ((P.[Document Status] = 'Open') AND
(P.[POStat] <> 'Closed') AND
(P.[POStat] <> 'Received')) AND P.[Req Date] >= DATEADD(d, -15,
DATEDIFF(d, 0, GETDATE()))
GROUP BY
I.ITEMNMBR
ORDER BY MINDATE DESC
Lastly, as you can see, I have a date constraint for the past 15 days so that anything older than that won't show up at all.
Many thanks in advance!
I'm still not quite understanding your question, but I hope this gives you a start, you can try the rextester sample here
But what it looks like to me is you need a simple case statement:
select ITEMNMBR
,case when abs(datediff(day, MINDATE, convert(date,getdate()))) > abs(datediff(day, MAXDATE, convert(date,getdate()))) then 'MINDATE is greater' else 'MAXDATE is greater' end as ClosestDate
from myTable
Kindly let me know if you have any questions. I'd be happy to help.
I have taken your data and schema and made an approximation of what I believe your underlying data actually looks like. From that, your problem is actually very simple:
declare #t table(ItemNumber int, ReqDate datetime, DocumentStatus nvarchar(100), POStat nvarchar(100))
insert into #t values
(123456,'2017-10-15','Open','Not Closed')
,(123456,'2017-11-04','Open','Not Closed')
,(654321,'2017-09-29','Open','Not Closed')
,(654321,'2017-12-08','Open','Not Closed')
,(123456,'2017-10-11','Open','Closed')
,(123456,'2017-11-01','Closed','Not Closed')
,(654321,'2017-09-21','Closed','Not Closed')
,(654321,'2017-12-01','Open','Received');
select t.ItemNumber
,min(t.ReqDate) as MinDate
,max(t.ReqDate) as MaxDate
-- Find the difference in days for both Min and Max dates, converting to positive numbers where negative,
,case when abs(datediff(d,min(t.ReqDate),getdate())) < abs(datediff(d,max(t.ReqDate),getdate()))
then min(t.ReqDate) -- And then return the appropriate one.
else max(t.ReqDate)
end as ClosestDate
from #t t
where t.DocumentStatus = 'Open'
and t.POStat not in('Closed','Received')
and t.ReqDate >= dateadd(d,-15,cast(getdate() as date))
group by t.ItemNumber
order by MinDate desc;
Output:
+------------+-------------------------+-------------------------+-------------------------+
| ItemNumber | MinDate | MaxDate | ClosestDate |
+------------+-------------------------+-------------------------+-------------------------+
| 123456 | 2017-10-15 00:00:00.000 | 2017-11-04 00:00:00.000 | 2017-10-15 00:00:00.000 |
| 654321 | 2017-09-29 00:00:00.000 | 2017-12-08 00:00:00.000 | 2017-09-29 00:00:00.000 |
+------------+-------------------------+-------------------------+-------------------------+
Change the initial part of the query with this:
SELECT DISTINCT
I.ITEMNMBR,
MIN(PDATE1) AS MINDATE,
MAX(PDATE1) AS MAXDATE
IF(ABS(DATEDIFF(MIN(PDATE1)-SYSDATETIME())) >
ABS(DATEDIFF(MAX(PDATE1)-SYSDATETIME())),
MAX(PDATE1),MIN(PDATE1)) as CLOSESTDATE

Comparing dates using SQL

I have a date that looks like this: 2014-10-01 12:35:29.440
the table looks like this:
ORDER 1 | 2014-07-31 00:00:00.000
ORDER 2 | 2015-07-31 00:00:00.000
sorry i wanted ORDER 2 to show up.. As my get date returns todays date and that is GREATER than 2014-07-31 00:00:00.000
Here is what i have tried:
SELECT TOP 1 NAME
FROM ORDER_DATES
WHERE GETDATE() > ORDER_DATE
ORDER BY NAME DESC
Your question still isn't quite worded in a way that is conducive to what you need... but I think I understand what you want now based on the comments.
Based on the comment:
IF it doesnt match the date then it needs to return the next row.
Which is ORDER 2
Something like this should work:
SELECT TOP 1 name
FROM ORDER_DATES o
INNER JOIN (
-- This subquery finds the first date that occurs *after* the current date
SELECT MIN(ORDER_DATE) AS ORDER_DATE
FROM ORDER_DATES
WHERE ORDER_DATE > GETDATE()
) minDateAfterToday ON o.ORDER_DATE = minDateAfterToday.ORDER_DATE
ORDER BY name
This would work a lot better if you had an ID field in the table, but this should work with the given data, you'll potentially run into issues if you have two orders on the exact same date.
EDIT:
here's a fiddle showing the query in action:
http://sqlfiddle.com/#!6/f3057/1
DATEDIFF will come handy, also you have to order by ORDER_DATE:
SELECT TOP 1 NAME
FROM ORDER_DATES
WHERE DATEDIFF(DAY,ORDER_DATE,GETDATE())>0
ORDER BY ORDER_DATE DESC
You can write as:
SELECT NAME
FROM ORDER_DATES
WHERE cast(GETDATE()as date) > cast (ORDER_DATE as date)
ORDER BY NAME DESC
Demo
Check if you are querying against right table
declare #dt datetime = cast('2014-10-01 12:35:29.440' as datetime), #dt2 datetime= cast('2014-07-31 00:00:00.000' as datetime);
print(case when #dt > #dt2 then 1 else 0 end);
This piece of script shows output 1 i.e. condition should match for ORDER 1.
Verify if you are missing some thing.
Edit as per change to original question:
here the condition needed be reverted as date value is in future which is greater than current date
new query will be as
SELECT TOP 1 NAME
FROM ORDER_DATES
WHERE ORDER_DATE > GETDATE()
ORDER BY NAME DESC

SQL -- return 0s if no group exists

I have a rollup table that sums up raw data for a given hour. It looks something like this:
stats_hours:
- obj_id : integer
- start_at : datetime
- count : integer
The obj_id points to a separate table, the start_at field contains a timestamp for the beginning of the hour of the data, and the count contains the sum of the data for that hour.
I would like to build a query that returns a set of data per day, so something like this:
Date | sum_count
2014-06-01 | 2000
2014-06-02 | 3000
2014-06-03 | 0
2014-06-04 | 5000
The query that I built does a grouping on the date column and sums up the count:
SELECT date(start_at) as date, sum(count) as sum_count
FROM stats_hours GROUP BY date;
This works fine unless I have no data for a given date, in which case it obviously leaves out the row:
Date | sum_count
2014-06-01 | 2000
2014-06-02 | 3000
2014-06-04 | 5000
Does anyone know of a good way in SQL to return a zeroed-out row in the case that there is no data for a given date group? Maybe some kind of case statement?
You need a full list of dates first, then connect that list to your available dates and group by that. Try the following:
--define start and end limits
Declare #todate datetime, #fromdate datetime
Select #fromdate='2009-03-01', #todate='2014-06-04'
;With DateSequence( Date ) as
(
Select #fromdate as Date
union all
Select dateadd(day, 1, Date)
from DateSequence
where Date < #todate
)
--select result
SELECT DateSequence.Date, SUM(Stats_Hours.Count) AS Sum_Count
FROM
DateSequence
LEFT JOIN
Stats_Hours ON DateSequence.Date = Stats_Hours.Start_At
GROUP BY DateSequence.Date
option (MaxRecursion 0)
EDIT: CTE code from this post

trying to find the maximum number of occurrences over time T-SQL

I have data recording the StartDateTime and EndDateTime (both DATETIME2) of a process for all of the year 2013.
My task is to find the maximum amount of times the process was being ran at any specific time throughout the year.
I have wrote some code to check every minute/second how many processes were running at the specific time, but this takes a very long time and would be impossible to let it run for the whole year.
Here is the code (in this case check every minute for the date 25/10/2013)
CREATE TABLE dbo.#Hit
(
ID INT IDENTITY (1,1) PRIMARY KEY,
Moment DATETIME2,
COUNT INT
)
DECLARE #moment DATETIME2
SET #moment = '2013-10-24 00:00:00'
WHILE #moment < '2013-10-25'
BEGIN
INSERT INTO #Hit ( Moment, COUNT )
SELECT #moment, COUNT(*)
FROM dbo.tblProcessTimeLog
WHERE ProcessFK IN (25)
AND #moment BETWEEN StartDateTime AND EndDateTime
AND DelInd = 0
PRINT #moment
SET #moment = DATEADD(MINute,1,#moment)
END
SELECT * FROM #Hit
ORDER BY COUNT DESC
Can anyone think how i could get a similar result (I just need the maximum amount of processes being run at any given time), but for all year?
Thanks
DECLARE #d DATETIME = '20130101'; -- the first day of the year you care about
;WITH m(m) AS
( -- all the minutes in a day
SELECT TOP (1440) ROW_NUMBER() OVER (ORDER BY number) - 1
FROM master..spt_values
),
d(d) AS
( -- all the days in *that* year (accounts for leap years vs. hard-coding 365)
SELECT TOP (DATEDIFF(DAY, #d, DATEADD(YEAR, 1, #d))) DATEADD(DAY, number, #d)
FROM master..spt_values WHERE type = N'P' ORDER BY number
),
x AS
( -- all the minutes in *that* year
SELECT moment = DATEADD(MINUTE, m.m, d.d) FROM m CROSS JOIN d
)
SELECT TOP (1) WITH TIES -- in case more than one at the top
x.moment, [COUNT] = COUNT(l.ProcessFK)
FROM x
INNER JOIN dbo.tblProcessTimeLog AS l
ON x.moment >= l.StartDateTime
AND x.moment <= l.EndDateTime
WHERE l.ProcessFK = 25 AND l.DelInd = 0
GROUP BY x.moment
ORDER BY [COUNT] DESC;
See this post for why I don't think you should use BETWEEN for range queries, even in cases where it does semantically do what you want.
Create a table T whose rows represent some time segments.
This table could well be a temporary table (depending on your case).
Say:
row 1 - [from=00:00:00, to=00:00:01)
row 2 - [from=00:00:01, to=00:00:02)
row 3 - [from=00:00:02, to=00:00:03)
and so on.
Then just join from your main table
(tblProcessTimeLog, I think) to this table
based on the datetime values recorded in
tblProcessTimeLog.
A year has just about half million minutes
so it is not that many rows to store in T.
I recently pulled some code from SO trying to solve the 'island and gaps' problem, and the algorithm for that should help you solve your problem.
The idea is that you want to find the point in time that has the most started processes, much like figuring out the deepest nesting of parenthesis in an expression:
( ( ( ) ( ( ( (deepest here, 6)))))
This sql will produce this result for you (I included a temp table with sample data):
/*
CREATE TABLE #tblProcessTimeLog
(
StartDateTime DATETIME2,
EndDateTime DATETIME2
)
-- delete from #tblProcessTimeLog
INSERT INTO #tblProcessTimeLog (StartDateTime, EndDateTime)
Values ('1/1/2012', '1/6/2012'),
('1/2/2012', '1/6/2012'),
('1/3/2012', '1/6/2012'),
('1/4/2012', '1/6/2012'),
('1/5/2012', '1/7/2012'),
('1/6/2012', '1/8/2012'),
('1/6/2012', '1/10/2012'),
('1/6/2012', '1/11/2012'),
('1/10/2012', '1/12/2012'),
('1/15/2012', '1/16/2012')
;
*/
with cteProcessGroups (EventDate, GroupId) as
(
select EVENT_DATE, (E.START_ORDINAL - E.OVERALL_ORDINAL) GROUP_ID
FROM
(
select EVENT_DATE, EVENT_TYPE,
MAX(START_ORDINAL) OVER (ORDER BY EVENT_DATE, EVENT_TYPE ROWS UNBOUNDED PRECEDING) as START_ORDINAL,
ROW_NUMBER() OVER (ORDER BY EVENT_DATE, EVENT_TYPE) AS OVERALL_ORDINAL
from
(
Select StartDateTime AS EVENT_DATE, 1 as EVENT_TYPE, ROW_NUMBER() OVER (ORDER BY StartDateTime) as START_ORDINAL
from #tblProcessTimeLog
UNION ALL
select EndDateTime, 0 as EVENT_TYPE, NULL
FROM #tblProcessTimeLog
) RAWDATA
) E
)
select Max(EventDate) as EventDate, count(GroupId) as OpenProcesses
from cteProcessGroups
group by (GroupId)
order by COUNT(GroupId) desc
Results:
EventDate OpenProcesses
2012-01-05 00:00:00.0000000 5
2012-01-06 00:00:00.0000000 4
2012-01-15 00:00:00.0000000 2
2012-01-10 00:00:00.0000000 2
2012-01-08 00:00:00.0000000 1
2012-01-07 00:00:00.0000000 1
2012-01-11 00:00:00.0000000 1
2012-01-06 00:00:00.0000000 1
2012-01-06 00:00:00.0000000 1
2012-01-06 00:00:00.0000000 1
2012-01-16 00:00:00.0000000 1
Note that the 'in-between' rows don't give anything meaningful. Basically this output is only tuned to tell you when the most activity was. Looking at the other rows in the out put, there wasn't just 1 process running on 1/8 (there was actually 3). But the way this code works is that by grouping the processes that are concurrent together in a group, you can count the number of simultaneous processes. The date returned is when the max concurrent processes began. It doesn't tell you how long they were going on for, but you can solve that with an additional query. (once you know the date the most was ocurring, you can find out the specific process IDs by using a BETWEEN statement on the date.)
Hope this helps.

Generate missing dates + Sql Server (SET BASED)

I have the following
id eventid startdate enddate
1 1 2009-01-03 2009-01-05
1 2 2009-01-05 2009-01-09
1 3 2009-01-12 2009-01-15
How to generate the missing dates pertaining to every eventid?
Edit:
The missing gaps are to be find out based on the eventid's. e.g. for eventid 1 the output should be 1/3/2009,1/4/2009,1/5/2009.. for eventtype id 2 it will be 1/5/2009, 1/6/2009... to 1/9/2009 etc
My task is to find out the missing dates between two given dates.
Here is the whole thing which i have done so far
declare #tblRegistration table(id int primary key,startdate date,enddate date)
insert into #tblRegistration
select 1,'1/1/2009','1/15/2009'
declare #tblEvent table(id int,eventid int primary key,startdate date,enddate date)
insert into #tblEvent
select 1,1,'1/3/2009','1/5/2009' union all
select 1,2,'1/5/2009','1/9/2009' union all
select 1,3,'1/12/2009','1/15/2009'
;with generateCalender_cte as
(
select cast((select startdate from #tblRegistration where id = 1 )as datetime) DateValue
union all
select DateValue + 1
from generateCalender_cte
where DateValue + 1 <= (select enddate from #tblRegistration where id = 1)
)
select DateValue as missingdates from generateCalender_cte
where DateValue not between '1/3/2009' and '1/5/2009'
and DateValue not between '1/5/2009' and '1/9/2009'
and DateValue not between '1/12/2009'and'1/15/2009'
Actually what I am trying to do is that, I have generated a calender table and from there I am trying to find out the missing dates based on the id's
The ideal output will be
eventid missingdates
1 2009-01-01 00:00:00.000
1 2009-01-02 00:00:00.000
3 2009-01-10 00:00:00.000
3 2009-01-11 00:00:00.000
and also it has to be in SET BASED and the start and end dates should not be hardcoded
Thanks in adavnce
The following uses a recursive CTE (SQL Server 2005+):
WITH dates AS (
SELECT CAST('2009-01-01' AS DATETIME) 'date'
UNION ALL
SELECT DATEADD(dd, 1, t.date)
FROM dates t
WHERE DATEADD(dd, 1, t.date) <= '2009-02-01')
SELECT t.eventid, d.date
FROM dates d
JOIN TABLE t ON d.date BETWEEN t.startdate AND t.enddate
It generates dates using the DATEADD function. It can be altered to take a start & end date as parameters. According to KM's comments, it's faster than using the numbers table trick.
Like rexem - I made a function that contains a similar CTE to generate any series of datetime intervals you need. Very handy for summarizing data by datetime intervals like you are doing.
A more detailed post and the function source code are here:
Insert Dates in the return from a query where there is none
Once you have the "counts of events by date" ... your missing dates would be the ones with a count of 0.