I have a database with a table of records storing timestamps between a series of interim-transactions and a completed transaction.
It's stored in a very odd way in the database, which is causing me problems.
Let's exemplify this as a relay-race. This is how the data's recorded.
RACE TIME RUNNER FINISHTIME
1 2011-09-28 11:27:01.437 1 2011-09-28 17:19:00.843
1 2011-09-28 12:35:33.427 2 2011-09-28 17:19:00.843
1 2011-09-28 12:36:15.270 3 2011-09-28 17:19:00.843
The "Time" indicates when the baton was passed and the last runner had finished.
So the math behind an individual runner's time is:
Time(Runner_n) = Time(Runner_n+1) - Time (Runner_n)
Except for the finishing runner, where there is no n+1 recorded. They get:
Time(Runner_final) = FinishTime - Time(final)
I was going to attempt making a new table and iterating through each race with a cursor-- to try and store tuples of: Race, RunnerID, TimeCompleted.
This doesn't require dynamic SQL at all, just a join. Join to the next record. If it exists, then you use the time from that record. Otherwise, use the finish time for the race:
select t.race, t.runner, t.time as starttime,
coalesce(tnext.time, t.finishtime) as endtime,
DATEDIFF(sec, t.time, coalesce(tnext.time, t.finishtime)) as Seconds
from t left outer join
t tnext
on t.race = tnext.race and
t.runner = tnext.runner - 1
Related
I am a bit rusty with my SQL and am running into a little issue with a query. In our application we have two relative tables to this problem. There are entries, and for each entry there are N steps.
We are trying to optimize our querying, so instead of asking for all entries all the time, we just ask for entries that were updated after we last checked. There can be a lot of steps, so this query is just supposed to return the entries and some step summary data, and we can separately query for steps if needed.
The entry start time and updated time are calculated from the first and most recent process step time respectively. We also have to group together entry statuses.
Here's the query as we build it in python, since it seems easier to read:
statement = 'SELECT e.serial_number, ' + \
'e.description, ' + \
'min(p.start_time) begin_time, ' + \
'group_concat(p.status) status, ' + \
'max(p.last_updated) last_updated, ' + \
'FROM entries e ' + \
'LEFT OUTER JOIN process_steps p ON e.serial_number = p.serial_number ' + \
# if the user provides a "since" date, only return entries updated after
# that date
if since is not None:
statement += ' WHERE last_updated > "{0}"'.format(since)
statement += ' GROUP BY e.serial_number'
The issue we are having is that if we apply that WHERE clause, it filters the process steps too. So for example if we have this situation with two entries:
Entry: 123 foo
Steps:
1. start time 10:00, updated 10:30, status completed
2. start time 11:00, updated 11:30, status completed
3. start time 12:00, updated 12:30, status failed
4. start time 13:00, updated 13:30, status in_progress
Entry: 321 bar
Steps:
1. start time 01:00, updated 01:30, status completed
2. start time 02:00, updated 02:30, status completed
If we query without the where, we would get all entries. So for this case it would return:
321, bar, 01:00, "completed,completed", 02:30
123, foo, 10:00, "completed,completed,failed,in_progress", 13:30
If I had time of 12:15, then it would only return this:
123, foo, 12:00, "failed,in_progress", 13:30
In that result, the start time comes from step 3, and the statuses are only from steps 3 and 4. What I'm looking for is the whole entry:
123, foo, 10:00, "completed,completed,failed,in_progress", 13:30
So basically, I want to filter the final results based on that last_updated value, but it is currently filtering the join results as well, which throws off the begin_time, last_updated and status values since they are calculated with a partial set of steps. Any ideas how to modify the query to get what I want here?
Edit:
It seems like there might be some naming issues here too. The names I used in the example code are equal to or similar to what we actually have in our code. If we change max(p.last_updated) last_updated to max(p.last_updated) max_last_updated, and change the WHERE clause to use max_last_updated as well, we get OperationalError: misuse of aggregate: max() We have also tried adding AS statements in there with no difference.
Create a subquery that selects updated processes first:
SELECT whatever you need FROM entries e
LEFT OUTER JOIN process_steps p ON e.serial_number = p.serial_number
WHERE e.serial_number in (SELECT distinct serial_number from process_steps
WHERE last_updated > "date here")
GROUP BY e.serial_number
You can do this with a having clause:
SELECT . . .
FROM entries e LEFT JOIN
process_steps ps
ON e.serial_number = ps.serial_number
GROUP BY e.serial_number
HAVING MAX(ps.last_updated) > <your value here>;
I am trying to get how long an activity has been "InProgress" based on the history data i have. Each history record contains StartTime and the "Stage" of an activity.
Stages flow like this:
Ready
InProgress
Completed
Also there is a stage named "OnHold" which puts an activity on Hold. While calculating how long an activity has been "InProgress", i need to subtract the amount of time it was "OnHold".
In the given example you will see Activity named "MA50665" went "InProgress" at "2014-07-17 13:08:04.013" and then was put on hold at "2014-07-17 13:12:14.473" which is roughly about 4 minutes. Then it went "InProgress" again at "2014-07-17 13:22:45.503" and was completed at around "2014-07-17 13:33:38.513" which is roughly around 11 minutes. Which means MA50665 was InProgress for about 11+4=15 minutes.
I have the query which is getting me close to what i am looking for. It gives me two records for "MA50665" which i am expecting but the EndTime for both the records comes to "2014-07-17 13:33:38.513" which is incorrect.
For start time "2014-07-17 13:08:04.013", EndTime should have been "2014-07-17 13:12:14.473" because that is when the "InProgress" stage ends. For the second row, StartTime and EndTime are correct.
How do i say in the query that Get the End Time for the stage from the next history row of that activity? I cannot hard code "+1" in the join .
Here is the SQLFiddle for the Table schema and query:http://sqlfiddle.com/#!3/37ef3/4
I think I'm seeing a duplicate row in your example that you say works but has the "+1" in it. Records 5 & 6, seem to be the same but have different end times. Assuming that you are correct here is a fix for the query.
SELECT ROW_NUMBER()OVER(ORDER BY T1.Seqid, T1.Historyid)Rnumber,
T1.Srid,
T1.Activityid,
T1.Seqid,
T1.Currentactstatus,
T1.Previousactstatus,
T1.Timechanged Statusstarttime,
Endtimehist.Timechanged Statusendtime
FROM T1
LEFT JOIN T1 Endtimehist ON T1.Srid = Endtimehist.Srid
AND T1.Activityid = Endtimehist.Activityid
AND T1.Currentactstatus = Endtimehist.Previousactstatus
WHERE T1.SRId = 'SR50660'
AND T1.Currentactstatus = 'InProgress'
AND T1.Previousactstatus = 'Ready'
AND T1.Historyid < Endtimehist.Historyid --This works but i cannot hard code +1 here as history ids may appear in the random incrementing order
ORDER BY T1.SRId DESC, T1.SeqId, T1.HistoryId
I am trying to sequence data and as it occurs there are instances where I have to order this sequence using hour/minutes and seconds. However when I use the rank/partition by function, it's almost as if it does not recognize this as chronological data at all. An example of the data I am trying to sequence is below:
Mod_Order Last_Activity ACTIVITY_DATE_DTTM hdm_modif_dttm
1 NULL 15/08/2007 00:00:00 59:47.3
2 NULL 27/09/2007 14:30:02 59:22.9
3 NULL 27/11/2007 15:30:02 59:10.5
3 NULL 27/11/2007 15:30:02 58:38.9
As you can see the last two ACTIVITY_DATE_DTTM date times are exactly the same so I need to go a step further - I removed the date from the hdm_modif_dttm field to see if it made any difference but it does not (I left it as time though as I figured it does not make any difference anyhow). So my code was as follows:
Update q
set q.Mod_Order = b.Mod_Order
from [#Temp_last_act_2]q
Left join
(
select
RANK () over
(partition by pathway_id
order by pathway_id, ACTIVITY_DATE_DTTM,hdm_modif_dttm) as Mod_Order,
PATHWAY_ID,
MODIF_DTTM,
ACTIVITY_DATE_DTTM
from #temp_Last_act_2
) as b on b.PATHWAY_ID = q.PATHWAY_ID
and b.MODIF_DTTM = q.MODIF_DTTM
and b.ACTIVITY_DATE_DTTM = q.ACTIVITY_DATE_DTTM
Is anyone aware of any limitations using this function that I am unaware of or is there a function that may handle this better (or am I being really daft?)
I have this problem: List of customers with their next scheduled, reoccurring appointment, that is either yearly, monthly, or quarterly.
The tables\columns I have are:
customer
customer_ID
service
customer_ID
service_RecID
Resource
service_RecID
Recurrence_RecID
Date_Time_Start
Recurrence
Recurrence_RecID
RecurType
RecurInterval
DaysOfWeek
AbsDayNbr
SelectInterval
It is modeled such that when the schedule is setup, the date_start_time is the date of when the first reoccurring appointment took place. Ex.
Recurrence_RecID = 10
RecurType = m (could be y, or d as well for yearly or daily)
RecurInterval = 6 (if recurType = y, this would mean every 6 years)
Given that the system generates these nightly, how would I write a query to calculate the next scheduled appointment, for each customer? I originally thought of using the Resource.Date_Time_Start and just cycling through until a variable nextAppointment >= today(), but is it good practice to run loops in SQL?
If anymore info is needed, let me know. Thank you much!
Edit: I will make a sqlfiddle.
I would suggest using a sub-query as opposed to looping. More efficient that way. This may not be exact but something like...
SELECT
*
FROM
(
SELECT
customer.customer_id,
service.service_RecID,
Resource.Date_Time_Start,
Recurrence.Recurrence_RecID,
RecurType,
RecurInterval,
DaysOfWeek,
AbsDayNbr,
SelectInterval,
NextAppointmentDate=
CASE
WHEN RecurType='m' THEN DATEADD(MONTH,RecurInterval,Resource.Date_Time_Start)
WHEN RecurType='y' THEN DATEADD(YEAR,RecurInterval,Resource.Date_Time_Start)
ELSE
NULL
END
FROM
Recurrence
INNER JOIN Resource ON Resource.Recurrence_RecID=Recurrence.Recurrence_RecID
INNER JOIN service ON service.service_RecID=Resource.service_RecID
INNER JOIN customer ON customer.customer_ID=service.customerID
)AS X
WHERE
NextAppointmentDate>=GETDATE()
ORDER BY Fields...
I have to determine stopped time of an vehicle that sends back to server its status data every 30 second and this data is stored in a table of a database.
The fields of a status record consist of (vehicleID, ReceiveDate, ReceiveTime, Speed, Location).
Now what I want to do is, determine each suspension time at the point that vehicle speed came to zero to the status the vehicle move again and so on for next suspension time.
For example on a given day, a given vehicle may have 10 stopped status and I must determine duration of each by a query.
The result can be like this:
id Recvdate Rtime Duration
1 2010-05-01 8:30 45min
1 2110-05-01 12:21 3hour
This is an application of windows functions (called analytic functions in Oracle).
Your goal is to assign a "block number" to each sequence of stops. That is, all stops in a sequence (for a vehicle) will have the same block number, and this will be different from all other sequences of stops.
Here is a way to assign the block number:
Create a speed flag that says 1 when speed > 0 and 0 when speed = 0.
Enumerate all the records where the speed flag = 1. These are "blocks".
Do a self join to put each flag = 0 in a block (this requires grouping and taking the max blocknum).
Summarize by duration or however you want.
The following code is a sketch of what I mean. It won't solve your problem, because you are not clear about how to handle day breaks, what information you want to summarize, and it has an off-by-1 error (in each sequence of stops it includes the previous non-stop, if any).
with vd as
(
select vd.*,
(case when SpeedFlag = 1
then ROW_NUMBER() over (partition by id, SpeedFlag) end) as blocknum
from
(
select vd.*, (case when speed = 0 then 0 else 1 end) as SpeedFlag
from vehicaldata vd
) vd
)
select id, blocknum, COUNT(*) as numrecs, SUM(duration) as duration
from
(
select vd.id, vd.rtime, vd.duration, MAX(vdprev.blocknum) as blocknum
from vd
left outer join vd vdprev
on vd.id = vdprev.id
and vd.rtime > vdprev.rtime
group by vd.id, vd.rtime, vd.duration
) vd
group by id, blocknum