MySQL subtract from isolated subquery - mysql

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)

Related

Get cheapset prices in price table

I have a price table. I want to list the cheapest price of the products with the same IDs in my table. How can I do it?
Table Name : prices
userID| productsID | price | stock | maks
-------------------------------------------
1 | C120221 | 100 | 3 | 1
2 | C120221 | 200 | 5 | 5
3 | BR120221 | 500 | 7 | 3
4 | BR120221 | 600 | 9 | 0
5 | BR120221 | 700 | 11 | 2
SQL
SELECT
MIN(price ) AS price ,
GROUP_CONCAT(userID) AS userID
FROM prices
WHERE price > 0
GROUP BY productsID
ORDER BY price ASC
In this case I want to list all the info of the user with the cheapest price. In the current query, I can get the data in the productsID and price fields, but I cannot get the data in the userID, stock and max fields of the user with these data. Where is the problem?
You are probably trying to find the line or lines that contain the lowest price.
SELECT
prices.*
FROM
(
SELECT
MIN(price) AS price,
productsID
FROM
prices
WHERE
price > 0
GROUP BY
productsID
) AS t1
JOIN prices ON prices.price = t1.price
AND prices.productsID = t1.productsID

Calculate balance per each category group

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 |

Update one table with near values from another table

I have two tables.
Products
------------
ID | Sales | Rank
==================
1 | 0 | 100
2 | 0 | 105
3 | 0 | 200
4 | 0 | 900
Sales
ID | Sales | Rank
==================
1 | 2000 | 99
2 | 5000 | 106
3 | 8000 | 800
4 | 2500 | 950
I want to update sales.products with sales.sales based on the rank. for example
set products.sales=sales.sales where sales.sales is nearest to product.sales
In the above case below will be the results of the query.
Products
------------
ID | Sales | Rank
==================
1 | 2000 | 100
2 | 5000 | 105
3 | 5000 | 200
4 | 2500 | 900
Try to find sales of the product from sales table based on rank, If not found then find anything where Product.Rank is NEAREST value to sales.rank.
thanks
Because MySQL doesn't have window functions, you have to do it the "hard way". First, build a query that calculates what the minimum difference is for each product rank:
select p.rank, min(abs(s.rank - p.rank)) diff
from sales s
cross join products p
group by 1
Then use that to find which rank is closest by finding the rank with that difference, joining again and discarding all but the highest sales to break ties:
update Products
join (select p.rank, min(abs(s.rank - p.rank)) diff
from Sales s
cross join Products p
group by 1) x on Products.rank = x.rank
join Sales s1 on abs(Products.rank - s1.rank) = x.diff
left join Sales s2 on abs(Products.rank - s2.rank) = x.diff
and s2.sales > s1.sales
and s1.rank != s2.rank
set Products.sales = s1.sales
where s2.rank is null
Since SQLFiddle is down for the count, here's the full script:
create table Products (ID int, Sales int, Rank int);
insert into Products values
(1,0,100),
(2,0,105),
(3,0,200),
(4,0,900);
create table Sales (ID int, Sales int, Rank int);
insert into Sales values
(1,2000,99),
(2,5000,106),
(3,8000,800),
(4,2500,950);
update Products
join (select p.rank, min(abs(s.rank - p.rank)) diff
from Sales s
cross join Products p
group by 1) x on Products.rank = x.rank
join Sales s1 on abs(Products.rank - s1.rank) = x.diff
left join Sales s2 on abs(Products.rank - s2.rank) = x.diff
and s2.sales > s1.sales
and s1.rank != s2.rank
set Products.sales = s1.sales
where s2.rank is null;
select * from Products;
Output:
+------+-------+------+
| ID | Sales | Rank |
+------+-------+------+
| 1 | 2000 | 100 |
| 2 | 5000 | 105 |
| 3 | 5000 | 200 |
| 4 | 2500 | 900 |
+------+-------+------+

Select SUM from multiple tables for every record in MySQL table

I'm having a table with main invoice data, and two table with invoice items:
items which are based on hourly work, with an hourly rate and an amount of hours
items which are products, with a unit count an unit price
For the invoice overview page, I'd like to retrieve all invoices and their total amounts with one query.
A simplified schema
invoices_main
| invoice_id |
| 1 |
| 2 |
| 3 |
invoices_items_products
| item_id | invoice_id | item_count | item_unit_price |
| 1 | 1 | 1 | 999.95 |
| 2 | 1 | 20 | 49.50 |
| 3 | 2 | 3 | 15.00 |
| 4 | 2 | 5 | 5.00 |
| 5 | 3 | 2 | 150.00 |
invoices_items_hourly
| item_id | invoice_id | item_hours | item_hourly_rate |
| 1 | 1 | 3.50 | 90.00 |
| 2 | 1 | 1.00 | 140.00 |
| 3 | 2 | 12.00 | 90.00 |
| 4 | 3 | 1.50 | 90.00 |
With the help of this question, I've constructed the following query:
SELECT
I.invoice_id,
IFNULL(
SUM(ROUND(P.item_unit_price * P.item_count, 2)),
0
) + IFNULL(
SUM(ROUND(H.item_hourly_rate * H.item_hours, 2)),
0
) AS invoice_total_amount
FROM
invoices_main I
LEFT JOIN invoices_items_products P ON I.invoice_id = P.invoice_id
LEFT JOIN invoices_items_hours H ON I.invoice_id = H.invoice_id
GROUP BY
I.invoice_id
It works kind of, but if an invoice has both products and hourly items, with at least multiple entries for one of both, items are duplicated due to the joins and the total amount becomes way too high.
Thus, in the above example schema, it goes wrong with invoice_id 1 and 2, but work with 3.
How can I retrieve a list of invoices with their respective total amounts, in a way that works even if an invoice has multiple products and multiple hourly items?
Try putting both left join's into a subquery instead.
SELECT
I.invoice_id,
IFNULL
(
(
SELECT SUM(ROUND(H.item_hourly_rate * H.item_hours, 2))
FROM invoices_items_hours AS H
WHERE H.invoice_id = I.invoice_id
)
, 0
) +
IFNULL
(
(
SELECT SUM(ROUND(P.item_unit_price * P.item_count, 2))
FROM invoices_items_products AS P
WHERE P.invoice_id = I.invoice_id
)
, 0
) AS invoice_total_amount
FROM invoices_main AS I
GROUP BY I.invoice_id
As mentioned in the comments, you should sum up the revenue in each table per invoice_id before doing the join. If you're looking to get the revenue from both of these places then you can add (B.unit_revenue + C.hourly_revenue) total_revenue to the first SELECT statement below.
SELECT A.invoice_id, B.unit_revenue, C.hourly_revenue FROM
invoices_main AS A
JOIN (
SELECT invoice_id, SUM(item_count * item_unit_price) unit_revenue
FROM invoices_items_products GROUP BY invoice_id
) B
ON
A.invoice_id = B.invoice_id
JOIN (
SELECT invoice_id, SUM(item_hours * item_hourly_rate) hourly_revenue FROM
invoices_items_hours GROUP BY invoice_id
) C
ON
A.invoice_id = C.invoice_id

MySQL: Selecting distinct with aggregate functions

I'm having some trouble selecting a distinct order id from my orderItems table.
My orderItems table contains:
orderId | itemId | orderedQuantity | inStockQuantity | backorderQuantity | isBackorderOfId | id.
orderedQuantity is the total ordered amount, inStockQuantity is the quantity available at the time of purchase and backorderQuantity is the rest. isBackorderOfId correlates to the id column when an order item is a backorder of another order item.
My query is trying to select a distinct orderId of entries where the backorderQuantity < a SUM(inStockQuantity) where isBackorderOfId = id. This would result in a list if distinct order ids which have remaining backorders to be shipped.
Test data:
orderId | itemId | orderedQuantity | inStockQuantity | backorderQuantity | isBackorderOfId | id
1 | 114 | 10 | 6 | 4 | 0 | 23
1 | 255 | 4 | 3 | 1 | 0 | 24
2 | 114 | 3 | 3 | 0 | 23 | 25
Query:
SELECT items1.* FROM orderItems items1
LEFT JOIN orderItems items2 ON items2.isBackorderOfId = items1.id
WHERE items1.backorderQuantity > '0'
GROUP BY items2.isBackorderOfId
HAVING SUM(items2.inStockQuantity) < items1.backorderQuantity
OR SUM(items2.inStockQuantity) IS NULL`
This results in both the first and second row which has the same orderId. I can't use SELECT DISTINCT because of the use of aggregate functions.
Any help would be appreciated.
SELECT items1.orderId
FROM orderItems items1
LEFT JOIN
orderItems items2
ON items2.isBackorderOfId = items1.id
GROUP BY
items1.orderId
HAVING COALESCE(SUM(items2.inStockQuantity), 0) < SUM(items1.backorderQuantity)