Track state of time series event in SQL column - sql

This seems really simple but I can't figure it out, working in SQL Server. I have time series data and I want a column to track the state of ON/OFF events for each row, so when there's an ON event then the Desired Output column will have a 1 for each subsequent event until there is an OFF event. Please see example below. Thanks so much!
TimeStamp
Event
Desired Output
1:01
ON
1
1:02
OFF
0
1:04
other
0
1:05
other
0
1:06
ON
1
1:10
other
1
1:12
other
1
1:14
OFF
0
1:15
other
0

You can compare the cumulative count of ONs and OFFs:
select t.*,
(case when sum(case when event = 'ON' then 1 else 0 end) over (order by timestamp) >
sum(case when event = 'OFF' then 1 else 0 end)
then 1 else 0
end) as desired
from t;

WITH
flagged AS
(
SELECT
timestamp,
CASE WHEN event = 'ON' THEN 1
WHEN event = 'OFF' THEN 0 END AS state
FROM
#table
),
cumulative AS
(
SELECT
*,
COUNT(state) OVER (ORDER BY timestamp) AS state_group
FROM
flagged
)
SELECT
timestamp,
MAX(state) OVER (PARTITION BY state_group) AS persisted_state
FROM
cumulative

I realized there could actually be two ON events in a row, or sometimes entries can be missing. So to track the state of this event I think using a WHILE loop may be the best option.
WHILE #CurrentRow <= #TotalRows
BEGIN
SELECT #DesiredOutput = CASE WHEN Event = 'ON' THEN 1 ELSE CASE WHEN Event = 'OFF' THEN 0 ELSE #DesiredOutput END END
FROM #Table
WHERE #CurrentRow = Row_Number
UPDATE #Table
SET DesiredOutput = #DesiredOutput
WHERE #CurrentRow = Row_Number
SET #CurrentRow += 1
END

Related

Return the latest date based from a range of columns

I have a query that counts the number of completed tasks as well as returning the original values.
I'd like to add a new column which returns the most recent date (in this case task_1_completed_date or task_2_completed_date but in reality there are 20 task fields)
(CASE WHEN task_1_completed_date IS NOT NULL THEN 1 ELSE 0 END +
CASE WHEN task_2_completed_date IS NOT NULL THEN 1 ELSE 0 END
) AS task_completed_total
from (select JSON_EXTRACT_SCALAR(data, '$.task1.date') as task_1_completed_date
JSON_EXTRACT_SCALAR(data, '$.task2.date') as task_2_completed_date
from table
WHERE pet_store = 'london'
)
Not sure how to proceed, should I use a subquery here to order the task completion dates?
Use order by
(CASE WHEN task_1_completed_date IS NOT
NULL THEN 1 ELSE 0 END +
CASE WHEN task_2_completed_date IS NOT
NULL THEN 1 ELSE 0 END
) AS task_completed_total
from (select JSON_EXTRACT_SCALAR(data,
'$.task1.date') as task_1_completed_date
JSON_EXTRACT_SCALAR(data,
'$.task2.date') as task_2_completed_date
from table
WHERE pet_store = 'london'
)where rownum=1 order by
task_completed_total desc
-- if rownum doesn't work use Limit 1
I think you could use GROUP BY and MAX to get the most recent date. see here: https://learn.microsoft.com/en-us/sql/t-sql/queries/select-group-by-transact-sql?view=sql-server-ver15

How to calculate start and end time an event in sql?

I have a table as below:
I want to calculate data in below format:
AreaID, Power_ON_Date, Power_OFF_Date, Diff_In_Minutes
Also, I need to handle:
Successive entries of same event. In case of successive entries of same event with different times, need to consider only the first occurrence of the event and ignore the others.
Merge two rows of successive OFF and ON event into 1 row to get the desired result.
You can do aggregation :
select areaid,
max(case when powerstatus = 'power on' then eventdatetime end) as Power_ON_Date,
min(eventdatetime) as Power_OFF_Date,
datediff(minute, min(eventdatetime), max(case when powerstatus = 'power on' then eventdatetime end)
) as diff_minute
from (select t.*,
sum(case when powerstatus = 'power off' then 1 else 0 end) over (partition by areaid order by eventdatetime) as grp
from table t
) t
group by areaid, grp;
Note : date_diff() is for SQL Server, however you didn't any specific database. So, the function definition may different

How to create a calculated field with a where clause in the calculation

The image shows the table from this code: select * from PunchClock
where punchmonth = 1 and PunchDay = 1 and PunchYear = 2018
I am trying to calculate the number of hours worked per day in a database. Our table for this has 2 columns that pertain to this. InOut is a column that has either 1 or 0 (1 = punch in, 0 = punch out), and then there is the punchdatetime. How could I use these two fields to calculate how many hours are worked per day.
I have tried to subtract the punch time in from the punch time out but that won't work.
select PunchMonth, PunchDay, PunchYear,
((PunchDateTime where InOut = 0) - (punchdatetime where InOut = 1))
from PunchClock
Error Message: Incorrect syntax near the keyword 'where'.
could be you need case (not where)
select PunchMonth
, PunchDay
, PunchYear
, case INOut = 0 then PunchDateTime else -PunchDateTime end
from PunchClock
I think you want a case expression. Also, because a given row has either an InOut value of 0 or 1 but not both, I think you need aggregation.
So, I'm guessing:
select PunchMonth, PunchDay, PunchYear,
datediff(second, min(case when InOut = 0 then punchdatetime end),
max(case when InOut = 1 then punchdatetime end)
) as seconds_diff
from PunchClock
group by PunchMonth, PunchDay, PunchYear;

SQL - Group data with same ID and Date that has been to every Machine but has a different Name

I am trying to create a query that will group data by CT ID and Date that have all 3 MachineID's (1, 10, and 20) and at least one different Sawing Pattern Name.
This Image shows a highlighted example of the data I'm trying to get back and the code i'm currently using
I'm trying to only show data similar to the highlighted rows in the image (CT ID 501573833) and exclude the data in the rows around it where the Sawing Pattern Name is the same at all 3 MachineID's.
Your description suggests group by and having. The conditions you describe can all go in the having clause:
select ct_id, date
from t
group by ct_id, date
having sum(case when machineid = 1 then 1 else 0 end) > 0 and
sum(case when machineid = 10 then 1 else 0 end) > 0 and
sum(case when machineid = 20 then 1 else 0 end) > 0 and
min(sawing_pattern_name) <> max(sawing_pattern_name)
Seems to me that an EXISTS could be useful here.
SELECT
[CT ID],
[MachineID],
[Sawing Pattern name],
[Time],
CAST([Time] AS DATE) AS [Date]
FROM [DataCollector].[dbo].[Maxicut] t
WHERE EXISTS
(
SELECT 1
FROM [DataCollector].[dbo].[Maxicut] d
WHERE d.[CT ID] = t.[CT ID]
AND CAST(d.[Time] AS DATE) = CAST(t.[Time] AS DATE)
AND d.[MachineID] != t.[MachineID]
AND REPLACE(d.[Sawing Pattern name],',','') != REPLACE(t.[Sawing Pattern name],',','')
);

Returning rows when flag field occurs on same Day

I have a flag field that returns either '0' or '1'. I want to return rows when both a '0' and a '1' occur on the same day. My sytax is off but you get what I mean.
select employee, date, flag, account,
from table1
where flag = 0 and 1 for date
You can aggregate by date and count the number of occurrences of each value:
select employee, date, account,
from table1
group by date, employee, account
having sum(case when flag = 0 then 1 else 0 end) > 0 and
sum(case when flag = 1 then 1 else 0 end) > 0;
In your case, assuming the flag is a number that only takes on the values of 0 and 1, you could simplify it to one of the following:
having count(distinct flag) = 2;
having min(flag) <> max(flag);
having sum(flag) > 0 and sum(1 - flag) > 0;