将整数强制转换为上限(十进制)的问题


10

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

问题是此查询是由外部库生成的,因此至少在此级别上我无法控制此代码。您知道如何解决此问题吗?

SELECT 20 AS x
  UNION SELECT null
  UNION SELECT 2.2;
+------+
| x    |
+------+
|  9.9 | -- why from 20 to 9.9
| NULL |
|  2.2 |
+------+

预期结果

+------+
| x    |
+------+
|   20 | -- or 20.0, doesn't matter really in my case
| NULL |
|  2.2 |
+------+

添加更多上下文,我使用带有扩展库http://entityframework-extensions.net/的 Entity Framework 6 来批量保存更改,特别是方法context.BulkSaveChanges();,该库使用“选择联合”创建查询。


1
20以某种方式变成9.9 ?! 那似乎不对。
dbdemon

2
DBFiddle上的MySQL 8.0显示了同样的废话:dbfiddle.uk/…–
dezso

更加令人困惑的... SELECT 20 UNION SELECT null UNION SELECT 40 UNION SELECT 4.3;工作正常
Evan Carroll '18

请发布您需要的输出。另外,您对生成的代码有多少控制权:例如,您可以将类型更改为20到20.0还是将类型更改为2.2到2?
Qsigma

我现在添加了更多上下文,在我的情况下,一种可能的解决方案是将代码中的值强制为20.0,我测试并解决了该问题,但它看起来像个错误,因为仅在涉及null的特定情况下发生,并且那个命令。
ngcbassman

Answers:


9

对我来说似乎是个错误,我可以通过以下方式确认这种令人困惑的行为:

10.2.14-MariaDB

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

SELECT cast(20 as double) UNION SELECT null UNION SELECT 2.2;

或确保您首先拥有double值:

SELECT 2.2 UNION SELECT null UNION SELECT 22;

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

select 20 union select null union select 2;
+------+
| 20   |
+------+
|   20 |
| NULL |
|    2 |
+------+

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

select 20 union select null union select 9.0;
+------+
| 20   |
+------+
| 9.9  |
| NULL |
| 9.0  |
+------+

错误:好像输出是十进制(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    |       |
+-------+--------------+------+-----+---------+-------+

该错误未隔离到命令行界面,对于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'),)

奇怪的是,如果首先移动null或最后移动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  |
+------+

如果将null放在首位,则结果类型为小数(20,1)。如果放置null,则最后一个结果类型为小数(3,1)

如果将另一条腿添加到联合中,该错误也将消失:

select 20 union select 6 union select null union select 9.0;
+------+
| 20   |
+------+
| 20.0 |
| 6.0  |
| NULL |
| 9.0  |
+------+

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

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

select 20 union select null union select null union select 9.0;
+------+
| 20   |
+------+
| 9.9  |
| NULL |
| 9.0  |
+------+

但是在开头添加null可以解决此问题:

select null union select 20 union select null union select null union select 9.0;
+------+
| NULL |
+------+
| NULL |
| 20.0 |
| 9.0  |
+------+

如预期的那样,将第一个值强制转换为十进制(3,1)。

最后,显式转换为十进制(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)

1
CAST to DOUBLE是MySQL中的语法错误。小数代替:SELECT CAST(20 AS DECIMAL) AS x UNION SELECT NULL UNION SELECT 2.2;
Qsigma

4
我冒昧将其报告为MariaDB Jira中的错误:jira.mariadb.org/browse/MDEV-15999
dbdemon

1
此问题似乎已在MariaDB 10.3中修复。(我刚刚使用10.3.6进行了测试,并且10.3.1也应该可以工作。)
dbdemon

2
奇怪的是,如果将20as 指定为没有问题020在MariaDB 10.2MySQL 8.0中,行为是相同的。看起来很像文字的长度会影响合并列的类型。无论如何,这绝对是我书中的错误。
Andriy M

1
我看到的是MySQL 8.0中的问题,即使没有null也可以 (尽管在MariaDB 10.2中可以正常工作)。如果您包含前导零或计算,则列大小也有所不同
Mick O'Hea

5

错误MDEV-15999

dbdemon提交的错误MDEV- 15999报告了此问题。自10.3.1起已修复。

奇怪的MySQL / MariaDB性质

从文档中

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

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

在这种情况下,它们会协调decimalinteger通过将整数提升为decimal不能包含该整数的a 。我知道这很可怕,但同样令人讨厌的是,它默默地表现出来。

SELECT CAST(20 AS decimal(2,1));
+--------------------------+
| CAST(20 AS decimal(2,1)) |
+--------------------------+
|                      9.9 |
+--------------------------+

这似乎为解决该问题铺平了道路。


SELECT cast(20 as signed) UNION SELECT null UNION SELECT 2.2 ;产生相同的(9.9)错误结果。但是,如果我们使用“ unisgned”,那么一切顺利。去图...
ypercubeᵀᴹ

SELECT -20 UNION SELECT null UNION SELECT 2.2 ;正常工作也一样SELECT 20. UNION SELECT null UNION SELECT 2.2 ;
舍甫琴科中号

3
此处的关键见解是MySQL选择了可以保留2.2过于狭窄而无法保留的数据类型20。您可以通过将最后一个 select子句更改为来看到这一点CAST(2.2 AS DECIMAL(10,2)),它20.0作为第一行给出(隐式运行CAST(20 AS DECIMAL(10,2)))。
IMSoP '18 -4-24

1
奇怪的是,这不仅仅是基于的数据类型2.2。如果你尝试的select 20000 union select null union select 2.229999.99,你会在一个decimal(6,2)。它总是太短而无法保存该值
Mick O'Hea '18

1
@Mick是的 如果您将NULL值放在中间,它将计算精度1 short。
Salman,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.