联合将整数转换为天花板的问题(十进制)

ngc*_*man 10 mysql datatypes union mysql-5.6 decimal

我有这种情况,看起来 MySQL 正在采用最大的十进制值并尝试将其他值转换为该值。

问题是这个查询是由外部库生成的,所以我无法控制这个代码,至少在这个级别。你知道如何解决这个问题吗?

SELECT 20 AS x
  UNION SELECT null
  UNION SELECT 2.2;
+------+
| x    |
+------+
|  9.9 | -- why from 20 to 9.9
| NULL |
|  2.2 |
+------+
Run Code Online (Sandbox Code Playgroud)

预期结果

+------+
| x    |
+------+
|   20 | -- or 20.0, doesn't matter really in my case
| NULL |
|  2.2 |
+------+
Run Code Online (Sandbox Code Playgroud)

添加更多上下文,我使用 Entity Framework 6 和扩展库http://entityframework-extensions.net/批量保存更改,特别是方法 context.BulkSaveChanges();,该库使用“select union”创建查询.

Len*_*art 9

对我来说看起来像是一个错误,我可以在以下方面确认这种令人费解的行为:

10.2.14-MariaDB
Run Code Online (Sandbox Code Playgroud)

如果可能,您可以将整数值转换为双精度值:

SELECT cast(20 as double) UNION SELECT null UNION SELECT 2.2;
Run Code Online (Sandbox Code Playgroud)

或确保您首先拥有双倍值:

SELECT 2.2 UNION SELECT null UNION SELECT 22;
Run Code Online (Sandbox Code Playgroud)

阅读@Evan Carroll 回答中的评论后的进一步观察

select 20 union select null union select 2;
+------+
| 20   |
+------+
|   20 |
| NULL |
|    2 |
+------+
Run Code Online (Sandbox Code Playgroud)

好的,使用 int 值似乎不会产生错误。

select 20 union select null union select 9.0;
+------+
| 20   |
+------+
| 9.9  |
| NULL |
| 9.0  |
+------+
Run Code Online (Sandbox Code Playgroud)

错误:似乎输出是十进制(2,1)

create table tmp as select * from (select 20 as x 
                                   union 
                                   select null 
                                   union 
                                   select 9.0) as t

describe tmp;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| x     | decimal(2,1) | YES  |     | NULL    |       |
+-------+--------------+------+-----+---------+-------+
Run Code Online (Sandbox Code Playgroud)

该错误与命令行界面无关,它也存在于 python2-mysql-1.3.12-1.fc27.x86_64 中:

>>> import MySQLdb
>>> db = MySQLdb.connect(host="localhost", user="*****", passwd="*****", db="test") 
>>> cur = db.cursor()
>>> cur.execute("SELECT 20 union select null union select 2.2")
3L
>>> for row in cur.fetchall() :
...     print row
... 
(Decimal('9.9'),)
(None,)
(Decimal('2.2'),)
Run Code Online (Sandbox Code Playgroud)

奇怪的是,如果首先或最后移动 null,错误就会消失:

select null union select 20 union select 9.0;
select 20 union select 9.0 union select null;

+------+
| NULL |
+------+
| NULL |
| 20.0 |
| 9.0  |
+------+
Run Code Online (Sandbox Code Playgroud)

如果先放置 null,则结果类型为 decimal(20,1)。如果将 null 放在最后结果类型为 decimal(3,1)

如果将另一条腿添加到并集,错误也会消失:

select 20 union select 6 union select null union select 9.0;
+------+
| 20   |
+------+
| 20.0 |
| 6.0  |
| NULL |
| 9.0  |
+------+
Run Code Online (Sandbox Code Playgroud)

结果类型十进制(20,1)

在中间添加另一个 null 会保留错误:

select 20 union select null union select null union select 9.0;
+------+
| 20   |
+------+
| 9.9  |
| NULL |
| 9.0  |
+------+
Run Code Online (Sandbox Code Playgroud)

但是在开头添加一个 null 可以修复它:

select null union select 20 union select null union select null union select 9.0;
+------+
| NULL |
+------+
| NULL |
| 20.0 |
| 9.0  |
+------+
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,将第一个值转换为 decimal(3,1) 有效。

最后,显式转换为 decimal(2,1) 会产生相同的错误,但带有警告:

select cast(20 as decimal(2,1));
+--------------------------+
| cast(20 as decimal(2,1)) |
+--------------------------+
| 9.9                      |
+--------------------------+
1 row in set, 1 warning (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

  • 我冒昧地将此报告为 MariaDB Jira 中的错误:https://jira.mariadb.org/browse/MDEV-15999 (4认同)
  • 奇怪的是,如果您将 `20` 指定为 `020`,就没有问题。行为是相同的 [在 MariaDB 10.2](https://dbfiddle.uk/?rdbms=mariadb_10.2&fiddle=ebdc7bde304c7d02070c0d187379f42f) 和 [在 MySQL 8.0](https://dbfiddle.uk/?rdbms=mysqlfiddle=8 ebdc7bde304c7d02070c0d187379f42f)。看起来很像 *literal* 的长度会影响组合列的类型。无论如何,这绝对是我书中的一个错误。 (2认同)

Eva*_*oll 5

错误 MDEV-15999

dbdemon提交的错误MDEV- 15999报告了这一点。它已在 10.3.1 中修复。

奇怪的 MySQL/MariaDB 性质

从文档中,

第一个SELECT语句中的列名用作返回结果的列名。每条SELECT语句对应位置列出的选定列应具有相同的数据类型。(例如,第一个语句选择的第一列应该与其他语句选择的第一列具有相同的类型。)

如果对应SELECT列的数据类型不匹配,则UNION结果中列的类型和长度会考虑所有SELECT语句检索到的值。

在这种情况下,他们调和decimal,并integer通过促进一个整数decimal,可以不包含它。我知道这很可怕,但同样可怕的是这种默默地表现出来的行为。

SELECT CAST(20 AS decimal(2,1));
+--------------------------+
| CAST(20 AS decimal(2,1)) |
+--------------------------+
|                      9.9 |
+--------------------------+
Run Code Online (Sandbox Code Playgroud)

这似乎为这个问题铺平了道路。

  • 这里的关键见解是 MySQL 选择了一种数据类型,它可以容纳“2.2”但*太窄*不能容纳“20”。您可以通过将 *last* select 子句更改为 `CAST(2.2 AS DECIMAL(10,2))` 来查看这一点,它将 `20.0` 作为第一行(隐式运行 `CAST(20 AS DECIMAL(10,2)) )`)。 (3认同)