I have the following table:
Date | Time | Value | ReceivedTime
2022-04-01| 00:59:59 | 5 | 00:30:15
2022-04-01| 13:59:59 | 15 | 13:30:00
2022-04-02| 21:59:59 | 5 | 21:30:15
2022-04-02| 22:59:59 | 25 | 22:25:15
2022-04-02| 23:59:59 | 25 | 23:00:15
2022-04-03| 14:59:59 | 50 | 00:30:15
2022-04-03| 15:59:59 | 555 | 00:30:15
2022-04-03| 16:59:59 | 56 | 00:30:15
I want to get maximum value along with Date,ReceivedTime.
Expected Result:
Date | Value | ReceivedTime
2022-04-01 | 15 | 13:30:00
2022-04-02 | 25 | 23:00:15
2022-04-03 | 555 | 00:30:15
This answer assumes that, in the event of two or more records being tied on a given day for the same highest value, you want to retain the single record with the most recent ReceivedTime. We can use DISTINCT ON here:
SELECT DISTINCT ON (Date) Date, Value, ReceivedTime
FROM yourTable
ORDER BY Date, Value DESC, ReceivedTime DESC;
I want to generate intervals like this:
- ID | START_INTERVAL
- 1 | 12:00
- 2 | 12:30
- 3 | 13:00
- 4 | 13:30
- 5 | 16:00
- 6 | 16:30
- 7 | 17:00
- 8 | 17:30
From this:
- ID | START | STOP | INTERVAL
- 1 | 2018-05-03 12:00:00 | 2018-05-03 14:00:00 | 30
- 2 | 2018-05-03 16:00:00 | 2018-05-03 18:00:00 | 30
It is possible to generate this from t-sql or i need to use PHP ?
So, you want some kind of recursive cte
with cte as (
select id, start, stop, interval from table t
union all
select id, dateadd(MINUTE, interval, start) start, stop, interval
from cte c
where start < stop
)
select id, start as start_interval from cte c
order by 1
Similar to this question: Is there a way to toggle expanded table formatting mode in PrestoDB cli?.
Is there a way to enable expanded table formatting mode in HIVE? I want to inspect a few records in a wide table before starting a big query job.
Copying the example from the other question:
Before expanded table formatting:
select * from sometable;
id | time | humanize_time | value
----+-------+---------------------------------+-------
1 | 09:30 | Early Morning - (9.30 am) | 570
2 | 11:30 | Late Morning - (11.30 am) | 690
3 | 13:30 | Early Afternoon - (1.30pm) | 810
4 | 15:30 | Late Afternoon - (3.30 pm) | 930
(4 rows)
After:
select * from sometable;
-[ RECORD 1 ]-+---------------------------
id | 1
time | 09:30
humanize_time | Early Morning - (9.30 am)
value | 570
-[ RECORD 2 ]-+---------------------------
id | 2
time | 11:30
humanize_time | Late Morning - (11.30 am)
value | 690
-[ RECORD 3 ]-+---------------------------
id | 3
time | 13:30
humanize_time | Early Afternoon - (1.30pm)
value | 810
-[ RECORD 4 ]-+---------------------------
id | 4
time | 15:30
humanize_time | Late Afternoon - (3.30 pm)
value | 930
You can use a combination of CROSS JOIN , CASE and UNION ALL.
select
c.col,
case c.col
when 'id' then id
when 'time' then time
when 'humanize_time' then humanize_time
when 'value' then value
end as data
from sometable t
cross join
(
select 'id' as col
union all select 'time' as col
union all select 'humanize_time' as col
union all select 'value' as col
) c ORDER BY id;
We're building a simple scheduling platform using SQL Server as the backend database (with ASP.NET as the web API).
The relevant tables in the database are as follows:
CREATE TABLE Employee
(
EmpID INT NOT NULL, -- identity column
FirstName VARCHAR(64),
LastName VARCHAR(64),
... -- other columns identifying the employee
IsActive BIT NOT NULL -- false if employee shouldn't show as available
)
CREATE TABLE Shifts
(
ShiftID INT NOT NULL, -- identity column
EmpID INT NOT NULL, -- foreign key
startTime TIME NOT NULL, -- only the time of day
endTime TIME NOT NULL,
expiryDate DATE, -- no availability after this day
weekdayMask INT NOT NULL -- 0x01 = Monday, 0x04 = Wednesday, etc. ORed together
)
CREATE TABLE Appointment
(
ApptID INT NOT NULL, -- identity column
EmpID INT NOT NULL, -- foreign key
apptDate DATE -- day appointment takes place
startTime TIME NOT NULL, -- only the time of day
endTime TIME NOT NULL,
)
I'm now trying to write a table function (or a stored procedure could do it too) that will obtain a calendar of availability for a given employee, given the employee's ID and a date. I need to take a few things into account:
Assume all appointments occur in discrete half-hour blocks. An appointment is allowed to be multiple consecutive blocks. We round up to the next block, so if an appointment says it ends at 1:33 PM, the 1:30-2:00PM block should still be considered occupied.
An employee must be present for an entire block to be available. If the employee says their shift ends at 4:45 PM, then 4:30 should not be a valid block starting time.
An employee can have multiple shifts per day, e.g. 9 AM - 12 PM, and 1 PM to 5 PM.
(The easy part) The function should show nothing for an employee who is unavailable, and should show nothing if the employee's expiryDate has passed.
The desired return from the function is a list of half hour blocks that the employee is available during, listed by start block time.
Example: Assume employee 1 has a shift from 9 AM to 3 PM. Employee 1 has an appointment scheduled from 10 AM to 11 AM already, and one from 1:30 PM to 2 PM. The desired return from the function would be a list like this:
AvailableBlockStartingTime
--------------------------
9:00 AM
9:30 AM
11:00 AM
11:30 AM
12:00 PM
12:30 PM
1:00 PM
2:00 PM
2:30 PM
So basically, I need to conditionally generate records at a given interval - in this case a half hour - but skip records if an appointment record exists for the given time block.
The "naive" thing I've been able to do is to test a given time and see if an employee is available for an appointment at that time, e.g. given "today" and "11:30 AM", I can determine whether the employee is available or not.
So I need to either automatically repeat the above in half-hour steps, and add all the results to a "table" that I can return, or use some kind of SQL magic that will generate "ranges" of records, and then exclude things from those ranges.
Ideas?
The problem of generating a set of time ranges can be solved with an adhoc query, a time table.
declare #starttime time(0) = '09:00';
declare #endtime time(0) = '15:00';
with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
/* adhoc time range table */
, blocks as (
select top (48)
[block]=convert(time(0),dateadd(minute,30*(row_number() over (order by (select 1)) -1),0))
, [next_block]=convert(time(0),dateadd(minute,30*(row_number() over (order by (select 1))),0))
from n as deka cross join n as hecto
order by row_number() over (order by (select 1))
)
select *
from blocks b
where b.block >= #starttime
and b.block < #endtime;
rextester demo: http://rextester.com/WJZY39264
returns:
+----------+------------+
| block | next_block |
+----------+------------+
| 09:00:00 | 09:30:00 |
| 09:30:00 | 10:00:00 |
| 10:00:00 | 10:30:00 |
| 10:30:00 | 11:00:00 |
| 11:00:00 | 11:30:00 |
| 11:30:00 | 12:00:00 |
| 12:00:00 | 12:30:00 |
| 12:30:00 | 13:00:00 |
| 13:00:00 | 13:30:00 |
| 13:30:00 | 14:00:00 |
| 14:00:00 | 14:30:00 |
| 14:30:00 | 15:00:00 |
+----------+------------+
Decoding your weekdayMask can get brittle if you are depending on Monday to be the first day of the week and using datepart(weekday,...) (this changes based on language settings, and is overridden with set datefirst).
My solution would be to check the datename() of the weekday and pull the associated value from a table, (here I use a table value constructor in a common table expression instead.)
The other critiera can be applied using joins, where, isnull() or coalesce() to deal with null values (for expiryDate), a subquery to get the related value for a weekday to compare to the weekdayMask, and not exists() to check for overlaps.
The final question is whether or not to use a procedure or table-valued function. If you were going to use a table-valued function, I would strongly suggest using an inline table valued function for the significant performance improvement over a comparable multi-statement table valued function. Since that seemed a little more tricky to write than a procedure, that is the one I chose for this answer.
Rewriting a table valued function as a procedure should be pretty easy if you decide you that is what you would prefer.
create function dbo.udf_availability_by_empId_Date (
#empid int
, #date date
) returns table with schemabinding as return (
/* Weekday mask using weekday names
to avoid any issue with datefirst and datepart()
this uses a 7 row lookup table for the bitmask check */
with Mask as (
select weekday_name, weekday_value
from (values ('Monday',1) ,('Tuesday',2) ,('Wednesday',4) ,('Thursday',8)
,('Friday',16) ,('Saturday',32) ,('Sunday',64)) w (weekday_name,weekday_value))
/* adhoc numbers table to generate range in half hour increments */
, n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
/* adhoc time range table */
, blocks as (
select top (48)
[block]=convert(time(0),dateadd(minute,30*(row_number() over (order by (select 1)) -1),0))
, [next_block]=convert(time(0),dateadd(minute,30*(row_number() over (order by (select 1))),0))
from n as deka cross join n as hecto
order by row_number() over (order by (select 1))
)
select b.block
from dbo.employee e
inner join dbo.shifts s
on s.EmpId = e.Empid
inner join blocks b
on b.block >= s.starttime
and b.block < s.endtime
where e.EmpId = #empid
and e.IsActive = 1
and isnull(s.ExpiryDate,'20630405') >= #date
/* get the value to check against the weekday bitmask */
and s.weekdaymask & (
select weekday_value
from Mask
where weekday_name = datename(weekday,#date)
) != 0
/* check for appointments that overlap the block */
and not exists (
select 1
from dbo.appointment a
where a.endtime > b.block
and b.next_block > a.starttime
and a.apptDate = #date
and a.Empid = #empid
)
);
go
If you want to change the display of the time returned, then you can use convert(varchar(10),a.block,100) to get an AM/PM formatted string, but I would recommend letting your application layer do the formating.
For your testcase:
select
a.block
, AvailableBlockStartingTime = convert(varchar(10),a.block,100)
from dbo.udf_availability_by_empId_Date(1,'20170416') as a
rextester demo: http://rextester.com/TXBUP51531
returns:
+----------+----------------------------+
| block | AvailableBlockStartingTime |
+----------+----------------------------+
| 09:00:00 | 9:00AM |
| 09:30:00 | 9:30AM |
| 11:00:00 | 11:00AM |
| 11:30:00 | 11:30AM |
| 12:00:00 | 12:00PM |
| 12:30:00 | 12:30PM |
| 13:00:00 | 1:00PM |
| 14:00:00 | 2:00PM |
| 14:30:00 | 2:30PM |
+----------+----------------------------+
Since it is a table valued function we might as have some fun with apply().
/* show all employee availability for a given date */
select
e.EmpId
, e.FirstName
, e.IsActive
, a.block
from dbo.Employee e
outer apply dbo.udf_availability_by_empId_Date(e.EmpId,'20170416') a
returns:
+-------+----------------------+----------+----------+
| EmpId | FirstName | IsActive | block |
+-------+----------------------+----------+----------+
| 1 | fdmillion | True | 09:00:00 |
| 1 | fdmillion | True | 09:30:00 |
| 1 | fdmillion | True | 11:00:00 |
| 1 | fdmillion | True | 11:30:00 |
| 1 | fdmillion | True | 12:00:00 |
| 1 | fdmillion | True | 12:30:00 |
| 1 | fdmillion | True | 13:00:00 |
| 1 | fdmillion | True | 14:00:00 |
| 1 | fdmillion | True | 14:30:00 |
| 2 | shift expired person | True | NULL |
| 3 | inactive person | False | NULL |
| 4 | other person | True | NULL |
+-------+----------------------+----------+----------+
Or checking the schedule for an employee for a week:
/* show a weeks worth of availability for a given empid */
declare #empid int = 1
declare #fromdate date = '20170410';
;with n as (select [Date]=dateadd(day,n,#fromdate) from (values(0),(1),(2),(3),(4),(5),(6)) t(n))
select
[Date] = convert(char(10),n.[Date],120)
, Weekday_Name = datename(weekday,n.[Date])
, a.block
from n
outer apply dbo.udf_availability_by_empId_Date(#empid,[Date]) a
order by n.[Date]
returns:
+------------+--------------+----------+
| Date | Weekday_Name | block |
+------------+--------------+----------+
| 2017-04-10 | Monday | NULL |
| 2017-04-11 | Tuesday | NULL |
| 2017-04-12 | Wednesday | NULL |
| 2017-04-13 | Thursday | NULL |
| 2017-04-14 | Friday | 09:00:00 |
| 2017-04-14 | Friday | 09:30:00 |
| 2017-04-14 | Friday | 10:00:00 |
| 2017-04-14 | Friday | 10:30:00 |
| 2017-04-14 | Friday | 11:00:00 |
| 2017-04-14 | Friday | 11:30:00 |
| 2017-04-14 | Friday | 12:00:00 |
| 2017-04-14 | Friday | 12:30:00 |
| 2017-04-14 | Friday | 13:00:00 |
| 2017-04-14 | Friday | 13:30:00 |
| 2017-04-14 | Friday | 14:00:00 |
| 2017-04-14 | Friday | 14:30:00 |
| 2017-04-15 | Saturday | 09:00:00 |
| 2017-04-15 | Saturday | 09:30:00 |
| 2017-04-15 | Saturday | 10:00:00 |
| 2017-04-15 | Saturday | 10:30:00 |
| 2017-04-15 | Saturday | 11:00:00 |
| 2017-04-15 | Saturday | 11:30:00 |
| 2017-04-15 | Saturday | 12:00:00 |
| 2017-04-15 | Saturday | 12:30:00 |
| 2017-04-15 | Saturday | 13:00:00 |
| 2017-04-15 | Saturday | 13:30:00 |
| 2017-04-15 | Saturday | 14:00:00 |
| 2017-04-15 | Saturday | 14:30:00 |
| 2017-04-16 | Sunday | 09:00:00 |
| 2017-04-16 | Sunday | 09:30:00 |
| 2017-04-16 | Sunday | 11:00:00 |
| 2017-04-16 | Sunday | 11:30:00 |
| 2017-04-16 | Sunday | 12:00:00 |
| 2017-04-16 | Sunday | 12:30:00 |
| 2017-04-16 | Sunday | 13:00:00 |
| 2017-04-16 | Sunday | 14:00:00 |
| 2017-04-16 | Sunday | 14:30:00 |
+------------+--------------+----------+
Additional note: time(0) is 3 bytes vs. 5 bytes for time (the default fractional precision is 7). You can shave 4 bytes of each row by including a precision of 0-2.
Reference:
common table expression
table value constructor (values (...),(...))
apply()
Generate a set or sequence without loops - 1 - Aaron Bertrand
When is a SQL function not a function? "If it’s not inline, it’s rubbish." - Rob Farley
TSQL User-Defined Functions: Ten Questions You Were Too Shy To Ask - Robert Sheldon
set datefirst
Main thing is that you didn't provide any sample data.
Also in your script above only Example part is important.
I mean is it necessary to understand those points in bullets.
IF I hv understood correctly then it is not so complicated.
Else Tell me what i hvn't understood.
DECLARE #Shift table(empid int,starttime time(0),endtime time(0))
insert into #Shift VALUES(1,'9:00 AM','3 PM')
--select * from #Shift
declare #Appointment table(empid int,starttime time(0),endtime time(0))
insert into #Appointment VALUES(1,'10:00 AM','11:00 AM')
,(1,'1:30 PM','2:00 PM')
--select * from #Appointment
declare #intervalInMin int=30
;with GENCTE as
(
select cast('12:00 AM' as time) as Caltime
union ALL
select dateadd(MINUTE,#intervalInMin,Caltime)
from gencte
where Caltime<=cast('23:00' as time)
)
select caltime AvailableBlockStartingTime
from gencte g
where exists(select empid from #Shift s
where g.caltime between s.starttime and s.endtime
and not EXISTS
(
select empid from #Appointment a where s.empid=a.empid
and g.caltime between a.starttime and a.endtime
)
)
I have a table with rows consisting of values and timestamps. A row is inserted whenever the value has changed, and the timestamp indicates when the change has happened. For example, something like this:
id | value | timestamp
-----+-------+----------------------------
1 | 736 | 2014-03-18 16:38:22.20548
2 | 531 | 2014-06-18 16:38:22.664324
3 | 24 | 2014-07-18 16:38:22.980137
4 | 530 | 2014-09-22 10:01:36.13856
5 | 529 | 2014-09-23 10:01:27.202026
I need a query in Postgresql which generates a table with one row for every month of the year. Every row has a timestamp (first day of the month) and a value. The value is the last value of the first table which was inserted before the beginning of the given month. The value should be 0 if there are no matching rows in the first table. Something like this:
id | value | timestamp
-----+-------+----------------------------
1 | 0 | 2014-01-01 00:00:00.000000
2 | 0 | 2014-02-01 00:00:00.000000
3 | 0 | 2014-03-01 00:00:00.000000
4 | 736 | 2014-04-01 00:00:00.000000
5 | 736 | 2014-05-01 00:00:00.000000
6 | 736 | 2014-06-01 00:00:00.000000
7 | 531 | 2014-07-01 00:00:00.000000
8 | 24 | 2014-08-01 00:00:00.000000
9 | 24 | 2014-09-01 00:00:00.000000
10 | 529 | 2014-10-01 00:00:00.000000
11 | 529 | 2014-11-01 00:00:00.000000
12 | 529 | 2014-12-01 00:00:00.000000
I tried for a while, but I didn't manage to get the full result. I guess I need to generate a list of months like this.
SELECT
*
FROM
generate_series('2014-01-01 00:00'::timestamp, now(), '1 month') AS months
And then do something like this to get the last occurrency before a month:
SELECT
*
FROM first_table
WHERE timestamp < --current_month_selection--
ORDER BY timestamp desc
LIMIT 1;
I guess one needs an OUTER JOIN and a CASE conditional...
Unfortunately I didn't manage to put it all together. Can somebody help me?
Actually, I think I solved this by myself, it was quite trivial:
SELECT
month,
COALESCE((SELECT value
FROM first_table
WHERE timestamp < month
ORDER BY timestamp DESC
LIMIT 1),0)
FROM
generate_series('2014-01-01 00:00'::timestamp, now(), '1 month') AS month