相同的查询和数据结构,但MySQL 8返回的结果与MySQL 5不同?
我已将数据库从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;
旧版本(MySQL5)将返回3条记录(520,519,520),而MySQL8只返回一个帐户id 520。预期输出将与旧版本相同。
您认为是什么原因导致此问题?迁移数据库版本时应如何确保查询结果一致?
非常感谢您的帮助。
解决方案
出现这种行为的一般原因是mySQL使用了一些与您使用的变量不兼容的优化。您使用了相当多的变量,所以我不想在这里找出哪些优化对您有特别的影响,但请参见Myanswer 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将实际生成查询的所有行,并随后(可能)以您希望的方式计算变量,因此您应该会得到预期的结果。(如果你没有,你可能忘记了一些限制,所以试着把它添加到更多的地方)。
一般说来,尽管变量的使用通常和实际上都像预期的那样工作,但在MySQL8之前,变量的使用就已经正式变得脆弱了,例如,参见documentation
对于其他语句,例如SELECT,您可能会得到预期的结果,但这并不能保证。在下面的语句中,您可能认为MySQL首先计算@a,然后再进行赋值:
SELECT @a, @a:=@a+1, ...;
但是,涉及用户变量的表达式的计算顺序未定义。
还有一个更一般的警告:您使用变量的方式从MySQL8开始就不再推荐使用,并且可能会在MySQL的某些未来版本中导致语法错误。您可能希望查看(recursive) Common Table Expressions和How to create a MySQL hierarchical recursive query?中的一些指针,了解如何在不使用变量的情况下重写查询。
相关文章