I want to get the ID numbers for the last 24 hour range. Say I run a task at 4:00AM each morning and want to get the previous 24 hours of data going back to 4:00AM the previous day. I need to get the id codes to search the correct tables. If the data is like this what would be the best way to query the ID numbers?
ID
Start Time
EndTime
2112
2021-08-10 23:25:28.750
NULL
2111
2021-08-06 17:42:27.400
2021-08-10 23:25:28.750
2110
2021-08-03 20:21:14.093
2021-08-06 17:42:27.400
So if I had the date range of 8/10 - 8/11 I would need to get two codes. 2111 and 2112. If I need to get 8/11 - 8/12 I would only get 2112 as the endtime is null.
Any thoughts on the best way to query this out?
You need to do something like that :
DECLARE #employee TABLE(
ID int,
StartTime datetime,
EndTime datetime
)
INSERT INTO #employee SELECT '2112','2021-08-10 23:25:28.750',NULL
INSERT INTO #employee SELECT '2111','2021-08-06 17:42:27.400','2021-08-10 23:25:28.750'
INSERT INTO #employee SELECT '2110','2021-08-03 20:21:14.093','2021-08-06 17:42:27.400'
SELECT ID,* from #employee where
EndTime >= GETDATE()-1 OR EndTime is null
It will takes -1 day from execution time . So if you execute it right now you will heave only null value in output - because now it's 14.08 and this Edtime is null ( still running i think ).
DBFiddleDemo
Related
I have a SQL database that collects temperature and sensor data from the barn.
The table definition is:
CREATE TABLE [dbo].[DataPoints]
(
[timestamp] [datetime] NOT NULL,
[pointname] [nvarchar](50) NOT NULL,
[pointvalue] [float] NOT NULL
)
The sensors report outside temperature (degrees), inside temperature (degrees), and heating (as on/off).
Sensors create a record when the previous reading has changed, so temperatures are generated every few minutes, one record for heat coming ON, one for heat going OFF, and so on.
I'm interested in how many minutes of heat has been used overnight, so a 24-hour period from 6 AM yesterday to 6 AM today would work fine.
This query:
SELECT *
FROM [home_network].[dbo].[DataPoints]
WHERE (pointname = 'Heaters')
AND (timestamp BETWEEN '2022-12-18 06:00:00' AND '2022-12-19 06:00:00')
ORDER BY timestamp
returns this data:
2022-12-19 02:00:20 | Heaters | 1
2022-12-19 02:22:22 | Heaters | 0
2022-12-19 03:43:28 | Heaters | 1
2022-12-19 04:25:31 | Heaters | 0
The end result should be 22 minutes + 42 minutes = 64 minutes of heat, but I can't see how to get this result from a single query. It also just happens that this result set has two complete heat on/off cycles, but that will not always be the case. So, if the first heat record was = 0, that means that at 6 AM, the heat was already on, but the start time won't show in the query. The same idea applies if the last heat record is =1 at, say 05:15, which means 45 minutes have to be added to the total.
Is it possible to get this minutes-of-heat-time result with a single query? Actually, I don't know the right approach, and it doesn't matter if I have to run several queries. If needed, I could use a small app that reads the raw data, and applies logic outside of SQL to arrive at the total. But I'd prefer to be able to do this within SQL.
This isn't a complete answer, but it should help you get started. From the SQL in the post, I'm assuming you're using SQL Server. I've formatted the code to match. Replace #input with your query above if you want to test on your own data. (SELECT * FROM [home_network].[dbo]...)
--generate dummy table with sample output from question
declare #input as table(
[timestamp] [datetime] NOT NULL,
[pointname] [nvarchar](50) NOT NULL,
[pointvalue] [float] NOT NULL
)
insert into #input values
('2022-12-19 02:00:20','Heaters',1),
('2022-12-19 02:22:22','Heaters',0),
('2022-12-19 03:43:28','Heaters',1),
('2022-12-19 04:25:31','Heaters',0);
--Append a row number to the result
WITH A as (
SELECT *,
ROW_NUMBER() OVER(ORDER BY(SELECT 1)) as row_count
from #input)
--Self join the table using the row number as a guide
SELECT sum(datediff(MINUTE,startTimes.timestamp,endTimes.timestamp))
from A as startTimes
LEFT JOIN A as endTimes on startTimes.row_count=endTimes.row_count-1
--Only show periods of time where the heater is turned on at the start
WHERE startTimes.row_count%2=1
Your problem can be divided into 2 steps:
Filter sensor type and date range, while also getting time span of each record by calculating date difference between timestamp of current record and the next one in chronological order.
Filter records with ON status and summarize the duration
(Optional) convert to HH:MM:SS format to display
Here's the my take on the problem with comments of what I do in each step, all combined into 1 single query.
-- Step 3: Convert output to HH:MM:SS, this is just for show and can be reduced
SELECT STUFF(CONVERT(VARCHAR(8), DATEADD(SECOND, total_duration, 0), 108),
1, 2, CAST(FLOOR(total_duration / 3600) AS VARCHAR(5)))
FROM (
-- Step 2: select records with status ON (1) and aggregate total duration in seconds
SELECT sum(duration) as total_duration
FROM (
-- Step 1: Use LEAD to get next adjacent timestamp and calculate date difference (time span) between the current record and the next one in time order
SELECT TOP 100 PERCENT
DATEDIFF(SECOND, timestamp, LEAD(timestamp, 1, '2022-12-19 06:00:00') OVER (ORDER BY timestamp)) as duration,
pointvalue
FROM [dbo].[DataPoints]
-- filtered by sensor name and time range
WHERE pointname = 'Heaters'
AND (timestamp BETWEEN '2022-12-18 06:00:00' AND '2022-12-19 06:00:00')
ORDER BY timestamp ASC
) AS tmp
WHERE tmp.pointvalue = 1
) as tmp2
Note: As the last record does not have next adjacent timestamp, it will be filled with the end time of inspection (In this case it's 6AM of the next day).
I do not really think it would be possible to achieve within single query.
Option 1:
implement stored procedure where you can implement some logic how to calculate these periods.
Option 2:
add new column (duration) and on insert new record calculate difference between NOW and previous timestamp and update duration for previous record
This question might looks simple and repeated, Since I am beginner in SQL, I have stuck up with this problem.
I have created a table variable to store hour range in a 24 hr format. Here is the code
DECLARE #TIMERANGE TABLE ([TIME] NVARCHAR(MAX))
;with hrs (time)
AS
(
SELECT 0
UNION ALL
SELECT time+1
FROM hrs WHERE time<23
)
INSERT INTO #TIMERANGE select
RIGHT ('0000' + CONVERT(VARCHAR, time), 4) + '-' + RIGHT('0000' + CONVERT(VARCHAR, time + 1), 4) AS [TIME]
from hrs
output for this table is:
TIME
0000-0001
0001-0002
0002-0003
0003-0004
0004-0005
0005-0006
0006-0007
0007-0008
0008-0009
0009-0010
0010-0011
0011-0012
0012-0013
0013-0014
0014-0015
0015-0016
0016-0017
0017-0018
0018-0019
0019-0020
0020-0021
0021-0022
0022-0023
0023-0024
Condition is, I want to join this with my real table with a specific condition
Id Date Time Score
1 2008-01-01 00:05 15
2 2008-01-01 00:15 20
3 2008-01-02 10:15 05
4 2008-01-02 11.00 55
I want to find the sum of score in specific time range, Eg, 00.15 will falls in Time range 0000-0001.
Desired output is:
Time Range Score
0000-0001 25
........ ..
........ ..
Please Help
I am hoping I understood the requirements. I see why you did the CTE. I've done that to support graphs so every hour in the day is represented, with or without resulting data.
I re factored the query to produce the following:
declare #tmp TABLE (MyDate DATE, MyTime TIME,Score INT)
INSERT INTO #tmp VALUES('2008-01-01','00:05',15),
('2008-01-01','00:15',20),
('2008-01-02','10:15',05),
('2008-01-02','11:00',55)
SELECT SUM(Score) Score,datepart(hour,GETDATE()) TimeRange FROM #tmp Group By datepart(hour,MyTime)
The result will show the SUM (or average or whatever you need) by Hour. If you still need to graph the result THEN join back into your CTE on the Hour component of the time.
Hope this helps.
I have the following data:
ID Date interval interval_date tot_activity non-activity
22190 2011-09-27 00:00:00 1000 2011-09-27 10:00:00.000 265 15
I have another table with this data:
Date ID Start END sched_non_activity non_activity
10/3/2011 12:00:00 AM HBLV-22267 10/3/2011 2:02:00 PM 10/3/2011 2:11:00 PM 540
Now, in the second table's non_activity field, I would like this to be the value from the first table. However, I need to capture the tot_activity - non_activity where the intervals(in 15 min increments) from the first table, fall in the same time frame as the start and end of the second table.
I have tried setting variables and setting a loop where for each row it verifies the starttime by interval, but I have no idea how to return a variable with only one record, as I keep getting errors that my variable is getting too many results.
I have tried looking everywhere for tutorials and I can't find anything to help me out. Anyone have any pointers or tutorials on looping they could share?
You need to generate the interval end dates somehow; I'm assuming that there is always a record in the first table with a 15 minute interval record. In this case, an example would look like this:
;WITH Intervals
AS
(SELECT
Interval_date
,DATEADD(ms,840997,Interval_date) AS interval_end
,nonactivity
FROM A)
--Select query for Validation
--SELECT
-- b.[Date]
-- ,b.ID
-- ,b.Start
-- ,b.sched_non_activity
-- ,i.nonactivity
--FROM B
--JOIN Intervals AS i
--ON b.Start >= i.Interval_date
--AND b.[END] <= i.interval_end
UPDATE B
SET non_activity = i.nonactivity
FROM B
JOIN Intervals AS i
ON b.Start >= i.Interval_date
AND b.[END] <= i.interval_end
Obviously, you might need to tweak this depending on the exact circumstances.
Let's say there is this table which stores the number of visitors for each day.
When I want to query the table and create a graph from it a problem arises.
The days without activity have no corresponding rows on the table.
For example
Day1 - 7
Day2 - 8
Day4 - 7
And the graph generated would not be correct. Since it needs a 0 for Day3.
Now, without using anything other than SQL is it possible to create those values for the inactivity days?
I thought of creating another table which would create all the dates for the 30days to come each time the scripts gets executed and the problem would have been fixed, but I'm wondering if there is a more practical solution.
Thanks in advance.
Your solution of creating a table with the 30 days is a very simple and practical solution.
You can do it without an extra table if you really want to, but it's not pleasant. SQL is not really designed to allow you to select data that doesn't exist in your database. A much easier solution in general is to add the missing rows client-side rather than trying to write a complex SQL statement to do this.
Using Sql Server 2005+ and CTE Recursive (Using Common Table Expressions) you could try
DECLARE #Table TABLE(
DateVal DATETIME,
Visists INT
)
INSERT INTO #Table SELECT '01 Jan 2010', 10
INSERT INTO #Table SELECT '03 Jan 2010', 1
INSERT INTO #Table SELECT '05 Jan 2010', 30
INSERT INTO #Table SELECT '10 Jan 2010', 50
;WITH MinMax AS (
SELECT MIN(DateVal) Startdate,
MAX(DateVal) EndDate
FROM #Table
),
DateRange AS(
SELECT StartDate DateVal
FROM MinMax
UNION ALL
SELECT DateRange.DateVal + 1
FROM DateRange,
MinMax
WHERE DateRange.DateVal + 1 <= MinMax.EndDate
)
SELECT DateRange.DateVal,
ISNULL(t.Visists,0) TotalVisits
FROM DateRange LEFT JOIN
#Table t ON DateRange.DateVal = t.DateVal
With output as
DateVal TotalVisits
----------------------- -----------
2010-01-01 00:00:00.000 10
2010-01-02 00:00:00.000 0
2010-01-03 00:00:00.000 1
2010-01-04 00:00:00.000 0
2010-01-05 00:00:00.000 30
2010-01-06 00:00:00.000 0
2010-01-07 00:00:00.000 0
2010-01-08 00:00:00.000 0
2010-01-09 00:00:00.000 0
2010-01-10 00:00:00.000 50
I wouldn't call this SQL only, since it uses a PostgreSQL specific function - but there may be something similar in whatever database your using.
PostgreSQL has a nice function: generate_series
You can use this function to create a series of 30 days.
select current_date + s.a as dates from generate_series(0,30) as s(a);
dates
------------
2010-04-22
2010-04-23
2010-04-24
(.. etc ..)
You can then use that in a query, something like:
select vpd.visits, temp.dates
from (select current_date + s.a as dates from generate_series(0,30) as s(a)) as temp
left outer join visits_per_day vpd on vpd.day = temp.dates
visits | dates
--------+------------
10 | 2010-04-22
| 2010-04-23
20 | 2010-04-24
| 2010-04-25
| 2010-04-26
30 | 2010-04-27
No, there is no standard way using only SQL to add an indeterminate number of missing rows into the result of an SQL query without first storing those rows in a table.
Either you can have a single table which contains all the dates over which your application will operate or you can have a table into which you put only the dates that your current query will use. If you choose the second solution, remember to plan for different users executing the same query with different date ranges at the same time — you'll want the table to be temporary and user-specific if your DBMS supports that.
In a MySQL query I am using the timediff/time_to_sec functions to calculate the total minutes between two date-times.
For example:
2010-03-23 10:00:00
-
2010-03-23 08:00:00
= 120 minutes
What I would like to do is exclude any breaks that occur during the selected time range.
For example:
2010-03-23 10:00:00
-
2010-03-23 08:00:00
-
(break 08:55:00 to 09:10:00)
= 105 minutes
Is there a good method to do this without resorting to a long list of nested IF statements?
UPDATE1:
To clarify - I am trying to calculate how long a user takes to accomplish a given task. If they take a coffee break that time period needs to be excluded. The coffee breaks are a at fixed times.
sum all your breaks that occur during the times, and then subtract to the result of the timediff/time_to_sec function
SELECT TIME_TO_SEC(TIMEDIFF('17:00:00', '09:00:00')) -- 28800
SELECT TIME_TO_SEC(TIMEDIFF('12:30:00', '12:00:00')) -- 1800
SELECT TIME_TO_SEC(TIMEDIFF('10:30:00', '10:15:00')) -- 900
-- 26100
Assuming this structure :
CREATE TABLE work_unit (
id INT NOT NULL,
initial_time TIME,
final_time TIME
)
CREATE TABLE break (
id INT NOT NULL,
initial_time TIME,
final_time TIME
)
INSERT work_unit VALUES (1, '09:00:00', '17:00:00')
INSERT break VALUES (1, '10:00:00', '10:15:00')
INSERT break VALUES (2, '12:00:00', '12:30:00')
You can calculate it with next query:
SELECT *, TIME_TO_SEC(TIMEDIFF(final_time, initial_time)) total_time
, (SELECT SUM(
TIME_TO_SEC(TIMEDIFF(b.final_time, b.initial_time)))
FROM break b
WHERE (b.initial_time BETWEEN work_unit.initial_time AND work_unit.final_time) OR (b.final_time BETWEEN work_unit.initial_time AND work_unit.final_time)
) breaks
FROM work_unit