【问题标题】:Same query and data structure, but MySQL 8 returns different result with MySQL 5?相同的查询和数据结构,但 MySQL 8 与 MySQL 5 返回的结果不同?
【发布时间】:2021-12-18 11:15:31
【问题描述】:

我已将数据库从 AWS Aurora 1.22.3(与 MySQL 5.6 兼容)迁移到 MySQL 8,并有一个查询返回账户及其父账户(如果是父账户,则返回子账户)。

帐户表将包含:

account_id(主键),parent_account_id(外键), 帐户名,...

例如,我有账户 520 和父账户 519:

account_id,tenant_id,parent_account_id,account_name,account_code,account_class,account_type,account_description,status,is_master_account,currency_code,tax_type,pending_balance,authorised_balance,total_balance,related_party_id,creator_id
519,1,NULL,"SANTOS LIMITED - Trade1",000604,ASSET,FINRECEIVABLE,NULL,ACTIVE,1,AUD,BASEXC,0.000000,0.000000,0.000000,321,1
520,1,519,"SANTOS LIMITED - Trade Card1",000604-1,ASSET,FINRECEIVABLE,NULL,ACTIVE,0,AUD,BASEXC,0.000000,0.000000,0.000000,321,1

这是我的查询:

    SELECT 
        t_all.account_id -- , parent_level, t_all.level 
    FROM ( 
        -- GET ALL CHILDREN 
        SELECT 
            account_id, 
            parent_account_id, 
            null as parent_level, 
            (@l:=@l + 1) AS level 
        FROM 
        ( 
            SELECT 
                account_id, 
                tenant_id, 
                parent_account_id 
            FROM Account 
            ORDER BY 
                parent_account_id, 
                account_id 
        ) account_sorted, 
        ( 
            SELECT @pv := 520, @l := 0, @cl := 0 
        ) initialisation 
        WHERE 
        account_id = @pv OR 
        find_in_set(parent_account_id, @pv) > 0 
        AND 
        @pv := concat(@pv, ',', account_id) 
        
        UNION 
        
        -- GET ALL PARENTS 
        SELECT 
            account_id, 
            parent_account_id, 
            level as parent_level, 
            null as level 
        FROM 
        ( 
            SELECT 
                _id AS account_id, 
                parent_account_id, 
                @cl := @cl + 1 AS level 
            FROM 
            ( 
                SELECT 
                    @r AS _id, 
                    ( 
                        SELECT @r := parent_account_id 
                        FROM Account 
                        WHERE account_id = @r 
                    ) AS parent_account_id, 
                    @l := @l + 1 AS level 
                FROM 
                ( 
                    SELECT @r := 520, @l := 0, @cl := 0 
                ) vars, 
                Account h 
                WHERE 
                    @r <> 0 
                ORDER BY level DESC 
            ) qi 
        ) qo 
    ) as t_all 
    order by level desc , parent_level asc; 

旧版本(MySQL 5)将返回 3 条记录(520,519,520),而 MySQL 8 仅返回一个 account_id 520。预期输出与旧版本相同。

您认为可能导致问题的原因以及在迁移数据库版本时我应该如何确保查询结果一致?

非常感谢您的帮助。

【问题讨论】:

  • MySQL 8 已弃用对上述用户变量的支持。您应该使用递归 CTE 和/或分析函数。示例数据在这里会有所帮助。
  • 感谢@TimBiegeleisen,我已经更新了问题中的示例数据。我应该搜索用户变量,看看它如何影响查询。
  • 阅读有关使用 MySQL 8+ 的递归分层查询。

标签: mysql sql database database-migration amazon-aurora


【解决方案1】:

这种行为的一般原因是 MySQL 使用了一些与您使用变量不兼容的优化。你使用了很多变量,所以我不是想找出哪些优化在这里特别适合你,但请参阅例如以我的answer here 为例。一般来说,如果变量的值发生变化,MySQL 会对您的子查询做出一些假设,这些假设不一定正确。

一般的解决方案是阻止 MySQL 进行那些优化,目前普遍可以通过物化来完成。您可以通过为所有子查询添加任意大的限制来实现这一点,例如

... FROM Account 
ORDER BY parent_account_id, account_id 
LIMIT 100000000 -- add this
...

... FROM Account WHERE account_id = @r 
LIMIT 100000000 -- add this
...

这意味着 MySQL 将实际生成查询的所有行并随后(可能)按照您希望的方式评估您的变量,因此您应该得到您期望的结果。 (如果您不这样做,您可能忘记了一些限制,因此请尝试将其添加到更多地方)。

虽然一般来说,虽然它通常和实际上按预期工作,但变量的使用甚至在 MySQL 8 之前就已经正式脆弱,参见例如documentation

对于其他语句,例如 SELECT,您可能会得到您期望的结果,但这不能保证。在下面的语句中,您可能会认为 MySQL 先评估 @a,然后再进行赋值:

 SELECT @a, @a:=@a+1, ...;

但是,涉及用户变量的表达式的计算顺序未定义

作为更一般的警告:自 MySQL 8 以来,您使用变量的方式已被弃用,并且可能会在 MySQL 的某些未来版本中导致语法错误。您可能需要查看 (recursive) Common Table ExpressionsHow to create a MySQL hierarchical recursive query? 以了解如何在没有变量的情况下重写您的查询。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-27
    • 2014-09-24
    • 2020-03-12
    • 1970-01-01
    • 2020-10-24
    • 2012-02-05
    • 1970-01-01
    相关资源
    最近更新 更多