Oracle group orders by date and sum the total - sql

My Orders table looks like:
order_id (number)
order_total (number)
created_date (timestamp)
status (varchar2)
My goal is to get a set of rows where each row represents all orders on that date, so I'm trying to group the orders by date and get the sum of the order_total. I'm also limiting the results by only selecting orders from the last 30 days.
To clarify, for example if there were 30 orders in the last 30 days all on unique days then I would get 30 rows in the result. Another example: if there were 10 orders on 30th July, and only 1 order on 31st July then I'm aiming to get 2 rows in the result set, with order_total summed for all 10 orders in the first row, and the second row would of course have the order_total of the single order on the 31st.
My attempt so far:
select
sum(order_total) total_amount,
to_char(created_date, 'DD/MM/YYYY') grouped_date
from
orders
where
status = 'Complete' and
created_date >= (sysdate-30)
group by
to_char(created_date, 'DD'), to_char(created_date, 'MM'), to_char(created_date, 'YYYY')
order by
created_date asc
This gives an error:
ORA-00936: missing expression
I have tried to use the solution from this question but I don't think it quite fits for my scenario (this is where my group by expression has come from).

Assuming order_id should not be there, and that created_date has a time component (which seems likely as it's a timestamp), you need to truncate the date to remove the time when doing the aggregation:
select
sum(order_total) as total_amount,
to_char(trunc(created_date), 'DD/MM/YYYY') as grouped_date
from
orders
where
status = 'Complete' and
created_date >= trunc(sysdate-30)
group by
trunc(created_date)
order by
trunc(created_date) asc
I've also applied trunc to the where clause, otherwise it would ignore any orders 30 days ago between midnight and whatever time you ran the query today. And I've used the trunc'd date directly in the order by, rather than the column alias, so that the order is right when you go across a month-end - ordering by the DD/MM/YYYY string value would put 01/07/2013 before 30/06/2013, for example.
Quick SQL Fiddle.

Related

Getting sum of a column that needs a distinct value from other column

I have this table where I wanted to get the sum of the balance column but each item should have a unique value from the date column.
I'm trying to find all the rows in the balance column that are the same and have the same date, and then find the sum of the balance column.
sample data with unique dates:
balance
date
700
2021-07-03
700
2021-09-03
300
2021-09-04
500
2021-09-05
query used goes like:
select distinct a.balance, a.date from table a where a.date between (some date) and (some other date)
I have tried:
select sum(a.balance), a.date from table a where a.date between (some date) and (some other date) group by a.date
but the balance column shows the sum of all of the values in the column but shows distinct dates as shown below.
balance
date
893938
2021-07-03
858585
2021-09-03
728366
2021-09-04
665322
2021-09-05
I guess this is a job for a subquery. So let's take your problem step by step.
I'm trying to find all the rows in the balance column that are the same and have the same date,
This subquery gets you that, I believe. It give the same result as SELECT DISTINCT but it also counts the duplicated rows.
SELECT COUNT(*) num_same_rows, balance, date
FROM `table`
WHERE a.datum BETWEEN '2021-01-01' AND '2021-09-01'
GROUP BY date, balance
and then find the sum of the balance column.
Nest the subquery like this.
SELECT SUM(balance) summed_balance, date
FROM (
SELECT COUNT(*) num_same_rows, balance, date
FROM `table`
WHERE a.datum BETWEEN '2021-01-01' AND '2021-09-01'
GROUP BY date, balance
) subquery
GROUP BY date
If you only want to consider rows that actually have duplicates, change your subquery to
SELECT COUNT(*) num_same_rows, balance, date
FROM `table`
WHERE a.datum BETWEEN '2021-01-01' AND '2021-09-01'
GROUP BY date, balance
HAVING COUNT(*) >= 1
Be careful here, though. You didn't tell us what you want to do, only how you want to do it. The way you described your problem calls for discarding duplicated data before doing the sums. Is that right? Do you want to discard data?
2nd query you posted looks OK - sort of.
However, I think that it is the fact that date column contains not only date, but also time (as DATE datatype in Oracle does). Therefore, I'd say that it is trunc you need. Something like this:
SELECT TRUNC (a.datum) datum,
SUM (a.balance) sum_balance
FROM table_a a
WHERE a.datum BETWEEN DATE '2021-01-01' AND DATE '2021-09-01'
GROUP BY TRUNC (a.datum)

Write a query to display the total month wise sales amount received in the past 1 year

Table StructureWrite a query to display the total month wise sales amount received in the past 1 year . Display details like sales month, total sales amount. Give an alias_name as MONTH for retrieved sales month, TURN_OVER for sales amount. Sort the result by amount in descending order.
(Hint: Use table Sales_info. Use to_char for retrieving the month. Net amount for sales amount calculation. Use sysdate for calculation of past 1 year sales. DATA IS CASE-SENSITIVE.)
The code I have written is fetching me all years sales data.
select to_char(Sales_Date,'Month')"MONTH"
Net_Amount as Turn_Over
from Sales_Info
where Sales_Date= add_months(Sysdate,-12)
select to_char(Sales_Date,'MON')"MONTH",
Net_Amount as TURN_OVER
from Sales_Info
where Sales_Date > add_months(Sysdate,-12)
order by Net_Amount desc;
I'm not going to do your homework for you, but here is a list of things currently missing from your query:
a comma in the SELECT list
you need greater than, not equals because you want all dates "more than the moment it was a year ago"
you need to break your data into groups where each group has the same month, and you need to sum up all the data in that month, so your query needs to have the words GROUP BY and SUM in it..
The code I have written is fetching me all years sales data
No, the query as it stands will be giving you only the sales that happened at exactly the current date-time, one year ago, which is probably 0 records
You are pretty close. What you are missing is the GROUP BY and summary functions:
select to_char(Sales_Date, 'Month') as "MONTH"
SUM(Net_Amount) as Turn_Over
from Sales_Info
where Sales_Date= add_months(Sysdate, -12)
group by to_char(Sales_Date, 'Month');
Note that there are still some significant issues with the query. For instance, I really am not a fan of using month names for what you are doing. It leaves out the year. In fact, the above query is going to combine data from the current month and the same month last year.
I would instead go for complete months. And use trunc() instead:
select trunc(Sales_Date, 'MON') as "MONTH"
SUM(Net_Amount) as Turn_Over
from Sales_Info
where Sales_Date = add_months(trunc(Sysdate, 'MON'), -12) and
Sales_Date < trunc(sysdate, 'MON')
group by to_char(Sales_Date, 'Month')
order by "MONTH".
In a real-world environment, this would typically provided cleaner, more useful results. In addition, because the first column is actually the date, it is easy to sort by.
The ans will be :
Select to_char(Sales_Date , 'MON' ) as "MONTH" , sum(Net_Amount) as TURN_OVER
from Sales_Info
where Sales_Date > add_months(Sysdate , -12)
group by to_char(Sales_Date , 'MON')
order by TURN_OVER desc;

Using Date to find the inequality for sales than 500

I'm curious as to find the daily average sales for the month of December 1998 not greater than 100 as a where clause. So what I imagine is that since the table consists of the date of sales (sth like 1 december 1998, consisting of different date, months and year), amount due....First I'm going to define a particular month.
DEFINE a = TO_DATE('1-Dec-1998', 'DD-Month-YYYY')
SELECT SUBSTR(Sales_Date, 4,6), (SUM(Amount_Due)/EXTRACT(DAY FROM LAST_DAY(Sales_Date))
FROM ......
WHERE SUM(AMOUNT_DUE)/EXTRACT(DAY FROM LAST_DAY(&a)) < 100
I'm stuck as to extract the sum of amount due in the month of december 1998 for the where clause....
How can I achieve the objective?
To me, it looks like this:
select to_char(sales_date, 'mm.yyyy') month,
avg(amount_due) avg_value
from your_table
where sales_date >= trunc(date '1998-12-01', 'mm')
and sales_date < add_months(trunc(date '1998-12-01', 'mm'), 1)
group by to_char(sales_date, 'mm.yyyy')
having avg(amount_due) < 100;
WHERE clause can be simplified; it shows how to fetch certain period:
trunc to mm returns first day in that month
add_months to the above value (first day in that month) will return first day of the next month
the bottom line: give me all rows whose sales_date is >= first day of this month and < first day of the next month; basically, the whole this month
Finally, the where clause you used should actually be the having clause.
As long as the amount_due column only contains numbers, you can use the sum function.
Below SQL query should be able to satisfy your requirement.
Select SUM(Amount_Due) from table Sales where Sales_Date between '1-12-1998' and '31-12-1998'
OR
Select SUM(Amount_Due) from table Sales where Sales_Date like '%-12-1998'

How query works with missing Group by values

I am tryng to write a query to pull the total discount as well as revenue of my customer's orders by day. Note that for each ship_id there will be several items so I want to have the sum clauses as below and in the end, I have that group by and I want to group them by ship_id (this is unique for each order).
I got a result of 2M rows (this could be correct because we are a big) but I am not sure if my group by is correct. What if I only put ship_id there? How my query understands it?
d.ship_id
,to_char(d.order_datetime,'yyyy/mm/dd hh24:mi:ss') as datetime_order
,to_char(d.order_datetime, 'D') as day_order
,to_char(d.order_datetime, 'MM') as month_order
,sum(di.discount) as discount
,sum(di.price * di.units) AS price
FROM
table1 d
JOIN
table2 di
ON
d.ship_id = di.ship_id
GROUP BY
d.ship_id
,to_char(d.order_datetime,'yyyy/mm/dd hh24:mi:ss')
,to_char(d.order_datetime, 'D')
,to_char(d.order_datetime, 'MM')
In this query you grouping result by
ship_id (order)
d.order_datetime yyyy/mm/dd hh24:mi:ss (i think this is wrong if you need group only by day) this group is grouping by day,month,year,hour,minute and seconds
d.order_datetime 'D' (grouping by day correct)
d.order_datetime 'mm' (grouping by month correct, doesn't change results of grouping by day)
P.S.: I dont see grouping by costumer in anywhere.
Your query will sumarize all values groupped by itens on group by:
If you put, something like:
Select ship_id, ,sum(di.discount) as discount
,sum(di.price * di.units) AS price from table1 group by ship_id
The query will return Price and discount groupped by ship_id, when you make a group by you must put all non-*aggregate fields on group by.
*Aggregate funtions (Sum,AVG,count...)
Try explain better your problem to make me understand your situation(problem).

Sorting months while im converting them to text

I have to do a consult which must give me the following information:
Month | Quantity
-------------------
January | XX
February | XX
... | ..
So, I thought in:
select to_char(to_timestamp(to_char(date_part('month', orderdate), '999'), 'MM'), 'Mon'), count(*) as quantity from orders group by 1 ORDER BY 1
The problem is: months were sorted by "text" I mean:
Apr
Aug
Dec
...
How to solve it?
I suggest date_trunc() instead. It truncates date / timestamp to the given unit.
For two reasons:
You want the number of orders in August of a particular year, like 2012, not the sum for August of all years in the table. date_trunc('month', orderdate) does exactly that and prevents that you mix multiple years by accident. You get multiple rows for multiple years.
You can both ORDER BY and GROUP BY this one expression, the query is a bit faster.
SELECT to_char(date_trunc('month', orderdate), 'Mon') AS "Month" -- repeat expr.
,count(*) AS "Quantity"
FROM orders
GROUP BY date_trunc('month', orderdate) -- 1 item covers it
ORDER BY date_trunc('month', orderdate);
db<>fiddle here
Old sqlfiddle
For full month names, like your first example implies:
to_char(date_col, 'Month')
For non-English, localized names:
to_char(date_col, 'TMMonth')
Details in the manual.
First of all, your to_char is a lot more complicated that it needs to be, just this:
to_char(orderdate, 'Mon')
should be sufficient.
You're grouping and ordering by the first value that you select, that's what your 1 means. So of course the results are being sorted by month name, that's what you're asking for. Instead you want to group and order by the month component of the date, not its string representation. Something like this:
select to_char(orderdate, 'Mon') as "Month",
count(*) as "Quantity"
from orders
group by extract(month from orderdate), to_char(orderdate, 'Mon')
order by extract(month from orderdate)
You need both values in the GROUP BY to make it play nice with both your SELECT and your ORDER BY at the same time.