ปัญหาเกี่ยวกับการรวมเลขจำนวนเต็มถึงจำนวน (ทศนิยม)


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

การเพิ่มบริบทเพิ่มเติมฉันใช้ Entity Framework 6 กับส่วนขยายของไลบรารีhttp://entityframework-extensions.net/เพื่อบันทึกการเปลี่ยนแปลงเป็นกลุ่มโดยเฉพาะอย่างยิ่งบริบทของเมธอด.BulkSaveChanges (); ไลบรารีนี้สร้างคิวรีโดยใช้ "select union" .


1
20 กลายเป็น 9.9! ดูเหมือนจะไม่ถูกต้อง
dbdemon

2
MySQL 8.0 บน DBFiddle แสดงเรื่องไร้สาระเหมือนกัน: dbfiddle.uk/ …
dezso

ความสับสนยิ่งขึ้น ... ใช้SELECT 20 UNION SELECT null UNION SELECT 40 UNION SELECT 4.3;งานได้ดี
Evan Carroll

กรุณาโพสต์เอาท์พุทที่คุณต้องการ นอกจากนี้คุณมีการควบคุมมากกว่าโค้ดที่สร้างขึ้นเช่นคุณสามารถเปลี่ยนประเภทของ 20 เป็น 20.0 หรือประเภท 2.2 เป็น 2 ได้หรือไม่
Qsigma

ฉันได้เพิ่มบริบทมากขึ้นตอนนี้วิธีแก้ปัญหาหนึ่งที่เป็นไปได้ในกรณีของฉันคือบังคับค่าให้เป็น 20.0 ในรหัสฉันทดสอบและแก้ไขปัญหา แต่ดูเหมือนข้อผิดพลาดเพราะจะเกิดขึ้นเฉพาะในสถานการณ์ที่เฉพาะเจาะจงเท่านั้น ลำดับที่
ngcbassman

คำตอบ:


9

ดูเหมือนว่าเป็นข้อบกพร่องสำหรับฉันและฉันสามารถยืนยันพฤติกรรมที่ทำให้งงนี้ใน:

10.2.14-MariaDB

ถ้าเป็นไปได้คุณสามารถแปลงค่าจำนวนเต็มเป็นสองเท่า:

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

หรือตรวจสอบให้แน่ใจว่าคุณมีค่าสองเท่าก่อน

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'),)

ข้อผิดพลาดมากพอที่จะหายไปถ้ามีการย้ายโมฆะครั้งแรกหรือครั้งสุดท้าย:

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)

เพิ่มโมฆะกลางในการรักษาข้อผิดพลาดอื่น ๆ :

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
อยากรู้อยากเห็นมีปัญหาหากคุณระบุไม่เป็น20 020ลักษณะการทำงานจะเหมือนกันใน MariaDB 10.2และใน MySQL 8.0 ดูเหมือนความยาวของตัวอักษรจะมีผลต่อประเภทของคอลัมน์ที่รวมกันมาก ไม่ว่าในกรณีใด ๆ นี่เป็นข้อผิดพลาดในหนังสือของฉัน
Andriy M

1
ฉันเห็นปัญหาใน MySQL 8.0 แม้ไม่มีโมฆะ (แม้ว่าจะใช้ได้ใน MariaDB 10.2) นอกจากนี้ยังมีความแตกต่างของขนาดคอลัมน์ถ้าคุณใส่ศูนย์นำหน้าหรือการคำนวณ
Mick O'Hea

5

Bug MDEV-15999

Bug MDEV-15999ยื่นโดยdbdemonรายงานสิ่งนี้ มันได้รับการแก้ไขใน 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 |
+--------------------------+

ซึ่งดูเหมือนจะปูทางสำหรับปัญหานี้


SELECT cast(20 as signed) UNION SELECT null UNION SELECT 2.2 ;สร้างผลลัพธ์ที่ผิดเดียวกัน (9.9) แต่ถ้าเราใช้ "ไม่มีการคาดเดา" มันก็ใช้ได้ดี ไปคิด ...
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ที่จะถือ คุณสามารถเห็นสิ่งนี้ได้โดยการเปลี่ยนส่วนคำสั่งที่เลือกล่าสุดเป็นCAST(2.2 AS DECIMAL(10,2))ซึ่งให้20.0เป็นแถวแรก (เรียกใช้โดยนัยCAST(20 AS DECIMAL(10,2)))
IMSoP

1
สิ่งที่แปลกคือมันไม่ได้เป็นเพียง basing 2.2ประเภทข้อมูลบน ถ้าคุณพยายามที่select 20000 union select null union select 2.22คุณจะได้รับใน9999.99 decimal(6,2)ตัวเลขหนึ่งหลักสั้นเกินไปที่จะเก็บค่าไว้
Mick O'Hea

1
@ มิคใช่ ถ้าคุณโยนNULLค่าตรงกลางมันจะคำนวณความแม่นยำ 1 สั้น
Salman
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.