MYSQL JSON 选择查询每次运行都会返回不同的结果

Posted

技术标签:

【中文标题】MYSQL JSON 选择查询每次运行都会返回不同的结果【英文标题】:MYSQL JSON select query returning different results each time it is run 【发布时间】:2020-09-27 12:12:33 【问题描述】:

我对 JSON 数据有一个简单的选择查询,其中包含一些简单的计算,当我多次运行它时会返回不同的结果。

在我的一生中,我只是无法找出原因,到目前为止,我在寻找答案时也没有运气。我可以从查询统计信息中看到构建了一个临时表,我想它无法截断/在查询完成后保存在某处,但我无法找到(更不用说截断/删除)这个表了。不过,这只是一个可行的假设——答案可能在一个环境中或......

任何人都可以找到任何逻辑吗?

我在本地实例的 mysql 数据库上使用 MySQL Workbench。 MySQL:版本 8.0.19 MySQL 社区服务器 - GPL。

这是表格的简化版:

CREATE TABLE regulation_entries (
    id INTEGER UNSIGNED AUTO_INCREMENT,
    employee_id VARCHAR(10) NOT NULL,
    regulation JSON NOT NULL,
    PRIMARY KEY (id)
    );

# Sample data to work with    

INSERT INTO regulation_entries VALUES
    (DEFAULT, 2, '"entry_base_salary": "8000", "pension_pct": "0.08"'),
    (DEFAULT, 3, '"entry_base_salary": "10000", "pension_pct": "0.08"'),
    (DEFAULT, 5, '"entry_base_salary": "11000", "pension_pct": "0.08"'),
    (DEFAULT, 8, '"entry_base_salary": "11000", "pension_pct": "0.08"'),
    (DEFAULT, 9, '"entry_base_salary": "9000", "pension_pct": "0.08"'),
    (DEFAULT, 1, '"entry_base_salary": "14000", "pension_pct": "0.10"'),
    (DEFAULT, 6, '"entry_base_salary": "13000", "pension_pct": "0.08"'),
    (DEFAULT, 7, '"entry_base_salary": "14000", "pension_pct": "0.08"'),
    (DEFAULT, 2, '"base_salary_adjustment": "500"'),
    (DEFAULT, 9, '"base_salary_adjustment": "800"'),
    (DEFAULT, 3, '"base_salary_adjustment": "400"'),
    (DEFAULT, 5, '"base_salary_adjustment": "350"'),
    (DEFAULT, 8, '"base_salary_adjustment": "200"'),
    (DEFAULT, 9, '"base_salary_adjustment": "1250"'),
    (DEFAULT, 1, '"base_salary_adjustment": "-200"'),
    (DEFAULT, 1, '"base_salary_adjustment": "50"'),
    (DEFAULT, 6, '"base_salary_adjustment": "700"'),
    (DEFAULT, 7, '"base_salary_adjustment": "825"');

# The query that is bugging me:

SELECT employee_id, 
        SUM(regulation->>'$.entry_base_salary') AS entry_base_salary,
        regulation->>'$.pension_pct' AS pension_pct,
        AVG(regulation->>'$.pension_pct') * SUM(regulation->>'$.entry_base_salary') AS entry_pension,
        SUM(regulation->>'$.base_salary_adjustment') AS salary_adjustments,
        SUM(regulation->>'$.entry_base_salary') + SUM(regulation->>'$.base_salary_adjustment') AS future_salary,
        AVG(regulation->>'$.pension_pct') * (SUM(regulation->>'$.entry_base_salary') + SUM(regulation->>'$.base_salary_adjustment')) AS future_pension 
    FROM sreg.regulation_entries 
    GROUP BY employee_id
    ORDER BY employee_id

当我运行查询时,我希望看到:

employee_id #entry_base_salary  #pension_pct    #entry_pension  #salary_adjustments #future_salary  #future_pension#
#1          #14000              #0.10           #1400           #1250               #15250          #1525
#2          #8000               #0.08           #640            #1100               #9100           #728
#3          #10000              #0.08           #800            #1000               #11000          #880
#5          #11000              #0.08           #880            #950                #11950          #956
#6          #13000              #0.08           #1040           #1900               #14900          #1192
#7          #14000              #0.08           #1120           #2025               #16025          #1282
#8          #11000              #0.08           #880            #800                #11800          #944
#9          #9000               #0.08           #720            #2650               #11650          #932

但如果我再次执行相同的查询,我会得到新的随机结果。例如:

employee_id #entry_base_salary  #pension_pct    #entry_pension  #salary_adjustments     #future_salary  #future_pension#

1           #14000              #0.10           #1400           #3050                   #17050          #1705
2           #8000               #0.08           #640            #2900                   #10900          #872
3           #10000              #0.08           #800            #2800                   #12800          #1024
5           #11000              #0.08           #880            #2750                   #13750          #1100
6           #13000              #0.08           #1040           #3700                   #16700          #1336
7           #14000              #0.08           #1120           #3825                   #17825          #1426
8           #11000              #0.08           #880            #2600                   #13600          #1088
9           #9000               #0.08           #720            #4450                   #13450          #1076

Another example

1           #14000              #0.10           #1400           #2.119191149652875e88   #15250          #1525
2           #8000               #0.08           #640            #2.119191149652875e88   #9100           #728
3           #10000              #0.08           #800            #2.119191149652875e88   #11000          #880
5           #11000              #0.08           #880            #2.119191149652875e88   #11950          #956
6           #13000              #0.08           #1040           #2.119191149652875e88   #14900          #1192
7           #14000              #0.08           #1120           #2.119191149652875e88   #16025          #1282
8           #11000              #0.08           #880            #2.119191149652875e88   #11800          #944
9           #9000               #0.08           #720            #2.119191149652875e88   #11650          #932

And a third

1           #14000              #0.10           #1400           #3650                   #17650          #1765
2           #8000               #0.08           #640            #3500                   #11500          #920
3           #10000              #0.08           #800            #3400                   #13400          #1072
5           #11000              #0.08           #880            #3350                   #14350          #1148
6           #13000              #0.08           #1040           #4300                   #17300          #1384
7           #14000              #0.08           #1120           #4425                   #18425          #1474
8           #11000              #0.08           #880            #3200                   #14200          #1136
9           #9000               #0.08           #720            #5050                   #14050          #1124

有人见过这个吗?有什么解释吗?或者更好的是,有谁知道我必须改变什么才能获得一致的结果?

【问题讨论】:

我只检查了员工 1,但您的期望值对我来说似乎不正确! 嗯......我相信它是。员工 1 有 10% 的养老金,而其他员工只有 8%(只是为了测试它是如何处理的),这可能解释了为什么它看起来与您不同。但无论是否如此,最大的问号仍然是结果为何会发生变化。 不一致只与base_salary_adjustment有关,请确保您针对具有该字段的相同行运行查询 以防万一它是相关的,你正在运行什么版本的 MySQL @RiggsFolly 不,你是对的。调整也是错误的。 1250 应该适用于员工 9。 【参考方案1】:

这根本不是专门针对 JSON 的。这是 MySQL 8.0.18 中的一个错误,已在 8.0.20 中修复。该错误可能发生在非 JSON 列中。

https://bugs.mysql.com/bug.php?id=97920聚合函数[sum()]返回随机数

事实上,这些数字并不是完全随机的,而是每次运行查询时都会增加。这就像 SUM() 在临时表中累积总和,而 same 临时表用于查询的后续运行,而不会将总和归零。

这是上述错误中给出的测试用例的结果:

mysql> SELECT ym, ROUND(SUM(vb)), ROUND(SUM(vc)) FROM t_test GROUP BY ym;
+--------+----------------+----------------+
| ym     | ROUND(SUM(vb)) | ROUND(SUM(vc)) |
+--------+----------------+----------------+
| 201912 |          22675 |            227 |
+--------+----------------+----------------+

mysql> SELECT ym, ROUND(SUM(vb)), ROUND(SUM(vc)) FROM t_test GROUP BY ym;
+--------+----------------+----------------+
| ym     | ROUND(SUM(vb)) | ROUND(SUM(vc)) |
+--------+----------------+----------------+
| 201912 |          22675 |            454 |
+--------+----------------+----------------+

mysql> SELECT ym, ROUND(SUM(vb)), ROUND(SUM(vc)) FROM t_test GROUP BY ym;
+--------+----------------+----------------+
| ym     | ROUND(SUM(vb)) | ROUND(SUM(vc)) |
+--------+----------------+----------------+
| 201912 |          22675 |            682 |
+--------+----------------+----------------+

在我测试时,每次运行查询时它都会继续增加 227。 227 是正确的结果,在我第一次运行查询时返回。

此外,如果要求和的基础数据不包含任何 NULL,则不会出现此问题。

mysql> SELECT ym, ROUND(SUM(vb)), ROUND(SUM(vc)) FROM t_test where vc is not null GROUP BY ym;
+--------+----------------+----------------+
| ym     | ROUND(SUM(vb)) | ROUND(SUM(vc)) |
+--------+----------------+----------------+
| 201912 |            252 |            227 |
+--------+----------------+----------------+

每次运行此查询时都会返回正确的值 227。

您的查询也会发生同样的情况。由于您的表达式 regulation->>'$.base_salary_adjustment' 在 JSON 不包含该键的行上返回 NULL,因此在使用 SUM() 时会出现相同的错误。

如果我修改查询以将 NULL 转换为 0,它会给出正确的结果,并且无论我运行多少次查询都不会改变。

SUM(COALESCE(regulation->>'$.base_salary_adjustment', 0)) AS salary_adjustments

每次引用该 JSON 键时(或在可能为 NULL 的表达式上使用 SUM() 时都必须这样做。

我建议你升级到 MySQL 8.0.20。

【讨论】:

那么entry_base_salary 怎么样?有些行没有设置entry_base_salary 字段,但它仍然在查询结果中给出了正确的总和。顺序重要吗? 显然这很重要。这是一个错误,所以所有合乎逻辑的行为都没有实际意义。 @BillKarwin 谢谢!我知道这里的某个天才可以找到答案……我感到安慰的是,这是一个错误,所以我可以取消与收缩师的约会;) 大声笑——MySQL 让你大吃一惊! @BillKarwin 我花了一段时间,因为系统表更新一直失败,但我接受了你的建议并更新到 MySQL 8.0.20,你就对了。现在计算就像一个魅力。再次感谢您的帮助!

以上是关于MYSQL JSON 选择查询每次运行都会返回不同的结果的主要内容,如果未能解决你的问题,请参考以下文章

MySQL使用Group Bu返回不正确的值[重复]

MYSQL - 如果我有一个 json 类型的列,我可以根据 JSON 中的内容进行选择

MySQL 查询返回 JSON 数据需要很长时间

如果子查询在 MySQL 中返回多于 1 行,如何将 JSON 放入列数据中

如果选择查询返回空结果,请在 mysql 中运行另一个选择查询

CloudKit - 每次运行相同的查询时,CKQueryOperation 结果都不同