Postgres convert string to time - sql

I have the following query:
SELECT id, start_date::TIME, occurrence->0->>'startsOn' FROM service WHERE name='A.F';
Which return:
id | start_date | ?column?
------+------------+---------------------------------
1573 | 18:00:00 | Mon, 29 Jun 2015 18:00:00 +0000
1592 | 10:00:00 | Wed, 24 Jun 2015 10:00:00 +0000
1605 | 18:00:00 | Thu, 25 Jun 2015 18:00:00 +0000
1571 | 10:00:00 | Mon, 29 Jun 2015 10:00:00 +0000
1591 | 20:15:00 | Tue, 30 Jun 2015 20:15:00 +0000
1578 | 18:00:00 | Mon, 29 Jun 2015 20:00:00 +0000
1620 | 12:00:00 | Sat, 27 Jun 2015 12:00:00 +0000
(7 rows)
what I am trying to do is convert occurrence->0->>'startsOn' to time, so the expected result should be:
id | start_date | ?column?
------+------------+---------------------------------
1573 | 18:00:00 | 18:00:00
1592 | 10:00:00 | 10:00:00
1605 | 18:00:00 | 18:00:00
1571 | 10:00:00 | 10:00:00
1591 | 20:15:00 | 20:15:00
1578 | 18:00:00 | 20:00:00
1620 | 12:00:00 | 12:00:00
i tried the following:
SELECT id, start_date::TIME, occurrence->0->>'startsOn'::TIME FROM service WHERE name='A.F';
But it is not working as it gives me the following syntax error:
ERROR: invalid input syntax for type time: "startsOn"

select ('[{"startsOn":"Mon, 29 Jun 2015 18:00:00 +0000"}]'::json->0->>'startsOn')::timestamp::time
I did not have column "occurrence" so I mocked it up from your output

Related

SQL - Split open & Close time Into intervals of 30 minutes

Purpose: I work in Hospitality Industry. I want to understand at what time the Restaurant is full and what time it is less busy. I have the opening and closing times, I want to split it 30 minute interval period.
I would really appreciate if you could ease help me.
Thanking you in advance
Table
Check# Open CloseTime
25484 17:34 18:06
25488 18:04 21:22
Output
Check# Open Close Duration
25484 17:34 18:00 0:25
25484 18:00 18:30 0:30
25488 18:08 18:30 0:21
25488 18:30 19:00 0:30
25488 19:00 19:30 0:30
25488 19:30 20:00 0:30
25488 20:00 20:30 0:30
25488 20:30 21:00 0:30
25488 21:00 21:30 0:30
I am new to SQL. I am good at Excel, but due to its limitations i want to use SQL. I just know the basics in SQL.
I have tried on the google, but could not find solution to it. All i can see use of Date Keywords, but not the Field name in the code, hence i am unable to use them.
Could you try this, it works in MySQL 8.0:
WITH RECURSIVE times AS (
SELECT time '0:00' AS `Open`, time '0:30' as `Close`
UNION ALL
SELECT addtime(`Open`, '0:30'), addtime(`Close`, '0:30')
FROM times
WHERE `Open` < time '23:30'
)
SELECT c.`Check`,
greatest(t.`Open`, c.`Open`) `Open`,
least(t.`Close`, c.`CloseTime`) `Close`,
timediff(least(t.`Close`, c.`CloseTime`), greatest(t.`Open`, c.`Open`)) `Duration`
FROM times t
JOIN checks c ON (c.`Open` < t.`Close` AND c.`CloseTime` > t.`Open`);
| Check | Open | Close | Duration |
| ----- | -------- | -------- | -------- |
| 25484 | 17:34:00 | 18:00:00 | 00:26:00 |
| 25484 | 18:00:00 | 18:06:00 | 00:06:00 |
| 25488 | 18:04:00 | 18:30:00 | 00:26:00 |
| 25488 | 18:30:00 | 19:00:00 | 00:30:00 |
| 25488 | 19:00:00 | 19:30:00 | 00:30:00 |
| 25488 | 19:30:00 | 20:00:00 | 00:30:00 |
| 25488 | 20:00:00 | 20:30:00 | 00:30:00 |
| 25488 | 20:30:00 | 21:00:00 | 00:30:00 |
| 25488 | 21:00:00 | 21:22:00 | 00:22:00 |
->Fiddle
This works for SQL Server 2019:
WITH times([Open], [Close]) AS (
SELECT cast({t'00:00:00'} as time) as "Open",
cast({t'00:30:00'} as time) as "Close"
UNION ALL
SELECT dateadd(minute, 30, [Open]), dateadd(minute, 30, [Close])
FROM times
WHERE [Open] < cast({t'23:30:00'} as time)
)
SELECT c.[Check],
iif(t.[Open] > c.[Open], t.[Open], c.[Open]) as [Open],
iif(t.[Close] < c.[CloseTime], t.[Close], c.[CloseTime]) as [Close],
datediff(minute,
iif(t.[Open] > c.[Open], t.[Open], c.[Open]),
iif(t.[Close] < c.[CloseTime], t.[Close], c.[CloseTime])) Duration
FROM times t
JOIN checks c ON (c.[Open] < t.[Close] AND c.[CloseTime] > t.[Open]);
Check | Open | Close | Duration
25484 | 17:34:00.0000000 | 18:00:00.0000000 | 26
25484 | 18:00:00.0000000 | 18:06:00.0000000 | 6
25488 | 18:04:00.0000000 | 18:30:00.0000000 | 26
25488 | 18:30:00.0000000 | 19:00:00.0000000 | 30
25488 | 19:00:00.0000000 | 19:30:00.0000000 | 30
25488 | 19:30:00.0000000 | 20:00:00.0000000 | 30
25488 | 20:00:00.0000000 | 20:30:00.0000000 | 30
25488 | 20:30:00.0000000 | 21:00:00.0000000 | 30
25488 | 21:00:00.0000000 | 21:22:00.0000000 | 22
->Fiddle

Converting day datetime to timestamp in Snowflake

I have a datetime data in a table in Snowflake and I want to convert it into a timestamp
|-----------------------------------------|
| Date |
|-----------------------------------------|
| Wed 22 Mar 2022 12:51:21 -0500 |
| Sun 28 Apr 2022 02:21:19 -0500 |
| Mon 21 Mar 2021 18:31:59 -0500 |
| Fri 12 Jan 2022 19:41:46 -0500 |
| Thu 09 Feb 2022 23:51:17 -0500 |
| Tue 17 May 2021 07:61:07 -0500 |
| Wed 07 Oct 2022 01:71:01 -0500 |
|-----------------------------------------|
The output I want is:
|------------------------------------|
| Date |
|------------------------------------|
| 03/22/2022 12:51:21 -0500 |
| 04/28/2022 02:21:19 -0500 |
| 03/21/2021 18:31:59 -0500 |
| 01/12/2022 19:41:46 -0500 |
| 02/09/2022 23:51:17 -0500 |
| 05/17/2021 07:61:07 -0500 |
| 10/07/2022 01:71:01 -0500 |
|------------------------------------|
The methods I tried:
select to_date(date) from my_table
select to_date(date, 'mm/dd/yyyy h24:mi:ss') from my_table
select to_timestamp_tz(date) from my_table
etc.. None of the above conversions worked
using the correct formatting tokens, your valid datetime strings can be parsed. Depending if you what to have or not have timezone part on the timestamp, indicates which function you should use.
SELECT column1
,TRY_TO_TIMESTAMP_tz(column1, 'dy dd mon yyyy hh:mi:ss tzhtzm') as tz
,TRY_TO_TIMESTAMP(column1, 'dy dd mon yyyy hh:mi:ss tzhtzm') as default
,TRY_TO_TIMESTAMP_ntz(column1, 'dy dd mon yyyy hh:mi:ss tzhtzm') as ntz
FROM VALUES
('Wed 22 Mar 2022 12:51:21 -0500'),
('Sun 28 Apr 2022 02:21:19 -0500'),
('Mon 21 Mar 2021 18:31:59 -0500'),
('Fri 12 Jan 2022 19:41:46 -0500'),
('Thu 09 Feb 2022 23:51:17 -0500'),
('Tue 17 May 2021 07:61:07 -0500'),
('Thu 07 Oct 2022 01:71:01 -0500')
gives:
COLUMN1
TZ
DEFAULT
NTZ
Wed 22 Mar 2022 12:51:21 -0500
2022-03-22 12:51:21.000 -0500
2022-03-22 12:51:21.000
2022-03-22 12:51:21.000
Sun 28 Apr 2022 02:21:19 -0500
2022-04-28 02:21:19.000 -0500
2022-04-28 02:21:19.000
2022-04-28 02:21:19.000
Mon 21 Mar 2021 18:31:59 -0500
2021-03-21 18:31:59.000 -0500
2021-03-21 18:31:59.000
2021-03-21 18:31:59.000
Fri 12 Jan 2022 19:41:46 -0500
2022-01-12 19:41:46.000 -0500
2022-01-12 19:41:46.000
2022-01-12 19:41:46.000
Thu 09 Feb 2022 23:51:17 -0500
2022-02-09 23:51:17.000 -0500
2022-02-09 23:51:17.000
2022-02-09 23:51:17.000
Tue 17 May 2021 07:61:07 -0500
null
null
null
Thu 07 Oct 2022 01:71:01 -0500
null
null
null
because that last two are invalid times, if you correct the time to be in the valid range, the day being wrong is ignored.

SQL-Aggregate Timeseries Table (HourOfDay, Val) to Average Value of HourOfDay by Weeekday (fi. Avg of Mondays 10:00-11:00, 11:00-12:00,...,Tue...)

So far I made an SQL query that provides me with a table containing the amount of customers handled for each hour of the day - given a arbitrary start and an end datetime value (from Grafana interface). The result might be over many weeks. My goal is to implement an hourly heatmap by weekday with averaged values.
How do I aggregate those customer per hour to show the average value of that hours per weekday?
So let's say I got 24 values per day over 19 days. How do I aggregate so I get 24 values for each mon, tue, wed, thu, fri, sat, sun - each hour representing the average value for those days?
Also only use data of full weeks, so strip leading and trailing days, that are not part of a fully represented week (so same amount of individual weekdays representing an average value).
Here is a segment on how the return of my SQL query looks so far. (hour of each day, number of customers):
...
2021-12-13 11:00:00 | 0
2021-12-13 12:00:00 | 3
2021-12-13 13:00:00 | 4
2021-12-13 14:00:00 | 4
2021-12-13 15:00:00 | 7
2021-12-13 16:00:00 | 17
2021-12-13 17:00:00 | 12
2021-12-13 18:00:00 | 18
2021-12-13 19:00:00 | 15
2021-12-13 20:00:00 | 8
2021-12-13 21:00:00 | 10
2021-12-13 22:00:00 | 1
2021-12-13 23:00:00 | 0
2021-12-14 00:00:00 | 0
2021-12-14 01:00:00 | 0
2021-12-14 02:00:00 | 0
2021-12-14 03:00:00 | 0
2021-12-14 04:00:00 | 0
2021-12-14 05:00:00 | 0
2021-12-14 06:00:00 | 0
2021-12-14 07:00:00 | 0
2021-12-14 08:00:00 | 0
2021-12-14 09:00:00 | 0
2021-12-14 10:00:00 | 12
2021-12-14 11:00:00 | 12
2021-12-14 12:00:00 | 19
2021-12-14 13:00:00 | 11
2021-12-14 14:00:00 | 11
2021-12-14 15:00:00 | 12
2021-12-14 16:00:00 | 9
2021-12-14 17:00:00 | 2
...
So (schematically, example data) startDate 2021-12-10 11:00 to endDate 2021-12-31 17:00
-------------------------------
...
Mon 2021-12-13 12:00 | 3
Mon 2021-12-13 13:00 | 4
Mon 2021-12-13 14:00 | 4
...
Mon 2021-12-20 12:00 | 1
Mon 2021-12-20 13:00 | 6
Mon 2021-12-20 13:00 | 2
...
Mon 2021-12-27 12:00 | 2
Mon 2021-12-27 13:00 | 2
Mon 2021-12-27 13:00 | 3
...
-------------------------------
into this:
strip leading fri 10., sat 11., sun 12.
strip trailing tue 28., wen 29., thu 30., fri 31.
average hours per weekday
-------------------------------
...
Mon 12:00 | 2
Mon 13:00 | 4
Mon 14:00 | 3
...
Tue 12:00 | x
Tue 13:00 | y
Tue 13:00 | z
...
-------------------------------
My approach so far:
WITH CustomersPerHour as (
SELECT dateadd(hour, datediff(hour, 0, Systemdatum),0) as DayHour, Count(*) as C
FROM CustomerList
WHERE CustomerID > 0
AND Datum BETWEEN '2021-12-010T11:00:00Z' AND '2021-12-31T17:00:00Z'
AND EntryID IN (62,65)
AND CustomerID IN (SELECT * FROM udf_getActiveUsers())
GROUP BY dateadd(hour, datediff(hour, 0, Systemdatum), 0)
)
-- add null values on missing data/insert missing hours
SELECT DATEDIFF(second, '1970-01-01', dt.Date) AS time, C as Customers
FROM dbo.udf_generateHoursTable('2021-12-03T18:14:56Z', '2022-03-13T18:14:56Z') as dt
LEFT JOIN CustomersPerHour cPh ON dt.Date = cPh.DayHour
ORDER BY
time ASC
Hi simpliest solution is just do what you have written in example. Create custom base for aggregation.
So first step is to prepare your data in aggregated table with Date & Hour precision & customer count.
Then create base.
This is example of basic idea:
-- EXAMPLE
SELECT
DATENAME(WEEKDAY, GETDATE()) + ' ' + CAST(DATEPART(HOUR, GETDATE()) + ':00' AS varchar(8))
-- OUTPUT: Sunday 21:00
You can concatenate data and then use it in GROUP BY clause.
Adjust this query for your use case:
SELECT
DATENAME(WEEKDAY, <DATETIME_COL>) + ' ' + CAST(DATEPART(HOUR, <DATETIME_COL>) AS varchar(8)) + ':00' as base
,SUM(...) as sum_of_whatever
,AVG(...) as avg_of_whatever
FROM <YOUR_AGG_TABLE>
GROUP BY DATENAME(WEEKDAY, <DATETIME_COL>) + ' ' + CAST(DATEPART(HOUR, <DATETIME_COL>) AS varchar(8)) + ':00'
This create base exactly as you wanted.
You can use this logic to create other desired agg. bases.

How to change the default day of week and timestamp using date_trunc in snowflake sql

I have a timestamp variable as input, and I want to group the data by week, with a week defined as being between saturday 21:00:00 and saturday 20:59:59. I am querying from a snowflake database.
My data looks like this:
employee_id | shift_started_at | hours_worked
1 | '2018-09-12 08:00:00' | 2
2 | '2018-09-10 22:00:00' | 8
1 | '2018-09-18 08:00:00' | 3
I am trying something like this:
alter session set week_start = 6;
SELECT dateadd('hour',21,date_trunc('week',shift_started_at)) as week_starts_at,
min(shift_started_at) as first_shift_of_week,
max(shift_started_at) as last_shift_of_week,
sum(hours_worked)
FROM table
group by 1;
But even though this query gives me the right date for week_starts_at, the min and max select statements show that the group by statement is ignoring the dateadd function. In short, my weeks are being counted from midnight to midnight on saturday. Any advice on how to change the default timestamp used by date_trunc? Thank you!
The problem is that you're applying date_trunc(week..) before adjusting the time by hours. One solution would be:
first, move the shift times by 3 hours forward, so 9pm shift starts on Sunday midnight
then truncate to a week, with Sunday being the first day of the week
then move the result back 3 hours, to 21 on Saturday
Here's an example:
alter session set week_start = 7, timestamp_output_format='YYYY-MM-DD HH24:MI:SS DY';
create or replace table x(t timestamp);
insert into x values('2018-09-14 08:00:00'),('2018-09-15 20:59:59'),('2018-09-15 21:00:00'),('2018-09-16 23:00:00'), ('2018-09-22 20:59:59'),('2018-09-22 21:00:00');
select t, dateadd(hour, 3, t), date_trunc(week, dateadd(hour, 3, t)), dateadd(hour, -3, date_trunc(week, dateadd(hour, 3, t))) from x;
-------------------------+-------------------------+---------------------------------------+----------------------------------------------------------+
T | DATEADD(HOUR, 3, T) | DATE_TRUNC(WEEK, DATEADD(HOUR, 3, T)) | DATEADD(HOUR, -3, DATE_TRUNC(WEEK, DATEADD(HOUR, 3, T))) |
-------------------------+-------------------------+---------------------------------------+----------------------------------------------------------+
2018-09-14 08:00:00 Fri | 2018-09-14 11:00:00 Fri | 2018-09-09 00:00:00 Sun | 2018-09-08 21:00:00 Sat |
2018-09-15 20:59:59 Sat | 2018-09-15 23:59:59 Sat | 2018-09-09 00:00:00 Sun | 2018-09-08 21:00:00 Sat |
2018-09-15 21:00:00 Sat | 2018-09-16 00:00:00 Sun | 2018-09-16 00:00:00 Sun | 2018-09-15 21:00:00 Sat |
2018-09-16 23:00:00 Sun | 2018-09-17 02:00:00 Mon | 2018-09-16 00:00:00 Sun | 2018-09-15 21:00:00 Sat |
2018-09-22 20:59:59 Sat | 2018-09-22 23:59:59 Sat | 2018-09-16 00:00:00 Sun | 2018-09-15 21:00:00 Sat |
2018-09-22 21:00:00 Sat | 2018-09-23 00:00:00 Sun | 2018-09-23 00:00:00 Sun | 2018-09-22 21:00:00 Sat |
-------------------------+-------------------------+---------------------------------------+----------------------------------------------------------+
The last column you can use for grouping, and things should work.

Replacing a field with duplicate info to zero in a query

I have a table like this
|data| fromDateTime | toDateTime |
------------------------------------------------
| 2 | 2017-09-08 08:00:00 | 2017-09-08 11:00:00 |
| 2 | 2017-09-08 08:00:00 | 2017-09-08 11:00:00 |
| 2 | 2017-09-08 08:00:00 | 2017-09-08 11:00:00 |
| 3 | 2017-09-08 11:30:00 | 2017-09-08 17:00:00 |
| 3 | 2017-09-08 11:30:00 | 2017-09-08 17:00:00 |
| 4 | 2017-09-08 17:00:00 | 2017-09-08 19:00:00 |
| 4 | 2017-09-08 17:00:00 | 2017-09-08 19:00:00 |
| 4 | 2017-09-08 17:00:00 | 2017-09-08 19:00:00 |
And I need to return this
|data| fromdatetime | toDateTime |
------------------------------------------------
| 2 | 2017-09-08 08:00:00 | 2017-09-08 11:00:00 |
| 0 | 2017-09-08 08:00:00 | 2017-09-08 11:00:00 |
| 0 | 2017-09-08 08:00:00 | 2017-09-08 11:00:00 |
| 3 | 2017-09-08 11:30:00 | 2017-09-08 17:00:00 |
| 0 | 2017-09-08 11:30:00 | 2017-09-08 17:00:00 |
| 4 | 2017-09-08 17:00:00 | 2017-09-08 19:00:00 |
| 0 | 2017-09-08 17:00:00 | 2017-09-08 19:00:00 |
| 0 | 2017-09-08 17:00:00 | 2017-09-08 19:00:00 |
The moral of the story is that the data in the "data" column contains duplicate information that gets summed in a report resulting in bad info I NEED THAT ROW THOUGH I cannot get rid of the row with distinct. Also there are many other columns that get returned with this query that contain similar but sometimes different information with no primary key associated with it so I need the rows I cannot use distinct
You can do using LAG:
Select IIF(LAG(data,1, 0) OVER (ORDER BY data) = data, 0, data) as data
,fromDateTime
,toDateTime
From #temp;
I hope you are not using version older then SQL Server 2012.