compare only hours in timestamp column postgresql - database

I have a table with date like that:
OBJECT TIMESTAMP_START TIMESTAMP_END
House 2020-02-20 09:33:24 2020-02-20 09:33:33
Dog 2020-02-20 18:00:03 2020-02-21 18:33:22
Cat 2020-02-11 19:00:00 2020-02-11 19:15:23
I need to extract all objects,start timestamp and end timestamp whose timestamp start is between (18:00 hours and 09:00)
In that case was Dog and Cat
How could I make that in postgreSql ? Do you think is possible easily?
Thanks!

Since you exclude both bounds, a rare case where BETWEEN is correct:
select *
from tbl
where timestamp_start::time NOT BETWEEN time '09:00' AND time '18:00';

You cannot do this with TIME alone because in hours 09:00 is always less than 18:00, and from 09:00 to 18:00 is the time to be excluded. You can get this by truncating to the start and adding the appropriate interval.
with the_table (object, timestamp_start,timestamp_end ) as
( values ('House', '2020-02-20 09:33:24'::timestamp, '2020-02-20 09:33:33'::timestamp)
, ('Dog', '2020-02-20 18:00:03'::timestamp, '2020-02-21 18:33:22'::timestamp)
, ('Cat', '2020-02-11 19:00:00'::timestamp, '2020-02-11 19:15:23'::timestamp)
, ('Mouse', '2020-02-11 20:00:00'::timestamp, '2020-02-12 08:00:00'::timestamp)
)
select *
from the_table
where timestamp_start between
date_trunc('day', timestamp_start) + interval '18 hours' and
date_trunc('day', timestamp_start) + interval '1 day 9 hours' ;
Of course this get all such rows matching the times even if they are years old. You might want to consider that as well. Just a suggestion.

You can cast the timestamp to time
select *
from the_table
where timestamp_start::time > time '18:00'
and timestamp_start::time < time '09:00'

Related

Creating multiple rows in a SQL table from a source table that has summarised data

I have a table t_times that looks like this
start_time
End_time
Number_of_slots
Slot_Duration
08:00
09:00
6
10
09:00
09:30
1
30
I need to create a table using the values in the t_times to create t_slots
start_time
End_time
Duration
08:00
08:10
10
08:10
08:20
10
08:20
08:30
10
08:30
08:40
10
08:40
08:50
10
08:50
09:00
10
09:00
09:30
30
In essence for every value specified in the number_of_slots field I need to:
Create a new row in the target table
Add the corresponding time slot (start and end) using the duration taking into account the slot_duration field's value. I.e. for a value of 6 I need to add 6 rows with ten minute increments each that uses start_time as the starting value
I can do it with a cursor but it seems a very roundabout way of doing this. Can someone please point me in the SQL direction? Thanks!!!
So the main idea here is to use classic approach with number table. It is quite common to have such table as a persistent one, so the one won't need to generate it on run time. It's a common tool for analytical stuff as yours.
Second is JOIN it with the source one on the right predicate.
use tempdb
go
drop table if exists src
go
create table src (
start_time time
,End_time time
,Number_of_slots int
,Slot_Duration int
)
go
insert into src values
('08:00', '09:00', 6, 10)
,('09:00', '09:30', 1, 30)
go
;with nums as (
select row_number() over(order by (select null)) as n
from sys.all_columns
)
select start_time, dateadd(MINUTE, n*Slot_Duration, start_time) as end_time
, Slot_Duration as Duration
from src
join nums on src.Number_of_slots >= nums.n
order by start_time, end_time

Adding days to a date in Netezza

I have a query pulling dates from field [DATE] BETWEEN '10/1/2017' AND '10/31/2017'
I want to add days to the the end date in the between criteria (10/31/2017). It seems impossible. I can add months perfectly using ADD_MONTHS, but there doesn't seem to be a function ADD_DAYS.
Your help is greatly appreciated!
add_months deals with the special cases that arise from having variable length months.
For other intervals of time, things are much simpler:
To add 5 days to the current day, use this:
SYSTEM.ADMIN(ADMIN)=> select current_date, current_date + interval '5 days';
DATE | ?COLUMN?
------------+---------------------
2017-12-19 | 2017-12-24 00:00:00
(1 row)
T2DB.ADMIN(ADMIN)=> select * from interval_test where col1 between (current_timestamp - interval '2 days') and (current_timestamp + interval '3 days');
COL1
------------
2017-12-19
(1 row)

Populating a list of dates without a defined end date - SQL server

I have a list of accounts and their cost which changes every few days.
In this list I only have the start date every time the cost updates to a new one, but no column for the end date.
Meaning, I need to populate a list of dates when the end date for a specific account and cost, should be deduced as the start date of the same account with a new cost.
More or less like that:
Account start date cost
one 1/1/2016 100$
two 1/1/2016 150$
one 4/1/2016 200$
two 3/1/2016 200$
And the result I need would be:
Account date cost
one 1/1/2016 100$
one 2/1/2016 100$
one 3/1/2016 100$
one 4/1/2016 200$
two 1/1/2016 150$
two 2/1/2016 150$
two 3/1/2016 200$
For example, if the cost changed in the middle of the month, than the sample data will only hold two records (one per each unique combination of account-start date-cost), while the results will hold 30 records with the cost for each and every day of the month (15 for the first cost and 15 for the second one). The costs are a given, and no need to calculate them (inserted manually).
Note the result contains more records because the sample data shows only a start date and an updated cost for that account, as of that date. While the results show the cost for every day of the month.
Any ideas?
Solution is a bit long.
I added an extra date for test purposes:
DECLARE #t table(account varchar(10), startdate date, cost int)
INSERT #t
values
('one','1/1/2016',100),('two','1/1/2016',150),
('one','1/4/2016',200),('two','1/3/2016',200),
('two','1/6/2016',500) -- extra row
;WITH CTE as
( SELECT
row_number() over (partition by account order by startdate) rn,
*
FROM #t
),N(N)AS
(
SELECT 1 FROM(VALUES(1),(1),(1),(1),(1),(1),(1),(1),(1),(1))M(N)
),
tally(N) AS -- tally is limited to 1000 days
(
SELECT ROW_NUMBER()OVER(ORDER BY N.N) - 1 FROM N,N a,N b
),GROUPED as
(
SELECT
cte.account, cte.startdate, cte.cost, cte2.cost cost2, cte2.startdate enddate
FROM CTE
JOIN CTE CTE2
ON CTE.account = CTE2.account
and CTE.rn = CTE2.rn - 1
)
-- used DISTINCT to avoid overlapping dates
SELECT DISTINCT
CASE WHEN datediff(d, startdate,enddate) = N THEN cost2 ELSE cost END cost,
dateadd(d, N, startdate) startdate,
account
FROM grouped
JOIN tally
ON datediff(d, startdate,enddate) >= N
Result:
cost startdate account
100 2016-01-01 one
100 2016-01-02 one
100 2016-01-03 one
150 2016-01-01 two
150 2016-01-02 two
200 2016-01-03 two
200 2016-01-04 one
200 2016-01-04 two
200 2016-01-05 two
500 2016-01-06 two
Thank you #t-clausen.dk!
It didn't solve the problem completely, but did direct me in the correct way.
Eventually I used the LEAD function to generate an end date for every cost per account, and then I was able to populate a list of dates based on that idea.
Here's how I generate the end dates:
DECLARE #t table(account varchar(10), startdate date, cost int)
INSERT #t
values
('one','1/1/2016',100),('two','1/1/2016',150),
('one','1/4/2016',200),('two','1/3/2016',200),
('two','1/6/2016',500)
select account
,[startdate]
,DATEADD(DAY, -1, LEAD([Startdate], 1,'2100-01-01') OVER (PARTITION BY account ORDER BY [Startdate] ASC)) AS enddate
,cost
from #t
It returned the expected result:
account startdate enddate cost
one 2016-01-01 2016-01-03 100
one 2016-01-04 2099-12-31 200
two 2016-01-01 2016-01-02 150
two 2016-01-03 2016-01-05 200
two 2016-01-06 2099-12-31 500
Please note that I set the end date of current costs to be some date in the far future which means (for me) that they are currently active.

DateTime in sql only comparing the time

ID DateTime Code
---------- -------------- ----------
58 2015-01-01 20:00:00 1111
58 2015-01-11 10:00:00 8523
58 2015-01-11 03:00:00 4555
58 2015-01-19 00:01:00 8888
9 2015-01-01 20:00:00 4444
how do i count the number of codes for a specific ID ignoring which date it is but it must be between 20:00:00 and 06:00:00
select count(code) as count from table 1 where ID='58' and DateTime between '20:00:00' and '06:00:00'
the expected output would be
count
3
SELECT count(code) as count
FROM table1
WHERE
ID='58' and
(CAST(DateTime as time) >= '20:00'
or CAST(DateTime as time) <= '06:00')
EDIT: John, I understand the issue. Here is a full solution to handle those cases:
In order to use variables:
DECLARE #HourBegin time = '07:00'
DECLARE #HourEnd time = '17:30'
SELECT count(code) as count
FROM table1
WHERE
ID='58' and
(CAST(DateTime as time) between #HourBegin and #HourEnd or
((CAST(DateTime as time) <= #HourEnd or
CAST(DateTime as time) >= #HourBegin) and
#HourBegin > #HourEnd)
)
Almost the same as previous answer, but with hours it looks nicer for me and might be you need DISTINCT code
SELECT count(DISTINCT code) as count
FROM table1
WHERE
ID='58' and
(DATEPART(HOUR,DateTime) >= 20
or DATEPART(HOUR,DateTime) < 6)
UPDATED: changed from <= 6 to < 6
Update
This answer applies to MySQL.
When I started writing the answer, the question was tagged mysql and sql-server. The OP edited it in the meantime.
This query should do what you want on MySQL.
SELECT count(code) AS `count`
FROM `table 1`
WHERE ID='58'
AND TIME(`DateTime`) NOT BETWEEN '06:00:01' AND '19:59:59'
The MySQL function TIME() extracts only the time component from a DATETIME value.
On version 5.7, MySQL added support for fractional seconds (up to 6 digits) on DATETIME columns. The query above will include the entries having time greater than 06:00:00 but smaller than 06:00:01 (events that happened during the first second after 6 AM sharp).
For MySQL 5.7 and newer, the correct query is:
SELECT count(code) AS `count`
FROM `table 1`
WHERE ID='58'
AND (TIME(`DateTime`) <= '06:00:00' OR '20:00:00' <= TIME(`DateTime`))
I don't know about SQL Server.

SQL Server: Group results on time interval

I've got an MS SQL Server table that records our plant's alarm events with a row for each alarm and a datetime column to capture when the alarm happened.
We run our plant in 12 hour shifts (6 am to 6pm, 6pm to 6am) and I need to figure out how many alarms we're getting each shift. How do I group my results to get that?
The original table looks something like this:
DateTime Alarm Name
2010-01-05 14:32:22 Overpressure
2010-01-05 21:32:59 Underspeed
2010-01-06 05:58:13 Underspeed
2010-01-06 06:02:46 Machine Current Fault
And we need to group the results something like this:
Date Shift Count
2010-01-05 Day 1
2010-01-05 Night 2
2010-01-06 Day 1
Note that if alarms happen between 6 pm on say Jan 5th and 6 am on Jan 6th, they all get counted as Night Shift from Jan 5th.
Any advice?
In this solution, I work out the shift start/end times by subtracting 6 hours from the event time.
DECLARE #t TABLE
([DateTime] DATETIME
,[Alarm Name] VARCHAR(30)
)
INSERT #t
SELECT '2010-01-05 14:32:22','Overpressure'
UNION SELECT '2010-01-05 21:32:59','Underspeed'
UNION SELECT '2010-01-06 05:58:13','Underspeed'
UNION SELECT '2010-01-06 06:02:46','Machine Current Fault'
SELECT CONVERT(CHAR(10),DATEADD(hh,-6,[DateTime]),120) AS date
,CASE WHEN DATEPART(hh,DATEADD(hh,-6,[DateTime])) < 12
THEN 'day'
ELSE 'night'
END AS shift
,COUNT(1) AS cnt
FROM #t
GROUP BY CONVERT(CHAR(10),DATEADD(hh,-6,[DateTime]),120)
,CASE WHEN DATEPART(hh,DATEADD(hh,-6,[DateTime])) < 12
THEN 'day'
ELSE 'night'
END
order by 1,2

Resources