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
Related
i want to get data from table, and example of data like this:
Event Name
Start Date
End Date
Event 1
2022-07-30 00:00:00
2022-08-06 23:59:59
Event 2
2022-08-08 00:00:00
2022-08-15 23:59:59
value of example is "2022-08-07 00:00:00", what i want is get data "Event 1" as the latest event because there is no event starting in "2022-08-07".
and when the value of example "2022-08-08 12:12:12", what i want is get data "Event 2" because there is a event starting from that date. And when the value is "2022-08-09 08:00:00" i want to still get the data "Event 2", because the date is still lower than end date.
How to query in MySQL, so i can result like that?
Assuming proper column names, it would be as below for your initial query (swap out the date for each of your examples):
SELECT *
FROM table
WHERE StartDate <= "2022-08-07 00:00:00"
ORDER BY StartDate desc
LIMIT 1;
Of course if you're basing it of when you run the SQL:
SELECT *
FROM table
WHERE StartDate <= now()
ORDER BY StartDate desc
LIMIT 1;
My approach would be the following nested SELECTs - replace the mentioned timestamp '2022-08-16 12:12:12' with NOW() or any timestamp you need in your case
SELECT * FROM
(
(
SELECT a.*,-1 AS mynumber
FROM `testtest2` AS a
WHERE start<='2022-08-16 12:12:12' AND end >='2022-08-16 12:12:12'
)
UNION
(
SELECT a.*,b.mynumber
FROM `testtest2` AS a,
(SELECT COUNT(*) AS mynumber FROM `testtest2` WHERE start<='2022-08-16 12:12:12' AND end >='2022-08-16 12:12:12') AS b
WHERE end<='2022-08-16 12:12:12' ORDER BY end DESC LIMIT 1
)
) c
WHERE mynumber<=0
Explanation
One SELECT retrieves all events currently active (start <= timestamp AND end >= timestamp) and sets the column mynumber to the fixed value -1 (which clearly is < 0).
The next SELECT retrieves events that have already passed, sorted descending by the column end and only retrieve the first (= newest/last) event. The column mynumber is filled with the numer of events that are currently running - so this column contains either a 0 (if there are no events currently running) or a positive number (> 0) if there are other events running.
These two SELECTs are combined using UNION and used as source for the outer SELECT which only retrieves events with a value <=0 for the column mynumber.
This way you get either the active events OR (in case there are no active events) the last/newest event which has already passed.
I need to make a query that checks if the start and endtime that a user wants to plan something aren't already planned
I currently have this query:
select *
from planned_activities
where
user_id = 161
and
'2022-01-11 17:36:00' between start_time and end_time
or '2022-01-11 18:36:00' between start_time and end_time
or ('2022-01-11 17:36:00' <= start_time and '2022-01-13 18:36:00' >= end_time);
I find it hard to explain but I basicly want to return the data from the other planned activity if the planning isn't possible.
SELECT EXISTS ( SELECT NULL
FROM planned_activities
WHERE #current_user_id = planned_activities.user_id
AND #planned_activity_start < planned_activities.end_time
AND #planned_activity_end > planned_activities.start_time ) row_exists
The query checks does the row for current user which overlaps with entered time range exists. Returns one row with one column row_exists, possible values are 1 (the overlapping is found) or 0 (entered time range is free).
If adjacent time ranges are not allowed too then use weak comparing operators.
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
I have a report that displays a graph. The X axis uses the date from the below query. Where the query returns no date, I am getting gaps and would prefer to return a value. Is there any way to force a date where there are no records?
SELECT
DATE(instime),
CASE
WHEN direction = 1 AND duration > 0 THEN 'Incoming'
WHEN direction = 2 THEN 'Outgoing'
WHEN direction = 1 AND duration = 0 THEN 'Missed'
END AS type,
COUNT(*)
FROM taxticketitem
GROUP BY
DATE(instime),
CASE
WHEN direction = 1 AND duration > 0 THEN 'Incoming'
WHEN direction = 2 THEN 'Outgoing'
WHEN direction = 1 AND duration = 0 THEN 'Missed'
END
ORDER BY DATE(instime)
One possible way is to create a table of dates and LEFT JOIN your table with them. The table could look something like this:
CREATE TABLE `datelist` (
`date` DATE NOT NULL,
PRIMARY KEY (`date`)
);
and filled with all dates between, say Jan-01-2000 through Dec-31-2050 (here is my Date Generator script).
Next, write your query like this:
SELECT datelist.date, COUNT(taxticketitem.id) AS c
FROM datelist
LEFT JOIN taxticketitem ON datelist.date = DATE(taxticketitem.instime)
WHERE datelist.date BETWEEN `2012-01-01` AND `2012-12-31`
GROUP BY datelist.date
ORDER BY datelist.date
LEFT JOIN and counting not null values from right table's ensures that the count is correct (0 if no row exists for a given date).
You would need to have a set of dates to LEFT JOIN your table to it. Unfortunately, MySQL lacks a way to generate it on the fly.
You would need to prepare a table with, say, 100000 consecutive integers from 0 to 99999 (or how long you think your maximum report range would be):
CREATE TABLE series (number INT NOT NULL PRIMARY KEY);
and use it like this:
SELECT DATE(instime) AS r_date, CASE ... END AS type, COUNT(instime)
FROM series s
LEFT JOIN
taxticketitems ti
ON ti.instime >= '2013-01-01' + INTERVAL number DAY
AND ti.instime < '2013-01-01' + INTERVAL number + 1 DAY
WHERE s.number <= DATEDIFF('2013-02-01', '2013-01-01')
GROUP BY
r_date, type
Had to do something similar before.
You need to have a subselect to generate a range of dates. All the dates you want. Easiest with a start date added to a number:-
SELECT DATE_ADD(SomeStartDate, INTERVAL (a.I + b.1 * 10) DAY)
FROM integers a, integers b
Given a table called integers with a single column called i with 10 rows containing 0 to 9 that SQL will give you a range of 100 days starting at SomeStartDate
You can then left join your actual data against that to get the full range.
I have a couple of tables to deal with. One is like this:
dbo.userActivity
userID Action Time
1 25 12:00
1 10 12:01
1 12 12:35
1 6 13:54
2 10 6:47
2 42 6:48
3 8 11:54
etc.
The other table is a schedule that looks like this:
dbo.userSchedule
userID schedule_start schedule_stop
1 07:00 09:00
2 11:00 12:30
3 14:00 15:00
etc.
What I need to do for each row in dbo.userActivity is determine if each action was an hour before the schedule_start, between the schedule_start and schedule_stop times, an hour after schedule_stop, or any other time.
So I need to append a column to dbo.userActivity with the values 'before', 'during', 'after', 'other' based on the time calculations.
I'm really not sure how to do this and would appreciate any help you all could offer.
EDIT:
Okay, so I have that mostly working. I've just seen some of the real data I'll be working on and noticed that the activity times are full datetime stamps, whereas the activity schedule is just in time. So I need to convert the datetime to something I can compare.
This looks like it works:
SELECT *
FROM
(
SELECT CONVERT(TIME, SCANDATE) as scanTime
FROM appData
) st
WHERE st.scanTime <= '6:00'
ORDER BY st.scanTime
Just as an example. But when I try to incorporate that into the case statement below like this, it doesn't work. It applies the same THEN to every row.
SELECT
CASE
WHEN EXISTS
(
SELECT *
FROM
(
SELECT CONVERT(TIME, SCANDATE) as scanTime
FROM appData
) st
WHERE st.scanTime <= '6:00'
)
THEN 'Before 6 am'
ELSE '6 am or after'
END
FROM appData
Any further thoughts on this?
First you would want to add the column (NULL) to your table.
ALTER TABLE dbo.UserActivity
ADD TimeStatus VARCHAR(6) NULL;
Next you would write your update query. It can be written with a CASE statement:
UPDATE ua
SET ua.TimeStatus =
CASE
WHEN CAST(ua.Time AS TIME) >= us.Schedule_Start
AND CAST(ua.Time AS TIME) <= us.Schedule_stop THEN 'During'
WHEN CAST(ua.Time AS TIME) >= DATEADD(HOUR, -1, us.Schedule_Start)
AND CAST(ua.Time AS TIME) <= us.ScheduleStart THEN 'Before'
WHEN CAST(ua.Time AS TIME) >= us.Schedule_Stop
AND CAST(ua.Time AS TIME) <= DATEADD(HOUR, 1, us.Schedule_Stop)
THEN 'After'
ELSE 'Other'
END
FROM dbo.UserActivity AS ua
INNER JOIN dbo.userSchedule AS us ON ua.UserId = us.UserId
Once all the columns have data, you can set the column to NOT NULL if you have updated the application to know about, and populate, this new column.
I would also consider not storing the string values in this table, but instead a smaller value that will foreign key to a reference table instead. With only 4 values, you can use a TINYINT and save space for each dbo.UserActivity record that you store.
If you decide to go this route, you would just take the values from the reference table, and replace the string values with the ID values. If Before = 0, during = 1, After = 2, and Other = 3
UPDATE ua
SET ua.TimeStatusId =
CASE
WHEN CAST(ua.Time AS TIME) >= us.Schedule_Start
AND CAST(ua.Time AS TIME) <= us.Schedule_stop THEN 1
WHEN CAST(ua.Time AS TIME) >= DATEADD(HOUR, -1, us.Schedule_Start)
AND CAST(ua.Time AS TIME) <= us.ScheduleStart THEN 0
WHEN CAST(ua.Time AS TIME) >= us.Schedule_Stop
AND CAST(ua.Time AS TIME) <= DATEADD(HOUR, 1, us.Schedule_Stop)
THEN 2
ELSE 3
END
FROM dbo.UserActivity AS ua
INNER JOIN dbo.userSchedule AS us ON ua.UserId = us.UserId
You would then get the text values for the UI with an INNER JOIN to your TimeStatus reference table