MySQL Select one row with unique attribute value - mysql

I'm building a stock keeping system and decided to store each product's balance (everytime it's updated) into the following table:
+------------+--------------+---------+------+
| Product_id | Warehouse_id | Balance | Date |
+------------+--------------+---------+------+
Example:
Staff adds 10 pieces to product_id 123 in warehouse_id 5
+------------+--------------+---------+-------------+
| Product_id | Warehouse_id | Balance | Date |
+------------+--------------+---------+-------------+
| 123 | 5 | 10 | 2013-09-16 |
+------------+--------------+---------+-------------+
Staff then adds 3 pieces to product 234 in warehouse_id 5, and
5 pieces to 123 in warehouse_id 5,
+------------+--------------+---------+-------------+
| Product_id | Warehouse_id | Balance | Date |
+------------+--------------+---------+-------------+
| 123 | 5 | 10 | 2013-09-16 |
| 234 | 5 | 3 | 2013-09-18 |
| 123 | 5 | 15 | 2013-09-21 |
+------------+--------------+---------+-------------+
*Notice the date column
Now let me add a few more rows
+------------+--------------+---------+-------------+
| Product_id | Warehouse_id | Balance | Date |
+------------+--------------+---------+-------------+
| 123 | 5 | 10 | 2013-09-16 |
| 234 | 5 | 3 | 2013-09-18 |
| 123 | 5 | 15 | 2013-09-21 |
| 123 | 5 | 18 | 2013-09-24 |
| 234 | 5 | 10 | 2013-09-26 |
| 123 | 5 | 22 | 2013-09-29 |
+------------+--------------+---------+-------------+
How do i run a query that would get me all products' balances as at 25th of September 2013?
That means i need the following result:
+------------+--------------+---------+-------------+
| Product_id | Warehouse_id | Balance | Date |
+------------+--------------+---------+-------------+
| 234 | 5 | 3 | 2013-09-18 |
| 123 | 5 | 18 | 2013-09-24 |
+------------+--------------+---------+-------------+
In short I need the latest row (by date), per product_id.
Any help would be greatly appreciated!

Assuming that products' balances are being maintained per warehouse you can do it like this
SELECT t.product_id, t.warehouse_id, t.balance, t.date
FROM table1 t JOIN
(
SELECT warehouse_id, product_id, MAX(date) date
FROM table1
WHERE date <= '2013-09-25'
GROUP BY warehouse_id, product_id
) q
ON t.warehouse_id = q.warehouse_id
AND t.product_id = q.product_id
AND t.date = q.date
Output:
| PRODUCT_ID | WAREHOUSE_ID | BALANCE | DATE |
|------------|--------------|---------|------------|
| 234 | 5 | 3 | 2013-09-18 |
| 123 | 5 | 18 | 2013-09-24 |
Here is SQLFiddle demo

SELECT *
FROM TABLE
WHERE (PRODUCT_ID, DATE) IN
(SELECT PRODUCT_ID, MAX(DATE) FROM TABLE
WHERE DATE <= '2013-09-25'
GROUP BY PRODUCT_ID )

Query:
SQLFIDDLEExample
SELECT *
FROM table1 t
WHERE t.`Date` = (SELECT MAX(t2.`Date`)
FROM Table1 t2
WHERE t2.`Date` <= '2013-09-25'
AND t2.product_id = t.product_id)

Related

Retrieving data from the row containing the closest date to today with whereDate

I want to sort by price after filtering and grouping by date. However, because there are more than one relation, I cannot get the result I want.
The result I want is to get the price of the relation that is the closest to the end_date and sort it accordingly.
For this, the query, sql output, tables and demo page are as follows.
Thanks in advance ..
demo sqlfiddle
$query->join('tableB', 'tableA.id', '=', 'tableB.pro_id')
->select('tableA.*', 'tableB.start_date', 'tableB.end_date', 'tableB.old_daily')
->where(function($sq) {
$today = Carbon::now()->format('Y-m-d');
$sq->whereDate('end_date', '>=', $today);
})
->groupBy('tableA.id')
->orderBy('price', desc);
Query:
select `tableA`.*, `tableB`.`start_date`, `tableB`.`end_date`, `tableB`.`price`
from `tableA`
inner join `tableB` on `tableA`.`id` = `tableB`.`pro_id`
where (date(`end_date`) >= 2021-03-07)
group by `tableA`.`id`
order by `price` desc
tableA
| id | title |
|----|-------|
| 1 | pro1 |
| 2 | pro2 |
| 3 | pro3 |
tableB
| id | start_date | end_date | price | pro_id |
|----|------------|------------|-------|--------|
| 1 | 2021-06-01 | 2021-06-05 | 750 | 2 |
| 2 | 2021-05-01 | 2021-05-05 | 850 | 2 |
| 3 | 2021-04-01 | 2021-04-05 | 650 | 2 |
| 4 | 2021-06-01 | 2021-06-05 | 2750 | 1 |
| 5 | 2021-05-01 | 2021-05-05 | 2850 | 1 |
| 6 | 2021-04-01 | 2021-04-05 | 2650 | 1 |
| 7 | 2021-06-01 | 2021-06-05 | 1750 | 3 |
| 8 | 2021-05-01 | 2021-05-05 | 1850 | 3 |
| 9 | 2021-04-01 | 2021-04-05 | 1650 | 3 |
this query gives the result you want.
It would be a good choice to use "right join" in this step.
sqlfiddle
select `tableA`.*, `tableB`.`start_date`, `tableB`.`end_date`, `tableB`.`price`
from `tableA`
right join(
SELECT id, start_date, end_date, pro_id, price, DATEDIFF(`tableB`.`end_date`, '2021-03-07') diff
FROM `tableB`
GROUP BY id order by diff asc
) `tableB` on `tableA`.`id` = `tableB`.`pro_id`
where (date(`end_date`) >= '2021-03-07')
group by `tableA`.`id`
order by `price` desc
the closest to the end_date and sort it accordingly.
you should find the difference between the given date and end date then sort ascendingly.
ORDER BY DATEDIFF(end_date, '2021-03-07') ASC

Subtract two columns of different tables with different number of rows

How can I write a single query that will give me SUM(Entrance.quantity) - SUM(Buying.quantity) group by product_id.
The problem is in rows that not exist in the first or second table. Is possible to do this?
Entrance:
+---+--------------+---------+
| id | product_id | quantity|
+---+--------------+---------+
| 1 | 234 | 15 |
| 2 | 234 | 35 |
| 3 | 237 | 12 |
| 4 | 237 | 18 |
| 5 | 101 | 10 |
| 6 | 150 | 12 |
+---+--------------+---------+
Buying:
+---+------------+-------------+
| id | product_id | quantity|
+---+------------+-------------+
| 1 | 234 | 10 |
| 2 | 234 | 20 |
| 3 | 237 | 10 |
| 4 | 237 | 10 |
| 5 | 120 | 15 |
+---+------------+------------+
Desired result:
+--------------+-----------------------+
| product_id | quantity_balance |
+--------------+-----------------------+
| 234 | 20 |
| 237 | 10 |
| 101 | 10 |
| 150 | 12 |
| 120 | -15 |
+--------------+-----------------------+
This is tricky, because products could be in one table but not the other. One method uses union all and group by:
select product_id, sum(quantity)
from ((select e.product_id, quantity
from entrance e
) union all
(select b.product_id, - b.quantity
from buying b
)
) eb
group by product_id;
SELECT product_id ,
( Tmp1.enterquantity - Tmp2.buyquantity ) AS Quantity_balance
FROM entrance e1
CROSS APPLY ( SELECT SUM(quantity) AS enterquantity
FROM Entrance e2
WHERE e1.product_id = e2.product_id
) Tmp1
CROSS APPLY ( SELECT SUM(quantity) AS buyquantity
FROM Buying b2
WHERE e1.product_id = b2.product_id
) Tmp2
GROUP BY Product_id,( Tmp1.enterquantity - Tmp2.buyquantity )

mysql group by keeping the more recent date

I have the following table:
+------------+--------+-----+
| reg_dat | status | id |
+------------+--------+-----+
| 2016-01-31 | 10 | 1 |
| 2017-06-31 | 12 | 1 |
| 2015-01-31 | 12 | 4 |
| 2017-01-25 | 5 | 4 |
| 2017-01-11 | 3 | 2 |
+------------+--------+-----+
I would like to do a mysql query to group the rows by id and keeping only the more recent date... so the output should be the following:
+------------+--------+-----+
| reg_dat | status | id |
+------------+--------+-----+
| 2017-06-31 | 12 | 1 |
| 2017-01-25 | 5 | 4 |
| 2017-01-11 | 3 | 2 |
+------------+--------+-----+
Unfortunately my code doesn't work...
select *
from table
group by id
order by id, reg_dat DESC
Have you some suggestions?
You can do that using a JOIN and a subquery
SELECT t.reg_dat, t.status, t.id
FROM table t
JOIN (SELECT max(reg_dat) max_date, id FROM table GROUP BY id) t1
ON t.reg_dat = t1.max_date AND t.id = t1.id

Calculate sum for group records in MySQL

I have this table of orders
| ORDER_ID | PRODUCT | CUSTOMER | QTY | DATE
---------------------------------------------
| 1 | shoes | Nick | 1 | 01/01/2016
| 2 | shirts | Nick | 5 | 02/02/2016
| 3 | shoes | Paul | 10 | 03/03/2016
| 4 | shirts | Paul | 20 | 04/04/2016
So, How can I achieve this report result with ONE Select Statement?
| Date_of_Order | Customer | Quantity | PRODUCT_TOTAL_SALES |
-----------------------------------------------------------------
| 01/01/2016 | Nick | 1 | shoes : 11 |
| 02/02/2016 | Nick | 10 | shirts : 25 |
| 03/03/2016 | Paul | 5 | shoes : 11 |
| 04/04/2016 | Paul | 20 | shirts : 25 |
I know how to use concat(column1, ' ', column2) to create a combined column but I haven't succeed to add a sum for a grouped item there. When I try with left join I get the sum for a product ...BUT its always the whole sum and its not related to the dates of the order so when I try to filter the results on my query for a certain period I still get 11 for shoes and 25 for shirts...
You can group by multiple columns and get the sum for the smallest group.
If you want the daily sales, then instead of GROUP BY product use GROUP BY product, date
SELECT
o.`date` AS Date_of_Order,
SUM(o.qty) as Total_Quantity,
CONCAT(o.product, ':', SUM(o.qty))
FROM
orders o
GROUP BY product, `date`
ORDER BY `date`
Simple additional SELECT from same table can do that for entire period:
SELECT
o.`date` AS Date_of_Order,
o.Customer,
o.qty as Quantity,
(SELECT
CONCAT(oo.product, ':', SUM(oo.qty))
FROM
orders oo
WHERE
oo.product = o.product
) PRODUCT_TOTAL_SALES
FROM
orders o
Output:
+---------------+----------+----------+---------------------+
| Date_of_Order | Customer | Quantity | PRODUCT_TOTAL_SALES |
+---------------+----------+----------+---------------------+
| 01/01/2016 | Nick | 1 | shoes:11 |
| 02/02/2016 | Nick | 5 | shirts:25 |
| 03/03/2016 | Paul | 10 | shoes:11 |
| 04/04/2016 | Paul | 20 | shirts:25 |
+---------------+----------+----------+---------------------+
4 rows in set
If you want to filter by certain period, you must include it in both:
SELECT
o.`date` AS Date_of_Order,
o.Customer,
o.qty as Quantity,
(SELECT
CONCAT(oo.product, ':', sum(oo.qty))
FROM
orders oo
WHERE
oo.product = o.product
AND STR_TO_DATE(oo.`date`,'%d/%m/%Y') BETWEEN '2016-01-01' AND '2016-03-03'
) PRODUCT_TOTAL_SALES
FROM
orders o
WHERE
STR_TO_DATE(o.`date`,'%d/%m/%Y') BETWEEN '2016-01-01' AND '2016-03-03'
Output:
+---------------+----------+----------+---------------------+
| Date_of_Order | customer | Quantity | PRODUCT_TOTAL_SALES |
+---------------+----------+----------+---------------------+
| 01/01/2016 | Nick | 1 | shoes:11 |
| 02/02/2016 | Nick | 5 | shirts:5 |
| 03/03/2016 | Paul | 10 | shoes:11 |
+---------------+----------+----------+---------------------+
3 rows in set

mysql select most recent row by date for each user

I'm trying to select the most recent rows for every unique userid where pid = 50 and active = 1. I haven't been able to figure it out.
Here is a sample table
+-----+----------+-------+-----------------------+---------+
| id | userid | pid | start_date | active |
+-----+----------+-------+-----------------------+---------+
| 1 | 4 | 50 | 2015-05-15 12:00:00 | 1 |
| 2 | 4 | 50 | 2015-05-16 12:00:00 | 1 |
| 3 | 4 | 50 | 2015-05-17 12:00:00 | 0 |
| 4 | 4 | 51 | 2015-06-29 12:00:00 | 1 |
| 5 | 4 | 51 | 2015-06-30 12:00:00 | 1 |
| 6 | 5 | 50 | 2015-07-05 12:00:00 | 1 |
| 7 | 5 | 50 | 2015-07-06 12:00:00 | 1 |
| 8 | 5 | 51 | 2015-07-08 12:00:00 | 1 |
+-----+----------+-------+-----------------------+---------+
Desired Result
+-----+----------+-------+-----------------------+---------+
| id | userid | pid | start_date | active |
+-----+----------+-------+-----------------------+---------+
| 2 | 4 | 50 | 2015-05-16 12:00:00 | 1 |
| 7 | 5 | 50 | 2015-07-06 12:00:00 | 1 |
+-----+----------+-------+-----------------------+---------+
I've tried a bunch of things and this is the closest I got but unfortunately it is not quit there.
SELECT *
FROM mytable t1
WHERE
(
SELECT COUNT(*)
FROM mytable t2
WHERE
t1.userid = t2.userid
AND t1.start_date < t2.start_date
) < 1
AND pid = 50
AND active = 1
ORDER BY start_date DESC
plan
get last record grouping by userid where pid is 50 and is active
inner join to mytable to get the record info associated with last
query
select
my.*
from
(
select userid, pid, active, max(start_date) as lst
from mytable
where pid = 50
and active = 1
group by userid, pid, active
) maxd
inner join mytable my
on maxd.userid = my.userid
and maxd.pid = my.pid
and maxd.active = my.active
and maxd.lst = my.start_date
;
output
+----+--------+-----+------------------------+--------+
| id | userid | pid | start_date | active |
+----+--------+-----+------------------------+--------+
| 2 | 4 | 50 | May, 16 2015 12:00:00 | 1 |
| 7 | 5 | 50 | July, 06 2015 12:00:00 | 1 |
+----+--------+-----+------------------------+--------+
sqlfiddle
notes
as suggested by #Strawberry, updated to join also on pid and active. this will avoid the possibility of a record which is not active or not pid 50 but has exact same date also being rendered.