Oracle - Counts by time intervals - oracle

I have a table with some entries, and a DATE column indicating the date/time the record was inserted. What I need is to create a query where the data will be displayed in a tabular format with counts at the 15 minute intervals for the entire day. So, the output would look like:
LABEL DAY DATE 00:00 00:15 00:30 00:45.........23:30 23:45
ORDERS MONDAY 4/19/2021 1 1 0 1 0 1
ORDERS TUESDAY 4/20/2021 1 0 0 0 1 0
This is what they are asking for......and probably have to put that into a CSV at some later date. But for now, is there any simple way to make a query like this, or are we talking writing a procedure?

You can use conditional aggregation:
SELECT label,
TO_CHAR(TRUNC("DATE"), 'DAY') AS day,
TRUNC("DATE") AS "DATE",
COUNT( CASE TRUNC(("DATE" - TRUNC("DATE"))*96) WHEN 0 THEN 1 END ) AS "00:00",
COUNT( CASE TRUNC(("DATE" - TRUNC("DATE"))*96) WHEN 1 THEN 1 END ) AS "00:15",
COUNT( CASE TRUNC(("DATE" - TRUNC("DATE"))*96) WHEN 2 THEN 1 END ) AS "00:30",
COUNT( CASE TRUNC(("DATE" - TRUNC("DATE"))*96) WHEN 3 THEN 1 END ) AS "00:45",
COUNT( CASE TRUNC(("DATE" - TRUNC("DATE"))*96) WHEN 4 THEN 1 END ) AS "01:00"
-- ...
FROM table_name
GROUP BY TRUNC( "DATE" ), label
ORDER BY TRUNC( "DATE" ), label
Or PIVOT:
SELECT TO_CHAR(dt,'FMDAY') AS day,
t.*
FROM (
SELECT label,
TRUNC("DATE") AS dt,
TRUNC( ("DATE" - TRUNC("DATE")) * 96 ) AS time
FROM table_name
)
PIVOT(
COUNT(*) FOR time IN (
0 AS "00:00",
1 AS "00:15",
2 AS "00:30",
3 AS "00:45",
4 AS "01:00",
5 AS "01:15",
-- ...
92 AS "23:00",
93 AS "23:15",
94 AS "23:30",
95 AS "23:45"
)
) t;
db<>fiddle here

I'll try to describe as detailed as I can.
First of all you can truncate your minutes to 15 minutes intervals:
--test data:
with t(label,dt) as (
select 'ORDERS',date'2021-04-21'+numtodsinterval(level*2,'minute')
from dual
connect by level<=30
)
--main query:
select
dt, label, weekday, hh24, quarter, quarter*15 rounded_minutes
from
(
select
dt
,label
,to_char(dt,'day') weekday
,to_char(dt,'hh24') hh24
,trunc(to_number(to_char(dt,'mi'))*4/60) quarter
from t
);
Results:
DT LABEL WEEKDAY HH24 QUARTER ROUNDED_MINUTES
------------------- ------ ---------- ---- ---------- ---------------
2021-04-21 00:02:00 ORDERS wednesday 00 0 0
2021-04-21 00:04:00 ORDERS wednesday 00 0 0
2021-04-21 00:06:00 ORDERS wednesday 00 0 0
2021-04-21 00:08:00 ORDERS wednesday 00 0 0
2021-04-21 00:10:00 ORDERS wednesday 00 0 0
2021-04-21 00:12:00 ORDERS wednesday 00 0 0
2021-04-21 00:14:00 ORDERS wednesday 00 0 0
2021-04-21 00:16:00 ORDERS wednesday 00 1 15
2021-04-21 00:18:00 ORDERS wednesday 00 1 15
2021-04-21 00:20:00 ORDERS wednesday 00 1 15
2021-04-21 00:22:00 ORDERS wednesday 00 1 15
2021-04-21 00:24:00 ORDERS wednesday 00 1 15
2021-04-21 00:26:00 ORDERS wednesday 00 1 15
2021-04-21 00:28:00 ORDERS wednesday 00 1 15
2021-04-21 00:30:00 ORDERS wednesday 00 2 30
2021-04-21 00:32:00 ORDERS wednesday 00 2 30
2021-04-21 00:34:00 ORDERS wednesday 00 2 30
2021-04-21 00:36:00 ORDERS wednesday 00 2 30
2021-04-21 00:38:00 ORDERS wednesday 00 2 30
2021-04-21 00:40:00 ORDERS wednesday 00 2 30
2021-04-21 00:42:00 ORDERS wednesday 00 2 30
2021-04-21 00:44:00 ORDERS wednesday 00 2 30
2021-04-21 00:46:00 ORDERS wednesday 00 3 45
2021-04-21 00:48:00 ORDERS wednesday 00 3 45
2021-04-21 00:50:00 ORDERS wednesday 00 3 45
2021-04-21 00:52:00 ORDERS wednesday 00 3 45
2021-04-21 00:54:00 ORDERS wednesday 00 3 45
2021-04-21 00:56:00 ORDERS wednesday 00 3 45
2021-04-21 00:58:00 ORDERS wednesday 00 3 45
2021-04-21 01:00:00 ORDERS wednesday 01 0 0
As you can see we calculate a quarter of hour using simple trunc(to_number(to_char(dt,'mi'))*4/60). So we just need to multiply it to 4 to get rounded minutes.
Then you can just concatenate them and use pivot clause to aggregate:
with t(label,dt) as (
select 'ORDERS',date'2021-04-21'+numtodsinterval(level*2,'minute')
from dual
connect by level<=3000
)
select *
from (
select
label, weekday, hh24||':'||to_char(quarter*15,'fm00') hhmi
from
(
select
dt
,label
,to_char(dt,'day') weekday
,to_char(dt,'hh24') hh24
,trunc(to_number(to_char(dt,'mi'))*4/60) quarter
from t
)
)
pivot(
count(*) for hhmi in (
'00:00','00:15','00:30','00:45','01:00','01:15','01:30','01:45'
,'02:00','02:15','02:30','02:45','03:00','03:15','03:30','03:45'
,'04:00','04:15','04:30','04:45','05:00','05:15','05:30','05:45'
,'06:00','06:15','06:30','06:45','07:00','07:15','07:30','07:45'
,'08:00','08:15','08:30','08:45','09:00','09:15','09:30','09:45'
,'10:00','10:15','10:30','10:45','11:00','11:15','11:30','11:45'
,'12:00','12:15','12:30','12:45','13:00','13:15','13:30','13:45'
,'14:00','14:15','14:30','14:45','15:00','15:15','15:30','15:45'
,'16:00','16:15','16:30','16:45','17:00','17:15','17:30','17:45'
,'18:00','18:15','18:30','18:45','19:00','19:15','19:30','19:45'
,'20:00','20:15','20:30','20:45','21:00','21:15','21:30','21:45'
,'22:00','22:15','22:30','22:45','23:00','23:15','23:30','23:45'
)
);
Results (shortened for readbility):
LABEL WEEKDAY '00:00' '00:15' '00:30' '00:45' '01:00' '01:15' ... '23:15' '23:30' '23:45'
------ ---------- ---------- ---------- ---------- ---------- ---------- ---------- ... ---------- ---------- ----------
ORDERS friday 8 7 8 7 8 7 ... 7 8 7
ORDERS saturday 8 7 8 7 8 7 ... 7 8 7
ORDERS sunday 8 7 8 7 8 7 ... 0 0 0
ORDERS thursday 8 7 8 7 8 7 ... 7 8 7
ORDERS wednesday 7 7 8 7 8 7 ... 7 8 7

Related

Get the sales count within a month

I have monthly agents' sales data. I need to get the first 25 days sales count and last 5 days columns.
How to get in separately.
I have below table
Agent_ID Date Device
2343 1/1/2019 33330
3245 1/1/2019 43554
2343 5/1/2019 46665
3245 10/1/2019 78900
2343 15/1/2019 55678
2343 26/1/2019 45678
3245 28/1/2019 48900
2343 30/1/2019 56710
5645 12/1/2019 33067
5645 15/1/2019 44890
2121 31/1/2019 55810
I need to get this below output table
Agent_ID first_25days_sale_count Last_5days_sale_count
2343 3 2
3245 2 1
5645 2 0
2121 0 1
Some months have 28, 29 or 31 days so naively using "first 25 days" and "last 5 days" may lead to either double counting (i.e. days 24 and 25 when February has 28 days) or not counting some days (i.e. day 26 when the the month has 31 days). You should decide whether you want to count:
The first 25 days and then the remaining 3-6 days after that; or
The last 5 days and then the 23-26 days before that.
Whichever you chose, you can use conditional aggregation:
SELECT agent_id,
COUNT(
CASE
WHEN EXTRACT( DAY FROM "Date" ) <= 25
THEN 1
END
) AS first_25days_sale_count,
COUNT(
CASE
WHEN EXTRACT( DAY FROM "Date" ) > 25
THEN 1
END
) AS after_first_25days_sale_count,
COUNT(
CASE
WHEN "Date" < TRUNC( LAST_DAY( "Date" ) ) - INTERVAL '4' DAY
THEN 1
END
) AS not_last_5days_sale_count,
COUNT(
CASE
WHEN "Date" >= TRUNC( LAST_DAY( "Date" ) ) - INTERVAL '4' DAY
THEN 1
END
) AS last_5days_sale_count
FROM your_table
GROUP BY agent_id;
So, for your sample data:
CREATE TABLE your_table ( Agent_ID, "Date", Device ) AS
SELECT 2343, DATE '2019-01-01', 33330 FROM DUAL UNION ALL
SELECT 3245, DATE '2019-01-01', 43554 FROM DUAL UNION ALL
SELECT 2343, DATE '2019-01-05', 46665 FROM DUAL UNION ALL
SELECT 3245, DATE '2019-01-10', 78900 FROM DUAL UNION ALL
SELECT 2343, DATE '2019-01-15', 55678 FROM DUAL UNION ALL
SELECT 2343, DATE '2019-01-26', 45678 FROM DUAL UNION ALL
SELECT 3245, DATE '2019-01-29', 48900 FROM DUAL UNION ALL
SELECT 2343, DATE '2019-01-30', 56710 FROM DUAL UNION ALL
SELECT 5645, DATE '2019-01-12', 33067 FROM DUAL UNION ALL
SELECT 5645, DATE '2019-01-15', 44890 FROM DUAL UNION ALL
SELECT 2121, DATE '2019-01-31', 55810 FROM DUAL;
This outputs:
AGENT_ID | FIRST_25DAYS_SALE_COUNT | AFTER_FIRST_25DAYS_SALE_COUNT | NOT_LAST_5DAYS_SALE_COUNT | LAST_5DAYS_SALE_COUNT
-------: | ----------------------: | ----------------------------: | ------------------------: | --------------------:
3245 | 2 | 1 | 2 | 1
2121 | 0 | 1 | 0 | 1
5645 | 2 | 0 | 2 | 0
2343 | 3 | 2 | 4 | 1
db<>fiddle here

How to make SELECT that returns TIMESTAMP and a value per minute within the current quarter hour?

I have a table as below, and I need select to return the value per minute within the current quarter. For example, if now it's 15:19, I need select to return TIMESTAMP and the value in this quarter between 15:15 and 15:30.
That is, I need select to return the last minutes of the current quarter of an hour. DB is ORACLE.
TIMESTAMP | VALUE
11/11/2019 15:09 | 45
11/11/2019 15:10 | 10
11/11/2019 15:11 | 15
11/11/2019 15:12 | 35
11/11/2019 15:13 | 55
11/11/2019 15:14 | 25
11/11/2019 15:15 | 20
11/11/2019 15:16 | 22
11/11/2019 15:17 | 12
11/11/2019 15:18 | 10
11/11/2019 15:19 | 21
I have tried TRUNC, but no success.
You need trunc by 15 mins.
You can do it using following logic:
Select * from your_table
Where your_timestamp_col
between trunc(systimestamp,'dd') + floor(to_char(systimestamp,'sssss.ff') / 900) / 96
And trunc(systimestamp,'dd') + ceil(to_char(systimestamp,'sssss.ff') / 900) / 96
Here, 900 represent seconds in 15 mins and 96 represents total such quarters in a day (24 hours * 4 quarters = 96)
Cheers!!

How to convert week number and week day into date

I have CVS files which contain date information in three separate colums which I would like to combine. The information I have is:
Two digit year (field 2)
Week number (field 3)
Day of week number (field 4)
How can I convert these 3 numbers into normal date format of the form YYYYMMDD?
My input file looks like:
740054,17,40,1,0000000000001,25,25,test1,1
740054,17,40,2,0000000000001,24,24,test2,1
740054,17,40,4,0000000000001,19,19,test3,1
And the expected output I would like to have is:
740054,20171002,0000000000001,25,25,test1,1
740054,20171003,0000000000001,24,24,test2,1
740054,20171005,0000000000001,19,19,test3,1
As an example for the first line: October 2, 2017 is the Monday (1) of the 40th week of the year 2017
Does anybody know how to do such a conversion?
I will make the assumption that your week-number is according to the ISO 8601 definition (for other definitions see here). This ISO 8601 standard is widely used in the world: EU and most of other
European countries, most of Asia, and Oceania
The ISO 8601 standard states the following:
There are 7 days in a week
The first day of the week is a Monday
The first week is the first week of the year which contains a
Thursday. This means it is the first week with 4 days or more
in January.
With this definition, it is possible to have a week number 53. These occur with the first of January is on a
Friday (E.g. 2016-01-01, 2010-01-01). Or, if the year before was a
leap year, also a Saturday. (E.g. 2005-01-01)
December 2015 January 2016
Mo Tu We Th Fr Sa Su CW Mo Tu We Th Fr Sa Su CW
1 2 3 4 5 6 49 1 2 3 53
7 8 9 10 11 12 13 50 4 5 6 7 8 9 10 01
14 15 16 17 18 19 20 51 11 12 13 14 15 16 17 02
21 22 23 24 25 26 27 52 18 19 20 21 22 23 24 03
28 29 30 31 53 25 26 27 28 29 30 31 04
Given the year, week_number and day_of_week, how can we reconstruct the date? The answer requires several steps and will compute the day of the year (doy) of the requested date.
To compute the day of the year doy we first need to figure out when the first-week starts as explained above. If Jan 01 is a Tuesday, then the first week only contains 6 days and not 7, while if Jan 01 is a Friday, the first week starts only the week after. So we can solve this by adding an offset. The offset can be found in the following table:
dow001 str: Mo Tu We Th Fr Sa Su
dow001 num: 01 02 03 04 05 06 07
offset : 0 -1 -2 -3 3 2 1
and this offset is computed as 3-(dow001+2)%7
So with this, the day of the year is very easily computed:
doy = (week_number-1) * 7 + 3-(dow001+2)%7 + day_of_week
So having this, we can write the following GNU awk tool:
awk 'function compute_date(YYYY,CW,DOW) {
dow001 = strftime("%u",mktime(YYYY " 01 01 00 00 00"))
doy = (CW-1)*7 + (3 - (dow001+2)%7) + DOW
return strftime("%Y%m%d",mktime(YYYY " 01 " doy " 00 00 00"))}
}
BEGIN { FS = OFS = "," }
{ datestr = compute_date(2000+$2,$3,$4) }
{ print $1, datestr , $5,$6,$7,$8,$9 }' file
740054,20171002,0000000000001,25,25,test1,1
740054,20171003,0000000000001,24,24,test2,1
740054,20171005,0000000000001,19,19,test3,1

Oracle SQL How do you fill out missing months and corresponding 0 [duplicate]

I have an Mview that brings data group by idNumber and Month. So I want to display 0 if there is no data for an specific month. This is my query:
select MonthName, myCost, myNumber
from
(
select MONTH mm, myCost, myNumber
from myOracle_mv
) myTotals,
(
select to_char(date '2012-12-1' + numtoyminterval(level,'month'), 'mm') MonthName
from dual
connect by level <= 12
) ALLMONTHS
where mm = MonthName
So I was expecting:
Month Number Data
-----------------------
1 abc123 4444
2 0
3 abc123 4444
4 abc123 4444
5 0
6 abc123 4444
7 abc123 4444
8 0
9 abc123 4444
10 abc123 4444
11 0
12 abc123 4444
Instead I'm still getting:
1 abc123 4444
3 abc123 4444
4 abc123 4444
6 abc123 4444
7 abc123 4444
9 abc123 4444
10 abc123 4444
12 abc123 4444
Any ideas?
Thanks!
EDIT: Thanks for the answers. I did have the outer join in my Query but forgot to type in because was concentrating in changes the names of the table/columns.
So yes, I have tried with the OUTER JOIN and I still not getting the expected results. Any feedback is greatly appreciated.
EDIT: This is the data on myOracle_MV:
3777.24 AAA 1 2012
49973.12 AAA 2 2012
4049.91 AAA 3 2012
469.485 AAA 4 2012
5872.22 AAA 5 2012
65837.71 AAA 6 2012
566.23 AAA 7 2012
18432.95 AAA 8 2012
4337.75 AAA 12 2011
18811 BBB 1 2012
29872.67 BBB 2 2012
29068.55 BBB 3 2012
264957.8 BBB 4 2012
67673 BBB 5 2012
855.02 BBB 6 2012
5226.1 BBB 7 2012
2663.24 BBB 8 2012
5490.58 BBB 12 2011
3845.47 CCC 1 2012
3050.54 CCC 2 2012
3784.44 CCC 3 2012
799.73 CCC 4 2012
124884.2 CCC 5 2012
5157.24 CCC 6 2012
19184.78 CCC 7 2012
2280.05 CCC 8 2012
107.07 DDD 3 2012
181.78 DDD 4 2012
110.09 DDD 5 2012
18016.19 DDD 6 2012
1772.95 DDD 7 2012
63.32 DDD 8 2012
Very similar to exsiting answers, but this:
select months.month, mv.mycost, coalesce(mv.mynumber, 0) as mynumber
from (
select to_char(date '1970-01-01'
+ numtoyminterval(level - 1, 'month'), 'mm') as month
from dual
connect by level <= 12) months
left join myoracle_mv mv
on mv.month = months.month
order by months.month, mv.mycost, mv.mynumber;
gives this with the data you posted:
MONTH MYCOST MYNUMBER
----- ------ ----------
01 AAA 3777.24
01 BBB 18811
01 CCC 3845.47
02 AAA 49973.12
02 BBB 29872.67
02 CCC 3050.54
03 AAA 4049.91
03 BBB 29068.55
03 CCC 3784.44
03 DDD 107.07
04 AAA 469.485
04 BBB 264957.8
04 CCC 799.73
04 DDD 181.78
05 AAA 5872.22
05 BBB 67673
05 CCC 124884.2
05 DDD 110.09
06 AAA 65837.71
06 BBB 855.02
06 CCC 5157.24
06 DDD 18016.19
07 AAA 566.23
07 BBB 5226.1
07 CCC 19184.78
07 DDD 1772.95
08 AAA 18432.95
08 BBB 2663.24
08 CCC 2280.05
08 DDD 63.32
09 0
10 0
11 0
12 AAA 4337.75
12 BBB 5490.58
35 rows selected
If you want a zero to appear in the mynumber column then you can make that:
select months.month, mv.mycost, coalesce(mv.mynumber, 0) as mynumber
which gives:
...
08 DDD 63.32
09 0
10 0
11 0
12 AAA 4337.75
...
From the comments on Jafar's answer it sounds like maybe you'd got that far on your own but you want zero values for all mycost values for all months. If that is the case then you need to get the list of possible values for mycost and outer join to that as well. This is taking all values that are in the MV already:
select months.month, costs.mycost, coalesce(mv.mynumber, 0) as mynumber
from (
select to_char(date '1970-01-01'
+ numtoyminterval(level - 1, 'month'), 'mm') as month
from dual
connect by level <= 12) months
cross join (
select distinct mycost
from myoracle_mv) costs
left join myoracle_mv mv
on mv.month = months.month
and mv.mycost = costs.mycost
order by months.month, costs.mycost, mv.mynumber;
and gives:
MONTH MYCOST MYNUMBER
----- ------ ----------
01 AAA 3777.24
01 BBB 18811
01 CCC 3845.47
01 DDD 0
02 AAA 49973.12
02 BBB 29872.67
02 CCC 3050.54
02 DDD 0
03 AAA 4049.91
03 BBB 29068.55
03 CCC 3784.44
03 DDD 107.07
04 AAA 469.485
04 BBB 264957.8
04 CCC 799.73
04 DDD 181.78
05 AAA 5872.22
05 BBB 67673
05 CCC 124884.2
05 DDD 110.09
06 AAA 65837.71
06 BBB 855.02
06 CCC 5157.24
06 DDD 18016.19
07 AAA 566.23
07 BBB 5226.1
07 CCC 19184.78
07 DDD 1772.95
08 AAA 18432.95
08 BBB 2663.24
08 CCC 2280.05
08 DDD 63.32
09 AAA 0
09 BBB 0
09 CCC 0
09 DDD 0
10 AAA 0
10 BBB 0
10 CCC 0
10 DDD 0
11 AAA 0
11 BBB 0
11 CCC 0
11 DDD 0
12 AAA 4337.75
12 BBB 5490.58
12 CCC 0
12 DDD 0
48 rows selected
But hopefully you have another table that holds the possble mycost values (assuming that's representing something like a cost center, rather than a price; slightly hard to tell what's what) and you can use that instead of the subquery.
SQL Fiddle.
Also note that if you wanted to add a filter, e.g. to restrict data to a particular year, you'd need to do that in the in the left join clause, not as a where clause, or you'd revert the outer join to an inner one. For example, adding this:
where mv.year = 2011
would mean you only got back two rows:
MONTH MYCOST MYNUMBER
----- ------ ----------
12 AAA 4337.75
12 BBB 5490.58
But if you made than another condition on the outer join you'd still get 48 rows back, with 46 of them having zeros and two having the values above:
...
left join myoracle_mv mv
on mv.month = months.month
and mv.mycost = costs.mycost
and mv.year = 2011
order by months.month, costs.mycost, mv.mynumber;
...
11 CCC 0
11 DDD 0
12 AAA 4337.75
12 BBB 5490.58
12 CCC 0
12 DDD 0
48 rows selected
You'd need to do an outer join between the two inline views
select MonthName, myCost, myNumber
from (select MONTH mm, myCost, myNumber
from myOracle_mv
) myTotals
right outer join
(select to_char(date '2012-12-1' + numtoyminterval(level,'month'), 'mm') MonthName
from dual
connect by level <= 12) ALLMONTHS
on( myTotals.mm = allmonths.MonthName )
You can also use the old Oracle-specific (+) syntax for outer joins but I would generally suggest using the SQL standard syntax.
Maybe something like this
select MonthName, COALESCE(myCost,0), myNumber
from
(
select to_char(date '2012-12-1' + numtoyminterval(level,'month'), 'mm') MonthName
from dual
connect by level <= 12
) ALLMONTHS LEFT OUTER JOIN
(
select MONTH mm, myCost, myNumber
from myOracle_mv
) myTotals ON
mm = MonthName
you need an outer join ( (+) at the end of your query ):
select MonthName, myCost, myNumber from
(
select
MONTH mm, myCost, myNumber
from
myOracle_mv
) myTotals,
(
select
to_char(date '2012-12-1' + numtoyminterval(level,'month'), 'mm') MonthName
from
dual
connect by level <= 12
)ALLMONTHS
where mm = MonthName(+)
for your example, you don't need to calculate dates:
select MONTH mm, NVL(myCost, 0), myNumber
from
(select level from dual connect by level <= 12) NUM
left outer join myOracle_mv MV ON ( MV.MONTH = NUM.level )
;

Limit keywords for day in a Google Analytics API request

I'd like to know if there's a way to get the top 5 keywords by grouping them by days of the current month.
I'd like to receive a dataset like the following as result.
Supposing today is 4 of December i want to retrieve data for days from 1 to 4 of December, limiting the number of keywords for day to 5:
Day Keyword Visits
----------------------
01 keyword1 703
01 keyword2 688
01 keyword3 115
02 keyword1 109
02 keyword2 66
02 keyword3 53
02 keyword4 40
02 keyword5 23
03 keyword1 23
03 keyword2 19
03 keyword3 17
04 keyword1 14
04 keyword2 14
What i've currently done is setting the following parameters:
(you can test it here if you have a ganalytics account: http://code.google.com/intl/it-IT/apis/analytics/docs/gdata/gdataExplorer.html)
Dimensions: ga:day,ga:keyword
Metrics: ga:visits
Filters: ga:medium==organic;ga:keyword!=(not provided)
Sort: ga:day,-ga:visits,ga:keyword
Now i just need a method to limit the number of keywords for day (if possible).

Resources