SELECT data and latest stock from GROUP - mysql

I have to ask my question based on the following example.
mysql table
step 1: get sales per day
SELECT SUM(amount) as sold, productnumber, DATE(date) AS date
FROM foo
GROUP BY productnumber, DATE(date)
ORDER BY date;
returns:
step 2 / wanted: get sales and latest stock per day
Latest stock = last stock update per day, e.g. for productnumber 123 it's 7 on 2017-12-06:
So how to select this?
Clearly, just adding stock to the SELECT won't work:
SELECT stock, SUM(amount) as sold, productnumber, DATE(date) AS date
FROM foo
GROUP BY productnumber, DATE(date)
ORDER BY date;
It would return 0 instead of 7 on this product and on this day:
LEFT JOIN? Subquery?
How to write this SQL properly?
Thanks in advance!

The canonical way is a join:
SELECT f.*, f2.stock
FROM foo f JOIN
(SELECT f.productnumber, SUM(f.amount) as sold, DATE(f.date) as date,
MAX(f.date) as maxdate
FROM foo f
GROUP BY f.productnumber, DATE(f.date)
) f join
foo f2
ON f2.productnumber = f.productnumber AND f2.date = f.maxdate
ORDER BY date;

Related

Select 3 Different Columns (COUNT) from 3 different tables and group by Date

I have 3 different tables called that tracks the number of pens sold, pencils sold and ink sold.
**pens table:**
date who_bought
12.03.2020 a
12.03.2020 d
13.03.2020 b
14.03.2020 c
**pencils table:**
date who_bought
12.03.2020 z
16.03.2020 r
17.03.2020 j
17.03.2020 k
**ink table:**
date who_bought
11.03.2020 h
11.03.2020 j
13.03.2020 z
17.03.2020 r
I want to aggregate data and get for each day, how many pens, inks and pencils I sold. The "who_bought" column is not relevant (I mean the values). I just want to count the number of records for each day. finally, I want to sort by date.
In the above case, I would like to get results like
11.03.2020 pens:0 pencils:0 ink: 2
12.03.2020 pens:2 pencils:1 ink: 0
13.03.2020 pens:1 pencils:0 ink:1
14.03.2020 pens:0 pencils:0 ink:0
16.03.2020 pens:0 pencils:1 ink:0
17.03.2020 pens:0 pencils:2 ink:1
How can this be achieved?
I tried something like this, but it's not working:
select
COUNT(pens.*) as pens,
COUNT(pencils.*) as pencils,
COUNT(ink.*) as ink,
DATE(date) as date
from
pens
full join pencils on pencils.date=pens.date
full join ink on ink.date=pens.date
group by
date
order by
date asc
Your attempt using full join is on the right track; unfortunately, MySQL does not support this syntax.
You could do this with union all and conditional aggregation:
select
date,
sum(what = 'pens') no_pens,
sum(what = 'pencils') no_pencils,
sum(what = 'ink') no_inks
from (
select 'pens' what, date from pens
union all select 'pencils', date from pencils
union all select 'ink', date from ink
) t
group by date
If you want all dates, including those for which no sale happened for any product, then it is a bit different. Basically, ou need a calendar table for this. Here is one way to do it with a recursive query (available in MySQL 8.0 only).
with dates as (
select min(date) date, max(date) max_date
from (
select date from pens
union all select date from pencils
union all select date from ink
) t
union all
select date + interval 1 day from cte where date < max_date
)
select
d.date,
pn.no_pens,
pl.no_pencils,
ik.no_inks
from dates d
left join (select date, count(*) no_pens from pens group by date) pn on pn.date = d.date
left join (select date, count(*) no_pencils from pencils group by date) pl on pl.date = d.date
left join (select date, count(*) no_inks from inks group by date) ik on ik.date = d.date

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;

add a column in MySQL rank by deal by day

I am just learning MySQL. I need to find out rank of deals by day. Here I am adding the corresponding MYSQL query for my requirement that currently ranks all sales highest to lowest by day. Please help me to add a column that gives the rank to the deal highest to lowest and resetting the next day.
Here is my current working query,..
single day with title, price
SELECT
DATE(order_items.created_at) AS the_day,
order_items.deal_id,
SUM(order_items.item_total) AS daily_total,
SUM(order_items.qty) AS units_sold,
deals.price,
deals.title
FROM
order_items JOIN deals ON order_items.deal_id = deals.id
WHERE
order_items.created_at >= '2016-01-01 00:00:00' AND order_items.created_at < '2016-01-30 00:00:00'
AND
order_items.status=1
AND
order_items.paid=1
GROUP BY
order_items.deal_id
ORDER BY
the_day,
daily_total DESC;
The easiest way to do is that:
Use your existing SQL - I guess you need to update your SQL, make sure any non-aggregated columns in select should be in group by as well
Use PHP to loop (1-5), it works for multiple days
If you are happy to get top 5 for a single day, you can add limit 5 at end of your SQL
If you need top 5 results for each day in multiple days in one SQL, you need to update SQL to be more complicated. And here is a hint to use row id see example:
select increment counter in mysql
OK - Since you updated your question to return top 1 result per day, this is easier:
Step 1: get each day, each deal, report:
SELECT deal_id, date(created_at) ymd, sum(item_total) daily_total, sum(qty) units_sold
FROM order_items
WHERE substr(created_at,1,7) = '2016-01'
AND status = 1
AND paid = 1
GROUP BY 1,2
Step 2: Find the best deal of each day from step 1:
SELECT aa.ymd, max(aa.daily_total) max_total
FROM (
SELECT deal_id, date(created_at) ymd, sum(item_total) daily_total, sum(qty) units_sold
FROM order_items
WHERE substr(created_at,1,7) = '2016-01'
AND status = 1
AND paid = 1
GROUP BY 1,2
) as aa
GROUP BY 1;
Please note that max(item_total) not necessary same row as max(unit_sold), so you need to choose one, and cannot run togather
Step 3: Join step 2 with step 1 and deals to find out the rest of information:
SELECT aa.*, deals.price, deal.title
FROM (
SELECT aa.ymd, max(aa.daily_total) max_total
FROM (
SELECT deal_id, date(created_at) ymd, sum(item_total) daily_total, sum(qty) units_sold
FROM order_items
WHERE substr(created_at,1,7) = '2016-01'
AND status = 1
AND paid = 1
GROUP BY 1,2
) as aa
GROUP BY 1
) as bb
JOIN (
SELECT deal_id, date(created_at) ymd, sum(item_total) daily_total, sum(qty) units_sold
FROM order_items
WHERE substr(created_at,1,7) = '2016-01'
AND status = 1
AND paid = 1
GROUP BY 1,2
) as aa ON bb.ymd = aa.ymd and bb.max_total = aa.daily_total
JOIN deals ON aa.deal_id = deals.id
ORDER BY aa.ymd, aa.max_total

How to get rows with max date when grouping in MySQL?

I have a table with prices and dates on product:
id
product
price
date
I create a new record when price change. And I have a table like this:
id product price date
1 1 10 2014-01-01
2 1 20 2014-02-17
3 1 5 2014-03-28
4 2 25 2014-01-05
5 2 12 2014-02-08
6 2 30 2014-03-12
I want to get last price for all products. But when I group with "product", I can't get a price from a row with maximum date.
I can use MAX(), MIN() or COUNT() function in request, but I need a result based on other value.
I want something like this in final:
product price date
1 5 2014-03-28
2 30 2014-03-12
But I don't know how. May be like this:
SELECT product, {price with max date}, {max date}
FROM table
GROUP BY product
Alternatively, you can have subquery to get the latest get for every product and join the result on the table itself to get the other columns.
SELECT a.*
FROM tableName a
INNER JOIN
(
SELECT product, MAX(date) mxdate
FROM tableName
GROUP BY product
) b ON a.product = b.product
AND a.date = b.mxdate
I think the easiest way is a substring_index()/group_concat() trick:
SELECT product,
substring_index(group_concat(price order by date desc), ',', 1) as PriceOnMaxDate
max(date)
FROM table
GROUP BY product;
Another way, that might be more efficient than a group by is:
select p.*
from table t
where not exists (select 1
from table t2
where t2.product = t.product and
t2.date > t.date
);
This says: "Get me all rows from the table where the same product does not have a larger date." That is a fancy way of saying "get me the row with the maximum date for each product."
Note that there is a subtle difference: the second form will return all rows that on the maximum date, if there are duplicates.
Also, for performance an index on table(product, date) is recommended.
You can use a subquery that groups by product and return the maximum date for every product, and join this subquery back to the products table:
SELECT
p.product,
p.price,
p.date
FROM
products p INNER JOIN (
SELECT
product,
MAX(date) AS max_date
FROM
products
GROUP BY
product) m
ON p.product = m.product AND p.date = m.max_date
SELECT
product,
price,
date
FROM
(SELECT
product,
price,
date
FROM table_name ORDER BY date DESC) AS t1
GROUP BY product;

SQL query: calculate profile or loss in given 2 tables

I have 2 tables storing income:
id name date price isDel
1 abc 2012-01-26 613.90 0
2 cde 2012-01-25 530.10 0
and expenses:
id name price date isDel
1 b1 334.90 2012-01-26 0
2 b1 41.90 2012-01-25 0
3 d1 61.90 2012-01-25 0
I would like to get the result as follows:
date total income total expense profile n loss
2012-01-25 530.10 103.80 426.30
2012-01-26 613.90 344.90 26.90
Thanks. Can anyone provide SQL solutions? I did refer some member's question and answer at stackoverflow but I can't modify to want I need. Please help.
Provided you want profit/loss per day this should do:
SELECT Inc.date, TotalInc, TotalExp, TotalInc - TotalExp AS ProfitNLoss
FROM
(
SELECT date, SUM(price) as TotalInc
FROM Income
WHERE isdel = 0
GROUP BY date) as Inc
LEFT OUTER JOIN
(
SELECT date, SUM(price) as TotalExp
FROM Expense
WHERE isdel = 0
GROUP BY date) as Exp
ON Inc.date = Exp.date
UNION
SELECT Exp.date, TotalInc, TotalExp, TotalInc - TotalExp AS ProfitNLoss
FROM
(
SELECT date, SUM(price) as TotalInc
FROM Income
WHERE isdel = 0
GROUP BY date) as Inc
RIGHT OUTER JOIN
(
SELECT date, SUM(price) as TotalExp
FROM Expense
WHERE isdel = 0
GROUP BY date) as Exp
ON Inc.date = Exp.date
Argh, this is "give me the codes" question, but I'll give you few hints:
mySQL GROUP BY tutorial
mySQL GRUOP BY aggregate functions
mySQL subqueries
So what you need to do:
Create list of all unique dates (unfortunately I haven't find a way to generate list of dates within sql without table query or without long php array, if anyone knows a way to do this I'll be glad to add it to anser), so you will have to do this:
SELECT date FROM income GROUP BY date;
-- If there's a chance that you'll have record in expenses and not in income:
(SELECT date FROM income GROUP BY date)
UNION (SELECT date FROM expenses GROUP BY date;)
GROUP BY date;
-- Or create table containing just list of all dates (best option)
And than join all unique dates with expenses and income:
SELECT SUM(expenses.price) AS expenses, SUM(incomes.price) AS income, dates.date
FROM unique_dates -- or from your subquery
LEFT JOIN incomes ON incomes.date = unique_dates.date
LEFT JOIN expenses ON expenses.date = unique_dates.date
GROUP BY unique_dates.date
Oh and I have no idea what does profile n loss mean.
Thanks to Pheiberg and appreciated for your prof reply.
I just did some little change so it will return 0 when table has no record or return NULL, So that Profit calculation still able to cal even given -ve value. Here the code share to u guys.
note: im using MySQL
SELECT Inc.create_dt, IFNULL(TotalInc,0), IFNULL(TotalExp,0), IFNULL(TotalInc,0) - IFNULL(TotalExp,0) AS ProfitNLoss
FROM
(
SELECT create_dt, SUM(price) as TotalInc
FROM income
WHERE IsDel= 0
GROUP BY create_dt) as Inc
LEFT OUTER JOIN
(
SELECT create_dt, SUM(price) as TotalExp
FROM expenses
WHERE IsDel= 0
GROUP BY create_dt) as Exp
ON Inc.create_dt= Exp.create_dt
UNION
SELECT Exp.create_dt, IFNULL(TotalInc,0), IFNULL(TotalExp,0), IFNULL(TotalInc,0) - IFNULL(TotalExp,0) AS ProfitNLoss
FROM
(
SELECT create_dt, SUM(price) as TotalInc
FROM income
WHERE IsDel= 0
GROUP BY create_dt) as Inc
RIGHT OUTER JOIN
(
SELECT create_dt, SUM(price) as TotalExp
FROM expenses
WHERE IsDel= 0
GROUP BY create_dt) as Exp
ON Inc.create_dt= Exp.create_dt