Need to simplify SQL query (Enter date at four locations) - sql

I created a query which calculates the average of several sums over multiple tables. This needs to be run every week and how the code is made currently I need to change 4 dates in the query every time. I'm thinking this can be done more efficiently but i'm unsure how.
Select ROUND(
(Select sum (calls)
FROM (SELECT sum(ski.ANSTIME) AS calls
FROM SYNONYMS syn
JOIN SKILL ski on (syn.value = ski.split)
WHERE syn.ITEM_TYPE = 'split'
AND (SELECT (timestamp '1970-01-01 00:00:00 GMT' +numtodsinterval(ski.starttime_utc, 'SECOND'))
at time zone 'Europe/Warsaw'
FROM dual) >= '17-07-17 00:00:00 EUROPE/WARSAW' -- Date to be altered every week
AND (SELECT (timestamp '1970-01-01 00:00:00 GMT' +numtodsinterval(ski.starttime_utc, 'SECOND'))
at time zone 'Europe/Warsaw'
FROM dual) <= '24-07-17 00:00:00 EUROPE/WARSAW' -- Date to be altered every week
UNION ALL
SELECT sum(vdn.ANSTIME) AS calls
FROM SYNONYMS syn
JOIN VDN vdn on (syn.value = vdn.vdn)
WHERE syn.ITEM_TYPE = 'vdn'
AND (SELECT (timestamp '1970-01-01 00:00:00 GMT' +numtodsinterval(vdn.starttime_utc, 'SECOND'))
at time zone 'Europe/Warsaw'
FROM dual) >= '17-07-17 00:00:00 EUROPE/WARSAW' -- Date to be altered every week
AND (SELECT (timestamp '1970-01-01 00:00:00 GMT' +numtodsinterval(vdn.starttime_utc, 'SECOND'))
at time zone 'Europe/Warsaw'
FROM dual) <= '24-07-17 00:00:00 EUROPE/WARSAW')) -- Date to be altered every week
/ -- devided by
(SELECT sum (calltime)
FROM (SELECT sum(ski.acdcalls) AS calltime
FROM SYNONYMS syn
JOIN SKILL ski on (syn.value = ski.split)
WHERE syn.ITEM_TYPE = 'split'
AND (SELECT (timestamp '1970-01-01 00:00:00 GMT' +numtodsinterval(ski.starttime_utc, 'SECOND'))
at time zone 'Europe/Warsaw'
FROM dual) >= '17-07-17 00:00:00 EUROPE/WARSAW' -- Date to be altered every week
AND (SELECT (timestamp '1970-01-01 00:00:00 GMT' +numtodsinterval(ski.starttime_utc, 'SECOND'))
at time zone 'Europe/Warsaw'
FROM dual) <= '24-07-17 00:00:00 EUROPE/WARSAW' -- Date to be altered every week
UNION ALL
SELECT sum(vdn.acdcalls) AS calltime
FROM SYNONYMS syn
JOIN VDN vdn on (syn.value = vdn.vdn)
WHERE syn.ITEM_TYPE = 'vdn'
AND (SELECT (timestamp '1970-01-01 00:00:00 GMT' +numtodsinterval(vdn.starttime_utc, 'SECOND'))
at time zone 'Europe/Warsaw'
FROM dual) >= '17-07-17 00:00:00 EUROPE/WARSAW' -- Date to be altered every week
AND (SELECT (timestamp '1970-01-01 00:00:00 GMT' +numtodsinterval(vdn.starttime_utc, 'SECOND'))
at time zone 'Europe/Warsaw'
FROM dual) <= '24-07-17 00:00:00 EUROPE/WARSAW')) -- Date to be altered every week
,0) AS average
FROM dual

If I understand correctly you're trying to generate some weekly summary so instead of entering date you can try to use trunc(sysdate) for second date and trunc(sysdate - 7) for first.
Second possibility is to create temporary table (or just with statement) which will hold single date and join that to your query. Instead <= '24-07-17 00:00:00 EUROPE/WARSAW' you will have <= temp_date where temp_date comes from CTE.

Related

BigQuery - Query for each elements

I would like to loop over several elements for a query.
Here is the query :
SELECT
timestamp_trunc(timestamp, DAY) as Day,
count(1) as Number
FROM `table`
WHERE user_id="12345" AND timestamp >= '2021-07-05 00:00:00 UTC' AND timestamp <= '2021-07-08 23:59:59 UTC'
GROUP BY 1
ORDER BY Day
So I have for the user "12345" a row counter per each day between two dates, this is perfect.
But I would like to do this query for each user_id of my table.
Thank you very much
SELECT
timestamp_trunc(timestamp, DAY) as Day,
user_id,
count(1) as Number
FROM `table`
WHERE timestamp >= '2021-07-05 00:00:00 UTC' AND timestamp <= '2021-07-08 23:59:59 UTC'
GROUP BY 1, 2
ORDER BY Day
If you know the users, then use conditional aggregation:
SELECT timestamp_trunc(timestamp, DAY) as Day,
COUNTIF(user_id = 12345) as cnt_12345,
COUNTIF(user_id = 67) as cnt_67,
COUNTIF(user_id = 89) as cnt_89
FROM `table`
WHERE timestamp >= '2021-07-05 00:00:00 UTC' AND
timestamp <= '2021-07-09 00:00:00 UTC'
GROUP BY 1
ORDER BY 1;
Note the change that I made to the time comparison as well -- so you don't have to worry about fractions of a second before midnight.

How to subtract 2 timestamp (one as a date, other in HH24:MI format) and get the result in HH24:MI format in OracleSQL

I have a table with two columns, Start_time and SLA.
Start time updates for each day and is in a date format e.g., 01-Jun-2021 19:15:38
SLA column is having fixed HH24MI as 2010
I want 1915 - 2010 to be -00:55 (as in HH24MI format)
SELECT TO_CHAR((TO_CHAR(START_TIME,'HH24')||TO_CHAR(START_TIME,'MI'))-2010,'0000')
FROM DUAL;
Above will give the result as -0095 but I want it to be -00:55
Store the start_time as a DATE data type and the sla as an INTERVAL DAY TO SECOND data type:
CREATE TABLE table_name (
start_time DATE,
sla INTERVAL DAY TO SECOND
);
Then your data would be:
INSERT INTO table_name ( start_time, sla ) VALUES (
TO_DATE('01-Jun-2021 19:15:38', 'DD-MON-YYYY HH24:MI:SS', 'NLS_DATE_LANGUAGE=American'),
INTERVAL '20:10:00' HOUR TO SECOND
);
And, to find the difference, you can use:
SELECT start_time,
sla,
(start_time - TRUNC(start_time)) DAY TO SECOND - sla AS difference
FROM table_name
Which outputs:
START_TIME
SLA
DIFFERENCE
2021-06-01 19:15:38
+00 20:10:00.000000
-000000000 00:54:22.000000000
If you want the output as a formatted string, rather than as an interval, then:
SELECT start_time,
sla,
CASE WHEN difference < INTERVAL '0' HOUR THEN '-' END
|| TO_CHAR( ABS( EXTRACT( HOUR FROM difference ) ), 'FM00' )
|| TO_CHAR( ABS( EXTRACT( MINUTE FROM difference ) ), 'FM00' )
AS difference
FROM (
SELECT start_time,
sla,
(start_time - TRUNC(start_time)) DAY TO SECOND - sla AS difference
FROM table_name
)
Which outputs:
START_TIME
SLA
DIFFERENCE
2021-06-01 19:15:38
+00 20:10:00.000000
-0054
db<>fiddle here

SQLite group by every specific interval

let's assume that I have a table with entries and these entries contains timestamp column (as Long) which is telling us when that entry arrived into a table.
Now, I want to make a SELECT query, in which I want to know how many entries came in selected interval with concrete frequency.
For example: interval is from 27.10.2020 to 30.10.2020 and frequency is 6 hours. The result of the query would tell me how many entries came in this interval in 6 hour groups.
Like:
27.10.2020 00:00:00 - 27.10.2020 06:00:00 : 2 entries
27.10.2020 06:00:00 - 27.10.2020 12:00:00 : 5 entries
27.10.2020 12:00:00 - 27.10.2020 18:00:00 : 0 entries
27.10.2020 18:00:00 - 28.10.2020 00:00:00 : 11 entries
28.10.2020 00:00:00 - 28.10.2020 06:00:00 : 8 entries
etc ...
The frequency parameter can be inserted in hours, days, weeks ...
Thank you all for you help!
First you need a recursive CTE like that returns the time intervals:
with cte as (
select '2020-10-27 00:00:00' datestart,
datetime('2020-10-27 00:00:00', '+6 hour') dateend
union all
select dateend,
min('2020-10-30 00:00:00', datetime(dateend, '+6 hour'))
from cte
where dateend < '2020-10-30 00:00:00'
)
Then you must do LEFT join of this CTE to the table and aggregate:
with cte as (
select '2020-10-27 00:00:00' datestart,
datetime('2020-10-27 00:00:00', '+6 hour') dateend
union all
select dateend,
min('2020-10-30 00:00:00', datetime(dateend, '+6 hour'))
from cte
where dateend < '2020-10-30 00:00:00'
)
select c.datestart, c.dateend, count(t.datecol) entries
from cte c left join tablename t
on datetime(t.datecol, 'unixepoch') >= c.datestart and datetime(t.datecol, 'unixepoch') < c.dateend
group by c.datestart, c.dateend
Replace tablename and datecol with the names of your table and date column.
If your date column contains milliseconds then change the ON clause to this:
on datetime(t.datecol / 1000, 'unixepoch') >= c.datestart
and datetime(t.datecol / 1000, 'unixepoch') < c.dateend
Here is one option:
select
datetime((strftime('%s', ts) / (6 * 60 * 60)) * 6 * 60 * 60, 'unixepoch') newts,
count(*) cnt
from mytable
where ts >= '2020-10-27' and ts < '2020-10-30'
group by newts
order by newts
ts represents the datetime column in your table. SQLite does not have a long datatype, so this assumes that you have a legitimate date stored as text.
The logic of the query is to turn the date to an epoch timestamp, then round it to 6 hours, which is represented by 6 * 60 * 60.

SQL query data between dates, efficiency

I'm trying to search through a fairly large (56m+ row) table using an SQL query. The complication to just being able to do some quick SQL query like this:
Select *COLUMNS*
From *Table*
Where *Conditions* And
LOG_ENTRY_TIMESTAMP between {StartDate} and {EndDate}
is that I need to pull the 23:00 - 24:00 hour from the day before {StartDate} without pulling the rest of the data from that date. {StartDate} and {EndDate} are user entered fields in a DATE format. LOG_ENTRY_TIMESTAMP is a TIMESTAMP data type.
Is there a more time-efficient way of doing this than having to do something like:
TRUNC(CAST(LOG_ENTRY_TIMESTAMP AS DATE), 'HH') BETWEEN {StartDate}-1/24 and {EndDate}+23/24
Data will look like:
ITEM LOG_ENTRY_TIMESTAMP
---- ----------------------------------
A 2/12/2018 10:02:19.214528 AM -0500
B 2/14/2018 11:02:19.224528 PM -0500
C 2/16/2018 01:02:19.412528 AM -0500
D 2/16/2018 11:02:19.412528 PM -0500
And if I search from {StartDate} = 2/15/2018 through {EndDate} = 2/16/2018, I want to capture B & C.
I would suggest:
Where *Conditions* And
LOG_ENTRY_TIMESTAMP between {StartDate} - 1/24 and {EndDate}
I need data from 11pm the night before until 11pm tonight
if I search from {StartDate} = 2/15/2018 through {EndDate} = 2/16/2018, I want to capture B & C.
Assuming LOG_ENTRY_TIMESTAMP is indexed you can utilise that with:
Where *Conditions* And
LOG_ENTRY_TIMESTAMP >= {StartDate} - 1/24 and
LOG_ENTRY_TIMESTAMP < {EndDate} + 23/24
Again assuming that the variables are actually dates with time set to midnight, {StartDate} - 1/24 gives you 23:00 on the day before that start date, and {EndDate} + 23/24 gives you 23:00 on the end date.
With your sample data in a CTE and the filter dates as date literals:
with your_table (item, log_entry_timestamp) as (
select 'A', to_timestamp_tz('2/12/2018 10:02:19.214528 AM -0500',
'MM/DD/YYYY HH:MI:SS.FF6 AM TZHTZM') from dual
union all select 'B', to_timestamp_tz('2/14/2018 11:02:19.224528 PM -0500',
'MM/DD/YYYY HH:MI:SS.FF6 AM TZHTZM') from dual
union all select 'C', to_timestamp_tz('2/16/2018 01:02:19.412528 AM -0500',
'MM/DD/YYYY HH:MI:SS.FF6 AM TZHTZM') from dual
union all select 'D', to_timestamp_tz('2/16/2018 11:02:19.412528 PM -0500',
'MM/DD/YYYY HH:MI:SS.FF6 AM TZHTZM') from dual
)
select *
from your_table
where LOG_ENTRY_TIMESTAMP >= date '2018-02-15' - 1/24
and LOG_ENTRY_TIMESTAMP < date '2018-02-16' + 23/24;
I LOG_ENTRY_TIMESTAMP
- ---------------------------------
B 2018-02-14 23:02:19.224528 -05:00
C 2018-02-16 01:02:19.412528 -05:00
But you need to verify what the actual data types are for the values being used for the variables, and whether any time zone conversion is being done, which could affect the range of values you actually match.

Converting time intervals to HH:MI am/pm format using SQL

My requirement is to convert 15 minute time intervals into HH:MI am/pm format using SQL. Giving an example:
IF interval = 0 => time = 12:00 am
IF interval = 15 => time = 12:15 am
IF interval = 30 => time = 12:30 am
IF interval = 45 => time = 12:45 am
IF interval = 60 => time = 01:00 am
and so on.,
So far, I've only managed to get the hourly intervals converted to HH:MI a/p format i.e. 12:00a, 01:00a etc., Here's a sample of the query that I'm using against my custom table.
START_TIME is the column containing the 15 minute interval entries.
select START_TIME/15,
case when (mod(START_TIME/15,4) = 0) then
case when (mod(START_TIME/15, 48) = 0) then (decode(START_TIME/15,0,'12:00 am',48,'12:00 pm'))
else (
case when (START_TIME/15 < 48) then (START_TIME/ 60 || 'am')
else (START_TIME / 60 - 12 || 'pm')
end
)
end
else ' '
end from MY_TABLE;
Can someone help me get it to work for all 15 min intervals? Thanks.
Try letting oracle do the time adding for you.
By default adding one to a date adds a single day. You want to add the time in your start_time column.
We will start with a date with no time component using TRUNC.
SELECT TRUNC(SYSDATE) FROM DUAL;
Then add 1/(60*24) * START_TIME minutes to this date and print out only the time portion of the date.
select TO_CHAR (TRUNC(SYSDATE) + (1/(60*24) * START_TIME), 'HH:MI AM')
from MY_TABLE
Maybe I don't understand your problem. Oracle interval types already do this.
The purpose of intervals in Oracle are to store time offsets and to allow you to do what you seem to be asking. An interval can be added to a base date/timestamp to produce a new date/timestamp and formatted in any format use date/time formatting functions with TO_CHAR and TO_DATE.
create table intervals(base_time timestamp, offset_interval interval);
SQL> desc intervals
Name Null? Type
----------------------------------------- -------- ----------------------------
BASE_TIME TIMESTAMP(6)
OFFSET_INTERVAL INTERVAL DAY(2) TO SECOND(6)
SQL> insert into intervals values(sysdate, interval '15' minute);
1 row created.
SQL> insert into intervals values(sysdate, interval '30' minute);
1 row created.
SQL> insert into intervals values(sysdate, interval '45' minute);
1 row created.
SQL> insert into intervals values(sysdate, interval '60' minute);
1 row created.
SQL> insert into intervals values(sysdate, interval '90' minute);
1 row created.
SQL> insert into intervals values(sysdate, interval '120' minute);
1 row created.
And then add the base timestamp to the interval, and format with TO_CHAR()
SQL> select to_char(base_time + offset_interval, 'HH:MI AM') from intervals;
TO_CHAR(
--------
03:45 PM
04:00 PM
04:15 PM
04:30 PM
05:00 PM
05:30 PM
6 rows selected.