mysql - 如何在查询中执行递归查询或子查询?

标签 mysql subquery innodb

我不确定我是否需要一个递归查询,但这就是我正在尝试做的(一个调用 B 查询的 A 查询,它调用一个 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

dbfiddle.uk

如您所见,它不会引发错误,它会返回一个表,它可以正常工作...但不是我想要的。

第四行:

@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 值,但我想使用记录的 @avr 值代替 id = return_id。由于 @avr 是一个计算值,我必须执行子查询才能再次计算它......我想。问题是我不知道该怎么做。

enter image description here

因此,id = 9 的平均价格不应为 3.02...,而应为 2.72...

那么,当 mode = "Return" 时,如何在另一个查询中执行此查询,以便获取记录 WHERE 的 @avr 值subquery.id = return_id?

如果可能的话,我是 SQL 方面的新手,所以我仍然不确定哪些是可能的,哪些是不可能的。如果我需要在表中创建一个列、一个 View 或一个函数,对我来说都没有关系,我想我可以处理它们中的任何一个。

最佳答案

简介:您的数据库 fiddle 表明您似乎正在使用 MySQL 8.0。

由于您已经有一个有效的查询并且您只需要处理特定的 mode = 'Return' 情况,一个解决方案可能是将您的查询转换为 CTE,并将其自连接到将返回记录与其原始购买相关联。

这适用于 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

关于mysql - 如何在查询中执行递归查询或子查询?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54269594/

相关文章:

mysql - 如果先行查询至少有 1 个结果,则不返回任何结果?

PHP:刷新我的代码

Mysql - 从另一个表数据更新列而不减少行

mysql - 使用 BETWEEN 子句的 MySQL SELECT 语句的 Big-O 复杂度是多少?

mysql - Rails MySQL 查询时间困惑

mysql - symfony 1.4 推进 :build-all not working on Mysql 5. 5

php - 如何使用 Fabrik(joomla 组件)表单连接到数据库表并搜索匹配并返回结果?

php - 如何在没有cron的情况下向MySQL数据库写入数据?

mysql - 为什么MySQL使用子查询的LIMIT条件搜索比简单搜索更快?

sql - 查询包括子查询和分组比预期慢