SQL sales report by calendar week - mysql

I've been Googling for a few hours... thought this would be easy, but clearly not for me :)
I've got sales data in two tables and I want to generate a weekly sales report for a specific item. For this purpose, I don't care about dollar values, just number of units. An a "week" is either a calendar week (whatever start day, I don't care) or just 7-day chunks back from current (so week 1 is the last 7 days, week 2 is 8 - 15 days ago, etc) - whichever is easier. I'm simply trying to monitor sales trends over time. Preferably it would span back over years so that if its the first week of January, for example, it wouldn't show just one record.
The data comes from ZenCart. The relevant tables/column structure is here:
Table "orders" has columns: orders_id, date_purchased
Table "orders_products" has columns: orders_id, products_id, product_quantity
Where I'm having trouble is with the joins and syntax.

This worked for my needs:
SELECT o.date_purchased, CONCAT(YEAR(o.date_purchased), LPAD(WEEK(o.date_purchased), 2, '0')) as weekyear, op.products_id, SUM( op.products_quantity )
FROM orders_products op
LEFT JOIN orders o ON op.orders_id = o.orders_id
WHERE op.products_id = 331
GROUP BY weekyear
ORDER BY weekyear

If you have some date/datetime/timestamp column, you can use the week function in your where clause
select week(now()) as week, sum(units) as total
from sales
where week(sales_date) = week(now());
or the previous week
select week(now()) - 1 as week, sum(units) as total
from sales
where week(sales_date) = week(now()) - 1;
You must take care for the year wrap around from week 52/53 to week 0/1.
SQLFiddle for testing.

In order to take care of the year end wrap. for instance, week(12/30/2018)=52 and week(12/31/2018)=52 both are considered week 52 of 2018. the first day of 2019 starts on a Tuesday. you can write a case statement as follows to move 12/30/2018 and 12/31/2018 to the first week of 2019. so that you will have a complete 7 days week to compare:
case when order_date in ( '2018-12-30', '2018-12-31')
then 0
else week(order_date)
end as order_week

Related

How to find buyers with minimum 3 orders after optin date for the following 15 day period

I'm looking for a way to find all buyers who registered for the sales promotion in November 2022 only and also made at least 3 orders for the following 15 day period after the registration(OPTINDATE).
Here is my query that doesn't work:
SELECT ID, OPTINDATE,ORDERDATE, COUNT(ORDER)
FROM BUYER
INNER JOIN PURCHASE USING(ID)
WHERE OPTINDATE between '2022-11-01' and '2022-11-30'
AND ORDERDATE > OPTINDATE(day,15)
GROUP BY ID
HAVING COUNT(ORDER)>= 3;
I'Ve got syntax error.
This part doesn't make sense:
OPTINDATE(day, 15)
Perhaps you wanted to use the
ADDdate function:
ADDDATE(OPTINDATE, 15)

SQL Query To Obtain Values that contains multiple instances within a time range

I want to only find accounts that have more than one instance of a closed_date within their own 30 day, 3 month, and 1 year period which ends at their own monthly, quarterly, and annual expiration date. However each account has its own expiration date range.
My WHERE clause is where I can’t seem to figure out how to implement the proper 30 day, quarterly, and yearly range.
I'm not sure if the BETWEEN clause is appropriate or if I should be using a greater than / less than.
SELECT a.acct, COUNT(d.closed_date) AS cd, a.billing_expiration_date
FROM docupaid d
INNER JOIN account a ON a.acct=d.acct
WHERE (d.closed_date) BETWEEN (a.bill_expiration_date minus 30 days) AND a.billing_expiration_date
GROUP BY acct desc
HAVING cd>1
You can use DATE_SUB function:
SELECT a.acct,
COUNT(d.closed_date) AS cd,
a.billing_expiration_date
FROM docupaid d
INNER JOIN account a ON a.acct=d.acct
WHERE d.closed_date BETWEEN DATE_SUB(a.bill_expiration_date, INTERVAL 30 DAY)
AND a.billing_expiration_date
GROUP BY acct desc
HAVING cd > 1

MySQL WHERE Date > 15 Days

I have three tables. customers, DVDs, Movies. This is the last report I need to produce, and it's tripping me up. I currently have a field: dvd.Due_Date, which is the date that the product is due back. I need to retrieve all files where the Due Date is 15 days past the current date.
This is what I have so far:
SELECT customer.customer_id,
customer.customer_fname,
customer.customer_lname,
customer.customer_phone,
customer.customer_email,
dvd.DVD_ID, movie.Movie_Title,
dvd.Rental_Date, dvd.Due_Date
FROM customer INNER JOIN dvd
ON customer.customer_id = dvd.customer_id
INNER JOIN movie ON dvd.Movie_ID = movie.Movie_ID
WHERE DATEDIFF(Due_Date, CURDATE() ) > 15
I'm not getting any errors, I'm just not getting any results back, even though I have multiple items listed as due date of Feb. 10th. I do get all of the information I want if I remove everything past the WHERE statement, so I know that is working at least.
For DATEDIFF if the first item is a smaller date than the second item then it returns a negative number (as such could never be larger than 16) and not a positive one. So flip them, you want the later date as the first argument:
... WHERE DATEDIFF( CURDATE(), Due_Date ) > 15
I'm not sure what you mean by "all files where the Due Date is 15 days past the current date." However, try using logic like this:
SELECT c.customer_id, c.customer_fname, c.customer_lname, c.customer_phone, c.customer_email, d.DVD_ID, m.Movie_Title, d.Rental_Date, d.Due_Date
FROM customer c INNER JOIN
dvd d
ON c.customer_id = d.customer_id INNER JOIN
movie m
ON d.Movie_ID = m.Movie_ID
WHERE due_date >= date_sub(curdate(), interval 15 day);
You might want date_add() instead.

Calculate salary of tutor based on distinct sittings using mysql

I have the following table denoting a tutor teaching pupils in small groups. Each pupil has an entry into the database. A pupil may be alone or in a group. I wish to calculate the tutors "salary" as such: payment is based on time spent - this means that for each sitting (with one or more pupils) only one sitting will be calculated - distinct sittings! The start and end times are unix times.
<pre>
start end attendance
1359882000 1359882090 1
1359867600 1359867690 0
1359867600 1359867690 1
1359867600 1359867690 0
1360472400 1360477800 1
1360472400 1360477800 1
1359867600 1359867690 1
1359914400 1359919800 1
1360000800 1360006200 1
1360000800 1360006200 0
1360000800 1360006200 1
</pre>
This is what I tried: with no success - I can't get the right duration (number of hours for all distinct sittings)
SELECT YEAR(FROM_UNIXTIME(start)) AS year,
MONTHNAME(STR_TO_DATE(MONTH(FROM_UNIXTIME(start)), '%m')) AS month,
COUNT(DISTINCT start) AS sittings,
SUM(TRUNCATE((end-start)/3600, 1)) as duration
FROM schedules
GROUP BY
YEAR(FROM_UNIXTIME(start)),
MONTH(FROM_UNIXTIME(start))
Thanks for your proposals / support!
EDIT: Required results
Rate = 25
Year Month Sittings Duration Bounty
2013 February 2 2.2 2.2*25
2013 April 4 12.0 12.0*25
You could probably do something with subqueries, I've had a play with SQL fiddle, how does this look for you. Link to sql fiddle : http://sqlfiddle.com/#!2/50718c/3
SELECT
YEAR(d.date) AS year,
MONTH(d.date) AS month,
COUNT(*) AS sittings,
SUM(d.duration) AS duration_mins
FROM (
SELECT
DATE(FROM_UNIXTIME(s.start)) AS date,
s.attendance,
end-start AS duration
FROM schedules s
) d
GROUP BY
year,
month
I couldn't really see where attendance comes into this at present, you didn't specify. The inner query is responsible for taking the schedules, extracting a start date, and a duration (in seconds).
The outer query then uses these derived values but groups them up to get the sums. You could elaborate from here i.e. maybe you only want to select where attendance > 0, or maybe you want to multiply by attendance.
In this next example I have done this, calculating the duration in hours instead, and calculating the applicable duration for where sessions have >1 attendance along with the appropriate bounty assuming bounty == hours * rate : http://sqlfiddle.com/#!2/50718c/21
SELECT
YEAR(d.date) AS year,
MONTH(d.date) AS month,
COUNT(*) AS sittings,
SUM(d.duration) AS duration,
SUM(
IF(d.attendance>0,1,0)
) AS sittingsWorthBounty,
SUM(
IF(d.attendance>0,d.duration,0)
) AS durationForBounty,
SUM(
IF(d.attendance>0,d.bounty,0)
) AS bounty
FROM (
SELECT
DATE(FROM_UNIXTIME(s.start)) AS date,
s.attendance,
(end-start)/3600 AS duration,
(end-start)/3600 * #rate AS bounty
FROM schedules s,
(SELECT #rate := 25) v
) d
GROUP BY
year,
month
The key point here, is that in the subquery you do all the calculation per-row. The main query then is responsible for grouping up the results and getting your totals. The IF statements in the outer query could easily be moved into the subquery instead, for example. I just included them like this so you could see where the values came from.

Complex MySQL SELECT Query

Hi still getting my head around MySQL so was hoping someone may be able to shed some light on this one
I have a table named customers which has the following columns
msisdn BIGINT 20
join_date DATETIME
The msisdn is a unique value to identify customers.
There is a second table named ws_billing_all which has the following structure
id INTEGER 11 (Primary Key)
msisdn BIGINT 20
event_time DATETIME
revenue INTEGER
So this table stores all transactions for each of the customers in the customers table as identified by the msisdn.
What I need to do is to determine the amount from all customers that joined on a particular day after 30 days.
So for example, on the 2nd of Dec 2010, 1,100 customers were acquired. Based on the data in ws_billing_all, how much total revenue did the customers that joined on this day generate 30 days from this date.
I will probably need another table for this but not sure and really not sure on how to go about extracting this data. Any help would be appreciated.
#Cularis was very close... You only care about those customers that joined on the ONE DAY, and want all THEIR REVENUEs earned for the next 30 days... In this scenario, a customer would never have sales prior to their join date, so I didn't add an explicit between on their actual sales dates of consideration.
SELECT
date( c.Join_Date ) DateJoined,
count( distinct c.msisdn ) DistinctMembers,
count(*) NumberOfOrders,
SUM(w.revenue) AmountOfRevenue
FROM
customers c
JOIN ws_billing_all w
ON c.msisdn = w.msisdn
AND date( w.event_time ) <= date_add( c.Join_Date, INTERVAL 30 DAY )
WHERE
c.Join_Date >= SomeDateParameterValue
group by
date( c.Join_Date )
order by
date( C.Join_Date )
EDIT -- For clarification...
If you had 150 people join on Dec 1, 45 people on Dec 2, 83 people on Dec 3, you want to see the total revenue per group of people based on the day they joined going out 30 days of their sales... So...
Joined on Number of People Total Revenue after 30 days
Dec 1 150 $21,394 (up to Dec 31)
Dec 2 45 $ 4,182 (up to Jan 1)
Dec 3 83 $ 6,829 (up to Jan 2)
Does this better clarify what you want? Then we can adjust the query...
FINAL EDIT ...
I think I have what you INTENDED (with a count of orders too that might be useful). In the future, providing a sample output of something of complex nature would be helpful, even if it was as simple as I've done here.
With respect to my WHERE clause from the customers table.... Say you only cared about customers who joined within a given time frame, or only after a given date... THIS is where you would update the clause... if you want based on ALL people, then just remove it completely.
SELECT c.msisdn, SUM(w.revenue)
FROM customers c
INNER JOIN ws_billing_all w ON c.msisdn=w.msisdn
WHERE w.event_time BETWEEN c.join_date AND DATE_ADD(c.join_date, INTERVAL 30 DAY)
GROUP BY c.msisdn
You have to join both tables on the customer id. Then select all events that happened between the join date and 30 days after that. Group by the customer id and use SUM() to get total revenue per costumer.