How to SELECT from multiple ranges of time - sql-server

Lets say I have a table in SQLServer named Events. It contains some events with time stamp.
ID TimeStamp EventDescription
1 '2019-04-04 08:20' Machine Error 1
2 '2019-04-04 09:01' Machine Error 2
3 '2019-04-05 09:23' Machine Error 3
4 '2019-04-05 12:23' Machine Error 4
5 '2019-04-06 11:33' Machine Error 5
6 '2019-04-06 18:07' Machine Error 6
7 '2019-04-07 12:23' Machine Error 7
In addition I have second table named Ranges. It contains ranges of time.
ID From To
1 '2019-04-04 08:00' '2019-04-04 09:00'
2 '2019-04-05 10:30' '2019-04-05 16:00'
3 '2019-04-06 10:00' '2019-04-06 12:00'
I need to SELECT events from table Events where TimeStamp IS between ranges of time in table Ranges.
The result:
ID TimeStamp EventDescription
1 '2019-04-04 08:20' Machine Error 1
4 '2019-04-05 12:23' Machine Error 4
5 '2019-04-06 11:33' Machine Error 5
I have no idea what to do.
Do I have to use dynamic SQL to build this query?

Correlated subquery can be used here.
Select * from Events E
where exists (select 1 from Ranges where e.TimeStamp between [From] and [To])

I created your data as temp tables:
SELECT 1 ID, CAST('2019-04-04 08:20' AS DATETIME) TimeStamp, 'Machine Error 1' EventDescription
INTO #Events
UNION
SELECT 2 ID, CAST('2019-04-04 09:01' AS DATETIME) TimeStamp, 'Machine Error 2' EventDescription
UNION
SELECT 3 ID, CAST('2019-04-05 09:23' AS DATETIME) TimeStamp, 'Machine Error 2' EventDescription
UNION
SELECT 4 ID, CAST('2019-04-05 12:23' AS DATETIME) TimeStamp, 'Machine Error 2' EventDescription
UNION
SELECT 5 ID, CAST('2019-04-06 11:33' AS DATETIME) TimeStamp, 'Machine Error 2' EventDescription
UNION
SELECT 6 ID, CAST('2019-04-06 18:07' AS DATETIME) TimeStamp, 'Machine Error 2' EventDescription
UNION
SELECT 7 ID, CAST('2019-04-07 12:34' AS DATETIME) TimeStamp, 'Machine Error 2' EventDescription
SELECT 1 ID, CAST('2019-04-04 08:00' AS DATETIME) [From], CAST('2019-04-04 09:00' AS DATETIME) [To]
INTO #Ranges
UNION
SELECT 2 ID, CAST('2019-04-05 10:30' AS DATETIME) [From], CAST('2019-04-05 16:00' AS DATETIME) [To]
UNION
SELECT 3 ID, CAST('2019-04-06 10:00' AS DATETIME) [From], CAST('2019-04-06 12:00' AS DATETIME) [To]
And then it's as simple as joining them together:
SELECT E.*
FROM #Ranges R
JOIN #Events E ON E.TimeStamp BETWEEN R.[From] AND R.[To]

Related

Grouping based on Lag/Lead

In SQL Server 2014, I have the following table that tracks user activity:
USER_ID
EVENT
EVENT_DATE
15552221111
LOGIN
2022-06-01
15552221111
COMPLETE
2022-06-08
15552221111
LOGIN
2022-09-01
15552221111
SHUTDOWN
2022-09-11
15552222222
LOGIN
2022-04-01
15552222222
PROCESSING
2022-04-08
15552222222
PROCESSING
2022-06-10
15552222222
COMPLETE
2022-06-11
15552222222
LOGIN
2022-09-08
I need to create some sort of sequencing value, so that all records that have an event less than 60 days of each other shares the same number. Desired result:
USER_ID
EVENT
EVENT_DATE
SEQ
15552221111
LOGIN
2022-06-01
1
15552221111
COMPLETE
2022-06-08
1
15552221111
LOGIN
2022-09-01
2
15552221111
SHUTDOWN
2022-09-11
2
15552222222
LOGIN
2022-04-01
1
15552222222
PROCESSING
2022-04-08
1
15552222222
PROCESSING
2022-06-10
2
15552222222
COMPLETE
2022-06-11
2
15552222222
LOGIN
2022-09-08
3
Totally stuck, any ideas?
Here's some test code:
WITH testTable (USERID, EVENT, EVENT_DATE) AS
(
SELECT 15552221111, 'LOGIN', '2022-06-01' UNION ALL
SELECT 15552221111, 'COMPLETE', '2022-06-01' UNION ALL
SELECT 15552221111, 'LOGIN', '2022-09-01' UNION ALL
SELECT 15552221111, 'SHUTDOWN', '2022-09-11' UNION ALL
SELECT 15552222222, 'LOGIN', '2022-04-01' UNION ALL
SELECT 15552222222, 'PROCESSING', '2022-04-08 ' UNION ALL
SELECT 15552222222, 'PROCESSING', '2022-06-10' UNION ALL
SELECT 15552222222, 'COMPLETE', '2022-06-11' UNION ALL
SELECT 15552222222, 'LOGIN', '2022-09-08'
)
SELECT
USERID
, EVENT
, EVENT_DATE
, LEAD (EVENT_DATE, 1, 0) OVER (PARTITION BY USERID ORDER BY EVENT_DATE) NEXT_DATE
, ROW_NUMBER() OVER (PARTITION BY USERID ORDER BY EVENT_DATE) RECORD_SEQ
FROM testTable
I would use LAG() to determine the row when EVENT_DATE is more than 60 days when compare to previous row. And then perform a cumulative SUM() OVER (..) to get the SEQ that you want
CTE AS
(
SELECT
USERID
, EVENT
, EVENT_DATE
, CASE WHEN DATEDIFF(DAY
, LAG (EVENT_DATE, 1) OVER (PARTITION BY USERID ORDER BY EVENT_DATE)
, EVENT_DATE) > 60
THEN 1
ELSE 0
END AS S
FROM testTable
)
SELECT *, SUM(S) OVER (PARTITION BY USERID ORDER BY EVENT_DATE) + 1 AS SEQ
FROM CTE

Date Comparison of Two Tables in SQL SERVER

I had this Data,
Table One :
EmpID Date Absent
1 01/01/2018 1
1 01/02/2018 1
1 02/05/2018 1
1 03/25/2018 1
1 04/01/2018 0
1 05/02/2018 1
1 06/03/2018 1
Table Two
ID Amount DateEffective
1 5.00 02/06/2018
2 3.00 05/02/2018
3 10.00 06/03/2018
Desired Output
EmpID Month Year Absent Penalty
1 January 2018 2 5.00
1 February 2018 1 5.00
1 March 2018 1 3.00
1 April 2018 0 3.00
1 May 2018 1 13.00
1 June 2018 1 10.00
This is my Code
SELECT { fn MONTHNAME(one.Date) } AS MonthName, YEAR(one.Date) AS Year, SUM(one.Absent) AS Absent,
(
SELECT top 1 two.DailyRate
FROM table_two as two
WHERE EmpID = '1'
AND one.Date <= two.EffectivityDate
)
FROM table_one as one
WHERE EmpID = '1'
GROUP BY { fn MONTHNAME(one.Date) }, MONTH(one.Date), YEAR(one.DTRDate)
ORDER BY Year(one.Date),month(one.Date)
and it shows an error :
Column 'one.Date' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause
please help for this issue...
Thanks
Try this :
SELECT
one.EmpID
,DATENAME(MONTH,one.Date) AS [MonthName]
,YEAR(one.Date) AS [Year]
,SUM(one.Absent) AS [Absent]
,(SELECT top 1 two.Amount
FROM table_two as two
WHERE two.ID = one.EmpID
AND YEAR(two.DateEffective) >= YEAR(one.Date)
AND MONTH(two.DateEffective) >=MONTH(one.Date)
) AS [Penalty]
FROM table_one as one
WHERE
one.EmpID = '1'
GROUP BY one.EmpID,DATENAME(MONTH,one.Date), MONTH(one.Date), YEAR(one.Date)
ORDER BY Year(one.Date),month(one.Date)
From my understanding to do this,
select e.EmpID
,datename(month,e.Date)[month]
,year(e.Date) [year]
,sum(e.Absent) as [Abscount]
,a.Amount
from
empl e left join abs a
on datename(month,e.Date)=DATENAME(month,a.DateEffective)
group by e.EmpID,DATENAME(MONTH,e.Date), MONTH(e.Date), YEAR(e.Date) , a.Amount
order by Abscount desc
Revert me if any clarifications needed...
is this helpful.?
Create Table #TabOne(EmpID int,[Date] Date,[Absent] Bit)
Create Table #TabTwo(ID int,Amount float,DateEffective Date)
Insert into #TabOne
SELECT 1,'01/01/2018',1 Union All
SELECT 1,'01/02/2018',1 Union All
SELECT 1,'02/05/2018',1 Union All
SELECT 1,'03/25/2018',1 Union All
SELECT 1,'04/01/2018',0 Union All
SELECT 1,'05/02/2018',1 Union All
SELECT 1,'06/03/2018',1
Insert into #TabTwo
Select 1,5.00 ,'02/06/2018' Union All
Select 2,3.00 ,'05/02/2018' Union All
Select 3,10.00,'06/03/2018'
;with cte1
As
(
Select One.EmpID,MONTH(one.[Date]) As [mon],YEAR(one.[Date]) As [Year],two.Amount,one.[Absent],
ROW_NUMBER() OVER(partition by One.EmpID,One.[Date] order by DATEDIFF(dd,two.DateEffective,one.[Date]) desc) as rn
from #TabOne one
LEFT JOIN #TabTwo two on one.[Date]<=two.DateEffective
)
Select EmpID,DATENAME(month, DATEADD(month, [mon]-1, CAST('2008-01-01' AS datetime))) As [Month],
[Year],SUM(CASE WHEN [Absent]=0 then 0 ELSE 1 END) As [Absent] ,MAX(Amount) As Penalty
from cte1
where rn=1
Group by EmpID,[Year],[mon]
order by EmpID,[Year],[mon]
Drop Table #TabOne
Drop Table #TabTwo

Use Dynamic Syntax in a View or a Function

I have a dynamic query that I've written that looks like the following:
DECLARE #sql nvarchar(max)
SELECT #sql =
'select distinct datetable.Date
from (
select cast(DATEADD(day,-(a.a + (10 * b.a) + (100 * c.a) + (1000 * d.a)),getdate()) as date) AS Date
from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as d
union all
select cast(DATEADD(day,(a.a + (10 * b.a) + (100 * c.a) + (1000 * d.a)),getdate()) as date) AS Date
from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as d
) datetable
where '
+
replace(replace(replace(stuff((SELECT ' or datetable.Date between cast(''' + cast(cast(hld1.StrDate as date) as nvarchar(12)) + N''' as date) and cast(''' + cast(cast(hld1.endDate as date) as nvarchar(12))+ N''' as date)
'
from hld1 for xml path('')),1,3,''), '<', '<'), '>', '>'), '
', char(13)) +
'order by datetable.Date '
--print #sql
EXEC sys.sp_executeSQL #SQL
HLD1 is a list of holidays, where each holiday has a start and end date. The query itself returns a list of dates that are defined as holidays. (The reason that I can't just select the start dates and union them to the end dates is that there could very feasibly be a holiday with three days, and the middle day wouldn't show up in either list.
However, I'm using this monstrosity to create a function, and, as part of the function, I want to be able to do something like "If the date is in this list, then do the following."
My original plan was to set up a view that would just be the list of dates; however, this is not possible, because it uses a variable, and variables aren't allowed in views.
My next thought was to create a function that would just return the list. However, when I put in the syntax to create it as a function, I get the error The last statement included within a function must be a return statement.
I am unsure what path I should pursue from here. The reason that I can't just make a table and list out the dates manually is that currently the list only extends through 2016. In addition, the holiday list (start and end dates) may be created/added differently for different databases that the end goal function would be added to and used on.
If you need more background/information, please let me know and I'd be happy to provide. I'm just learning as I go. :)
Edit 1: I found the following link, but it doesn't appear to apply in this case: Create A View With Dynamic Sql
Why not just create a numbers or tally table as a persistent table or a view and avoid all this nastiness. 99% of this query is just generating a bunch of numbers.
For example, here is a view that performs 0 reads and will generate 10,000 rows of sequential numbers nearly instantly.
create View [dbo].[cteTally] as
WITH
E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select N from cteTally
GO
There is your numbers portion. The next part would be to create the persistent dates table with the holidays and such like you are doing.
Here is an awesome article from my buddy Dwain Camps (RIP) about creating a calendar table. http://www.sqlservercentral.com/blogs/dwainsql/2014/03/30/calendar-tables-in-t-sql/
--EDIT--
Here is an example of having a table (#Something) with start and end dates for a holiday. This will list each date between those two dates. Unless I am missing something this should be pretty much what you are trying to do.
create table #Something
(
HolidayName varchar(10)
, StartDate date
, EndDate date
)
insert #Something
select 'phroureo', '2016-03-01', '2016-03-05' union all
select 'Sean', '2016-07-04', '2016-07-05'
select HolidayName
, StartDate
, EndDate
, DATEADD(day, t.N - 1, StartDate) as ResultDate
from #Something s
join cteTally t on t.N <= DATEDIFF(day, StartDate, EndDate) + 1
order by HolidayName
drop table #Something

How to select only data with consecutive row number starting from 1

I have a table similar to the one below.
What I want to do is to select the rows with consecutive RowNo with the same job name must be selected if it begins with RowNo = 1. Here is the sample output:
Hope you can help. Thank you.
Try this
DECLARE #Tbl TABLE (RowNo INT, Jobname NVARCHAR(50), AuditDate DATETIME)
INSERT INTO #Tbl
SELECT 3, 'Backup Database Sales', '2016.07.26' UNION ALL
SELECT 1, 'Send Autoemail Sales Report', '2016.07.26' UNION ALL
SELECT 2, 'Send Autoemail Sales Report', '2016.07.25' UNION ALL
SELECT 3, 'Send Autoemail Sales Report', '2016.07.24' UNION ALL
SELECT 4, 'Update Sales Stats', '2016.07.23' UNION ALL
SELECT 4, 'Update Sales Stats', '2016.07.22' UNION ALL
SELECT 1, 'Generate new item codes', '2016.07.26' UNION ALL
SELECT 2, 'Generate new item codes', '2016.07.25'
;WITH CTE
AS
(
SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS Id FROM #Tbl
)
SELECT
*
FROM
#Tbl T
WHERE
EXISTS
(
SELECT TOP 1
1
FROM
(
SELECT
C.Jobname,
MIN(C.RowNo) MinRowNo,
MAX(C.RowNo) MaxRow
FROM
CTE C
GROUP BY
C.Jobname,
C.Id - C.RowNo
) A
WHERE
A.MinRowNo <> A.MaxRow AND
A.MinRowNo = 1 AND
A.Jobname = T.Jobname AND
T.RowNo BETWEEN A.MinRowNo AND A.MaxRow
)
Output
RowNo Jobname AuditDate
----------- -------------------------------------------------- -----------------------
1 Send Autoemail Sales Report 2016-07-26 00:00:00.000
2 Send Autoemail Sales Report 2016-07-25 00:00:00.000
3 Send Autoemail Sales Report 2016-07-24 00:00:00.000
1 Generate new item codes 2016-07-26 00:00:00.000
2 Generate new item codes 2016-07-25 00:00:00.000
SELECT T1.*
FROM
YourTable T1
INNER Join
YourTable T2
ON T1.RowNo = 1 AND T2.RowNo =1 AND T1.JobName=T2.Jobname
OR T1.RowNo > 1 AND T1.RowNo - 1 = T2.RowNo AND T1.JobName=T2.Jobname

Replacing the values in sql table

OrderID, OrderTime, OrderItem
1, 1 am, Orange
1, 2 am, Apple
2, 3 am, Grape
3, 2 am, Apple
3, 3 am, Coconut
1, 5 am, Banana
1, 6 am, Apple
The above is the original table. The below is the table I want. if the order id is the same for continuously, I wwant to change the order time to the minimum time.
OrderID, OrderTime, OrderItem
1, 1 am, Orange
1, 1 am, Apple
2, 3 am, Grape
3, 2 am, Apple
3, 2 am, Coconut
1, 5 am, Banana
1, 5 am, Apple
There is no "natural" order to your data in SQL Server if you don't specify an order then the order you get back is in no way guaranteed. You need a column that you can use to define an order by. Once you have this you can do something like
declare #data table
(
OrderItemId int identity(1,1) primary key,
OrderID int,
OrderTime datetime,
OrderItem varchar(10))
insert into #data
SELECT 1,'00:01:00','Orange' UNION ALL
SELECT 1,'00:01:00','Apple' UNION ALL
SELECT 2,'00:03:00','Grape' UNION ALL
SELECT 3,'00:02:00','Apple' UNION ALL
SELECT 3,'00:02:00','Coconut' UNION ALL
SELECT 1,'00:05:00','Banana' UNION ALL
SELECT 1,'00:05:00','Apple';
WITH cte1 AS
(
SELECT *, ROW_NUMBER() over (ORDER BY OrderItemId)-
ROW_NUMBER() over (PARTITION BY OrderID ORDER BY OrderItemId) AS Grp
FROM #data
), cte2 AS
(
SELECT *, MIN(OrderTime) OVER (PARTITION BY OrderID, Grp) AS MinOrderTime
FROM cte1
)
UPDATE cte2 SET OrderTime = MinOrderTime
SELECT OrderID,convert(varchar, OrderTime, 8) OrderItemId, OrderItem
FROM #data
ORDER BY OrderItemId

Resources