Adding multiple columns' rows together - mysql

I'm trying to arrange data in a table. The table has the following columns:
Customer Name, Amount, Day. The customer names are not distinct, the amount is an amount represented by dollars and the Day is over the course of 365 days.
I'm trying to arrange the amount paid per quarter, regardless of the customer name.
This is a homework assignment and I've tried this code
SELECT day as 'Quarter', SUM(amount) as 'Total Earnings'
FROM invoices
WHERE day BETWEEN 0 and 90
GROUP BY day
I'm running into 3 problems. I did the above code just to test that it would work for one quarter before i tried to tackle the whole year.
The first problem is that I need the day 'value' to be 'First' and I'm not sure how to do that at all.
Secondly, it is totaling the amounts, but not 0-90, it's totaling 1, 2, 3... 89, 90. Rather than a single row with the total 'amounts' for days 0-90.
Lastly, I'm not sure how to do another sum for the other quarters (91-180, 181-270, 271-365). I'm assuming possibly subqueries, but I'm not sure how to do that while using WHERE/BETWEEN.
My output should be something like:
Quarter | Total Earnings
-------------------------
First | 111111111
Second | 111111111
Third | 111111111
Fourth | 111111111

SELECT 'first' AS quarter, SUM(amount) AS total_earnings
FROM invoices where day between 0 AND 90
UNION ALL
SELECT 'second' AS quarter, SUM(amount) AS total_earnings
FROM invoices where day between 91 AND 180
UNION ALL
SELECT 'third' AS quarter, SUM(amount) AS total_earnings
FROM invoices where day between 181 AND 270
UNION ALL
SELECT 'fourth' AS quarter, SUM(amount) AS total_earnings
FROM invoices where day >= 271
This will get you the expected results. The group by you were using will try to group based on day unlike on quater

You could use a CASE to find what quarter a day is in and then group by that. Something like this:
SELECT `quarter` AS 'Quarter',
SUM(amount) AS 'Total Earnings'
FROM (
SELECT CASE WHEN DAY < (365/4)
THEN 'First'
WHEN t.`day` < (365/4)*2
THEN 'Second'
WHEN t.`day` < (365/4)*3
THEN 'Third'
ELSE 'Fourth'
END AS `quarter`,
t.*
FROM `table` t
) t2
GROUP BY `quarter`;
You could of course replace the 365/ whatever with just a number of days or set a variable for the number of days in a year like SET #days_in_year = 365;. I'm just manually calculating to give a quick explanation of what the number is.

With a CASE statement you can evaluate the Quarter and then you can group by Quarter:
SELECT
case
when day BETWEEN 0 and 90 then 'First'
when day BETWEEN 91 and 180 then 'Second'
when day BETWEEN 181 and 270 then 'Third'
else 'Fourth'
end Quarter,
SUM(amount) as `Total Earnings`
FROM invoices
GROUP BY Quarter
Change the day ranges as you like.

Related

How to find average order value and count in SQL when the rows are repeated? Interview question I didn't know to solve

During my interview, I had a simple sounding question about calculating daily average number of orders and daily average revenue. But in the table, records were arranged like this: If I place an order with 4 items, the 4 items were shown as individual records with the same order ID. But the order_value column had only the total order value in all the 4 records. How can I calculate
The average daily order value and daily average number of orders?
How many orders were delivered within the preparation time of 20 minutes?
Giving the table below for reference:
The way I understand it.
You start by calculating the metrics per day.
select
date(order_date) as order_day
, sum(final_bill) as daily_total_bill
, count(order_id) as daily_orders
, count(case
when timestampdiff(minute, order_date, prepared_date) <= 20
then order_id
end) as daily_ontime
from kitchen_orders
group by date(order_date)
order_day
daily_total_bill
daily_orders
daily_ontime
2020-12-01
95.30
3
3
2020-12-02
105.60
3
2
And then wrap that in a sub-query or cte, to get the averages and totals for all.
select
round(avg(daily_total_bill), 2) as avg_daily_bill,
round(avg(daily_orders), 1) as avg_daily_orders,
sum(daily_ontime) as total_ontime
from
(
select
date(order_date) as order_day
, sum(final_bill) as daily_total_bill
, count(order_id) as daily_orders
, count(case
when timestampdiff(minute, order_date, prepared_date) <= 20
then order_id
end) as daily_ontime
from kitchen_orders
group by date(order_date)
) q
avg_daily_bill
avg_daily_orders
total_ontime
100.45
3.0
5
Demo on db<>fiddle here

how to sum the last 12 months of data in a joined table

I have two joined tables, customers and orders, both linked by CustomerPK.
On the customer tale i am trying to sum the total spend of each customer (taking sum from join to orders table) and then also caluclate the last 12 months spend for that customer, it is this last line that isnt working, here is my code:
SELECT
customers.CustomerPk,
customers.CustomerCode,
customers.CustomerName,
customers.AddressLine1,
customers.AddressLine2,
customers.AddressLine3,
customers.AddressLine4,
customers.AddressLine5,
customers.Town,
customers.County,
customers.PostCode,
customers.Country,
SUM(orders.OrderValue) AS `Total Spend`,
SUM(orders.OrderValue) WHERE orders.OrderDate >= curdate() - interval 1 year as `Last 12 Month Spend`
FROM customers
LEFT OUTER JOIN orders ON customers.CustomerPk = orders.CustomerFk
GROUP BY customers.CustomerPk, customers.CustomerCode, customers.CustomerName, customers.AddressLine1, customers.AddressLine2, customers.AddressLine3, customers.AddressLine4, customers.AddressLine5, customers.Town, customers.County, customers.PostCode, customers.Country
The table was working until i added the following line:
SUM(orders.OrderValue) WHERE orders.OrderDate >= curdate() - interval 1 year as Last 12 Month Spend
I need this WHERE statement to apply only to this line and not to the overall table as it will remove results.
Many thanks in advance
You cannot add a WHERE clause to a column. But if you want to sum only values for which a condition is met you can use a CASE expression in the sum(). If your condition is met, return the value, that should be added to the sum. Otherwise return zero -- the sum won't change if you add zero.
...
sum(CASE
WHEN orders.orderdate >= curdate() - interval 1 year THEN
orders.ordervalue
ELSE
0
END) `Last 12 Month Spend`
...

selecting the month day and year from a transaction table

I want to create two queries for my table which has fields name,surname and amount paid,the first query should select the day,month and the amount paid,the second query should select a month,year in that year and the total amount paid in that month,lets say john paid on 2013-05-01, on 2013-05-03,while peter paid on 2013-04-08, i want the first query to output
month and day amount
05-01 200
05-03 400
04-08 50
and the second query should output:
month and year total
2013-05 600
2013-04 50
I know I can use the sum aggregate function to select the total but the tricky part is how to select the day and the month in the format above,
first query will be
SELECT DATE_FORMAT(date, "%m-%d") AS 'month and day',price as amount FROM `tablename`
and second query will be
SELECT DATE_FORMAT(date, "%Y-%m") AS 'month and year' , SUM(price) AS total FROM `tablename` GROUP BY YEAR(date), MONTH(date)

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.

mysql select data with calculation between 2 tables and group by month

I have 2 mysql tables like bellow:
Table income Table expense
id amount date id amount date
1 200 2011-12-10 1 100 2011-12-21
2 300 2011-12-14 2 150 2012-01-01
3 500 2012-01-05 2 200 2012-01-03
I want to get data in this way:
month profit
december, 2011 400
january, 2012 150
Is this possible in a single query?
What you want is a union with some date magic for the aggregate:
select
date_format(x.date, '%M %Y') as `month`,
sum(amount) as profit
from
(select amount, date
from income
union all
select amount*-1 as amount, date
from expense
) x
group by
date_format(x.date, '%M %Y')
The specific thing we're using here is date_format to get the dates the way we want it. Also, we're multiplying amount by -1 when it comes from expense to make the calculation correct. Of course, we could have returned different columns for income and expense and then did the math on them in the sum, such as sum(x.income_amount, x.expense_amount), but there's no need to do that when you can just quickly do a *-1 to the column on before it hits the sum.
The other important thing is the group by. Here, we're grouping by a function that formats the date as Month, YYYY, so it will get the divisions you're looking for.