How to create one to many records in postgres? - postgresql

I have a table that I am trying to populate multiple values based on Did.
Original table:
Sid
Did
Time1s
Time1e
Time2s
Time2e
Time3s
Time3e
U1
1
8:00
8:30
8:40
9:20
10:00
10:30
U2
2
9:00
9:30
10:00
10:30
11:00
11:30
And I would need like the below
Output :
Sid
Did
Time1s
Time1e
U1
1
8:00
8:30
U1
1
8:40
9:20
U1
1
10:00
10:30
U2
2
9:00
9:30
U2
2
10:00
10:30
U2
2
11:00
11:30
How would I do this in postgres? Thanks in advance.

You can do it using this approach
SELECT t.sid,
CASE v.i WHEN 1 THEN "Time1s"
WHEN 2 THEN "Time2s"
WHEN 3 THEN "Time3s"
END AS Time1s,
...
FROM your_table t
CROSS JOIN (VALUES (1), (2), (3)) v(i)

Related

TSQL, counting coherent days of holiday

I hope someone can help me on this one. :-)
I wish to count coherent periods of holiday to see if anyone had coherent holiday more than three days in a row. In other words it is not enough to count the number of days overall. The days have to be coherent. In the example of my data below I have illustrated three people with each their own days of holiday. Person 1234 has two periods of two days of holiday in a row, so this person has no periods above three days since there is a day in between two periods (the 3rd). Person 1235 and 1236 each have one period above three days. Time of day in the timestamps has no relevance, so data can be formatted as just date.
What I have:
ID
Start
1234
2022-01-01 00:00:00
1234
2022-01-02 00:00:00
1234
2022-01-04 06:50:00
1234
2022-01-05 06:50:00
1235
2022-01-04 06:50:00
1235
2022-01-05 06:50:00
1235
2022-01-06 00:00:00
1236
2022-01-01 00:00:00
1236
2022-01-02 00:00:00
1236
2022-01-03 06:50:00
1236
2022-01-04 06:50:00
1236
2022-01-05 06:50:00
1236
2022-01-08 00:00:00
What I hope to get:
ID
N holidays > 3 days
1234
0
1235
1
1236
1
Anyways, any help will be appreciated!
Kind regards,
Jacob
This is a "gaps and islands" problem. You need to first group the data into "islands", which in your case is groups of consecutive holidays. Then summarize them in your final result set
Side note: your question requests greater than 3 days, but your expected output uses greater than or equal to 3 so I used that instead.
DROP TABLE IF EXISTS #Holiday;
DROP TABLE IF EXISTS #ConsecutiveHoliday
CREATE TABLE #Holiday (ID INT,StartDateTime DATETIME)
INSERT INTO #Holiday
VALUES (1234,'2022-01-01 00:00:00')
,(1234,'2022-01-02 00:00:00')
,(1234,'2022-01-04 06:50:00')
,(1234,'2022-01-05 06:50:00')
,(1235,'2022-01-04 06:50:00')
,(1235,'2022-01-05 06:50:00')
,(1235,'2022-01-06 00:00:00')
,(1236,'2022-01-01 00:00:00')
,(1236,'2022-01-02 00:00:00')
,(1236,'2022-01-03 06:50:00')
,(1236,'2022-01-04 06:50:00')
,(1236,'2022-01-05 06:50:00')
,(1236,'2022-01-08 00:00:00');
WITH cte_Previous AS (
SELECT A.ID,B.StartDate
,IsHolidayConsecutive = CASE WHEN DATEADD(day,-1,StartDate) /*Current day minus 1*/ = LAG(StartDate) OVER (PARTITION BY ID ORDER BY StartDate) /*Previous holiday date*/
THEN 0
ELSE 1
END
FROM #Holiday AS A
CROSS APPLY (SELECT StartDate = CAST(StartDateTime AS DATE)) AS B
),
cte_Groups AS (
SELECT *,GroupID = SUM(IsHolidayConsecutive) OVER (PARTITION BY ID ORDER BY StartDate)
FROM cte_Previous
)
/*Groups of holidays taken consecutively*/
SELECT ID
,StartDate = MIN(StartDate)
,EndDate = MAX(StartDate)
,NumOfDays = COUNT(*)
INTO #ConsecutiveHoliday
FROM cte_Groups
GROUP BY ID,GroupID
ORDER BY ID,StartDate
/*See list of consecutive holidays taken*/
SELECT *
FROM #ConsecutiveHoliday
/*Formatted result*/
SELECT ID
,[N holidays >= 3 days] = COUNT(CASE WHEN NumOfDays >= 3 THEN 1 END)
FROM #ConsecutiveHoliday
GROUP BY ID

TSQL - How do I aggregate timestamp intervals for one particular pattern of occurence in a data set?

I have the rather challenging task of building a query from a data set off logged status changes, where I need to find and aggregate the spans between two different statuses for a given record id, but the occurrence of that pattern is both variable in occurrence and inconsistent.
However, I can't imagine this hasn't been done before. What I'm looking for is a pointer in the right direction as to what SQL techniques ought to be used to extract this information.
Here's an example of what the data set is like:
id status datetime
1001 A 1/1/15 12:00 PM
1001 B 1/1/15 1:00 PM
1001 C 1/1/15 2:00 PM
1001 D 1/1/15 3:00 PM
1001 B 1/1/15 4:00 PM
1001 C 1/1/15 5:00 PM
1001 D 1/1/15 6:00 PM
1002 A 1/1/15 12:00 PM
1002 B 1/1/15 1:00 PM
1002 C 1/1/15 2:00 PM
1002 D 1/1/15 3:00 PM
1003 A 1/1/15 12:00 PM
1003 B 1/1/15 1:00 PM
1003 C 1/1/15 2:00 PM
1003 B 1/1/15 3:00 PM
1003 C 1/1/15 4:00 PM
1003 D 1/1/15 5:00 PM
1004 A 1/1/15 12:00 PM
1004 B 1/1/15 2:00 PM
1004 A 1/1/15 3:00 PM
1004 B 1/1/15 4:00 PM
1004 C 1/1/15 5:00 PM
1004 D 1/1/15 6:00 PM
In this scenario, I'm trying to find the sum of all time spans between any status B to status C change for each record id. As you can see, that pattern happens sometimes once, sometimes never, sometimes multiple times, and sometimes only partially (A to B back to A for instance, which would not be counted)
So conceptually, the output I'm looking for would look like this:
id total b-c minutes
1001 120
1002 60
1003 120
1004 60
My actual data is not so neatly cut into 1 hour chunks, of course.
I'm more of a programmer than a database person. I could do something iterative in C# easily, but I'm trying to learn what techniques would be used in SQL to perform the same task?
with TB as ( /* get the B rows and the timestamp of the next status */
select
id, status, tstamp,
(
select min(tstamp) from T as t2
where t2.id = t1.id and t2.tstamp > t1.tstamp
) as next_tstamp
from T as t1
where status = 'B'
)
select id, sum(datediff(ss, tstamp, next_tstamp)) /* or some other timespan function */
from TB as tb
where /* check that next status is a C. assumes tstamp is unique per id */
(select status from T where T.id = TB.id and T.tstamp = TB.next_tstamp) = 'C'
group by id
A SQL Server 2008 option with CROSS APPLY
select t1.id, sum(datediff(ss, t1.tstamp, t2.tstamp))
from
T as t1 cross apply
(
select top 1 status, tstamp /* using top is non-standard */
from T as t2
where t2.id = t1.id and t2.tstamp > t1.tstamp
order by tstamp desc
) as t2
where t1.status = 'B' and t2.status = 'C'
group by t1.id
This is a good candidate for using a windowing function. Here's one way to do it:
with
b_to_c_transitions as
(select id, status, datetime c_time,
lag(datetime) over (partition by id order by datetime) b_time
from logtable where status in ('B','C'))
select id, sum(datediff(minute, b_time, c_time))
from b_to_c_transitions where status='C' group by id

Group by each date in a range having events with StartingTimestamp and EndingTimestamp

I would like to count all the events having in a calendar within January and group them by date. This events got a StartingTimestamp and an EndingTimestamp.
For example (Table rp.Calendar):
StartingTimestamp EndingTimestamp Title
24.01.2014 08:00 24.01.2014 10:00 Meeting
25.01.2014 17:00 26.01.2014 08:00 Home time
24.01.2014 26.01.2014 Holiday
26.01.2014 17:00 29.01.2014 08:00 Weekend
Now, the result I need, is:
Date Counter
24.01.2014 2
25.01.2014 2
26.01.2014 3
27.01.2014 1
28.01.2014 1
29.01.2014 1
This is your answer:
SELECT CONVERT(varchar(10),StartingTimestamp,110) AS Date, Count(*) AS Counter
FROM YourTableName
GROUP BY CONVERT(varchar(10),StartingTimestamp,110)
Change 110 to desire format:
101 mm/dd/yy
102 yy.mm.dd
103 dd/mm/yy
104 dd.mm.yy
105 dd-mm-yy
106 dd mon yy
107 Mon dd, yy
108 hh:mm:ss
110 mm-dd-yy
111 yy/mm/dd
112 yymmdd
see more on http://technet.microsoft.com/en-us/library/aa226054(v=sql.80).aspx
This will do for January or any month but it can be tweaked for longer periods if required:
WITH January AS (
SELECT 1 AS n
UNION ALL
SELECT n+1 FROM January WHERE n+1<=31
)
SELECT n,COUNT(*)
FROM January
JOIN yourtable ON n BETWEEN datepart(d,StartingTimestamp) AND datepart(d,EndingTimestamp)
GROUP BY n

How can I group by 2 fields and having by an interval type?

I've got this table:
TABLE T (
id int,
month int,
interval hours
);
and I want to group by id and month, and add the hours.
For example:
id month hours
-------------------
1 1 08:00:00
1 1 09:00:00
1 2 10:00:00
1 2 11:00:00
I want:
1 1 17:00:00
1 2 21:00:00
I tried this:
SELECT * FROM T
GROUP BY T.id , T.month
HAVING SUM( SELECT EXTRACT ( epoch FROM T.hours ) / 3600 );
but it doens't work and I can't fix it.
SELECT
id,
month,
sum(extract ('epoch' from hours)/3600)
FROM
hours
GROUP BY
id,
month
SQL Fiddle

interval overlapping in tsql

i need to get splited intervals and the number of overlapping intervals, eg
basedata:
interval A: startTime 08:00, endTime 12:00
interval B: startTime 09:00, endTime 12:00
interval C: startTime 12:00, endTime 16:00
interval D: startTime 13:00, endTime 14:00
now i have a separate interval from 10:00 to 15:00 and have to determine what intervals are intersected at first. result should be something like:
1: 10:00 - 12:00 ( intersecting with interval A )
2: 10:00 - 12:00 ( intersecting with interval B )
3: 12:00 - 15:00 ( intersecting with interval C )
4: 13:00 - 14:00 ( intersecting with interval D )
this part works fine, the following causes the trouble:
i need some kind of weighting for parallel intervals. this also means, that it can occur that an interval-intersection must be splitted n times, if it's ( partly ) intersected by another one.
in the upper example the expecting result would be:
1: 10:00 - 12:00 -> weightage: 50%
2: 10:00 - 12:00 -> weightage: 50%
3.1: 12:00 - 13:00 -> weightage: 1oo%
3.2: 13:00 - 14:00 -> weightage: 50%
3.3: 14:00 - 15:00 -> weightage: 50%
4: 13:00 - 14:00 -< weightage: 100%
the splitting of interval 3 is caused by the intersecting with interval 4 between 13:00 and 14:00.
sql-server is ms-sql 2008.
thanks for help in advance!
If I understand what you're trying to do correctly, shouldn't your expected result be
1: 10:00 - 12:00 -> weightage: 50%
2: 10:00 - 12:00 -> weightage: 50%
3.1: 12:00 - 13:00 -> weightage: 1oo%
3.2: 13:00 - 14:00 -> weightage: 50%
3.3: 14:00 - 15:00 -> weightage: 50%
4: 13:00 - 14:00 -< weightage: 50%
since 13:00-14:00 is used twice?