共用体が整数を天井にキャストする際の問題(10進数)


10

私はこのシナリオを持っています。MySQLが最大の10進数値を取り、他の値をそれにキャストしようとしているようです。

問題は、このクエリが外部ライブラリによって生成されるため、少なくともこのレベルでは、このコードを制御できません。これを修正する方法を知っていますか?

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();を使用します。このライブラリは、「select union」を使用してクエリを作成します。


1
20はどういうわけか9.9になる!?それは正しくないようです。
dbdemon 2018

2
:DBFiddle上のMySQL 8.0は、同じナンセンス示しdbfiddle.uk/...
dezso

さらに混乱する... SELECT 20 UNION SELECT null UNION SELECT 40 UNION SELECT 4.3;うまくいく
エヴァン・キャロル

Plsは必要な出力をポストします。また、生成されるコードをどの程度制御できますか。たとえば、タイプを20から20.0に、またはタイプを2.2から2に変更できますか?
Qsigma 2018

ここでさらにコンテキストを追加しました。私の場合の1つの可能な解決策は、コードの値を20.0に強制することです。問題をテストして修正しましたが、nullが関係する特定のシナリオでのみ発生するため、バグのように見えますその注文。
ngcbassman

回答:


9

私にはバグのように見え、私はこの不可解な動作を以下で確認できます:

10.2.14-MariaDB

可能であれば、整数値をdoubleにキャストできます。

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 |
+------+

OK、int値を使用してもエラーは発生しないようです。

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

エラー:出力はdecimal(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を最初または最後に移動すると、エラーは消えます。

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が最初に配置された場合、結果の型は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  |
+------+

結果の型decimal(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  |
+------+

予想通り、最初の値を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)

1
CAST to DOUBLEはMySQLの構文エラーです。小数ではなく動作します:SELECT CAST(20 AS DECIMAL) AS x UNION SELECT NULL UNION SELECT 2.2;
Qsigma

4
私はこれをMariaDB Jiraの
dbdemon

1
この問題はMariaDB 10.3で修正されたようです。(私は10.3.6でテストしたばかりで、10.3.1も機能するはずです。)
dbdemon

2
不思議なことに、20as を指定しても問題はありません020MariaDB 10.2MySQL 8.0の動作は同じです。リテラルの長さが結合された列のタイプに影響するように見えます。いずれにしても、これは間違いなく私の本のバグです。
Andriy M

1
MySQL 8.0でnullがなくても問題が発生しています (ただし、MariaDB 10.2では問題なく動作します)。先行ゼロまたは計算を含めると、列サイズにも違いがあります
Mick O'Hea

5

バグMDEV-15999

バグMDEV-15999により提出dbdemonはこのことを報告しました。10.3.1で修正されました。

奇妙なMySQL / MariaDBの性質

ドキュメントから、

最初のSELECTステートメントの列名は、返される結果の列名として使用されます。SELECTステートメントの対応する位置にリストされている選択された列は、同じデータ型でなければなりません。(たとえば、最初のステートメントで選択された最初の列は、他のステートメントで選択された最初の列と同じタイプでなければなりません。)

対応するSELECT列のデータ型が一致しない場合、UNION結果の列の型と長さは、すべてのSELECTステートメントによって取得された値を考慮に入れます。

この場合、彼らは和解decimalintegerの整数を促進することにより、decimalそれを含めることができません。私はそれが恐ろしいことを知っていますが、同様に恐ろしいのは、このように静かに振る舞うことです。

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 ;
Andriy M

3
ここでの重要な洞察は、MySQLが保持できるが、保持するに2.2狭すぎるデータ型を選択したこと20です。あなたは、変更することで、これを見ることができる最後のselect句CAST(2.2 AS DECIMAL(10,2))与え、20.0最初の行(暗黙的に実行されているようにCAST(20 AS DECIMAL(10,2)))。
IMSoP 2018

1
奇妙なのは、データ型がに基づいているだけではないということです2.2。あなたselect 20000 union select null union select 2.22が取得しようとすると9999.99、でdecimal(6,2)。常に1桁では短すぎて値を保持できません
Mick O'Hea

1
@ミックうん。NULL真ん中に値を投入すると、精度1の短さが計算されます。
Salman A
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.