I've got a view that lists the times a particular damper is open (in a manufacturing environment). I'm limited to querying this view. Basically, the data looks like this:
dateTime value
9/1/15 0:01 1
9/1/15 0:10 0
9/1/15 1:10 1
9/1/15 2:00 NULL
9/1/15 3:01 0
I need to find every time the damper is open (1) and determine when it closed (0). The nulls we're assuming to be open (same as 1).
What's the best way to find every entry where the value = 1 and then the next 0 after that 1?
It is a Historian DB, but it basically mimics SQL in nearly every way.
Here's the code that wound up working - for posterity. :)
SELECT DateTime, Value,
(SELECT TOP (1) hClose.dateTime FROM history hClose WHERE
hClose.dateTime > history.DateTime
AND hClose.TagName = 'TAGNAMEHERE'
AND hClose.Value < 10
AND wwRetrievalMode = 'average'
AND wwResolution = 600000
ORDER BY dateTime) as closeTime
FROM history
WHERE dateTime BETWEEN '9/1/15 00:00' AND '10/1/15 0:00'
AND tagName = 'TAGNAMEHERE'
AND (VALUE > 10 OR VALUE IS NULL)
AND wwRetrievalMode = 'average'
AND wwResolution = 60000
Related
I have a MySQL table with three columns: takenOn (datetime - primary key), sleepDay (date), and type (int). This table contains my sleep data from when I go to bed to when I get up (at a minute interval).
As an example, if I go to bed on Oct 29th at 11:00pm and get up on Oct 30th at 6:00am, I will have 420 records (7 hours * 60 minutes). takenOn will range from 2016-10-29 23:00:00 to 2016-10-30 06:00:00. sleepDay will be 2016-10-30 for all 420 records. type is the "quality" of my sleep (1=asleep, 2=restless, 3=awake). I'm trying to get how many times I was restless/awake, which can be calculated by counting how many times I see type=2 (or type=3) consecutively.
So far, I have to following query, which works for one day only. Is this the correct/"efficient" way of doing this (as this method requires that I have the data without any "gaps" in takenOn)? Also, how can I expand it to calculate for all possible sleepDays?
SELECT
sleepDay,
SUM(CASE WHEN type = 2 THEN 1 ELSE 0 END) AS TimesRestless,
SUM(CASE WHEN type = 3 THEN 1 ELSE 0 END) AS TimesAwake
FROM
(SELECT s1.sleepDay, s1.type
FROM sleep s1
LEFT JOIN sleep s2
ON s2.takenOn = ADDTIME(s1.takenOn, '00:01:00')
WHERE
(s2.type <> s1.type OR s2.takenOn IS NULL)
AND s1.sleepDay = '2016-10-30'
ORDER BY s1.takenOn) a
I have created an SQL Fiddle - http://sqlfiddle.com/#!9/b33b4/3
Thank you!
Your own solution is quite alright, given the assumptions you are aware of.
I present here an alternative solution, that will deal well with gaps in the series, and can be used for more than one day at a time.
The downside is that it relies more heavily on non-standard MySql features (inline use of variables):
select sleepDay,
sum(type = 2) TimesRestless,
sum(type = 3) TimesAwake
from (
select #lagDay as lagDay,
#lagType as lagType,
#lagDay := sleepDay as sleepDay,
#lagType := type as type
from (select * from sleep order by takenOn) s1,
(select #lagDay := '',
#lagType := '') init
) s2
where lagDay <> sleepDay
or lagType <> type
group by sleepDay
To see how it works it can help to select the second select statement on its own. The inner-most select must have the order by clause to make sure the middle query will process the records in that order, which is important for the variable assignments that happen there.
See your updated SQL fiddle.
I Need to retrieve values from database to plot them in graph. For that I need to get values on criteria basis. Data matching different criteria has to be returned as different rows/ column to my query
(i.e)
I have a table called TABLEA which has a column TIME. I need to get the value based on time critreia as a result, count of rows which are matching TIME>1 and TIME<10 as a result, TIME>11 and TIME <20 as a result and so on. Is it possible to get the values in a single query. I use Mysql with JDBC.
I should plot all the counts in a graph
Thanks in advance.
select sum(case when `time` between 2 and 9 then 1 else 0 end) as count_1,
sum(case when `time` between 12 and 19 then 1 else 0 end) as count_2
from your_table
This can be done with CASE statements, but they can get kind of verbose. You may just want to rely on Boolean (true/false) logic:
SELECT
SUM(TIME BETWEEN 1 AND 10) as `1 to 10`,
SUM(TIME BETWEEN 11 and 20) as `11 to 20`,
SUM(TIME BETWEEN 21 and 30) as `21 to 30`
FROM
TABLEA
The phrase TIME BETWEEN 1 AND 10) will either returnTRUEorFALSEfor each record.TRUEbeing equivalent to1andFALSEbeing equivalent to0`, we then only need sum the results and give our new field a name.
I also made the assumption that you wanted records where 1 <= TIME <= 10 instead of 1 < TIME < 10 which you stated since, as stated, it would drop values where the TIME was 1,10,20, etc. If that was your intended result, then you can just adjust the TIME BETWEEN 1 AND 10 to be TIME BETWEEN 2 AND 9 instead.
I need a help to create a query to return if the period selected already has a meeting in the room.
I have table rep_reuniao with the following design
ID --> int
ROOM --> int
DATE_BEGIN --> DATETIME
DATE_END --> DATETIME
Now an insert of a row in the table rep_reuniao looks like this
ID --> 1
ROOM --> 2
DATE_BEGIN --> 2014-02-21 17:00:00
DATE_END --> 2014-02-21 18:00:00
I will create an Jquery Ajax script to return a true or false JSON response based on the selected datetime.
So the query will have the ROOM, DATE_BEGIN in this format 2014-02-21 17:00 and DATE_END in the same format 2014-02-21 18:00
I Have checked another responses, but the most of them is a query to show the Rooms available. In my desing, a room is always available, except when exist a register in the table.
Example 1
ROOM 1 is already in use from 2014-02-21 17:00:00 to 2014-02-21 18:00:00
Need create a query to return a value (Anything) if the user select a room in:
BEGIN = 2012-02-21 16:30:00
END = 2012-02-21 17:30:00
See? The room in use in the table begins 17:00 so today my query easly return a value if the begin selected by the user is the same. But the End date selected by the user is between the time already in use.
Well, the selected time by the user cannot be in use lookin into the begin date and end date. I have no ideas right now, anyone can help me?
You need to check if one date range overlaps another date range.
New Date Range = |-----------|
Test1 = |=====|
Test2 = |=====|
Test3 = |=====|
Test4 = |=====|
Test5 = |=====|
Test6 = |=================|
Only Test1 and Test5 do not overlap.
Sorry if has any English errors. If someone are able to fix, I'll appreciate.
The correct logic over overlapping timeframes is that two time frames overlap when both these conditions are true:
the first starts before the other ends
the first ends after the other starts
In SQL, these can be expressed easily:
SELECT r.*
FROM rep_reuniao r
WHERE #UserBegin <= r.DATE_END AND
#UserEnd >= r.DATE_BEGIN;
Note that this can return multiple rows, when multiple meetings occur during the specified timeframe.
If you want true/false or 0/1, then use aggregation and case:
SELECT (case when count(*) = 0 then 'false' else 'true' end) as HasOverlappingRooms
FROM rep_reuniao r
WHERE #UserBegin <= r.DATE_END AND
#UserEnd >= r.DATE_BEGIN;
Try this, if the query returns any rows that means the room is already booked.
A simple explanation is the user gives #UserBegin and #UserEnd time that they need the room for.
And if the #UserBegin is between a row's begin and end then it is already booked.
Similarly, if the #UserEnd is between a row's begin and end, even then the room is booked.
SELECT *
FROM rep_reuniao r
WHERE (#UserBegin BETWEEN r.DATE_BEGIN AND r.DATE_END)
OR (#UserEnd BETWEEN r.DATE_BEGIN AND r.DATE_END)
OR (#UserBegin <= r.DATE_BEGIN AND #UserEnd >= r.DATE_END)
From here you can extend this to return a bool like shown
SELECT CASE WHEN EXISTS ( /* The above query */ ) THEN 0 ELSE 1
This should return 1 or 0, when the room is available or not respectively.
You could try the following query. It should catch when a date in the table falls within your range, or when you range falls within a date in the table:
SELECT CASE WHEN COUNT(*) > 1 THEN 1 ELSE 0 END AS AlreadyBooked
FROM rep_reuniao r
WHERE (#UserBegin BETWEEN r.DATE_BEGIN AND r.DATE_END)
OR (r.DATE_BEGIN BETWEEN #UserBegin AND #UserEnd)
The query checks for any rows where either #UserBegin falls within the range in the row or where the row's start date falls withing #userBegin - #UserEnd. It will find cases where the ranges partially overlap, or where either range falls within the other.
I am currently trying to summarise some data tables into a report. Each record in the table consists of a date range, something like this:
StartDate EndDate
--------------------
13/04/13 15/04/13
17/04/13 24/04/13
28/04/13 03/05/13
05/05/13 10/05/13
Assuming the date ranges signify something like days of leave, I want to be able to calculate the total amount of days of leave per month. I came across the DatePart function which seems to work apart from one edge case: when the date range crosses a month boundary. Since the DatePart function returns the month for one given date, I am no longer able to use that to determine the amount of days of leave for that edge case record (in the example above it is record 3), since it applies to two separate months.
Ideally I want my final table to look like:
Month #OfDays
--------------------
4 11 (1st record - 2, 2nd record - 7, 3rd record - 2)
5 8 (3rd record - 3, 4th record - 5)
I've considered some messy options, such as populating a temporary table having each record signifying a different day and then doing a query on that, but I am not sure how this ties in with a report. Right now my report record source is the (incorrect) query, is it possible to have a record source as a VBA function that returns a recordsource?
Another thing I thought was to possibly to have an initial query that splits up any edge cases into two seperate records, where the date range only covers one month, and then use that for my final grouping query. Is that even possible?
I feel there may be a much simpler solution to this problem yet I can't see it.
If anyone has any ideas it would be much appreciated!
To accomplish your task using Access queries you will need to create a table named [Numbers] with a single Number (Long Integer) column named [n] containing the numbers 1, 2, 3, ... up to the highest year you expect to be working with. I created mine as follows
n
----
1
2
3
...
2499
2500
You'll also need to paste the following VBA function into an Access Module
Public Function IsValidDayOfYear(YearValue As Long, DayValue As Long) As Boolean
Dim IsLeapYear As Boolean
If (YearValue Mod 400) = 0 Then
IsLeapYear = True
ElseIf (YearValue Mod 100) = 0 Then
IsLeapYear = False
ElseIf (YearValue Mod 4) = 0 Then
IsLeapYear = True
Else
IsLeapYear = False
End If
IsValidDayOfYear = (DayValue <= IIf(IsLeapYear, 366, 365))
End Function
Let's assume that your source table is called [DateRanges]. We'll start by creating a query that generates every day of the year for each year represented in the source table. The trick here is that DateSerial() "rolls over" month boundaries, so
DateSerial(2013, 1, 32) = #2013-02-01#
and
DateSerial(2013, 1, 234) = #2013-08-22#
SELECT DateSerial(yr.n, 1, dy.n) AS [Date]
FROM Numbers yr, Numbers dy
WHERE
(
yr.n
BETWEEN (SELECT MIN(DatePart("yyyy", DateRanges.StartDate)) FROM DateRanges)
AND (SELECT MAX(DatePart("yyyy", DateRanges.EndDate)) FROM DateRanges)
)
AND (dy.n < 367) AND IsValidDayOfYear(yr.n, dy.n)
For your sample data, that query returns all days in 2013.
Let's save that query as [AllDays]. Now we can use it to extract the individual days for each date range (omitting StartDate so the final counts match yours in the question)
SELECT [Date] FROM AllDays
WHERE EXISTS
(
SELECT * FROM DateRanges
WHERE AllDays.[Date] BETWEEN DateAdd("d", 1, DateRanges.StartDate) AND DateRanges.EndDate
)
That returns the individual days corresponding to each range, i.e.,
Date
----------
2013-04-14
2013-04-15
2013-04-18
2013-04-19
2013-04-20
2013-04-21
2013-04-22
2013-04-23
2013-04-24
2013-04-29
2013-04-30
2013-05-01
2013-05-02
2013-05-03
2013-05-06
2013-05-07
2013-05-08
2013-05-09
2013-05-10
We can save that query as [RangeDays] and then use it to calculate our counts by month...
SELECT DatePart("m", [Date]) AS [Month], COUNT(*) AS NumOfDays
FROM RangeDays
GROUP BY DatePart("m", [Date])
...returning
Month NumOfDays
----- ---------
4 11
5 8
I am creating a view that involves a little bit of fiddling with timestamps.
I have a table A with timestamps. The view will process the timestamps to see if each timestamp is within a certain range (9 AM - 5 PM). If the timestamp is within that range, I will fetch data matching the exact time in another table (B). Otherwise, I will fetch the next day (or this day's) first valid time (which is 9 AM) and the corresponding data from there.
Examples:
A record with timestamp of 12/28/2012 17:01 -> fetch data from B
for 12/29/2012 09:00, set flag to after.
A record with timestamp of 12/28/2012 08:59 -> fetch data from B
for 12/28/2012 09:00, set flag to before.
A record with timestamp of 12/28/2012 09:55 -> fetch data from B
for 12/28/2012 09:55, set flag to null.
Here is what I have so far (not working, some in pseudocode). I mainly don't know how to set the flag based on the comparison and then, based on flag, perform next operation on b - all in one statement.
CREATE VIEW C as
SELECT time, (CASE WHEN (time< '9:00' ) THEN'before'
CASE WHEN(time> '17:00') THEN'after' else null END) AS flag FROM A
//These two should be combined into one create view statement
//The below is utterly wrong, I know, but explains what I mean
SELECT(
CASE WHEN (flag=='before') THEN SELECT * FROM B WHERE B.time = time set hour='9:00'
CASE WHEN(flag=='after') THEN SELECT* FROM B WHERE B.time = time + one day set hour='9:00'
ELSE SELECT* FROM B WHERE B.time = time ) as data
Tested using this fiddle: http://sqlfiddle.com/#!2/15be5/50
SELECT
q.time as original_time_check,
q.flag as flag_check,
case q.flag
when 'before' then q.NINE_AM_ON_THE_DAY
when 'after' then q.NINE_AM_THE_NEXT_DAY
else q.time
end as time
FROM
(
SELECT
time,
date(time) + INTERVAL 9 HOUR as NINE_AM_ON_THE_DAY,
date(time) + INTERVAL '1 9' DAY_HOUR as NINE_AM_THE_NEXT_DAY,
case
when time < (date(time) + INTERVAL 9 HOUR) then 'before'
when time < (date(time) + INTERVAL 17 HOUR) then 'in-range'
else 'after'
end as flag
FROM
Your_table
) q