我不确定是否确实需要递归查询,但这就是我要做的事情(一个A查询,一个B查询,一个B查询,一个A ...递归查询)。
这是我最小的完整可验证代码:
我有一个这样的表(MySQL v5.7,InnoDB):
CREATE TABLE transactions
(
id INT PRIMARY KEY AUTO_INCREMENT,
code VARCHAR(10),
date DATETIME,
mode ENUM('Buy', 'Sell', 'Count', 'Return'),
quantity INT,
price DECIMAL(10,2),
price_currency ENUM('ARS', 'USD'),
usd_to_ars DECIMAL(10,2),
return_id INT NULL DEFAULT NULL
)
然后我用一些物品填充它:
INSERT INTO transactions (code, date, mode, quantity, price, price_currency, usd_to_ars)
VALUES
("a", "20180101", 'Buy', 4, 10, 'ARS', 3.7),
("a", "20180102", 'Buy', 9, 8, 'ARS', 5.8),
("a", "20180103", 'Sell', -3, 0, 'USD', 0),
("b", "20180104", 'Buy', 5, 5, 'USD', 8.9),
("a", "20180105", 'Buy', 2, 7, 'USD', 3.4),
("b", "20180106", 'Buy', 1, 8, 'ARS', 9),
("a", "20180107", 'Sell', -8, 0, 'USD', 4.4),
("a", "20180108", 'Buy', 9, 9, 'ARS', 3.2);
INSERT INTO transactions (code, date, mode, quantity, price, price_currency, usd_to_ars, return_id)
VALUES ("a", "20180109", 'Return', 6, 2, 'ARS', 2, 2);
最后我执行以下代码:
SELECT *
FROM
(SELECT
id, date, code, mode, quantity, price, price_currency, usd_to_ars, return_id,
@acm := @acm + quantity as stock,
@avr := (@avr * (@acm - quantity) +
if(quantity > 0, quantity *
if(mode = "Return", @avr,
if(price_currency = 'USD', price, price / usd_to_ars)
),
quantity * @avr)
) / @acm as average_price_usd
FROM
transactions t1,
(SELECT @acm := 0) x,
(SELECT @avr := 0) y) t2
ORDER BY id DESC
如您所见,它不会引发错误,它返回一个表,它可以工作……但是不像我想要的那样。
在第四行:
@avr := (@avr * (@acm - quantity) + if(quantity > 0, quantity * if(mode = "Return", @avr, if(price_currency = 'USD', price, price / usd_to_ars)), quantity * @avr)) / @acm as average_price_usd
我想更改以下参数的@avr
:
If(mode = "Return", @avr, [...])
目前,如果mode = "Return"
为true
,则使用当前的@avr
值,但是我想使用记录{{1} 1}}。由于@avr
是一个计算值,我必须执行一个子查询才能再次计算它……我认为。问题是我不知道该怎么做。
因此,id = return_id
的平均价格不应为@avr
,而应为id = 9
。
那么,如何在3.02...
时在另一个查询中执行此查询,以便获取记录2.72...
的{{1}}值?
如果可能的话,我是SQL知识的新手,所以我仍然不确定哪些事情是可行的,哪些是不可能的。对我来说,是否需要在表,视图或函数中创建列都没关系,我认为我可以使用它们中的任何一个。
答案 0 :(得分:0)
简介:您的数据库小提琴表明您似乎正在使用MySQL 8.0。
由于您已经有一个有效的查询,并且只需要处理特定的mode = 'Return'
情况,一种解决方案可以是将查询转换为CTE,并对其进行自我联接以将Return记录与其相关联原始购买。
这在this db fiddle中有效:
WITH cte AS (
SELECT * FROM (SELECT
id, date, code, mode, quantity, price, price_currency, usd_to_ars, return_id,
@acm := @acm + quantity as stock,
@avr := (@avr * (@acm - quantity) +
if(quantity > 0, quantity *
if(mode = "Return", @avr,
if(price_currency = 'USD', price, price / usd_to_ars)
),
quantity * @avr)
) / @acm as average_price_usd
FROM
transactions t1,
(SELECT @acm := 0) x,
(SELECT @avr := 0) y) t2
)
SELECT
cte.id,
cte.date,
cte.code,
cte.mode,
cte.quantity,
cte.price,
cte.price_currency,
cte.usd_to_ars,
cte.return_id,
cte.stock,
COALESCE(cte2.average_price_usd, cte.average_price_usd) average_price_usd
FROM cte LEFT JOIN cte cte2 ON cte.mode = 'Return' AND cte2.id = cte.return_id
ORDER BY cte.id DESC
此外,您似乎还可以使用 MySQL 8.0窗口函数实现您的目标。对我来说,计算average_price_usd
的逻辑尚不清楚,但是这里有一个查询使用这种技术来计算库存。您可能可以对其进行修改,以添加平均价格的逻辑(应该进入上述查询的CTE部分):
SELECT
id,
code,
date,
mode,
quantity,
price,
price_currency,
usd_to_ars,
return_id,
SUM(quantity) OVER (ORDER BY date) stock,
NULL average_price_usd
FROM transactions
ORDER BY date desc
编辑:这是MySQL <8.0的一种(丑陋的)解决方法,它使用两个子查询(请参见this db fiddle):
SELECT
cte.id,
cte.date,
cte.code,
cte.mode,
cte.quantity,
cte.price,
cte.price_currency,
cte.usd_to_ars,
cte.return_id,
cte.stock,
COALESCE(cte2.average_price_usd, cte.average_price_usd) average_price_usd
FROM
(
SELECT
id, date, code, mode, quantity, price, price_currency, usd_to_ars, return_id,
@acm := @acm + quantity as stock,
@avr := (@avr * (@acm - quantity) +
if(quantity > 0, quantity *
if(mode = "Return", @avr,
if(price_currency = 'USD', price, price / usd_to_ars)
),
quantity * @avr)
) / @acm as average_price_usd
FROM
transactions t1,
(SELECT @acm := 0) x,
(SELECT @avr := 0) y
) cte
LEFT JOIN (
SELECT
id, date, code, mode, quantity, price, price_currency, usd_to_ars, return_id,
@acm2 := @acm2 + quantity as stock,
@avr2 := (@avr2 * (@acm2 - quantity) +
if(quantity > 0, quantity *
if(mode = "Return", @avr2,
if(price_currency = 'USD', price, price / usd_to_ars)
),
quantity * @avr2)
) / @acm2 as average_price_usd
FROM
transactions t1,
(SELECT @acm2 := 0) x,
(SELECT @avr2 := 0) y
) cte2 ON cte.mode = 'Return' AND cte2.id = cte.return_id
ORDER BY cte.id DESC