Let say that I have the 2 following tables
id| price id| qty
1 | 1000 1 | 0.5
2 | 1020 3 | 1
3 | 1040 6 | 1.5
4 | 1050
5 | 1070
6 | 1090
And for the result I want to use the last available qty to compute each value
id| price| qty | value (qty * price)
1 | 1000 | 0.5 | 500
2 | 1020 | 0.5 | 510
3 | 1040 | 1 | 1040
4 | 1050 | 1 | 1050
5 | 1070 | 1 | 1070
6 | 1090 | 1.5 | 1635
I can't find a way to do that. Can you help ?
It's just a matter of using a join however you will not get the exact result that you are looking for because you do not have a complete set of values in your second table for the items..
SELECT id, price, qty, qty*price as value from qtys LEFT JOIN prices on id
An INNER JOIN would do just as well.
SELECT id, price, qty, qty*price as value from qtys INNER JOIN prices on id
This will produce something like this:
id| price| qty | value (qty * price)
1 | 1000 | 0.5 | 500
3 | 1040 | 1 | 1040
6 | 1090 | 1.5 | 1635
it's not possible for join on ids 2, 4 and 5. In order to produce the exact output you will have to intrapolate some values for the qty for the missing items. However, you seem to expect 0.5 for some of the missing values and 1.0 for the other.
An SQL JOIN clause is used to combine rows from two or more tables,
based on a common field between them.
The most common type of join is: SQL INNER JOIN (simple join). An SQL
INNER JOIN returns all rows from multiple tables where the join
condition is met.
Source: http://www.w3schools.com/sql/sql_join.asp
You can use a correlated subquery to look up the qty value, then an aggregation:
select id, price, qty, qty * price as value
from (select t1.*,
(select t2.qty
from t2
where t2.id <= t1.id
order by t2.id desc
limit 1
) as qty
from t1
) t;
Related
I have table my_table which contains groups of categories, each category has initial budget (original_budget):
I am trying to add a new column balance so it contains the balance after reducing expense from the original_budget in each category group. Something like:
my try:
SELECT category, expense, original_budget, (original_budget-expense) AS balance
FROM my_table GROUP BY category order by `trans_date`
MySQL version: innodb_version 5.7.25
10.2.23-MariaDB
If you are using MySQL 8+, then it is fairly straightforward to use SUM here as a window function:
SELECT
trans_date,
category,
expense,
original_budget,
original_budget - SUM(expense) OVER
(PARTITION BY category
ORDER BY trans_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) balance
FROM my_table
ORDER BY
category,
trans_date;
Demo
On earlier versions of MySQL, we can try to compute the rolling balance using a correlated subquery:
SELECT
trans_date,
category,
expense,
original_budget,
original_budget - (SELECT SUM(t2.expense) FROM my_table t2
WHERE t1.category = t2.category AND
t2.trans_date <= t1.trans_date) balance
FROM my_table t1
ORDER BY
category,
trans_date;
Demo
For All MySQL versions:
You can use MySQL User defined Variable to reduce balance amount for a category. For this keep same category records together with sorted dates.
SELECT
category,
expense,
original_budget,
IF(#cat <> category, #budg:= original_budget - expense, #budg:= #budg - expense) AS balance,
#cat:= category -- Set category to current value so we can compare it in next iteration
FROM my_table,
(SELECT #cat:= '' AS c, #budg:= NULL AS b) AS t
ORDER BY category, `trans_date`;
Output:
| category | expense | original_budget | balance | #cat:= category |
| A | 10 | 100 | 90 | A |
| A | 2 | 100 | 88 | A |
| A | 1 | 100 | 87 | A |
| B | 12 | 300 | 288 | B |
| B | 1 | 300 | 287 | B |
| B | 1 | 300 | 286 | B |
| B | 1 | 300 | 285 | B |
I want to search TABLE1 and count which number_id has the most 5's in experience column.
TABLE1
+-------------+------------+
| number_id | experience |
+-------------+------------+
| 20 | 5 |
| 20 | 5 |
| 19 | 1 |
| 18 | 2 |
| 15 | 3 |
| 13 | 1 |
| 10 | 5 |
+-------------+------------+
So in this case it would be number_id=20
Then do an inner join on TABLE2 and map the number that matches the number_id in TABLE1.
TABLE2
+-------------+------------+
| id | number |
+-------------+------------+
| 20 | 000000000 |
| 29 | 012345678 |
| 19 | 123456789 |
| 18 | 223456789 |
| 15 | 345678910 |
| 13 | 123457898 |
| 10 | 545678910 |
+-------------+------------+
So the result would be:
000000000 (2 results of 5)
545678910 (1 result of 5)
So far I have:
SELECT number, experience, number_id, COUNT(*) AS SUM FROM TABLE1
INNER JOIN TABLE2 ON TABLE1.number_id = TABLE2.id
WHERE experience = '5' order by SUM LIMIT 10
But it's returning just
545678910
How can I get it to return both results and by order of number of instances of 5 in the experience column?
Thanks
This query will give you the results that you want. The subquery fetches all the number_id that have experience values of 5. The SUM(experience=5) works because MySQL uses a value of 1 for true and 0 for false. The results of the subquery are then joined to table2 to give the number field. Finally the results are ordered by the number of experience=5:
SELECT t2.number, t1.num_fives
FROM (SELECT number_id, SUM(experience = 5) AS num_fives
FROM table1
WHERE experience = 5
GROUP BY number_id) t1
JOIN table2 t2
ON t2.id = t1.number_id
ORDER BY num_fives DESC
Output:
number num_fives
000000000 2
545678910 1
SQLFiddle Demo
Add a group by clause:
SELECT number, experience, number_id, COUNT(*) AS SUM
FROM TABLE1
JOIN TABLE2 ON TABLE1.number_id = TABLE2.id
WHERE experience = '5'
GROUP BY 1, 2, 3 -- <<< Added this clause
ORDER BY SUM
LIMIT 10
I have to tables. products table and product_variants table.
In product table, I have
id, product_name, photo.
In product_variants table, I have
id, product_id, packet_size, price.
I have to add products first, then will add variants. It is possible that no variants have been added for that.
I want to fetch all the product with one variant details from variants table with the lowest price.
Example scenario:
products table
id product_name photo
1 product_1 1.png
2 product_2 2.png
3 product_3 3.png
4 product_4 4.png
product_variants
id product_id packet_size price
1 1 100 ML 50 RS.
2 1 200 ML 100 Rs.
3 1 300 ML 150 RS.
4 2 300 L 300 Rs.
5 2 200 L 200 Rs.
6 3 200 K 200 Rs.
I want the result to be as below:
1 product_1 1.png 100 ML 50 RS.
2 product_2 2.png 200 L 200 Rs.
3 product_3 3.png 200 K 200 Rs.
Thanks.
Based on all the other questions your can find for "mysql join group min" or "mysql join group max" there are some catches when using GROUP BY and MIN/MAX and JOINs. You can use the following approach:
SELECT (only) the min price of a variant for a given product.
SELECT the remaining info of the found variant.
SELECT the product info of the found variant.
For step 1 the query will look like this:
SELECT
product_id,
MIN(price) as price
FROM
product_variants
GROUP BY
product_id
This will result in the following output (ids may differ):
+------------+-------+
| product_id | price |
+------------+-------+
| 1 | 50 |
| 2 | 200 |
| 4 | 200 |
+------------+-------+
For step 2 we use this result in a JOIN query on itself:
SELECT
v2.product_id,
v2.packet_size,
v2.price
FROM
product_variants v2
JOIN
(SELECT -- here is your first query
product_id,
MIN(price) as price
FROM
product_variants
GROUP BY
product_id
) v1 -- notice the "v1" alias here, its needed
USING (product_id, price)
The result will be:
+------------+-------------+-------+
| product_id | packet_size | price |
+------------+-------------+-------+
| 1 | 100 ML | 50 |
| 2 | 200 L | 200 |
| 4 | 200 L | 200 |
+------------+-------------+-------+
And for step 3 it's just a "normal" JOIN between two tables:
SELECT
p.id,
p.product_name,
p.photo,
v3.packet_size,
v3.price
FROM
products p
JOIN
(SELECT -- here is query 2
v2.product_id,
v2.packet_size,
v2.price
FROM
product_variants v2
JOIN
(SELECT -- here is query 1
product_id,
MIN(price) as price
FROM
product_variants
GROUP BY product_id
) v1
USING (product_id, price)
) v3
ON
p.id = v3.product_id;
And this will get you the following result (again ids may differ):
+----+--------------+-------+-------------+-------+
| id | product_name | photo | packet_size | price |
+----+--------------+-------+-------------+-------+
| 1 | product_1 | 1.png | 100 ML | 50 |
| 2 | product_2 | 2.png | 200 L | 200 |
| 4 | product_3 | 3.png | 200 L | 200 |
+----+--------------+-------+-------------+-------+
And if you use a LEFT JOIN instead of a "normal" JOIN, you get products as well which doesn't have any variants:
+----+--------------+-------+-------------+-------+
| id | product_name | photo | packet_size | price |
+----+--------------+-------+-------------+-------+
| 1 | product_1 | 1.png | 100 ML | 50 |
| 2 | product_2 | 2.png | 200 L | 200 |
| 4 | product_3 | 3.png | 200 L | 200 |
| 5 | product_4 | 4.png | NULL | NULL |
+----+--------------+-------+-------------+-------+
The big catch is to fetch the remaining info of a found variant. You cannot do this in one query because the group function will fetch the correct value, but the other column values after the GROUP BY will not be from the found group function value, but instead will be most likely from the first row in the group.
I have tried to solve the following problem for the last couple of hours and could not find anything that pointed me in the right direction on Google or Stackoverflow. I believe that this could be a similar problem, but I did not really understand what the author wanted to achieve, hence I am trying it with my own concrete example:
I have a table that basically tracks prices of different products over time:
+------------+--------+----------+
| Product_id | Price | Time |
+------------+--------+----------+
| 1 | 1.30 | 13:00:00 |
| 1 | 1.10 | 13:30:00 |
| 1 | 1.50 | 14:00:00 |
| 1 | 1.60 | 14:30:00 |
| 2 | 2.10 | 13:00:00 |
| 2 | 2.50 | 13:30:00 |
| 2 | 1.90 | 14:00:00 |
| 2 | 2.00 | 14:30:00 |
| 3 | 1.45 | 13:00:00 |
| 3 | 1.15 | 13:30:00 |
| 3 | 1.50 | 14:00:00 |
| 3 | 1.55 | 14:30:00 |
+------------+--------+----------+
I would now like to query the table so that the rows with max. Price for each product are returned:
+------------+--------+----------+
| Product_id | Price | Time |
+------------+--------+----------+
| 1 | 1.60 | 14:30:00 |
| 2 | 2.50 | 13:30:00 |
| 3 | 1.55 | 14:30:00 |
+------------+--------+----------+
Also, in case of duplicates, i.e. if there is a max. Price at two different points in time, it should only return one row, preferably the one with the smallest value of time.
I have tried MAX() and GREATEST(), but could not achieve the desired outcome to show the wanted values for each product. Efficiency of the query is not the most important factor, but I have about 500 different products with several million rows of data, hence splitting the table by unique product did not seem like an appropriate solution.
Group the data product id and pick the max price and max time
select t1.product_id,t1.price,min(t1.time) as time from your_table t1
inner join (
select Product_id,max(price)as price from
your_table group by Product_id
) t2 on t1.Product_id=t2.Product_id and t1.price=t2.price group by t1.product_id
Sql Fiddle Example:
http://sqlfiddle.com/#!9/020c3/9
http://sqlfiddle.com/#!9/020c3/1
SELECT p.*
FROM prices p
LEFT JOIN prices p1
ON p.product_id = p1.product_id
AND p.time<p1.time
WHERE p1.product_id IS NULL
If you need maximum price to get you can:
http://sqlfiddle.com/#!9/020c3/6
SELECT p.*
FROM prices p
LEFt JOIN prices p1
ON p.product_id = p1.product_id
AND p.price<p1.price
WHERE p1.product_id IS NULL;
And the last approach since I didn't get the goal from the beggining:
http://sqlfiddle.com/#!9/ace04/2
SELECT p.*
FROM prices p
LEFt JOIN prices p1
ON p.product_id = p1.product_id
AND (
p.price<p1.price
OR (p.price=p1.price AND p.time<p1.time)
)
WHERE p1.product_id IS NULL;
This solution is assuming the existence of an additional my_table.id column that needs to be used in case there are duplicate values for (Product_id, price, time) in your table. id is assumed to be a unique value in the table.
SELECT *
FROM my_table t1
WHERE NOT EXISTS (
SELECT *
FROM my_table t2
WHERE t1.Product_id = t2.Product_id
AND ((t1.price < t2.price) OR
(t1.price = t2.price AND t1.time > t2.time) OR
(t1.price = t2.price AND t1.time = t2.time AND t1.id > t2.id))
)
Alternatively, the predicate on price and time could also be expressed using a row value expression predicate (not sure if it's more readable, as t1 and t2 columns are mixed in a each row value expression):
SELECT *
FROM my_table t1
WHERE NOT EXISTS (
SELECT *
FROM my_table t2
WHERE t1.Product_id = t2.Product_id
AND (t1.price, t2.time, t2.id) < (t2.price, t1.time, t1.id)
)
I have a table containing inventory
ID | Product ID | In_Transit | Quantity | Cumulative Quantity
=====+================+==============+==============+====================
1 | 1 | 0 | 1000 | 1000
2 | 1 | 0 | 1 | 1001
3 | 1 | 1 | 54 | 1055
4 | 1 | 1 | 1 | 1056
So the total inventory for product id 1 is '1056' I get this using a SELECT MAX(ID) subquery join with the table to get its cumulative quantity which is 1056.
I would like to get the Inventory total (subtracting all the amounts in transit)
So 1056 - 54 - 1 = 1001
How would I get this in one query so i get
Product ID | Total Inventory | Inventory on Hand (Excluding in Transit |
===========+=================+=========================================
1 | 1056 | 1001
Also i need to use the cumulative inventory to get the total as opposed to 'SUM', except for summing those in transit because (those not in transit) have a large number of records and they take ages to SUM. I can use it to sum those in transit because there are far fewer records
SELECT
a.product_id
, a.cumulative as total_inventory
, a.cumulative - COALESCE(b.quantity,0) AS inventory_on_hand
FROM table1 a
JOIN
( SELECT MAX(id) AS max_id
FROM table1
GROUP BY product_id
) m ON (m.max_id = a.id)
LEFT JOIN
( SELECT product_id, SUM(quantity)
FROM table1
WHERE in_transit = 1
GROUP BY product_id
) b ON (a.product_id = b.product_id)