Get average value per day/hour without Group By - sql

In a single table, say I have a log of mileage and timestamps. I want to get the average mileage per day and per hour. I can't use an inherent "Group By" clause because of the date format.
Here is some sample data:
Table: tb_mileage
===============================================
f_mileagetimestamp f_mileage
-----------------------------------------------
2014-08-11 11:13:02.000 50
2014-08-11 16:12:55.000 100
2014-08-11 16:55:00.000 30
2014-08-12 11:12:50.000 80
2014-08-12 16:12:49.000 100
2014-08-13 08:12:46.000 40
2014-08-13 08:45:31.000 100
So, the ideal result set would appear as follows (PER DAY) (note, format of date doesn't matter):
Date Average
------------------------------------------------
08/11/2014 60
08/12/2014 90
08/13/2014 70
The ideal result set would appear as follows (PER HOUR) (note, format of date doesn't matter):
Date Average
------------------------------------------------
08/11/2014 11:00:00 50
08/11/2014 16:00:00 65
08/12/2014 11:00:00 80
08/12/2014 16:00:00 100
08/13/2014 08:00:00 70
Note that the example here is purely theoretical and simplified, and doesn't necessarily reflect the exact criteria necessary for the real-world implementation. This is merely to push my own learning, because all the examples I found to do similar things were hugely complex, making learning difficult.

Try this for the dates version.
select cast(t.f_mileagetimestamp as date) as dt, avg(t.f_mileage) as avg_mileage
from
tb_mileage t
group by cast(t.f_mileagetimestamp as date)
order by cast(t.f_mileagetimestamp as date) asc;
For the hours version, you can use this.
select t2.dt, avg(t2.f_mileage) as avg_mileage
from
(
select substring(CONVERT(nvarchar(100), t1.f_mileagetimestamp, 121), 1, 13) + ':00' as dt, t1.f_mileage
from
tb_mileage t1
) t2
group by t2.dt
order by t2.dt asc;

I think this should work for the "day" version:
select cast(f_mileagetimestamp as date), avg(f_mileage)
from tb_mileage
group by cast(f_mileagetimestamp as date)
order by cast(f_mileagetimestamp as date);
For the hour, I would just use the function:
select cast(f_mileagetimestamp as date), datepart(hour, f_mileagetimestamp), avg(f_mileage)
from tb_mileage
group by cast(f_mileagetimestamp as date), datepart(hour, f_mileagetimestamp)
order by cast(f_mileagetimestamp as date), datepart(hour, f_mileagetimestamp);

Related

SQL server calculate total of minutes per day

I am trying to calculated the time of an intervention per day:
Imagine the following scenario:
Intervention Start
Intervention End
Total time
01/09/2021 10:00:00
01/09/2021 12:00:00
01/09/2021 02:00:00
02/09/2021 23:30:00
03/09/2021 01:30:00
02/09/2021 00:30:00 and 03/09/2021 01:30:00
What is the best way to achieve this?
We can have more than 1 day of difference between interventions, also.
You can use a difference as milliseconds, seconds, minutes ... depending on your precision requirements. ie:
select interventionStart, interventionEnd, datediff(seconds, interventionStart, interventionEnd) totalTime
from myTable;
You could then convert seconds to your desire of display (ie: in .Net you might use TimeSpan's ToString() method.)
EDIT: If you absolutely need times "per date", then you could do this:
WITH
adjusted AS (
SELECT
interventionStart, interventionEnd
FROM myTable
WHERE CAST(interventionStart AS DATE)=CAST(interventionEnd AS DATE)
UNION ALL
SELECT
interventionStart, CAST(interventionEnd AS DATE) interventionEnd
FROM myTable
WHERE CAST(interventionStart AS DATE)!=CAST(interventionEnd AS DATE)
UNION ALL
SELECT
CAST(interventionEnd AS DATE) interventionStart, interventionEnd
FROM myTable
WHERE CAST(interventionStart AS DATE)!=CAST(interventionEnd AS DATE)
)
SELECT
adjusted.interventionStart
, adjusted.interventionEnd
, DATEDIFF(SECOND, interventionStart, interventionEnd) totalTime
, DATEADD(
SECOND, DATEDIFF(SECOND, interventionStart, interventionEnd)
, CAST(CAST(adjusted.interventionStart AS DATE) AS DATETIME)
) ifYouWish
FROM adjusted;
DbFiddle demo is here
You could try this if it answers your question.
SELECT Intervention_Start, Intervention_End, AGE(Intervention_Start,
Intervention_End) AS Total_Time from <table_name>;
Time in seconds by date. You can covert it to minutes, hours at will
select cast(c.d as date) dt, datediff(second, case when c.d > m.InterventionStart then c.d else m.InterventionStart end,
case when c2.d < m.InterventionEnd then c2.d else m.InterventionEnd end) seconds
from calendar c -- contains d = datetime of the start of the day, 2021-09-01 00:00:00 etc
-- next day
cross apply (values (dateadd(day, 1, c.d))) c2(d)
left join mytable m on c.d < m.InterventionEnd and m.InterventionStart < c2.d
db-fiddle

Generate Dates in oracle with gaps during Time Period

I Need To Generate Dates During Specific Time Period ( Using Oracle SQL Developer 12& 18).
/
I Found Bellow Solution:
SELECT TO_DATE('01/01/2020','DD/MM/YYYY') + (ROWNUM - 1) DATEGEN
FROM ALL_OBJECTS
WHERE TO_DATE('01/01/2020','DD/MM/YYYY') + (ROWNUM -1) <= TO_DATE('01/11/2020','DD/MM/YYYY')
I Get Daily Dates (As Bellow):
01-JAN-20 12.00.00 AM
02-JAN-20 12.00.00 AM
03-JAN-20 12.00.00 AM
04-JAN-20 12.00.00 AM
05-JAN-20 12.00.00 AM
06-JAN-20 12.00.00 AM
I Need To Get Dates With Gap 5 days or 3 days (Example):
01-JAN-20 12.00.00 AM
05-JAN-20 12.00.00 AM
10-JAN-20 12.00.00 AM
15-JAN-20 12.00.00 AM
Any Suggestions?!
Thanks in Advance.
You can use the hiearchy query with simple logic as follows:
SELECT DATE '2020-01-01' + ( LEVEL * 5 ) - 1
FROM DUAL CONNECT BY
LEVEL <= ( DATE '2020-11-01' - DATE '2020-01-01' ) / 5;
Please note that in your question first two dates have 4 days of difference and in my solution, the First date will be 05-01-2020
You could use a standad recursive query:
with cte (dt) as (
select date '2020-01-01' from dual
union all
select dt + interval '5' day from cte where dt < date '2020-01-15'
)
select * from cte order by dt
You can adjust the boundaries as needed, or move them to another cte:
with
params (startdt, enddt) as (
select date '2020-01-01', date '2020-01-15' from dual
),
cte (dt, enddt) as (
select startdt dt, enddt from params
union all
select dt + interval '5' day, enddt from cte where dt < enddt
)
select dt from cte order by dt
Note that the intervals between the dates are not constant in your sample data (there are 4 days between the first two dates, then 5 days.
You might want to adjust the where clause of the recursive member depending on your actual requirement. Here, the last day may be greater the January 15th. If you don't want that, then change the where clause to:
where dt + interval '5' day < enddt

SQL Server Adding summing values based on the month

I have a table that has the following 2 columns:
Date and Price
I want to sum the prices based on the same month of the same year. This is what I have:
2012-07-27 00:00:00.000 0
2012-07-27 00:00:00.000 15000
2012-08-27 00:00:00.000 0
2012-08-27 00:00:00.000 12000
2012-09-28 00:00:00.000 1000
2012-09-28 00:00:00.000 9000
2012-10-26 00:00:00.000 0
I want the following:
2012-07-27 00:00:00.000 15000
2012-08-27 00:00:00.000 12000
2012-09-28 00:00:00.000 10000
2012-10-26 00:00:00.000 0
Thank you.
You could get your desired result given your sample data and output with:
SELECT DATE, SUM(Price)
FROM YourTable
GROUP BY DATE
ORDER BY DATE
If you had multiple dates in a month and wanted to return each distinct date but still show the monthly sum for each record you could use:
SELECT DATE, SUM(SUM(Price)) OVER (PARTITION BY YEAR(Date),MONTH(Date))
FROM YourTable
GROUP BY DATE
ORDER BY DATE
You'll want to use the date functions provided in a group by statement, something like:
select sum(price)
,cast(datepart(month, date_column) as varchar) + '-' + cast(datepart(year, date_column) as varchar) as year_month
from your_table
group by datepart(year, date_column), datepart(month, date_column)
Make sure to put the datepart(year, date_column) first, so that you don't get a sum of all prices from every January from all years, but instead get a sum of all prices from January 2012.
The result set for this query will look a little different than your example - you'd get:
07-2012 15000
08-2012 12000
09-2012 10000
10-2012 0
If you want to get the exact result set you provided, you can change the first column of the select statement:
select sum(price)
,date_column
from your_table
group by datepart(year, date_column), datepart(month, date_column)
The downside to this is that if you have multiple days from the same month/year, say 8/12/2013 and 8/13/2013, it will be hard to predict which of the two days you'll get in the final result set.
I agree with prekolna's answer that it is probably what you really need. However, to produce what you have in your question, you only need to group by the date since the dates are all the same in the month and since you have the dates displayed in the return:
SELECT
[Date],
SUM(Price) Price
FROM
a_table
GROUP BY
[Date];
The above assumes you only have one unique date entry per month and that you want the full date returned. These are admittedly bad assumptions. I would change prekolna's example slightly to ensure the date column can sort properly and to match it in the group by:
SELECT
CAST(DATEPART(YEAR, [Date]) AS varchar(4)) + '-' + RIGHT('0' + CAST(DATEPART(MONTH, [Date]) AS varchar(2)), 2) YearMonth,
SUM(Price) Price
FROM
a_table
GROUP BY
CAST(DATEPART(YEAR, [Date]) AS varchar(4)) + '-' + RIGHT('0' + CAST(DATEPART(MONTH, [Date]) AS varchar(2)), 2);
Or, you can do something else to return a full date like a MIN:
SELECT
MIN([Date]) [Date],
SUM(Price) Price
FROM
a_table
GROUP BY
CAST(DATEPART(YEAR, [Date]) AS varchar(4)),
CAST(DATEPART(MONTH, [Date]) AS varchar(2));

Group query results by month and year in postgresql

I have the following database table on a Postgres server:
id date Product Sales
1245 01/04/2013 Toys 1000
1245 01/04/2013 Toys 2000
1231 01/02/2013 Bicycle 50000
456461 01/01/2014 Bananas 4546
I would like to create a query that gives the SUM of the Sales column and groups the results by month and year as follows:
Apr 2013 3000 Toys
Feb 2013 50000 Bicycle
Jan 2014 4546 Bananas
Is there a simple way to do that?
I can't believe the accepted answer has so many upvotes -- it's a horrible method.
Here's the correct way to do it, with date_trunc:
SELECT date_trunc('month', txn_date) AS txn_month, sum(amount) as monthly_sum
FROM yourtable
GROUP BY txn_month
It's bad practice but you might be forgiven if you use
GROUP BY 1
in a very simple query.
You can also use
GROUP BY date_trunc('month', txn_date)
if you don't want to select the date.
select to_char(date,'Mon') as mon,
extract(year from date) as yyyy,
sum("Sales") as "Sales"
from yourtable
group by 1,2
At the request of Radu, I will explain that query:
to_char(date,'Mon') as mon, : converts the "date" attribute into the defined format of the short form of month.
extract(year from date) as yyyy : Postgresql's "extract" function is used to extract the YYYY year from the "date" attribute.
sum("Sales") as "Sales" : The SUM() function adds up all the "Sales" values, and supplies a case-sensitive alias, with the case sensitivity maintained by using double-quotes.
group by 1,2 : The GROUP BY function must contain all columns from the SELECT list that are not part of the aggregate (aka, all columns not inside SUM/AVG/MIN/MAX etc functions). This tells the query that the SUM() should be applied for each unique combination of columns, which in this case are the month and year columns. The "1,2" part is a shorthand instead of using the column aliases, though it is probably best to use the full "to_char(...)" and "extract(...)" expressions for readability.
to_char actually lets you pull out the Year and month in one fell swoop!
select to_char(date('2014-05-10'),'Mon-YY') as year_month; --'May-14'
select to_char(date('2014-05-10'),'YYYY-MM') as year_month; --'2014-05'
or in the case of the user's example above:
select to_char(date,'YY-Mon') as year_month
sum("Sales") as "Sales"
from some_table
group by 1;
There is another way to achieve the result using the date_part() function in postgres.
SELECT date_part('month', txn_date) AS txn_month, date_part('year', txn_date) AS txn_year, sum(amount) as monthly_sum
FROM yourtable
GROUP BY date_part('month', txn_date)
Thanks
Why not just use date_part function. https://www.postgresql.org/docs/8.0/functions-datetime.html
SELECT date_part('year', txn_date) AS txn_year,
date_part('month', txn_date) AS txn_month,
sum(amount) as monthly_sum
FROM payment
GROUP BY txn_year, txn_month
order by txn_year;
Take a look at example 6) of this tutorial -> https://www.postgresqltutorial.com/postgresql-group-by/
You need to call the function on your GROUP BY instead of calling the name of the virtual attribute you created on select.
I was doing what all the answers above recommended and I was getting a column 'year_month' does not exist error.
What worked for me was:
SELECT
date_trunc('month', created_at), 'MM/YYYY' AS month
FROM
"orders"
GROUP BY
date_trunc('month', created_at)
Postgres has few types of timestamps:
timestamp without timezone - (Preferable to store UTC timestamps) You find it in multinational database storage. The client in this case will take care of the timezone offset for each country.
timestamp with timezone - The timezone offset is already included in the timestamp.
In some cases, your database does not use the timezone but you still need to group records in respect with local timezone and Daylight Saving Time (e.g. https://www.timeanddate.com/time/zone/romania/bucharest)
To add timezone you can use this example and replace the timezone offset with yours.
"your_date_column" at time zone '+03'
To add the +1 Summer Time offset specific to DST you need to check if your timestamp falls into a Summer DST. As those intervals varies with 1 or 2 days, I will use an aproximation that does not affect the end of month records, so in this case i can ignore each year exact interval.
If more precise query has to be build, then you have to add conditions to create more cases. But roughly, this will work fine in splitting data per month in respect with timezone and SummerTime when you find timestamp without timezone in your database:
SELECT
"id", "Product", "Sale",
date_trunc('month',
CASE WHEN
Extract(month from t."date") > 03 AND
Extract(day from t."date") > 26 AND
Extract(hour from t."date") > 3 AND
Extract(month from t."date") < 10 AND
Extract(day from t."date") < 29 AND
Extract(hour from t."date") < 4
THEN
t."date" at time zone '+03' -- Romania TimeZone offset + DST
ELSE
t."date" at time zone '+02' -- Romania TimeZone offset
END) as "date"
FROM
public."Table" AS t
WHERE 1=1
AND t."date" >= '01/07/2015 00:00:00'::TIMESTAMP WITHOUT TIME ZONE
AND t."date" < '01/07/2017 00:00:00'::TIMESTAMP WITHOUT TIME ZONE
GROUP BY date_trunc('month',
CASE WHEN
Extract(month from t."date") > 03 AND
Extract(day from t."date") > 26 AND
Extract(hour from t."date") > 3 AND
Extract(month from t."date") < 10 AND
Extract(day from t."date") < 29 AND
Extract(hour from t."date") < 4
THEN
t."date" at time zone '+03' -- Romania TimeZone offset + DST
ELSE
t."date" at time zone '+02' -- Romania TimeZone offset
END)
I also need to find results grouped by YEAR and MONTH.
When I grouped them by TIMESTAMP, sum function grouped them with dates and minutes, but that wasn't what I wanted.
Using this query may be helpful for you.
select sum(sum),
concat(year, '-', month, '-', '01')::timestamp
from (select sum(t.final_price) as sum,
extract(year from t.created_at) as year,
extract(month from t.created_at) as month
from transactions t
where status = 'SUCCESS'
group by t.created_at) t
group by year, month;
transactions table
query result
As you can see in the picture, in '2022-07-01' I have two columns in table, and in query result they are grouped together.

SUM columns by hour (using DATETIME column)

I have a database that I need to sum 2 values using the datetime column. Example:
Date Offered
4/16/2012 08:00:00 2
4/16/2012 08:30:00 18
4/16/2012 09:00:00 14
4/16/2012 09:30:30 42
I need to sum the values of 08:00:00 with 08:30:00 (total: 20) and 09:00:00 with 09:30:00 (total: 56) and so on.
This should work for you
select datepart(hour,myDate), SUM(Offered)
from myTable
group by
datepart(hour,myDate),
dateadd(d, 0, datediff(d, 0, myDate))
You need to group by both the hour and the date if you want it summed by individual day, otherwise you'll include other days (IE April 15 etc...)
Your pseudo code
Select HOUR(date), sum(offered) as sumO
FROM YourTable
Group By Hour(date)
Hour(date) would need to be altered to the correct syntax for the database you're working with.
SELECT [Hour] = DATEPART(HOUR, [Date]), Offered = SUM(Offered)
FROM dbo.table_name
WHERE [Date] >= '20120416'
AND [Date] < '20120417'
GROUP BY DATEPART(HOUR, [Date])
ORDER BY [Hour];
Use a DatePart function.
Syntax depends on your database
Select DatePart("year", Date), Sum(Offered)
From table1
Group By DatePart("year", Date)