问题
How do I subtract row 1 from row 2 and row 2 from row 3, etc. in MySQL? The table i am pulling my data from contains multiple products and all products have multiple prices (on different dates)
The code i am working with:
SELECT
orderline_sales.product_name,
orderline_sales.price
FROM
orderline_sales
GROUP BY price
HAVING orderline_sales.product_name = 'Ibuprofen';
The result I am getting:
|---------------------|------------------|
| product_name | price |
|---------------------|------------------|
| Ibuprofen | 30.20 |
|---------------------|------------------|
| Ibuprofen | 32.20 |
|---------------------|------------------|
| Ibuprofen | 35.20 |
|---------------------|------------------|
The result I want:
|---------------------|------------------|------------------|
| product_name | price | price_change |
|---------------------|------------------|------------------|
| Ibuprofen | 30.20 | 0 |
|---------------------|------------------|------------------|
| Ibuprofen | 32.20 | 2 |
|---------------------|------------------|------------------|
| Ibuprofen | 35.20 | 3 |
|---------------------|------------------|------------------|
回答1:
You probably want to look into MySQL's user defined variables, and then you probably want to do something like this:
SET @prev := NULL;
SELECT
DATE(created_at),
price - COALESCE(@prev, price) AS price_change,
name,
(@prev := price) AS price FROM (
SELECT * FROM items ORDER BY DATE(created_at)
) t1
GROUP BY
name, price, DATE(created_at)
HAVING name = 'Ibuprofen'
ORDER BY DATE(created_at);
Query OK, 0 rows affected (0.00 sec)
I haven't checked syntax so it might be a little off but that is the general idea. Note that I added date so that you can order by it, otherwise the results may be meaningless.
EDIT:
Just ran this on my machine:
SET @prev := NULL;
SELECT
DATE(created_at),
price - COALESCE(@prev, price) AS price_change,
name,
(@prev := price) AS price FROM (
SELECT * FROM items ORDER BY DATE(created_at)
) t1
GROUP BY
name, price, DATE(created_at)
HAVING name = 'Ibuprofen'
ORDER BY DATE(created_at);
Query OK, 0 rows affected (0.00 sec)
+------------------+--------------+-----------+-------+
| DATE(created_at) | price_change | name | price |
+------------------+--------------+-----------+-------+
| 2018-12-10 | 0 | Ibuprofen | 110 |
| 2018-12-13 | -10 | Ibuprofen | 100 |
| 2018-12-13 | 20 | Ibuprofen | 120 |
+------------------+--------------+-----------+-------+
3 rows in set, 1 warning (0.00 sec)
SELECT * FROM items;
+----+-------+----------------+---------------------+
| id | price | name | created_at |
+----+-------+----------------+---------------------+
| 8 | 100 | Ibuprofen | 2018-12-13 12:52:35 |
| 9 | 110 | Ibuprofen | 2018-12-10 12:12:12 |
| 10 | 120 | Ibuprofen | 2018-12-13 12:52:35 |
| 11 | 1000 | Something else | 2018-12-13 13:01:19 |
+----+-------+----------------+---------------------+
4 rows in set (0.00 sec)
回答2:
Maybe something like this could work ?
DROP TABLE IF EXISTS test;
CREATE TABLE test (
product_name text,
price numeric
);
INSERT INTO test VALUES
('ibuprofen', 30.20),
('ibuprofen', 32.20),
('ibuprofen', 35.20);
SELECT DISTINCT t.product_name, t.current_price - t.previous_price AS price_change
FROM (SELECT te.product_name, te.price AS current_price,
LAG(te.price) over w AS previous_price,
row_number() over w AS rn
FROM test AS te
WINDOW w AS (ORDER BY te.product_name, te.price)
) AS t
WHERE t.product_name = 'ibuprofen'
AND t.rn <> 1
ORDER BY t.product_name, price_change;
This Query returns following result:
product_name | price_change
--------------+--------------
ibuprofen | 0.00
ibuprofen | 2.00
ibuprofen | 3.00
回答3:
Let's assume the original purpose was to track price changes (time/id). Assuming the GROUP BY was there to eliminate rows where price has not changed. You could do following (variation of @dave 's answer):
SELECT product_name, price, cast( ifnull(price_change,0) as decimal(6,2)) as price_change
FROM (
SELECT
product_name,
price - @prev AS price_change,
(@prev := price) AS price
FROM
orderline_sales
JOIN (SELECT @prev := null) as j
WHERE orderline_sales.product_name = 'Ibuprofen'
ORDER BY id
) as q
WHERE price_change is null or price_change!=0;
The difference to @dave's answer is the removal of incorrect use of GROUP BY
.
See db-fiddle.
来源:https://stackoverflow.com/questions/53769621/sql-how-to-subtract-result-row-1-from-row-2-row-2-from-row-3