SQL "transform" query - sql

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

Related

SQL - Fuzzy JOIN on Timestamp columns within X amount of time

Say I have two tables:
a:
timestamp
precipitation
2015-08-03 21:00:00 UTC
3
2015-08-03 22:00:00 UTC
3
2015-08-04 3:00:00 UTC
4
2016-02-04 18:00:00 UTC
4
and b:
timestamp
loc
2015-08-03 21:23:00 UTC
San Francisco
2016-02-04 16:04:00 UTC
New York
I want to join to get a table who has fuzzy joined entries where every row in b tries to get joined to a row in a. Criteria:
The time is within 60 minutes. If a match does not exist within 60 minutes, do not include that row in the output.
In the case of a tie where some row in b could join onto two rows in a, pick the closest one in terms of time.
Example Output:
timestamp
loc
precipitation
2015-08-03 21:00:00 UTC
San Francisco
3
What you need is an ASOF join. I don't think there is an easy way to do this with BigQuery. Other databases like Kinetica (and I think Clickhouse) support ASOF functions that can be used to perform 'fuzzy' joins.
The syntax for Kinetica would be something like the following.
SELECT *
FROM a
LEFT JOIN b
ON ASOF(a.timestamp, b.timestamp, INTERVAL '0' MINUTES, INTERVAL '60' MINUTES, MIN)
The ASOF function above sets up an interval of 60 minutes within which to look for matches on the right side table. When there are multiple matches, it selects the one that is closest (MAX would pick the one that is farthest away).
As per my understanding and based on the data you provided I think the below query should work for your use case.
create temporary table a as(
select TIMESTAMP('2015-08-03 21:00:00 UTC') as ts, 3 as precipitation union all
select TIMESTAMP('2015-08-03 22:00:00 UTC'), 3 union all
select TIMESTAMP('2015-08-04 3:00:00 UTC'), 4 union all
select TIMESTAMP('2016-02-04 18:00:00 UTC'), 4
);
create temporary table b as(
select TIMESTAMP('2015-08-03 21:23:00 UTC') as ts,'San Francisco ' as loc union all
select TIMESTAMP('2016-02-04 14:04:00 UTC') as ts,'New York ' as loc
);
select b_ts,a_ts,loc,precipitation,diff_time_sec
from(
select b.ts b_ts,a.ts a_ts,
ABS(TIMESTAMP_DIFF(b.ts,a.ts, SECOND)) as diff_time_sec,
*
from b
inner join a on b.ts between date_sub(a.ts, interval 60 MINUTE) and date_add(a.ts, interval 60 MINUTE)
)
qualify RANK() OVER(partition by b_ts ORDER BY diff_time_sec) = 1

INNER JOIN SQL with DateTime return multiple record

I have the following table:
Group RecDate oData
---------------------------------------
123 2022-03-20 02:00:00 F1xR
123 2022-03-21 02:30:00 F1xF
123 2022-03-22 05:00:00 F1xN
123 2022-03-15 04:00:00 F2xR
From the table above, I want to get the MAX date group by 2 char from oData field. Then I wrote a query like this:
SELECT a.Group, MAX(a.RecDate) RecDate, LEFT(a.oData, 2) oDataNo
INTO #t1
FROM TableData a
GROUP BY a.Group, LEFT(a.oData, 2)
SELECT * FROM #t1
Then, the result should be:
Group RecDate oDataNo
--------------------------------------------
123 2022-03-22 05:00:00 F1
123 2022-03-15 04:00:00 F2
From the result above (#t1), I want to join with the TableData to get the RIGHT character (1 digit) from oData field. So I INNER JOIN the #t1 with TableData. The JOIN field is RecDate. But it is strange that the result isn't what I want.
The query like:
SELECT RIGHT(a.oData,1) oDataStat, b.*
FROM TableData a
INNER JOIN #t1 b ON a.RecDate = b.RecDate
The wrong result like:
The result should be:
Group RecDate oDataNo oDataStat
-----------------------------------------------------------
123 2022-03-22 05:00:00 F1 N
123 2022-03-15 04:00:00 F2 R
Am I doing wrong approach?
Please advise. Really appreciated.
Thank you.
The query you provided returns the data you desire. However its cleaner to do it in a single query e.g.
WITH cte AS (
SELECT *
, RIGHT(a.oData,1) oDataStat
, ROW_NUMBER() OVER (PARTITION BY LEFT(a.oData, 2) ORDER BY RecDate DESC) rn
FROM TableData a
)
SELECT [Group], RecDate, oData, oDataStat
FROM cte
WHERE rn = 1
ORDER BY RecDate;
returns:
Group
RecDate
oData
oDataStat
123
2022-03-15 04:00:00
F2xR
R
123
2022-03-22 05:00:00
F1xN
N
Note: Your query as posted doesn't actually run due to not escaping [Group] - you should ensure everything you post has any errors removed first.

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.

Oracle SQL query about Date

I have a database table named availableTimeslot with fields pk, startDate, endDate, e.g.
PK startDate endDate
1. 2017-03-07 09:00:00 2017-03-07 18:00:00
2. 2017-03-07 18:00:00 2017-03-07 21:00:00
3. 2017-03-08 09:00:00 2017-03-08 18:00:00
records starting from 09:00:00 to 18:00:00 indicate it is a morning time slot, while 18:00:00 to 23:00:00 indicating it is a afternoon time slot
storing available timeslot dates (e.g. 2017-03-06, 2017-03-08) which are available for the customer to choose one.
Can I use one query to get exactly 10 available time slots dates starting on the day after the order date?
e.g. if I order a product on 2016-03-07, then the query returns
2017-03-08 09:00:00
2017-03-08 18:00:00
2017-03-09 09:00:00
2017-03-09 18:00:00
2017-03-10 ...
2017-03-11 ...
2017-03-13 ...
as 12 is a public holiday and not in the table.
In short, it returns 10 dates (5 days with each day having am and pm sessions)
remark: the available time slot dates are in order, but may not be consecutive
select available_date
from ( select available_date, row_number() over (order by available_date) as rn
from your_table
where available_date > :order_date
)
where rn <= 5;
:order_date is a bind variable - the date entered by the user/customer through the interface.
Do you want 5 for a single customer?
select ts.*
from (select ts.*
from customer c join
timeslots ts
on ts.date > c.orderdate
where c.customerid = v_customerid
order by ts.date asc
) ts
where rownum <= 5

Count changes from specific value within time period

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.