I have 2 tables
article_receive , which records the the receiving via following columns;
item | title |trans_id |qty|price|current_items | current_value
ADN231 | 12" Valve |jvn2333 |24 | 175 | 24 | 4200
ADN231 | 12" Valve |jvn2388 |12 | 185 | 36 | 6420
Current Items is always total of all items
Current Value is value of all transaction combined (4200 + 2220)
For Issuance , i have article_issue with following columns;
item | title | trans_id | qty
ADN231 | 12" Valve | ISU2333 | 6
ADN231 | 12" Valve | ISU2401 | 24
My requirement is , that i want to create a consumption report, which basically calculates the exact amount of items on every issuance using FIFO method.
2nd row in article_issue has items from 2 transactions and has 2 different prices. How to calculate it in MYSQL 8.0.15 Community Version.
Currently, i am using this SQL Statement;
SELECT
article_receives.id as RCVID, article_receives.item_id, article_receives.item_title, article_receives.quantity as rcv_qty,article_receives.transaction_id, article_receives.price as rate,
article_issues.id as ISUID,article_issues.quantity as isu_qty, article_issues.quantity * article_receives.price as value
FROM article_receives
LEFT JOIN article_issues ON article_receives.item_id = article_issues.item_id
ORDER BY article_receives.item_id
/** Some Column names are changed */
This gives me data in following state;
Please help me out in creating a proper consumption report in mysql.
On side note, This is an app developed in laravel 5.8 , so eloquent is also available.
Thanks
Thanks to P.Salmon provided link FIFO i was able to solve the problem.
MySql Query is :
WITH
running_purchase AS
( SELECT transaction_id, created_at, quantity, item_id, price,
SUM(quantity) OVER (PARTITION BY item_id
ORDER BY created_at, transaction_id
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW)
AS running_total
FROM article_receives
),
running_stock AS
( SELECT demand_id, created_at, quantity, item_id,
SUM(quantity) OVER (PARTITION BY item_id
ORDER BY created_at, demand_id
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW)
AS running_total
FROM article_issues
)
SELECT
s.demand_id, p.transaction_id,p.created_at as purchase_date, p.item_id, p.price, s.created_at as issue_date,
LEAST(p.running_total, s.running_total)
- GREATEST(s.running_total - s.quantity, p.running_total - p.quantity)
AS quantity
FROM running_purchase AS p
JOIN running_stock AS s
ON p.item_id = s.item_id
AND s.running_total - s.quantity < p.running_total
AND p.running_total - p.quantity < s.running_total
WHERE s.created_at BETWEEN '2019-06-01' AND DATE_ADD('2019-06-30', INTERVAL 1 DAY)
ORDER BY
p.item_id, p.created_at, p.transaction_id ;
Related
I am looking for a tricky MySQL query to return the most common column value for each value in another column. I could use PHP to do it by each result, but it'd be so much cooler to do it with a single query :)
For example, if have a table like this, called "transactions":
Id payee exp category
1 Amazon 25.00 Gifts
2 Amazon 30.21 Books
3 Amazon 12.98 Gifts
4 Amazon 15.00 Groceries
5 Amazon 14.54 Gifts
6 Alibaba 55.55 Stock
7 Alibaba 99.00 Stock
8 Alibaba 12.00 Fun
…
The type of result I would want is like this:
payee count(payee) category
Amazon 3 Gifts
Alibaba 2 Stock
…
I can do this:
SELECT `payee`, COUNT(`payee`), `category` FROM `transactions` WHERE 1 GROUP BY category ORDER BY COUNT(`payee`) DESC
and get close to what I want:
Amazon 3 Gifts
Alibaba 2 Stock
Amazon 1 Books
Amazon 1 Groceries
Alibaba 1 Fun
but I don't want the non-maximum counts (like Amazon,1,Books for example).
Do I have to do a subquery or something? Or use in?
You could filter the results of your existing query with a correlated subquery in a having clause, as follows:
select payee, count(*), category
from transactions t
group by payee, category
having count(*) = (
select count(*)
from transactions t1
where t1.payee = t.payee
group by category
order by count(*) desc limit 1
)
order by count(*) desc
Demo on DB Fiddle:
payee | count(*) | category
:------ | -------: | :-------
Amazon | 3 | Gifts
Alibaba | 2 | Stock
Alernatively, if you are running MySQL 8.0, you can rank the categories of each payee with window function rank() over(), and filter on the top record per group:
select payee, cnt, category
from (
select
payee,
count(*) cnt,
category,
rank() over(partition by payee order by count(*) desc) rn
from transactions
group by category, payee
) t
where rn = 1
Demo on DB Fiddle
I've searched for this topic but all I got was questions about grouping results by month. I need to retrieve rows grouped by month with summed up cost from start date to this whole month
Here is an example table
Date | Val
----------- | -----
2017-01-20 | 10
----------- | -----
2017-02-15 | 5
----------- | -----
2017-02-24 | 15
----------- | -----
2017-03-14 | 20
I need to get following output (date format is not the case):
2017-01-20 | 10
2017-02-24 | 30
2017-03-14 | 50
When I run
SELECT SUM(`val`) as `sum`, DATE(`date`) as `date` FROM table
AND `date` BETWEEN :startDate
AND :endDate GROUP BY year(`date`), month(`date`)
I got sum per month of course.
Nothing comes to my mind how to put in nicely in one query to achieve my desired effect, probably W will need to do some nested queries but maybe You know some better solution.
Something like this should work (untestet). You could also solve this by using subqueries, but i guess that would be more costly. In case you want to sort the result by the total value the subquery variant might be faster.
SET #total:=0;
SELECT
(#total := #total + q.sum) AS total, q.date
FROM
(SELECT SUM(`val`) as `sum`, DATE(`date`) as `date` FROM table
AND `date` BETWEEN :startDate
AND :endDate GROUP BY year(`date`), month(`date`)) AS q
You can use DATE_FORMAT function to both, format your query and group by.
DATE_FORMAT(date,format)
Formats the date value according to the format string.
SELECT Date, #total := #total + val as total
FROM
(select #total := 0) x,
(select Sum(Val) as Val, DATE_FORMAT(Date, '%m-%Y') as Date
FROM st where Date >= '2017-01-01' and Date <= '2017-12-31'
GROUP BY DATE_FORMAT(Date, '%m-%Y')) y
;
+---------+-------+
| Date | total |
+---------+-------+
| 01-2017 | 10 |
+---------+-------+
| 02-2017 | 30 |
+---------+-------+
| 03-2017 | 50 |
+---------+-------+
Can check it here: http://rextester.com/FOQO81166
Try this.
I use yearmonth as an integer (the year of the date multiplied by 100 plus the month of the date) . If you want to re-format, your call, but integers are always a bit faster.
It's the complete scenario, including input data.
CREATE TABLE tab (
dt DATE
, qty INT
);
INSERT INTO tab(dt,qty) VALUES( '2017-01-20',10);
INSERT INTO tab(dt,qty) VALUES( '2017-02-15', 5);
INSERT INTO tab(dt,qty) VALUES( '2017-02-24',15);
INSERT INTO tab(dt,qty) VALUES( '2017-03-14',20);
SELECT
yearmonths.yearmonth
, SUM(by_month.month_qty) AS running_qty
FROM (
SELECT DISTINCT
YEAR(dt) * 100 + MONTH(dt) AS yearmonth
FROM tab
) yearmonths
INNER JOIN (
SELECT
YEAR(dt) * 100 + MONTH(dt) AS yearmonth
, SUM(qty) AS month_qty
FROM tab
GROUP BY YEAR(dt) * 100 + MONTH(dt)
) by_month
ON yearmonths.yearmonth >= by_month.yearmonth
GROUP BY yearmonths.yearmonth
ORDER BY 1;
;
yearmonth|running_qty
201,701| 10.0
201,702| 30.0
201,703| 50.0
select succeeded; 3 rows fetched
Need explanations?
My solution has the advantage over the others that it will be re-usable without change when you move it to a more modern database - and you can convert it to using analytic functions when you have time.
Marco the Sane
my products table :
ProductId(inc.key) | Price | VersionCreatedDate | MainProductId
1 | 15 | 1-11-2016 | 1
2 | 20 | 1-11-2016 | 2
3 | 30 | 1-11-2016 | 3
4 | 10 | 2-11-2016 | 1 -> mainProductId 1 changed price(-5$)
5 | 20 | 3-11-2016 | 3 -> mainProductId 3 changed price(-10$)
6 | 30 | 4-11-2016 | 3 -> mainProductId 3 changed price(+10$)
I want to display the output as like this
Date | AvgPrice
1-11-2016 | 21.67 ((15+20+30)/3)
2-11-2016 | 20 ((10+20+30)/3)
3-11-2016 | 16.67 ((10+20+20)/3)
4-11-2016 | 20 ((10+20+30)/3)
How do I get the output with sql code?
Assuming you have a calendar table with all dates you need. And you have a main_products table with MainProductId as primary/unique key. The following query should return average prices for every day in october 2016.
select sub.date, avg(sub.Price) as Price
from (
select
c.date,
m.MainProductId,
(
select p.Price
from products
where p.MainProductId = m.MainProductId
and p.VersionCreatedDate < c.date + interval 1 day
order by p.VersionCreatedDate desc
limit 1
) as Price
from callendar c
cross join main_products m
where c.date between '2016-10-01' and '2016-10-31'
) sub
group by sub.date
order by sub.date
The subquery (derived table aliased as sub) returns a combination of all dates in the range and all "main products" from the main_products table. The recent price each "main product" for a specific date is calculated in the subselect (correlated subquery in the SELECT clause) using ORDER BY and LIMIT 1. This allows us to group the subquery result by date and calculate the average price per date.
It is even possible to eliminate the derived table and hope that mysql can use an index to GROUP BY date instead of working on a temp table:
select c.date, avg((
select p.Price
from products
where p.MainProductId = m.MainProductId
and p.VersionCreatedDate < c.date + interval 1 day
order by p.VersionCreatedDate desc
limit 1
)) as Price
from callendar c
cross join main_products m
where c.date between '2016-10-01' and '2016-10-31'
group by c.date
order by c.date
I have no clue if that query can be executed effiently (especially if mysql can). You should however have at least the following indexes: callendar(date), products(MainProductId, VersionCreatedDate)
I want to get the average of a calculated sum. I have tried the syntax from this stackoverflow answer So my SQL query looks like this:
SELECT AVG(iq.stockvalue_sum), iq.date
FROM(
SELECT CONCAT(DATE_FORMAT(s.date, '%Y'), '-01-01') as date,
SUM(GREATEST(s.stockvalue,0)) as stockvalue_sum
FROM stockvalues s
GROUP BY CONCAT(DATE_FORMAT(s.date, '%Y'), '-01-01')
) iq
However this is not giving me a correct average. I want to get the average stockvalue for each year. The idea behind the table is to save every day the stock and stockvalue for each product. So this specifiq query is to show the average stockvalue for each year it has data for.
Edit: Sample output data
Stockvalue | Year
_________________
- 205 | 2015
- 300 | 2014
Input data:
pid | val | date
______________________
- 1 | 100 | 28-04-2015
- 2 | 150 | 28-04-2015
- 1 | 80 | 27-04-2015
- 2 | 80 | 27-04-2015
....
- 1 | 100 | 29-01-2014
- 2 | 100 | 29-01-2014
- 1 | 200 | 30-01-2014
- 2 | 200 | 30-01-2014
So I need to calculate know the average of the total stockvalue. So the sum of all stockvalues for day X and the average of X
At minimum you are missing a group by in your outer query:
SELECT AVG(iq.stockvalue_sum), iq.date
FROM(
SELECT CONCAT(DATE_FORMAT(s.date, '%Y'), '-01-01') as date,
SUM(GREATEST(s.stockvalue,0)) as stockvalue_sum
FROM stockvalues s
GROUP BY CONCAT(DATE_FORMAT(s.date, '%Y'), '-01-01')
) iq
GROUP BY iq.date
However, given your inner query is returning a single year with a summed value, the average of that value would be the same. Perhaps you can clarify your intentions. Are you sure you need the inner query at all? Perhaps this is all you need?
select avg(GREATEST(stockvalue,0)), CONCAT(DATE_FORMAT(s.date, '%Y'), '-01-01') as date
from stockvalues
group by CONCAT(DATE_FORMAT(s.date, '%Y'), '-01-01')
I think you need to group your inner query by date, and your outer query by year to get the results you are after:
SELECT AVG(s.Stockvalue) AS Stockvalue
YEAR(s.Date) AS Date
FROM (
SELECT DATE(s.Date) AS Date,
SUM(GREATEST(s.stockvalue,0)) AS Stockvalue
FROM stockvalues AS s
GROUP BY DATE(s.Date)
) AS s
GROUP BY YEAR(s.Date);
I've been searching for answers for 2 day and still nothing. Please, help me.
I have a database with products, product's prices and the date when this prices were registered:
product_id | price | date
-------------------------
1 | 8.95 | 2012-12-01
2 | 3.40 | 2012-12-01
1 | 9.05 | 2012-12-19
3 | 2.34 | 2012-12-24
3 | 2.15 | 2012-12-01
1 | 8.80 | 2012-12-19
1 | 8.99 | 2012-12-02
2 | 3.45 | 2012-12-02
Observe that is possible to have different price values for a product on the same day (rows 3 and 6). This is because there are many suppliers for a single product. There is a supplier column on database too, but I found it irrelevant for the solution. You can add it to the solution if I'm wrong.
Basically what I want is to write a query that returns two combined sets of data, as follow:
First set is made by minimum price of products inserted in the last month. As today is jan, 15, query should read rows 3, 4 and 6, apply the minimum price, and return only rows 4 and 6, both with minimum price for that product on the last month.
Second set is made by last products inserted, with no price registered on last month. i.e, for products not shown in the first set, query should search for the last inserted ones.
I hope that is clear. Ask me more if it isn't.
The query result for this database should be:
product_id | price | date
-------------------------
1 | 8.80 | 2012-12-19 <-Min price for product 1 on last month
3 | 2.34 | 2012-12-24 <-Min price for product 3 on last month
2 | 3.45 | 2012-12-02 <-No reg for product 2 on last month, show last reg.
I've tried everything: UNION, (DATE_SUB(CURDATE(), INTERVAL 1 MONTH), MIN(price), MAX(date) etc, etc. Nothing works. I don't know where to search now, please help me.
(SELECT product_id, MIN(price), date
FROM products
WHERE date + INTERVAL 1 MONTH > NOW()
GROUP BY product_id)
UNION
(SELECT product_id, price, MAX(date)
FROM products
WHERE product_id NOT IN (SELECT product_id
FROM products
WHERE date + INTERVAL 1 MONTH > NOW()
GROUP BY product_id)
GROUP BY product_id)
This should work but I'm not sure it's the most optimized way to do it.
something like this will do the trick:
SELECT * FROM (
SELECT DISTINCT b.product_id, IF (c.min IS NULL,(SELECT ROUND(e.price,2) FROM products AS e WHERE e.product_id = b.product_id ORDER BY e.date DESC LIMIT 1 ),c.min) AS min, IF (c.date IS NULL,(SELECT f.date FROM products AS f WHERE f.product_id = b.product_id ORDER BY f.date DESC LIMIT 1 ),c.date) AS date, IF(c.min IS NULL,'<-No reg for product 2 on last month, show last reg.','<-Min price for product 1 on last month') as text FROM products AS b
LEFT JOIN
(SELECT a.product_id, round(MIN(a.price),2) AS min, a.date FROM products AS a WHERE a.date BETWEEN DATE_SUB(CURDATE(), INTERVAL 1 MONTH) AND CURDATE() GROUP BY a.product_id) AS c
ON (b.product_id = c.product_id)
) AS d
ORDER BY d.text, d.product_id
Gives output:
product_id|min|date|text
1|8.80|2012-12-19|<-Min price for product 1 on last month
3|2.34|2012-12-24|<-Min price for product 1 on last month
2|3.45|2012-12-02|<-No reg for product 2 on last month, show last reg.
Break it down into several sub-queries:
Products with prices in the last month, min price
join in date for that price
UNION
Products with no-prices in the last month, max date
join in price on that date
SQL Fiddle
Here
Query
SELECT MINPRICE.product_id, P.date, MINPRICE.price
FROM
(
-- Min price in last 31 days
SELECT product_id, MIN(price) AS price
FROM Prices
WHERE DATEDIFF(CURDATE(), date) < 31
GROUP BY product_id
) MINPRICE
-- Join in to get the date that the price occured on
INNER JOIN Prices P ON
P.product_id = MINPRICE.product_id
AND
P.price = MINPRICE.price
UNION
SELECT MAXDATE.product_id, MAXDATE.date, P.price
FROM
(
-- Product with no price in last 31 days - get most recent date
SELECT product_id, MAX(date) AS date
FROM Prices
WHERE product_id NOT IN
(
SELECT product_id
FROM Prices
WHERE DATEDIFF(CURDATE(), date) < 31
)
) MAXDATE
-- join in price on that date
INNER JOIN Prices P ON
P.product_id = MAXDATE.product_id
AND
P.date = MAXDATE.date
Not that I tested but you can try...
SELECT * FROM
(SELECT * FROM (
SELECT * FROM table ORDER BY date DESC)
as tmp GROUP BY product_id) t1
LEFT JOIN
(SELECT * FROM (
SELECT * FROM table WHERE date => CURDATE() ORDER BY price)
as tmp2 GROUP BY product_id) t2
ON t1.product_id = t2.product_id