Query with inner select and Group by - sql

I am struggling with this query, here is the table set up:
date | time | count
----------------------------
12/12/2015 | 0:00 | 8
12/12/2015 | 1:00 | 19
12/12/2015 | 2:00 | 36
12/13/2015 | 0:00 | 12
12/13/2015 | 1:00 | 22
12/13/2015 | 2:00 | 30
12/14/2015 | 0:00 | 14
12/14/2015 | 1:00 | 26
12/14/2015 | 2:00 | 38
What I would like my query to return is something like this:
date | time | count | DAY | AVG/HR | AVG/DAY
---------------------------------------------------------
12/12/2015 | 0:00 | 8 | MONDAY | 11.33 | 63
12/12/2015 | 1:00 | 19 | MONDAY | 22.33 | 63
12/12/2015 | 2:00 | 36 | MONDAY | 34.67 | 63
12/13/2015 | 0:00 | 12 | TUESDAY | 11.33 | 64
12/13/2015 | 1:00 | 22 | TUESDAY | 22.33 | 64
12/13/2015 | 2:00 | 30 | TUESDAY | 34.67 | 64
12/14/2015 | 0:00 | 14 | WEDNESDAY | 11.33 | 78
12/14/2015 | 1:00 | 26 | WEDNESDAY | 22.33 | 78
12/14/2015 | 2:00 | 38 | WEDNESDAY | 34.67 | 78
So basically that is returning all rows (there will be months worth of data in the table, with each day having 24 records/hours). And adding a day of the week field, and an Average of the count per hour along with a average of the count per day of the week. The last 2 are what I am struggling with. Here is what I have so far:
SELECT DATE, TIME, COUNT,
TO_CHAR(DATE, 'DAY'),
(SELECT AVG(t2.COUNT)
FROM tableXX t2
WHERE t2.time = t1.time
GROUP BY t2.time) AS AvgPerHr
(SELECT AVG(t2.COUNT)
FROM tableXX t2
WHERE TO_CHAR(t2.DATE, 'DAY') = TO_CHAR(t1.DATE, 'DAY')
GROUP BY TO_CHAR(t2.DATE, 'DAY')) AS AvgPerDay
FROM tableXX t1
ORDER BY DATE, TO_DATE(TIME, 'hh24:mi') ASC;
Any suggestions would be appreciated, the query above returns data, but it definitely isn't accurate.

This can be solved by using analytical functions.
SELECT DATE, TIME, COUNT,
TO_CHAR(DATE, 'DAY'),
AVG(t1.COUNT)
OVER (PARTITION BY TIME) AS AvgPerHr,
AVG(t1.COUNT)
OVER (PARTITION BY TO_CHAR(DATE, 'DAY')) AS AvgPerDay
FROM tableXX t1
ORDER BY DATE, TO_DATE(TIME, 'hh24:mi') ASC;

Try:
SELECT "DATE", "TIME", "COUNT", TO_CHAR(DATE, 'DAY') "DAY,
avg( "COUNT" ) Over (partition by "TIME" ) "AVG/HR",
SUM( "COUNT" ) Over (partition by "DATE" ) "AVG/DAY"
FROM tablexx
ORDER BY 1;
I use SUM( "COUNT" ) instead of AVG( "COUNT" ), since 63 in the first row of your example appears to be sum per day, not an average.

Related

Is there a way to select records inserted in previous hour from the last recorded inserted record?

I have a table that looks like this that has quite a few records in it:
+---------+------+------------------------+
| unit | temp | login_time_utc |
+---------+------+------------------------+
| 1 | 53 | 2022-01-24 10:02:06 |
| 1 | 62 | 2022-01-24 10:10:01 |
| 2 | 34 | 2022-01-24 10:04:00 |
| 2 | 65 | 2022-01-24 16:08:59 |
| 2 | 65 | 2022-01-24 16:03:56 |
| 2 | 74 | 2022-01-24 16:06:53 |
| 3 | 74 | 2022-01-24 16:05:51 |
| 3 | 83 | 2022-01-24 17:09:49 |
| 3 | 73 | 2022-01-24 18:07:46 |
| 4 | 74 | 2022-01-24 18:11:43 |
+---------+------+------------------------+
I would like to select all the records for each unit that were inserted in the last hour from the most recently inserted record of that respective unit. Is that possible?
I can do this easily if its just the last hour from now, but I don't know how to do this if its the last hour of each units most recent insert.
I cannot use a loop or a cursor in this situation.
with cutoff as (
select unit, max(login_time_utc) as max_login
from T group by unit
)
select data.*
from cutoff cross apply (
select * from T t
where t.unit = cutoff.unit
and t.login_time_utc >= dateadd(hour, -1, cutoff.max_login)
) as data
You can use a window function and a CTE to identify the MAX date per unit. Then use DATEDIFF to find all the records in the last hour.
WITH cte AS (
SELECT *, MAX(login_time_utc) OVER (PARTITION BY unit) AS login_time_utc_max
FROM yourtable
)
SELECT unit, temp, login_time_utc
FROM cte
WHERE DATEDIFF(SS, login_time_utc, login_time_utc_max) <= 3600
ORDER BY login_time_utc

Query to get maximum value based on timestamp every 4 hour

I have a sql table that stores data every 15 minutes, but I want to fetch the maximum value every 4 hour.
This is my Actual table:
+----+----+----+-------------------------+
| Id | F1 | F2 | timestamp |
+----+----+----+-------------------------+
| 1 | 24 | 30 | 2019-03-25 12:15:00.000 |
| 2 | 22 | 3 | 2019-03-25 12:30:00.000 |
| 3 | 2 | 4 | 2019-03-25 12:45:00.000 |
| 4 | 5 | 35 | 2019-03-25 13:00:00.000 |
| 5 | 18 | 23 | 2019-03-25 13:15:00.000 |
| ' | ' | ' | ' |
| 16 | 21 | 34 | 2019-03-25 16:00:00.000 |
+----+----+----+-------------------------+
The Output I am looking for is:
+----+----+----+
| Id | F1 | F2 |
+----+----+----+
| 1 | 24 | 35 |1st 4 Hours
+----+----+----+
| 2 | 35 | 25 |Next 4 Hours
+----+----+----+
I did use the query
select max(F1) as F1,
max(F2) as F2
from table
where timestamp>='2019/3/26 12:00:01'
and timestamp<='2019/3/26 16:00:01'
and it returns the first 4 hours value but when I Increase the timestamp from 4 hrs to 8 hrs it will still give me 1 max value rather than 2 per 4 hours.
I did try with the group by clause but wasn't able to get the expected result.
This should work
SELECT Max(f1),
Max(f2), datepart(hh,timestamp), convert(date,timestamp)
FROM TABLE
WHERE datepart(hh,timestamp)%4 = 0
AND timestamp>='2019/3/26 12:00:01'
AND timestamp<='2019/3/26 16:00:01'
GROUP BY datepart(hh,timestamp), convert(date,timestamp)
ORDER BY convert(date,timestamp) asc
Here is a relatively simple method:
select convert(date, timestamp) as dte,
(datepart(hour, timestamp) / 4) * 4 as hour,
max(F1) as F1,
max(F2) as F2
from table
group by convert(date, timestamp), (datepart(hour, timestamp) / 4) * 4;
This puts the date and hour into separate columns; you can use dateadd() to put them in one column.
Try this query:
declare #startingDatetime datetime = '2017-10-04 12:00:00';
select grp, max(F1) F1, max(F2) F2
from (
select datediff(hour, #startingDatetime, [timestamp]) / 4 grp, *
from MyTable
where [timestamp] > #startingDatetime
) a group by grp

How to group date by week in PostgreSQL?

I have pretty simple table which has 2 column. First one show time (timestamp), the second one show speed of car at that time (float8).
| DATE_TIME | SPEED |
|---------------------|-------|
| 2018-11-09 00:00:00 | 256 |
| 2018-11-09 01:00:00 | 659 |
| 2018-11-09 02:00:00 | 256 |
| other dates | xxx |
| 2018-11-21 21:00:00 | 651 |
| 2018-11-21 22:00:00 | 515 |
| 2018-11-21 23:00:00 | 849 |
Lets say we have period from 9 november to 21 november. How to group that period by week. In fact I want such result:
| DATE_TIME | AVG_SPEED |
|---------------------|-----------|
| 9-11 November | XXX |
| 12-18 November | YYY |
| 19-21 November | ZZZ |
I use PostgreSQL 10.4.
I use such SQL Statement to know the number of the week of the certain date:
SELECT EXTRACT(WEEK FROM TIMESTAMP '2018-11-09 00:00:00');
EDIT:
#tim-biegeleisen when I set period from '2018-11-01' to '2018-11-13' your sql statement return 2 result:
In fact I need such result:
2018-11-01 00:00:00 | 2018-11-04 23:00:00
2018-11-05 00:00:00 | 2018-11-11 23:00:00
2018-11-12 00:00:00 | 2018-11-13 05:00:00
As you can see in the calendar there are 3 week in that period.
We can do this using a calendar table. This answer assumes that a week begins with the first date in your data set. You could also do this assuming something else, e.g. a standard week according to something else.
WITH dates AS (
SELECT date_trunc('day', dd)::date AS dt
FROM generate_series
( '2018-11-09'::timestamp
, '2018-11-21'::timestamp
, '1 day'::interval) dd
),
cte AS (
SELECT t1.dt, t2.DATE_TIME, t2.SPEED,
EXTRACT(week from t1.dt) week
FROM dates t1
LEFT JOIN yourTable t2
ON t1.dt = t2.DATE_TIME::date
)
SELECT
MIN(dt)::text || '-' || MAX(dt) AS DATE_TIME,
AVG(SPEED) AS AVG_SPEED
FROM cte
GROUP BY
week
ORDER BY
MIN(dt);
Demo

Postgres - aggregate minutes to hour

I need some assistance with PostgresSQL. I am trying to group some records (5-, 10-, 15-, 20-, etc) into 60-minute intervals.
What i need is to GROUP BY and AVG the minute values within a given hour to the respective hour.
SELECT id, value,
extract(year from GDDP.timestamp) as YEAR,
extract(month from GDDP.timestamp) as MONTH,
extract(day from GDDP.timestamp) as DAY,
extract(hour from GDDP.timestamp) as "HOUR",
extract(minute from GDDP.timestamp) as MINUTE,
FROM GDDP
WHERE value > 0 AND
GDDP.timestamp BETWEEN '2016-07-01 00:00:00' and '2016-12-31 23:55:00'
ORDER BY YEAR, MONTH, DAY, HOUR
Currently, this is the result of the query above:
id | value | YEAR | MONTH | DAY | HOUR | MINUTE
-------------------------------------------------
1 | 100 | 2016 | 07 | 01 | 1 | 05
2 | 200 | 2016 | 07 | 01 | 1 | 10
3 | 100 | 2016 | 07 | 01 | 1 | 15
4 | 300 | 2016 | 07 | 01 | 1 | 20
5 | 200 | 2016 | 07 | 01 | 1 | 25
6 | 500 | 2016 | 07 | 01 | 1 | 30
But, I would like the result to look like this:
id | value | YEAR | MONTH | DAY | HOUR
---------------------------------------
1 | 233.3 | 2016 | 07 | 01 | 1
Thanks in advance for any assistance!
Use the aggregation function avg() in groups by year, month, day, hour
SELECT
min(id) as id,
avg(value) as value,
extract(year from gddp.timestamp) as year,
extract(month from gddp.timestamp) as month,
extract(day from gddp.timestamp) as day,
extract(hour from gddp.timestamp) as hour
FROM gddp
WHERE value > 0
AND gddp.timestamp BETWEEN '2016-07-01 01:00:00' AND '2016-12-31 01:23:00'
GROUP BY year, month, day, hour
ORDER BY year, month, day, hour;
id | value | year | month | day | hour
----+----------------------+------+-------+-----+------
1 | 233.3333333333333333 | 2016 | 7 | 1 | 1
(1 row)

SQL Get previous stocks based on modified dates

I have a pretty strange business requirement that I need to fulfill with the following two tables:
STOCK_TB (As of 20150319)
PRODUCT_ID STOCK_QTY
A 20
B 15
STOCK_MODIFIED_TB
PRODUCT_ID MODIFIED_QTY MODIFIED_DATE_FROM MODIFIED_DATE_TO
A 10 20150315 20150318
B -5 20150314 20150316
A -2 20150314 20150316
STOCK_TB represents the current stock of inventory, while STOCK_MODIFIED_TB represents the quantity of stocks modified in a date range. I need to select results of stocks for previous dates. Suppose the result was retrieved on 20150319 for dates 20150314-20150319. This is what the result should look like:
DATE PRODUCT_ID STOCK_QTY
20150314 A 18
20150314 B 10
20150315 A 28
20150315 B 10
20150316 A 28
20150316 B 10
20150317 A 30
20150317 B 15
20150318 A 30
20150318 B 15
20150319 A 20
20150319 B 15
In other words, the stocks for previous dates would be added/subtracted based on the date range given in STOCK_MODIFIED_TB
Is selecting data like this possible without cursors?
I'll try with this answer, of course my subquery in select looking not so well with performance I guess... :
SQLFIddleExample
SELECT cast(a.Date as date) Date,
st.PRODUCT_ID,
st.STOCK_QTY + isnull((SELECT SUM(MODIFIED_QTY)
FROM STOCK_MODIFIED_TB
WHERE MODIFIED_DATE_FROM <= CONVERT(VARCHAR(10), a.Date, 112)
AND MODIFIED_DATE_TO >= CONVERT(VARCHAR(10), a.Date, 112)
AND PRODUCT_ID = st.PRODUCT_ID ),0) STOCK_QTY
FROM STOCK_TB st,
(select DATEADD(day, number, '2015-01-01') Date
from master..spt_values
where type = 'p' ) a
WHERE a.Date between '2015-03-14' and '2015-03-19'
ORDER BY a.Date, st.PRODUCT_ID
Result:
| Date | PRODUCT_ID | STOCK_QTY |
|------------|------------|-----------|
| 2015-03-14 | A | 18 |
| 2015-03-14 | B | 10 |
| 2015-03-15 | A | 28 |
| 2015-03-15 | B | 10 |
| 2015-03-16 | A | 28 |
| 2015-03-16 | B | 10 |
| 2015-03-17 | A | 30 |
| 2015-03-17 | B | 15 |
| 2015-03-18 | A | 30 |
| 2015-03-18 | B | 15 |
| 2015-03-19 | A | 20 |
| 2015-03-19 | B | 15 |