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 - 如果我有一个 json 类型的列,我可以根据 JSON 中的内容进行选择
如果子查询在 MySQL 中返回多于 1 行,如何将 JSON 放入列数据中