MySQL value near by, join, abs with dynamic value - mysql

The goal is the get the price of the nearest number of the m3 and weight and put this in a table.
This sample works for me. But i hard code m3,weight,country and transportername
Query1:
SELECT
country,
transportername,
ABS(m3-0.5) as ABS_M3 ,
m3,
ABS(weight-5) as ABS_weight,
weight,
price
FROM database.transportcost
WHERE m3 >= 0.5 AND
weight >= 5 AND
country = "GB" AND
transportername like '%aa%'
Group by transportername
Order by ABS(m3-0.5) +
ABS(weight-5) +
price
Now i want to get the hard code value from a other table. I don't know what value i need to put in ~~xx.volumen~~ and ~~xx.gewicht~~.
Query2:
INSERT INTO database.transportcostPerItem (sku,country,transportname,weight,m3,price)
SELECT g.orderitemid, p2.country, p2.transportername, p2.weight, p2.m3, p2.price
FROM database.orderitem g
JOIN database.order i on i.orderid = g.orderid
JOIN database.matrixtable xx on g.orderitemid = xx.sku
JOIN database.vlog cc on i.orderid = cc.orderid
JOIN
(
SELECT *
FROM database.transportcost
Group by transportername
Order by ABS(m3-~~xx.volumen~~) +
ABS(weight-~~xx.gewicht~~) +
price
) as p2 on p2.country = i._country AND
p2.transportername = cc._transportername AND
p2.weight = xx.gewicht AND
p2.m3 = xx.volumen
;
Edit:
Sample data
Table: database.transportcost
country | transportername | m3 | weight | price
--------+-----------------+--------+--------+--------
GB | aa | 50.00 | 50 | 77.70
GB | bb | 0.50 | 125 | 83.19
GB | cc | 0.50 | 125 | 96.03
GB | bb | 0.60 | 150 | 83.19
GB | aa | 75.00 | 75 | 89.04
GB | cc | 0.60 | 150 | 96.03
GB | dd | 50.00 | 50 | 163.38
GB | cc | 0.70 | 175 | 96.03
GB | bb | 0.70 | 175 | 96.85
GB | ee | 0.53 | 175 | 102.78
GB | gg | 0.53 | 175 | 110.37
GB | aa | 100.00 | 100 | 89.04
GB | cc | 0.80 | 200 | 96.03
GB | bb | 0.80 | 200 | 96.85
GB | ff | 0.60 | 200 | 110.33
DE | aaa | 0.50 | 50 | 26.40
DE | bbb | 0.50 | 75 | 31.84
DE | aaa | 0.75 | 75 | 34.19
DE | ccc | 0.50 | 100 | 34.17
DE | bbb | 0.67 | 100 | 35.47
DE | ccc | 1.00 | 100 | 37.59
DE | ddd | 0.50 | 100 | 62.38
DE | ccc | 0.63 | 125 | 43.04
DE | bbb | 0.83 | 125 | 44.24
DE | aaa | 0.93 | 125 | 45.84
DE | eee | 0.50 | 125 | 53.80
DE | fff | 0.50 | 125 | 54.02
Query 1 results for the country GB
country | transportername | ABS_M3 | m3 | ABS_weight | weight | price
--------+-----------------+-----------------------+-------+------------+--------+-------
GB | aa | 49.5 | 50.00 | 45 | 50 | 77.70
GB | bb | 0 | 0.50 | 120 | 125 | 83.19
GB | cc | 0 | 0.50 | 120 | 125 | 96.03
GB | dd | 49.5 | 50.00 | 45 | 50 | 163.38
GB | ee | 0.030000000000000027 | 0.53 | 170 | 175 | 102.78
GB | gg | 0.030000000000000027 | 0.53 | 170 | 175 | 110.37
Query 1 results for the country DE
country | transportername | ABS_M3 | m3 | ABS_weight | weight | price
--------+-----------------+--------+------+------------+--------+-------
DE | aaa | 0 | 0.50 | 45 | 50 | 26.40
DE | bbb | 0 | 0.50 | 70 | 75 | 31.84
DE | ccc | 0 | 0.50 | 95 | 100 | 34.17
DE | ddd | 0 | 0.50 | 95 | 100 | 62.38
DE | eee | 0 | 0.50 | 120 | 125 | 53.80
DE | fff | 0 | 0.50 | 120 | 125 | 54.02
DE | ggg | 0 | 0.50 | 195 | 200 | 87.29
Results in the end Query 2 should look like this:
Table: database.transportcostPerItem
orderitemid | country | transportername | m3 | weight | price
------------+---------+-----------------+-----+--------+------
1 | GB | aa | 0,5 | 5 | 77.70
2 | DE | aaa | 0,5 | 5 | 26.40

It seems you are merely looking for the cheapest transport for given m3 and weight. In a first step you'd find all transports for at least the m3 and weight given. Of these you pick the lowest price then.
select *
from transportcost
where country = 'GB'
and m3 >= 0.5
and weight >= 5
order by price
limit 1;
If you wanted the measures closest to your given m3 and weight instead, even if that transport would be more expensive, you could order by the sum of the percentages:
select *
from transportcost
where country = 'GB'
and m3 >= 0.5
and weight >= 5
order by m3 / 0.5 + weight / 5, price
limit 1;
Now instead of given m3, weight, country, and transporter name, you get the values from an order details table. If this were just about a single order details record, the approach would be exactly the same, only instead of comparing with fixed values (m3 >= 0.5) you'd compare with the order details record's values (transportcost.m3 >= orderdetail.m3). Alas, with multiple order details records this approach doesn't work anymore, because we cannot limit the results to one row, but need one row per order details record instead. This would be solved with window functions (ROW_NUMBER, RANK etc.) or lateral joins (CROSS APPLY) in standard SQL. MySQL features neither.
Here is how to join the transport cost table:
select *
from <your order tables>
join transportcost tc
where tc.country = i._country
and tc._transportername = cc._transportername
and tc.m3 >= xx.volumen
and tc.weight >= xx.gewicht;
And now you must find a way to rank your results, so as to only get the best match per order detail. One way is to emulate ROW_NUMBER with variables. You may want to look this up in other answers. Another would be a limit subquery in the SELECT clause, but for this to work you need a single column identifying a record in the transport cost table. Let's say you add a column called ID and fill it with unique values...
select <some order columns>, tc.*
from
(
select <some order columns>,
(
select id
from transportcost tc
where tc.country = i._country
and tc._transportername = cc._transportername
and tc.m3 >= xx.volumen
and tc.weight >= xx.gewicht
order by tc.m3 / xx.volumen + tc.weight / xx.gewicht, tc.price
limit 1
) as best_transportcost_id
from <your order tables>
) data
join transportcost tc
where tc.id = data.best_transportcost_id;
(You could do this without an ID somehow by concatenating the values, e.g. 'GB-aa-50.00-50-77.70', so as to have a string identifying a transport cost record, but I don't recommend this. It's better to have a unique ID with an index for quick lookups. And even better would be to work with a better DBMS :-)

I used the solution of #Thorsten.
This is the results
INSERT INTO database.transportcostperitem (transportcostID, orderID, sku, country, transportername, weight, m3,price,shippingmethod,shippingstatus,shippingDate)
SELECT
transportcostID,
orderID,
sku,
_country,
transportername,
transportTmp._weightReal,
transportTmp._m3 ,
price,
_shippingMethod,
_shippingStatus,
_shippingDate
FROM(
SELECT
b.orderID,
o.sku,
b.transporter,
o._country,
i._shippingMethod,
i._shippingStatus,
i._shippingDate,
o._m3,
o._weightReal,
(
SELECT id
FROM database.transportcost tc
where
tc.transportername = b._transporterName AND
tc.country = o._country AND
tc.weight >= o._weightReal AND
tc.m3 >= o._m3
order by o._m3 / tc.m3 + o._weightReal / tc.weight , tc.price
limit 1
) as transportcostID
FROM database.orderitem o
JOIN database.order i on i.orderID = o.orderID
JOIN database.vlog b on b.orderID = o.orderID
WHERE sku >0
)as transportTmp
JOIN database.transportcost tc
where tc.id = transportcostID AND
_shippingMethod is not null
orderID by sku
;

Related

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: SELECT value and a minimum value on certain conditions in the same row

(MySQL) In 'simple' terms I need to add a minimum price column.
That is, the minimum price for each unique combination of PA and DA records.
Example Raw Data
id | PA | DA | price |
---|-----|------------|--------|
1 | SW1 | PO19 | 100 |
1 | W6 | E16 | 5 |
2 | SW1 | PO19 | 90 |
2 | W6 | E16 | 8 |
3 | TW6 | SO14 | 2000 |
3 | W6 | E16 | 9 |
Output from Example
id | PA | DA | price | MIN price|
---|-----|------------|--------|--------- |
1 | SW1 | PO19 | 100 | 90 |
1 | W6 | E16 | 5 | 5 |
2 | SW1 | PO19 | 90 | 90 |
2 | W6 | E16 | 8 | 5 |
3 | TW6 | SO14 | 2000 | 2000 |
3 | W6 | E16 | 9 | 5 |
e.g. above: for PA=SW1, DA=PO19 the MIN price=90 (id=2).
Ideally I would also like to only SELECT a particular id, but it still returns the "global" minimum.
e.g. if I want to select id=2, it returns:
id | PA | DA | price | MIN price|
---|-----|------------|--------|--------- |
2 | SW1 | PO19 | 90 | 90 |
2 | W6 | E16 | 8 | 5 |
I would post some attempts I've made but they've been useless attempts.
Regards,
George
The sub-select with the minimum price can be joined to the original table to get your result.
SELECT p.id, p.pa, p.da, p.price, minp.price min_price
FROM prices p
JOIN (SELECT pa, da, min(price) price from prices group by pa, da) minp
ON minp.pa = p.pa and minp.da = p.da
WHERE p.id = 2
You can use the the subquery like this
check output here sqlFiddle
select p.id,p.pa,p.da,
(select min(s.price)
from sample s
group by s.pa,s.da
having s.pa=p.pa and s.da=p.da)
from sample p
mind the formatting

mysql select records with greatest sum given constraints on other values

I've looked around but perhaps I'm not using the right terminology.
I have a table with 4 columns where I want to find the subset of records that 'optimize' one columns sum, based on constraints against the other columns. I basically want to mimic Excel's 'solver' add-in through MySQL.
For example, in the table below, I want to pick the exact 6 Brands that sum to the largest total 'Value' while (1) having a total 'price' <= 600 and (2) has at most 2 of any specific 'Type' value (eg, sum of A's B's, C's, D's, and E's <= 2 each).
I've tried using a running count, but that isn't getting me far. Any guidance is greatly appreciated.
Brand | Type | Price | Value |
Chrysler | A | 93 | 46.8 |
Ford | D | 98 | 46.3 |
Oldsmobile | C | 92 | 45.6 |
Saturn | D | 89 | 45.6 |
Plymouth | A | 104 | 45.3 |
Toyota | E | 90 | 42.6 |
Honda | C | 89 | 39.8 |
Subaru | C | 90 | 38.9 |
Jeep | C | 70 | 37.8 |
Buick | B | 73 | 36.4 |
Cadillac | A | 83 | 35.4 |
Nissan | A | 77 | 34.6 |
Cherry | E | 71 | 34.6 |
Fiat | E | 75 | 33.5 |
Mercedes | B | 79 | 33.3 |
Lexus | A | 81 | 31.9 |
BMW | B | 71 | 30.2 |
Volvo | B | 72 | 29.3 |
Peugot | E | 58 | 29.1 |
Kia | C | 59 | 28.2 |
Audi | E | 59 | 27.9 |
Volkswagen | B | 63 | 26.9 |
Mitsubishi | C | 61 | 26.6 |
Chevrolet | A | 71 | 25.3 |
Acura | B | 57 | 24.5 |
Here's a way of comparing all combinations of your rows six at a time, using a six-way self-join of your table (!!). This finds the top value combinations for total prices less than 600. (Notice that the prices less than six hundred constraint isn't a constraint at all in your dataset, because there's no individual price over 100). http://sqlfiddle.com/#!2/33fba/15/0
I am sorry, I don't get what you're trying to do with the As, Bs, Cs, etc.
SELECT SUM(a.price+b.price+c.price+d.price+e.price+f.price) AS price,
SUM(a.value+b.value+c.value+d.value+e.value+f.value) AS value,
a.brand AS a, b.brand AS b, c.brand AS c,
d.brand AS d, e.brand AS e, f.brand AS f
FROM t AS a
JOIN t AS b ON a.Brand < b.Brand
JOIN t AS c ON b.Brand < c.Brand
JOIN t AS d ON c.Brand < d.Brand
JOIN t AS e ON d.Brand < e.Brand
JOIN t AS f ON e.Brand < f.Brand
GROUP BY a.brand, b.brand, c.brand, d.brand, e.brand, f.brand
HAVING SUM(a.price+b.price+c.price+d.price+e.price+f.price) <= 600
ORDER BY SUM(a.value+b.value+c.value+d.value+e.value+f.value) DESC
LIMIT 10

Calculate difference between one column over multiple rows using coalesce and join

I have following table
+-----+--------+-----------+-----------+-------+
| id | job_id | source_id | target_id | value |
+-----+--------+-----------+-----------+-------+
| 204 | 5283 | 247 | 228 | 1201 |
| 349 | 4006 | 247 | 228 | 100 |
| 350 | 4007 | 247 | 228 | 500 |
| 351 | 4008 | 247 | 228 | 1000 |
| 352 | 4009 | 1 | 100 | 100 |
| 353 | 4010 | 1 | 100 | 500 |
| 354 | 4011 | 1 | 100 | 50 |
+-----+--------+-----------+-----------+-------+
I want to create a diff between the column value groupped by source_id and target_id. The older one (smaller id) should be compared with the newer one
I have searched a little bit and found coalesce. I have written a small query and it works in "general", but not as expexted:
SELECT
c.id, c.source_id, c.target_id, c.value, COALESCE(c1.value - c.value, -1) AS diff
FROM
changes c LEFT JOIN changes c1 ON (c1.source_id = c.source_id AND c1.target_id = c.target_id)
GROUP BY c.source_id, c.target_id, c.job_id
ORDER BY c.id
I got following result:
+-----+-----------+-----------+-------+------+
| id | source_id | target_id | value | diff |
+-----+-----------+-----------+-------+------+
| 204 | 247 | 228 | 1201 | 0 |
| 349 | 247 | 228 | 100 | 1101 |
| 350 | 247 | 228 | 500 | 701 |
| 351 | 247 | 228 | 1000 | 201 |
| 352 | 1 | 100 | 100 | 0 |
| 353 | 1 | 100 | 500 | -400 |
| 354 | 1 | 100 | 50 | 50 |
+-----+-----------+-----------+-------+------+
You can see the diff work for id 349 and 353, I want this for all rows like the following expected result:
+-----+-----------+-----------+-------+------+
| id | source_id | target_id | value | diff |
+-----+-----------+-----------+-------+------+
| 204 | 247 | 228 | 1201 | 1201 |
| 349 | 247 | 228 | 100 | 1101 |
| 350 | 247 | 228 | 500 | -400 |
| 351 | 247 | 228 | 1000 | -500 |
| 352 | 1 | 100 | 100 | 100 |
| 353 | 1 | 100 | 500 | -400 |
| 354 | 1 | 100 | 50 | 450 |
+-----+-----------+-----------+-------+------+
It would be no problem if the diff result is inverted.
What did I miss?
Thanks for any hints.
if you use user defined variables you don't need to join the table to itself. just do a row by row comparrision like so
SELECT
id,
job_id,
target_id,
if(#a = source_id, #b - value, value) as diff,
#b := value as value,
#a := source_id as source_id
FROM changes
CROSS JOIN (SELECT #a:=0, #b:=0)t
DEMO
I suspect that you're looking for something like this - although the COALESCE bit seems misleading to me...
SELECT a.*, COALESCE(b.value-a.value,a.value) diff
FROM
( SELECT x.* , COUNT(*) rank FROM changes x JOIN changes y ON y.id <= x.id GROUP BY x.id ) a
LEFT
JOIN
( SELECT x.* , COUNT(*) rank FROM changes x JOIN changes y ON y.id <= x.id GROUP BY x.id ) b
ON b.source_id = a.source_id
AND b.rank = a.rank - 1;
I think you want:
SELECT c.id,
c.source_id,
c.target_id,
c.value,
c.value - COALESCE(co.value, 0) delta
FROM changes c
LEFT JOIN (
SELECT ci.id, MAX(cio.id) prev_id
FROM changes ci
JOIN changes cio
ON cio.source_id = ci.source_id
AND cio.target_id = ci.target_id
AND cio.id < ci.id
GROUP BY ci.id
) link
ON link.id = c.id
LEFT JOIN changes co
ON co.id = link.prev_id
ORDER BY c.id
I have changed the logic slightly.
In your expected results, the first diff has gone from unknown (0?) to 1201 and is reported as a positive diff, but the second has gone from 1201 to 100 and is still reported as positive.
I have changed the name to delta, and given you the number required to move from the previous value to the new value. Obviously you can change this if you want to:
COALESCE(co.value-c.value, c.value) diff
which will get you the results you provided (with the diff 500 changed to -500, which I believe was a typo).

POS Application - Simplify SQL Mutli-Queries(MySQL)

Hei guy I'm working on a POS app with MySQL. Here is my situation:
Table "purchased_item"
| id | name | check_id | real_price |
| 1 | iPhone5 | 0001 | 399 |
| 2 | iPhone4 | 0001 | 199 |
| 3 | iPhone5s | 0002 | 599 |
| 4 | iPhone5c | 0003 | 399 |
| 5 | iMac 21" | 0003 | 999 |
| 6 | iPod Touch | 0003 | 99 |
| 7 | iPhone5 | 0004 | 399 |
| 8 | iPhone3G | 0004 | 99 |
| 9 | iPhone6 | 0005 | 899 |
| 10 | iPhone3Gs | 0005 | 101 |
And I want to know how many checks's total are larger than or qual(>=) 1000, so what I'm doing now is to do several times of query. In this example, I do 5 times and sum it manually by the host program.
Later the data grow, the queries become slow because there're tons of checks everyday. So I change to record it to another table.
Table "checks"
| id | total | sales |
| 0001 | 598 | A |
| 0002 | 599 | A |
| 0003 | 1497 | B |
| 0004 | 498 | B |
| 0005 | 1000 | A |
But another problem occur in the later time: When I need to adjust the real_price in "purchased_item" table, I also need to maintain the "total" column in "checks" table. It sounds doesn't a big matter but I'd like to find a better way to solve it.
Solved:
SELECT * FROM purchased_item
GROUP BY check_id
HAVING sum(real_price) >= 1000
And the result will be:
| id | name | check_id | real_price |
| 4 | iPhone5c | 0003 | 399 |
| 9 | iPhone6 | 0005 | 899 |
Further question: If I want to count the total price for checks, how can I do it?
I found it:
SELECT check_id,sum(real_price) FROM purchased_item
GROUP BY check_id
HAVING sum(real_price) >= 1000
Try it this way
SELECT i.id, i.name, i.check_id, i.real_price
FROM
(
SELECT MIN(id) id
FROM purchased_item
GROUP BY check_id
HAVING SUM(real_price) >= 1000
) q JOIN purchased_item i
ON q.id = i.id
ORDER BY q.id DESC
Sample output:
| ID | NAME | CHECK_ID | REAL_PRICE |
|----|----------|----------|------------|
| 9 | iPhone6 | 5 | 899 |
| 4 | iPhone5c | 3 | 399 |
...I want to count how many checks's total are over 1000
For that you can just do this
SELECT COUNT(*) total
FROM
(
SELECT check_id
FROM purchased_item
GROUP BY check_id
HAVING SUM(real_price) >= 1000
) q;
Sample output:
| TOTAL |
|-------|
| 2 |
Here is SQLFiddle demo
To update total in checks after adjusting real_price in purchased_item
UPDATE checks c JOIN
(
SELECT check_id, SUM(real_price) total
FROM purchased_item
WHERE check_id IN(5) -- whatever check(s)'s total you want to recalculate
GROUP BY check_id
) p
ON c.id = p.check_id
SET c.total = p.total;
Here is SQLFiddle demo