Get SUM of 1st column ON DISTINCT 2nd column - mysql

I need to get the sales total from our table for a certain date range, and display the results grouped by day of the week. (making a chart)
The following is what i have so far
SELECT DAYNAME(sale_date), SUM(total_price + total_tax)
FROM threewor_vend.vendsales AS s
WHERE s.register_id = '07709f8e-8d90-11e0-8e09-4040f540b50a'
AND sale_date >= DATE_SUB(CURRENT_DATE, INTERVAL 7 DAY)
GROUP BY DAYOFWEEK(sale_date)
This returns the day and total sales for each day, which is great.
Thing is, there are multiple rows 'per sale' if there is more than one item per sale. So the total price is duplicated a number of times. So i can't just get the sum of total_price.
I need to get the sum of the total_price, ONLY for each UNIQUE sale_id.
eg
sale id | item | itemqty | item value | item total | total_price |
---1----|-drum-|-1-------|-60.00------|-60.0-------|-$230.00-----|
---1----|-uke--|-1-------|-170.00-----|-170.0------|-$230.00-----|

In MySQL, you can group by the sale_id. This will eliminate the duplicates:
SELECT DAYNAME(sale_date), SUM(total_price + total_tax)
FROM (select s.*
from threewor_vend.vendsales s
group by sale_id
) s
WHERE s.register_id = '07709f8e-8d90-11e0-8e09-4040f540b50a'
AND sale_date >= DATE_SUB(CURRENT_DATE, INTERVAL 7 DAY)
GROUP BY DAYOFWEEK(sale_date);
As long as the other columns -- sale_date, total_price, total_tax, and register_id -- are the same on all the rows for a given sale_id, then this will work.

Since you're storing the total_price multiple times per sale, your results are being multiplied.
Here's one option using a subquery with DISTINCT assuming the four fields are all the same for each duplicated sale:
SELECT DAYNAME(sale_date), SUM(total_price + total_tax)
FROM (
SELECT DISTINCT sale_date, total_price, total_tax, register_id
FROM threewor_vend.vendsales
WHERE s.register_id = '07709f8e-8d90-11e0-8e09-4040f540b50a'
AND sale_date >= DATE_SUB(CURRENT_DATE, INTERVAL 7 DAY)
) t
GROUP BY DAYOFWEEK(sale_date)

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

MySQL most price change over time

price | date | product_id
100 | 2020-09-21 | 1
400 | 2020-09-20 | 2
300 | 2020-09-20 | 3
200 | 2020-09-19 | 1
400 | 2020-09-18 | 2
I add an entry into this table every day with a product's price that day.
Now I want to get most price drops for the last week (all dates up to 2020-09-14), in this example it would only return the product_id = 1, because that's the only thing that changed.
I think I have to join the table to itself, but I'm not getting it to work.
Here's something that I wanted to return the most price changes over the last day, however it's not working.
select pt.price, pt.date, pt.product_id, (pt.price - py.price) as change
from prices as pt
inner join (
select *
from prices
where date > '2020-09-20 19:33:43'
) as py
on pt.product_id = py.product_id
where pt.price - py.price > 0
order by change
I understand that you want to count how many times the price of each product changed over the last 7 days.
A naive approach would use aggregation and count(distinct price) - but it fails when a product's price changes back and forth.
A safer approach is window functions: you can use lag() to retrieve the previous price, and compare it against the current price; it is then easy to aggregate and count the price changes:
select product_id, sum(price <> lag_price) cnt_price_changes
from (
select t.*, lag(price) over(partition by product_id order by date) lag_price
from mytable t
where date >= current_date - interval 7 day
) t
group by product_id
order by price_changes desc
Try using MAX() and MIN() instead....
select MAX(pt.price), MIN(pt.price), MAX(pt.price) - MIN(pt.price) as change
from prices as pt
inner join (
select *
from prices
where date > '2020-09-20 19:33:43'
) as py
on pt.product_id = py.product_id
order by change
Instead of subtracting every row by every other row to get the result, you can find the max and min's easily by means of MAX() and MIN(), and, ultimately, **MAX() - MIN()**. Relevant lines from the linked MySQL documentation...
MAX(): Returns the maximum value of expr.
MIN(): Returns the minimum value of expr.
You won't be able to pull the other fields (id's, dates) since this is a GROUP BY() implied by the MAX() and MIN(), but you should then be able to get that info by query SELECT * FROM ... WHERE price = MAX_VALUE_JUST_ACQUIRED.
This examples will get you results per WeekOfYear and WeekOfMonth regarding the lowering of the price per product.
SELECT
COUNT(m1.product_id) as total,
m1.product_id,
WEEK(m1.ddate) AS weekofyear
FROM mytest m1
WHERE m1.price < (SELECT m2.price FROM mytest m2 WHERE m2.ddate<m1.ddate AND m1.product_id=m2.product_id LIMIT 0,1)
GROUP BY weekofyear,m1.product_id
ORDER BY weekofyear;
SELECT
COUNT(m1.product_id) as total,
m1.product_id,
FLOOR((DAYOFMONTH(ddate) - 1) / 7) + 1 AS weekofmonth
FROM mytest m1
WHERE m1.price < (SELECT m2.price FROM mytest m2 WHERE m2.ddate<m1.ddate AND m1.product_id=m2.product_id LIMIT 0,1)
GROUP BY weekofmonth,m1.product_id
ORDER BY weekofmonth;
Try this out in SQLFiddle.

Get count of departmant with total employees for period of month in mysql

I have requirement to get count of distinct department with total employees in period of month but unfortunately query is not working and throwing error
My table
Department_id emloyee_id date_time
1 1 2020-02-01
1 2 2020-02-04
3 7 2020-02-06
1 4 2020-02-07
expected output
total department=2
total employee of all department=4
But all should work based on last one record , I am getting sql syntax error
Query:
SELECT COUNT(DISTINCT department_id) x, COUNT(*) y
FROM department
WHERE date_time>=DATE_FORMAT(NOW() ,'%Y-%m-01')
AND date_time<DATE(NOW()+INTERVAL 1 DAY and status='1'
You can combine them within only one query :
SELECT COUNT(DISTINCT Department_id), COUNT(DISTINCT employee_id)
FROM department
WHERE date_time >= NOW() - INTERVAL 1 MONTH
AND status = '1';
counting both distinctly.
Update : If you mean to stay within the current month, then also
AND date_time>=DATE_FORMAT(NOW() ,'%Y-%m-01')
might be added to this query as in your original one.
It seems you should use month instead of day and are missing a bracket after month
SELECT COUNT(DISTINCT department_id) AS departments,
COUNT(*) AS employees
FROM department
WHERE date_time>=DATE_FORMAT(NOW() ,'%Y-%m-01')
AND date_time < DATE(NOW()+INTERVAL 1 MONTH)
AND status = '1';

efficient way to find last week customer

I have 3 columns(customerid, date_purchased, item) table with 2 weeks of data. I want to retrieve the customers that only bought from the first week. My logic is to find the max date subtract it all the rest of the dates and retrieve customers where that difference equal or less than 7. Here is what I did, but I have a problem with my query.
select distinct(customerid) from customer where datediff(max(date_purchased),Orderdate)<=7;
You could filter with a correlated subquery:
select distinct customerid
from customer
where date_purchased > (
select max(date_purchased) - interval 7 day from customer
)
You can first group by max() date_purchased per customer id then join it to get orderdate less than 7 days from your date of purchase.
select distinct(customerid)
from customer t1
inner join
(select max(date_purchased) date_purchased, customerid as date_purchased
from customer group by customerid) t2
on t2.customerid = t1.customerid
where datediff(t2.date_purchased, t1.Orderdate) <= 7
You can do this with aggregation, if you prefer:
select customerid
from customer
group by customerid
having max(date_purchased) > max(max(datepurchased)) over () - interval 7 day;

MYSQL select average number of entries

I have a table that has a unique key each time a user creates a case:
id|doctor_id|created_dt
--|---------|-----------
1|23 |datetimestamp
2|23 |datetimestamp
3|17 |datetimestamp
How can I select and return the average amount of entries a user has per month?
I have tried this:
SELECT avg (id)
FROM `cases`
WHERE created_dt BETWEEN DATE_SUB(CURDATE(),INTERVAL 90 DAY) AND CURDATE()
and doctor_id = 17
But this returns a ridiculously large value that cannot be true.
To clarify: I am trying to get something like doctor id 17 has an average of 2 entries per month into this table.
I think you were thrown off by the idea of "averaging". You don't want the average id, or average user_id. You want the average number of entries into the table, so you would use COUNT():
SELECT count(id)/3 AS AverageMonthlyCases
FROM `cases`
WHERE created_dt BETWEEN DATE_SUB(CURDATE(),INTERVAL 90 DAY) AND CURDATE()
group by doctor_id
Since you have a 90 day interval, you want to count the number of rows per 30 days, or the count/3.
SELECT AVG(cnt), user_id
FROM (
SELECT COUNT(id) cnt, user_id
FROM cases
WHERE created_dt BETWEEN <yourDateInterval>
GROUP BY user_id, year(created_dt), month(created_dt)
)
Since you need average number of entries, AVG function is not really applicable, because it is SUM()/COUNT() and obviously you do not need that (why would you need SUM of ids).
You need something like this
SELECT
doctor_id,
DATE(created_dt,'%m-%Y') AS month,
COUNT(id) AS visits
FROM `cases`
GROUP BY
`doctor_id`,
DATE(created_dt,'%m-%Y')
ORDER BY
`doctor_id` ASC,
DATE(created_dt,'%m-%Y') ASC
To get visits per month per doctor. If you want to average it, you can then use something like
SELECT
doctor_id,
SUM(visits)/COUNT(month) AS `average`
FROM (
SELECT
doctor_id,
DATE(created_dt,'%m-%Y') AS month,
COUNT(id) AS visits
FROM `cases`
GROUP BY
`doctor_id`,
DATE(created_dt,'%m-%Y')
ORDER BY
`doctor_id` ASC,
DATE(created_dt,'%m-%Y') ASC
) t1
GROUP BY
doctor_id
Obviously you can add your WHERE clauses, as this query is compatible for multiple years (i.e. it will not count January of 2013th and January of 2014th as one month).
Also, it takes into account if a doctor has "blank" months, where he did not have any patients, so it will not count those months (0 can destroy and average).
Use this, you'll group each doctor's total id, by month.
Select monthname(created_dt), doctor_id, count(id) as total from cases group by 1,2 order by 1
Also you can use GROUP_CONCAT() as nested query in order to deploy a pivot like table, where each column is each doctor_id.