Count changes from specific value within time period - sql

MS Access query question.
In its simplified version, I have a table containing a (support) ticketid column, status column and a timestamp column. The timestamp column indicates when the (new) status was set for the ticket.
My final goal is to count the number of tickets that changed status FROM status 18 within a certain time frame. This includes tickets that got status 18 within the time frame and 'lost' it again, but also tickets that already had status 18 at the start of the time frame.
My intermediate query below obviously is not correct, as it returns too many records. It does not just compare to the next record (in time), but any record.
SELECT *
FROM
(SELECT tkhtimestamp, tkhticketid, tkhstatusid FROM tickethistory WHERE tkhstatusid NOT IN (18) AND tkhtimestamp >= #3-jul-2014 9:00:00# AND tkhtimestamp <= #4-jul-2014 9:00:00#) AS new_status_records
INNER JOIN
(SELECT tkhtimestamp, tkhticketid, tkhstatusid FROM tickethistory WHERE tkhstatusid IN (18) AND tkhtimestamp <= #4-jul-2014 9:00:00#) AS old_status_records
ON old_status_records.tkhticketid = new_status_records.tkhticketid AND old_status_records.tkhtimestamp < new_status_records.tkhtimestamp
Can someone please advise on this, as I cannot seem to get this done. Other efforts using max/min and group by failed as well.
Thank you.

You had the right idea in using MAX/MIN and GROUP BY. For test data in [tickethistory]
tkhtimestamp tkhticketid tkhstatusid
------------------- ----------- -----------
2014-07-02 10:00:00 1 10
2014-07-02 12:00:00 2 18
2014-07-03 13:30:00 1 18
2014-07-03 13:35:00 1 99
2014-07-03 15:00:00 2 99
the following query will extract the current and previous timestamp for each new history record of each ticket:
SELECT
curr.tkhticketid,
curr.tkhtimestamp AS curr_timestamp,
MAX(prev.tkhtimestamp) AS prev_timestamp
FROM
tickethistory curr
INNER JOIN
tickethistory prev
ON curr.tkhticketid = prev.tkhticketid
AND curr.tkhtimestamp > prev.tkhtimestamp
GROUP BY
curr.tkhticketid,
curr.tkhtimestamp
It returns
tkhticketid curr_timestamp prev_timestamp
----------- ------------------- -------------------
1 2014-07-03 13:30:00 2014-07-02 10:00:00
1 2014-07-03 13:35:00 2014-07-03 13:30:00
2 2014-07-03 15:00:00 2014-07-02 12:00:00
We can then join that back to the [tickethistory] table (twice) to retrieve the current and previous status values
SELECT
tkh_curr.tkhticketid,
tkh_curr.tkhtimestamp AS curr_timestamp,
tkh_curr.tkhstatusid AS curr_status,
tkh_prev.tkhtimestamp AS prev_timestamp,
tkh_prev.tkhstatusid AS prev_status
FROM
(
(
SELECT
curr.tkhticketid,
curr.tkhtimestamp AS curr_timestamp,
MAX(prev.tkhtimestamp) AS prev_timestamp
FROM
tickethistory curr
INNER JOIN
tickethistory prev
ON curr.tkhticketid = prev.tkhticketid
AND curr.tkhtimestamp > prev.tkhtimestamp
GROUP BY
curr.tkhticketid,
curr.tkhtimestamp
) curr_prev
INNER JOIN
tickethistory tkh_curr
ON tkh_curr.tkhticketid = curr_prev.tkhticketid
AND tkh_curr.tkhtimestamp = curr_prev.curr_timestamp
)
INNER JOIN
tickethistory tkh_prev
ON tkh_prev.tkhticketid = curr_prev.tkhticketid
AND tkh_prev.tkhtimestamp = curr_prev.prev_timestamp
returning
tkhticketid curr_timestamp curr_status prev_timestamp prev_status
----------- ------------------- ----------- ------------------- -----------
1 2014-07-03 13:30:00 18 2014-07-02 10:00:00 10
1 2014-07-03 13:35:00 99 2014-07-03 13:30:00 18
2 2014-07-03 15:00:00 99 2014-07-02 12:00:00 18
If you save that query in Access with a name like [qryTicketHistoryChanges] then you should be able to run other queries against it to pull out the specific information you need.

Related

SQL query to add missing values per id/date

I have two tables, a table with id, date, value and a table with all the dates of interest. I'd like to do a SQL query such that I get a new table exactly the same as my first table but not with NULL values per ID when a date is not present for a given ID.
Table 1.
id
date
value
1
2021-01-01
10
1
2021-02-01
8
1
2021-04-01
20
2
2021-02-01
5
2
2021-04-01
6
Table 2.
date
2020-12-01
2021-01-01
2021-02-01
2021-03-01
2021-04-01
2021-05-01
After I "merge" the two tables the result would be:
id
date
value
1
2020-12-01
NULL
1
2021-01-01
10
1
2021-02-01
8
1
2020-03-01
NULL
1
2021-04-01
20
1
2021-05-01
NULL
2
2020-12-01
NULL
2
2021-01-01
NULL
2
2021-02-01
5
2
2021-03-01
NULL
2
2021-04-01
6
2
2021-05-01
NULL
Which SQL query do I need to run to get such result?
SELECT
u.id,
d.date,
t.value
FROM
(
SELECT DISTINCT id FROM table1
)
u
CROSS JOIN
table2 d
LEFT JOIN
table1 t
ON t.id = u.id
AND t.date = d.date
Though, I'd refrain from using date and other potential keywords as column names.

Problems with complex query

There are two tables.
In the first I have columns:
id - a person
time - the time of receiving the bonus (timestamp)
money - size of bonus
And the second:
id
time - time of getting a rank (timestamp)
range - military rank (int)
The task is to withdraw the amount and number of bonuses received by people in the rank of captain (range = 7) with aggregation by day.
I have no ideas how to do a table with this data. I can summarize data by all days such as
SELECT DISTINCTROW Payment.user_id AS user_id, Sum(IIf(IsNull(Payment.money),0,Payment.money)) AS [Sum - money], Count(Payment.money) AS [Count - Payment], Format(Payment.time, "Short Date") as day
FROM Payment
GROUP BY Payment.user_id, Format (Payment.time, "Short Date")
Having ((Count(Payment.money) > 0));
Can you help me with second part and summarize them? thanks
For example: first table (Payment):
user_id time money
a 01.01.10 00:00:00 15,00
a 01.01.10 10:00:00 2,00
a 03.01.10 00:00:00 3,00
c 04.01.10 00:00:00 4,00
c 04.01.10 00:05:00 5,00
d 06.01.10 00:00:00 6,00
e 07.01.10 00:00:00 7,00
e 08.01.10 00:00:00 8,00
The second one:
user_id time range
a 01.01.10 00:00:00 6
a 01.01.10 09:00:00 7
a 04.01.10 00:00:00 8
b 04.01.10 00:00:00 4
c 04.01.10 00:05:00 7
d 06.01.10 00:00:00 5
e 07.01.10 00:00:00 6
f 08.01.10 00:00:00 6
g 08.01.10 00:00:00 7
I expected:
user_id time sum
a 01.01.10 2
a 03.01.10 3
c 04.01.10 5
Here is one possible method using joins:
select t1.user_id, datevalue(p.time) as [time], sum(p.money) as [sum]
from
(
(select t.user_id, t.time from rank t where t.range = 7) t1
inner join payment p on t1.user_id = p.user_id
)
left join
(select t.user_id, t.time from rank t where t.range > 7) t2 on p.user_id = t2.user_id
where
p.time >= t1.time and (t2.user_id is null or p.time < t2.time)
group by
t1.user_id, datevalue(p.time)
I have assumed that your second table is called rank (this was not stated in your question).
Here, the subquery t1 obtains the set of users with range = 7 (captain), and the subquery t2 obtains the set of users with range > 7. I then select all records with a payment date greater than or equal to the date of promotion to captain, but less than any subsequent promotion (if it exists).
This yields the following result:
+---------+------------+------+
| user_id | time | sum |
+---------+------------+------+
| a | 01/01/2010 | 2.00 |
| a | 03/01/2010 | 3.00 |
| c | 04/01/2010 | 5.00 |
+---------+------------+------+
Unless I have misunderstood, I would argue that your expected result is incorrect as the payment below occurs before user_id = c achieved the rank of captain:
c 04.01.10 00:00:00 4,00
c 04.01.10 00:05:00 7

Join tables with dates within intervals of 5 min (get avg)

I want to join two tables based on timestamp, the problem is that both tables didn't had the exact same timestamp so i want to join them using a near timestamp using a 5 minute interval.
This query needs to be done using 2 Common table expressions, each common table expression needs to get the timestamps and group them by AVG so they can match
Freezer | Timestamp | Temperature_1
1 2018-04-25 09:45:00 10
1 2018-04-25 09:50:00 11
1 2018-04-25 09:55:00 11
Freezer | Timestamp | Temperature_2
1 2018-04-25 09:46:00 15
1 2018-04-25 09:52:00 13
1 2018-04-25 09:59:00 12
My desired result would be:
Freezer | Timestamp | Temperature_1 | Temperature_2
1 2018-04-25 09:45:00 10 15
1 2018-04-25 09:50:00 11 13
1 2018-04-25 09:55:00 11 12
The current query that i'm working on is:
WITH Temperatures_1 (
SELECT Freezer, Temperature_1, Timestamp
FROM TABLE_A
),
WITH Temperatures_2 (
SELECT Freezer, Temperature_2, Timestamp
FROM TABLE_B
)
SELECT A.Freezer, A.Timestamp, Temperature_1, Temperature_2
FROM Temperatures_1 as A
RIGHT JOIN Temperatures_2 as B
ON A.FREEZER = B.FREEZER
WHERE A.Timestamp = B.Timestamp
You should may want to modify your join criteria instead of filtering the output. Use BETWEEN to bracket your join value on the timestamps. I chose +/- 150 seconds because that's half of 2-1/2 minutes to either side (5-minute range to match). You may need something different.
;WITH Temperatures_1 (
SELECT Freezer, Temperature_1, Timestamp
FROM TABLE_A
),
WITH Temperatures_2 (
SELECT Freezer, Temperature_2, Timestamp
FROM TABLE_B
)
SELECT A.Freezer, A.Timestamp, Temperature_1, Temperature_2
FROM Temperatures_1 as A
RIGHT JOIN Temperatures_2 as B
ON A.FREEZER = B.FREEZER
AND A.Timestamp BETWEEN (DATEADD(SECOND, -150, B.Timestamp)
AND (DATEADD(SECOND, 150, B.Timestamp)
You should change the key of join two table by adding the timestamp. The timestamp you should need to approximate the datetime on both side tables A and B tables.
First you should check if the value of the left table (A) datetime is under 2.5 minutes then approximate to the near 5 min. If it is greater the approximate to the next 5 minutes. The same thing you should do on the right table (B). Or you can do this on the CTE and the right join remains the same as your query.

Select min/max from group defined by one column as subgroup of another - SQL, HPVertica

I'm trying to find the min and max date within a subgroup of another group. Here's example 'data'
ID Type Date
1 A 7/1/2015
1 B 1/1/2015
1 A 8/5/2014
22 B 3/1/2015
22 B 9/1/2014
333 A 8/1/2015
333 B 4/1/2015
333 B 3/29/2014
333 B 2/28/2013
333 C 1/1/2013
What I'd like to identify is - within an ID, what is the min/max Date for each block of similar Type? So for ID # 333 I want the below info:
A: min & max = 8/1/2015
B: min = 2/28/2013
max = 4/1/2015
C: min & max = 1/1/2013
I'm having trouble figuring out how to identify only uninterrupted groupings of Type within a grouping of ID. For ID #1, I need to keep the two 'A' Types with separate min/max dates because they were split by a Type 'B', so I can't just pull the min date of all Type A's for ID #1, it has to be two separate instances.
What I've tried is something like the below two lines, but neither of these accurately captures the case mentioned above for ID #1 where Type B interrupts Type A.
Max(Date) OVER (Partition By ID, Type)
or this:
Row_Number() OVER (Partition By ID, Type ORDER BY Date DESC)
,then selecting Row #1 for max date, and date ASC w/ row #1 for min date
Thank you for any insight you can provide!
If I understand right, you want the min/max values for an id/type grouped using a descending date sort, but the catch is that you want them based on clusters within the id by time.
What you can do is use CONDITIONAL_CHANGE_EVENT to tag the rows on change of type, then use that in your GROUP BY on a standard min/max aggregation.
This would be the intermediate step towards getting to what you want:
select ID, Type, Date,
CONDITIONAL_CHANGE_EVENT(Type) OVER( PARTITION BY ID ORDER BY Date desc) cce
from mytable
group by ID, Type, Date
order by ID, Date desc, Type
ID Type Date cce
1 A 2015-07-01 00:00:00 0
1 B 2015-01-01 00:00:00 1
1 A 2014-08-05 00:00:00 2
22 B 2015-03-01 00:00:00 0
22 B 2014-09-01 00:00:00 0
333 A 2015-08-01 00:00:00 0
333 B 2015-04-01 00:00:00 1
333 B 2014-03-29 00:00:00 1
333 B 2013-02-28 00:00:00 1
333 C 2013-01-01 00:00:00 2
Once you have them grouped using CCE, you can do an aggregate on this to get the min/max you are looking for grouping on cce. You can play with the order by at the bottom, this ordering seem to make the most sense to me.
select id, type, min(date), max(date)
from (
select ID, Type, Date,
CONDITIONAL_CHANGE_EVENT(Type) OVER( PARTITION BY ID ORDER BY Date desc) cce
from mytable
group by ID, Type, Date
) x
group by id, type, cce
order by id, 3 desc, 4 desc;
id type min max
1 A 2015-07-01 00:00:00 2015-07-01 00:00:00
1 B 2015-01-01 00:00:00 2015-01-01 00:00:00
1 A 2014-08-05 00:00:00 2014-08-05 00:00:00
22 B 2014-09-01 00:00:00 2015-03-01 00:00:00
333 A 2015-08-01 00:00:00 2015-08-01 00:00:00
333 B 2013-02-28 00:00:00 2015-04-01 00:00:00
333 C 2013-01-01 00:00:00 2013-01-01 00:00:00

SQL "transform" query

I have these data on a table (using SQL Server 2005):
ID ParentID StartTime EndTime
77 62 08:00:00 11:00:00
78 62 12:00:00 15:00:00
79 62 18:00:00 22:00:00
and I want to transform it into this:
ParentID BreakfastStart BreakfastEnd LunchStart LunchEnd DinnerStart DinnerEnd
62 08:00:00 11:00:00 12:00:00 15:00:00 18:00:00 22:00:00
Now the hard part is: assume I have no other data field specifying which record is breakfast, lunch or dinner. I want to associate them with lowest start time, i.e., the lower start time will be breakfast, next lower will be lunch and the higher will be dinner (assume all three (and only three) records are always filled).
Any ideas?
WITH q AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY parentID ORDER BY StartTime) AS rn
FROM mytable
)
SELECT qb.ParentID,
qb.StartTime AS BreakfastStart, qb.EndTime AS BreakfastEnd,
ql.StartTime AS LunchStart, ql.EndTime AS LunchEnd,
qd.StartTime AS DinnerStart, qd.EndTime AS DinnerEnd
FROM q qb
LEFT JOIN
q ql
ON ql.parentID = qb.parentID
AND ql.rn = 2
LEFT JOIN
q qd
ON qd.parentID = qb.parentID
AND qd.rn = 3
WHERE qb.rn = 1