Order total sales by month - sql

I am printing out the total sales for each state so long as they have over $6000 in sales for that month. The results need to be ordered by month(descending order). I noticed that I got 2 results for Texas for the same month. So what that tells me is it is just looking for individual sales over $6,000 even though I have a sum(so.total) which I thought would give the total sales for state ordered by the month. Not sure how to break down the sales by each individual month if the order_date field has day-month-year in it.(Using Oracle 10g)
Input:
SELECT c.state "State",to_char(so.order_date, 'Month')"Month", sum(so.total)"Sales"
FROM a_customer c
JOIN a_sales_order so ON c.customer_id=so.customer_id
GROUP BY c.state, so.order_date
HAVING sum(so.total)>6000
ORDER BY to_char(so.order_date, 'MM') desc
Output:
State Month Sales
----- --------- --------
TX September $8,400
CA July $8,377
TX March $7,700
TX March $8,540
CA February $46,370
MN February $6,400
CA February $24,650

I think there may be an issue in both your GROUP BY and ORDER BY clauses.
Try:
SELECT c.state AS "State",
to_char(so.order_date, 'Month') AS "Month",
sum(so.total) AS "Sales"
FROM a_customer c
JOIN a_sales_order so ON c.customer_id=so.customer_id
GROUP BY c.state,
to_char(so.order_date, 'Month')
HAVING sum(so.total)>6000
ORDER BY to_date(to_char(so.order_date, 'Month'), 'Month') desc
I don't have a working instance in front of me at the moment to try it but you need to ensure you are grouping on the same output as you are displaying, in your original example, you are grouping on the full order_date but displaying just the month portion.
The order by in your example will order by the text of the month rather than the actual chronological order.
Hope it helps.

Related

Obtain a customer's first purchase for one year

I want to get as many new customers as I have per month. I know I could get the minimum purchase, but the problem I have is that if a customer had already had a purchase, but stopped buying for more than a year, he is considered a new user again.
You could help me by getting how many new customers I have per month. That is, whose minimum purchase date has been in that month and has not bought anything in the year before that minimum date.
I tried with this code, but if in this case, a customer had his first purchase in February 2019 and then the next purchase was in March 2020, just consider the purchase of February, when he should be new user in February 2019 and March 2020
select to_char(B.fp, 'YYYY-MM') month, count(B.email)
from(
select A.email, A.first_purchase fp
from(
select email, date(min(created)) first_purchase
from "Order_table" oo
group by email)A
where A.first_purchase >= (A.first_purchase + INTERVAL '-1 year'))B
group by 1,2
Use lag():
select to_char(ot.fp, 'YYYY-MM') as yyyymm
count(*) filter (where ot.fp > ot.prev_fp + interval '1 year' or b.prev_fp is null) as cnt_new
from (select ot.*, lag(ot.fp) over (partition by ot.email order by ot.fp) as prev_fp
from Order_table ot
) ot
group by yyyymm;

Get quarter on quarter growth with SQL for current quarter

I'm trying to get the quarter on quarter revenue growth for only the current quarter from a dataset. I currently have a query that looks something like this
Select
x.Year,
x.quarter,
x.product,
x.company_id,
y.company,
SUM(x.revenue)
FROM
company_directory y
LEFT JOIN (
SELECT
DATEPART(YEAR, #date) year,
DATEPART(QUARTER, #date) quarter,
product,
SUM(revenue)
FROM sales
WHERE year => 2018 ) x
ON y.company_id = x.company_id
The data I get is in this format
Year Quarter Product company_id company revenue
2020 Q2 Banana 1092 companyX $100
What I'm trying to do is get quarter on quarter growth for revenue if it's reporting the current quarter. So for example, in the above data, because we're in Q2-2020, I want an extra column to say QoQ is x% which will compare Q2 vs Q1 revenue. If the row is reporting Q1-2020 or Q2-2019 QoQ will be empty because neither of those are the current quarter based on today's date.
Expected result
Year Quarter Product company_id company revenue QoQ
2020 Q2 Banana 1092 companyX $100 20%
2020 Q1 Pear 1002 companyX $23 NULL
I'm not entirely sure how to go about this, haven't had much luck searching. Any idea how I can implement?
You can use window functions.
select
s.yr,
s.qt,
s.product,
s.company_id,
cd.company,
s.revenue,
1.0 * (
s.revenue
- lag(s.revenue) over(partition by s.product, s.company_id order by s.yr, s.qt)
) / lag(s.revenue) over(partition by s.product, s.company_id order by s.yr, s.qt) as QoQ
from company_directory cd
inner join (
select
datepart(year, sales_date) yr,
datepart(quarter, sales_date) qt,
product,
company_id,
sum(revenue) revenue
from sales
where sales_date >= '2018-01-01'
group by
datepart(year, sales_date) year,
datepart(quarter, sales_date) quarter,
product,
company_id
) s on s.company_id = cd.company_id
Notes:
your code is not valid MySQL; I assume that you are running SQL Server instead of MySQL
the use of a variable in the subquery does not make sense - I assume that you have a column called sales_date in table sales that holds the sales date
your group by clauses are inconsistent with your select clauses - I assume that you want the quarter to quarter sales growth per company and per product
you might need to ajust the QoQ computation to your actual definition of the quarter to quarter growth
I don't see the point for a left join, so I used inner join instead
You need analytical window function LAG
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=40a7ba897df766f913ebd99e2f2a0f4e

Accumulating values until up to date

I'm working on an order system where orders come in. For the analytics department I want to build a view that accumulates all sales for a given day.
That is not an issue, I got the working query for that. More complicated is a second number where I want to show the accumulated sales to that day.
Meaning if I have $100 of sales on Feb 1 the column should show $100. If I have $200 of sales on Feb 2 that column should show $300 and so on.
This is what I came up with so far:
select
date_trunc('day', o.created_at) :: date,
sum(o.value) sales_for_day,
count(o.accepted_at) as num_of_orders_for_day,
-- sales_for_month_to_date
-- num_of_orders_for_month_to_date
from
orders o
where
status = 'accepted'
group by
date_trunc('day', o.accepted_at);
Just use window functions:
select date_trunc('day', o.created_at) :: date,
sum(o.value) as sales_for_day,
count(o.accepted_at) as num_of_orders_for_day,
sum(sum(o.value)) over (partition by date_trunc('month', o.accepted_at order by min(o.created_at)) as sales_for_month_to_date
sum(count(*)) over (partition by date_trunc('month', o.accepted_at order by min(o.created_at)) as num_of_orders_for_month_to_date
from orders o
where status = 'accepted'
group by date_trunc('day', o.accepted_at);
Based on the comments in your code, I surmise that you want month-to-date numbers, so this also partitions by month.

SQL : finding people that have made more than one order within a 30 day period

I have a (simplified) table called Orders that has the following columns:
OrderId,
PersonId
OrderDate
What I am trying to find out is how many people in the table have made more than one order within a 30 day period. For example, if Bob orders something on Januay 3, 2015 and then orders another something on January 21, 2015, he would be included in the list because he ordered two things within a 30 day period.
I have been trying to put together the SQL statements to do this, but I am not very good at this and can't seem to figure it out.
I am using SQL Server.
Thanks for any help.
You COULD try by doing with a join to the table itself based on same customer and order date within 30 of the order being compared to...
I would ensure to have TWO indexes on the table...
First by date for the range you may be expecting (OrderDate)
Second based on ( PersonID, OrderID, OrderDate ) to get them sequential for a given person for the join basis.
select
O.PersonID,
O.OrderDate,
O.OrderID,
MAX( O2.OrderDate ) as LastOrderDate,
COUNT(*) as TotalOrders
from
Orders O
JOIN Orders O2
on O.PersonID = O2.PersonID
AND O.OrderID < O2.OrderID
AND O2.OrderDate < DATEADD( day, 30, O.OrderDate);
where
O.OrderDate > '2014-01-01'
group by
O.PersonID,
O.OrderDate,
O.OrderID
Now, because this is a JOIN (not a left-join), it is guaranteeing another order exists for the same person but a higher order than the one based on the main query "O" alias, but ONLY within 30 days of the original O.OrderDate.
Now, this may create multiple instances for a single person, such as an example where person buys on Jan 1, Jan 20, Jan 29, Feb 1, Feb 10, May 4 because
From the original order Jan 1, that will encompass a count of 3 entries ending at Jan 29.
But now, the outer cycle gets to the Jan 20 record and it finds 3 entries after it up to and including Feb 10... Same person, but the rolling 30 days.
Similar for Jan 29, then Feb 1, but will not have an entry for Feb 10 as nothing after it within 30 days until the May 4th date, and nothing for May 4th as nothing after it period.
Now, if you only care about the WHO, once you've confirmed the above query WOULD work, you MAY only want those with the total orders MORE than 2... say 3 or more within 45 days? Who knows.. Just add a HAVING COUNT(*) > 2.
That should still work, but now, getting just the person... Ignore the rest of the columns and just do
Select distinct O.PersonID from ... rest of query
Select PersonId, count(PersonId)
from Orders
where OrderDate <= '2015-01-31' and OrderDate >= '2015-01-01'
group by PersonId
having count(PersonId) > 1

SQL Query to fetch number of employees joined over a calender year, broken down per month

I'm trying to find the number of employees joined over a calender year, broken down on a monthly basis. So if 15 employees had joined in January, 30 in February and so on, the output I'd like would be
Month | Employees
------|-----------
Jan | 15
Feb | 30
I've come up with a query to fetch it for a particular month
SELECT * FROM (
SELECT COUNT(EMP_NO), EMP_JN_DT
FROM EMP_REG WHERE
EMP_JN_DT between '01-NOV-09' AND '30-NOV-09'
GROUP BY EMP_JN_DT )
ORDER BY 2
How do I extend this for the full calender year?
SELECT Trunc(EMP_JN_DT,'MM') Emp_Jn_Mth,
Count(*)
FROM EMP_REG
WHERE EMP_JN_DT between date '2009-01-01' AND date '2009-12-31'
GROUP BY Trunc(EMP_JN_DT,'MM')
ORDER BY 1;
If you do not have anyone join in a particular month then you'd get no row returned. To over come this you'd have to outerjoin the above to a list of months in the required year.
SELECT to_date(EMP_JN_DT,'MON') "Month", EMP_NO "Employees"
FROM EMP_REG
WHERE EMP_JN_DT between date '2009-01-01' AND date '2009-12-31'
GROUP by "Month"
ORDER BY 1;
http://www.techonthenet.com/oracle/functions/extract.php
There is a function that returns month. What you need to do is just put it in group by
The number of employees in January can be selected in the following way:
SELECT EXTRACT(MONTH FROM HIREDATE) AS MONTH1, COUNT(*)
FROM employee
WHERE EXTRACT(MONTH FROM HIREDATE)=1
GROUP BY EXTRACT(MONTH FROM HIREDATE)