Tavana tamsayı dökümüyle tamsayı sorunu (ondalık)


10

Bu senaryo var, MySQL en büyük ondalık değeri alıyor ve diğer değerleri buna atmaya çalışır gibi görünüyor.

Sorun, bu sorgu, en azından bu düzeyde, bu kod üzerinde denetime sahip değil, bu nedenle bir dış kitaplık tarafından oluşturulur olmasıdır. Bunu nasıl düzeltebileceğine dair bir fikrin var mı?

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

Beklenen Sonuç

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

Daha fazla bağlam ekleyerek , toplu değişiklikleri, özellikle yöntem bağlamı.BulkSaveChanges (); kaydetmek için bir kütüphane http://entityframework-extensions.net/ bir Entity Framework 6 kullanıyorum , bu kütüphane "select union" kullanarak sorgular oluşturur .


1
20 her nasılsa 9.9 olur ?! Bu doğru görünmüyor.
dbdemon

2
DBFiddle'daki MySQL 8.0 aynı saçmalığı gösterir: dbfiddle.uk/…
dezso

Daha da kafa karıştırıcı ... iyi SELECT 20 UNION SELECT null UNION SELECT 40 UNION SELECT 4.3;çalışıyor
Evan Carroll

Pls ihtiyacınız çıktıyı gönderir. Ayrıca, oluşturulan kod üzerinde ne kadar kontrole sahip olursunuz: örneğin, 20'den 20.0'a veya 2.2'den 2'ye türünü değiştirebilirsiniz?
Qsigma

Şimdi daha fazla bağlam ekledim, benim durumumda olası bir çözüm değeri kodda 20.0 zorlamak için, ben test ve sorunu düzeltmek, ama bir hata gibi görünüyor çünkü sadece null dahil belirli senaryoda olur bu sipariş.
ngcbassman

Yanıtlar:


9

Bana bir hata gibi görünüyor ve ben bu şaşırtıcı davranış teyit edebilir:

10.2.14-MariaDB

Mümkünse tamsayı değerini bir çifte atayabilirsiniz:

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

veya önce çift değere sahip olduğunuzdan emin olun:

SELECT 2.2 UNION SELECT null UNION SELECT 22;

@Evan Carroll'ın cevabındaki yorumları okuduktan sonra daha fazla gözlem

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

Tamam, int değerlerini kullanmak hata üretmiyor gibi görünüyor.

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

HATA: Çıktı ondalık gibi görünüyor (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    |       |
+-------+--------------+------+-----+---------+-------+

Hata, komut satırı arabiriminde izole edilmez, python2-mysql-1.3.12-1.fc27.x86_64 için de vardır:

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

Boş olarak ilk veya son taşınırsa hata kaybolur:

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

+------+
| NULL |
+------+
| NULL |
| 20.0 |
| 9.0  |
+------+

Önce null yerleştirilirse, sonuç türü ondalıktır (20,1). Null değeri yerleştirilirse, sonuçta elde edilen son ondalık sayıdır (3,1)

Birliğe başka bir bacak eklenirse hata da kaybolur:

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

sonuç türü ondalık (20,1)

ortada başka bir null eklenmesi hatayı korur:

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

Ancak başlangıçta bir boş değer eklenmesi sorunu giderir:

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

Beklendiği gibi ilk değeri ondalık (3,1) değerine çevirir.

Son olarak, açıkça ondalık (2,1) değerine çevirmek aynı hatayı ancak uyarı ile üretir:

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 içindeki bir sözdizimi hatasıdır. Ondalık bir ondalık sayı yerine çalışır:SELECT CAST(20 AS DECIMAL) AS x UNION SELECT NULL UNION SELECT 2.2;
Qsigma

4
Bunu MariaDB Jira'da
dbdemon

1
Sorun MariaDB 10.3'te giderilmiş gibi görünüyor. (Sadece 10.3.6 ile test ettim ve 10.3.1'in de çalışması gerekiyor.)
dbdemon

2
20As olarak belirtirseniz merakla sorun olmaz 020. Davranış MariaDB 10.2 ve MySQL 8.0 ile aynıdır . Literal uzunluğunun birleştirilmiş sütunun türünü etkilediği gibi görünüyor . Her durumda, bu kesinlikle kitabımda bir hata.
Andriy M

1
(Bu MariaDB 10.2 Tamam çalışıyor rağmen) bile null olmadan MySQL 8.0 sorunu görüyorum . Önde gelen bir sıfır veya bir hesaplama
eklerseniz

5

Hata MDEV-15999

Dbdemon tarafından dosyalanan Bug MDEV-15999 bunu bildirdi. O zamandan beri 10.3.1'de düzeltildi.

Garip MySQL / MariaDB doğası

Dokümanlardan,

İlk ifadedeki sütun adları, SELECTdöndürülen sonuçların sütun adları olarak kullanılır. Her SELECTifadenin karşılık gelen konumlarında listelenen seçilen sütunlar aynı veri türüne sahip olmalıdır. (Örneğin, ilk ifade tarafından seçilen ilk sütun, diğer deyimler tarafından seçilen ilk sütunla aynı türde olmalıdır.)

Karşılık gelen SELECTsütunların veri türleri eşleşmezse, sonuçtaki sütunların türleri ve uzunlukları UNIONtüm SELECTifadeler tarafından alınan değerleri dikkate alır .

Bu durumda, uzlaşırlar decimalve integertamsayıyı decimaliçeremeyen bir değere yükselterek. Bunun korkunç olduğunu biliyorum, ama aynı derecede korkunç bu sessizce böyle davranıyor.

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

Bu da bu sorunun yolunu açıyor gibi görünüyor.


SELECT cast(20 as signed) UNION SELECT null UNION SELECT 2.2 ;aynı (9.9) yanlış sonucu verir. Ama "ungngned" kullanırsak, her şey yolunda gidiyor. Git şekil ...
ypercubeᵀᴹ

SELECT -20 UNION SELECT null UNION SELECT 2.2 ;olduğu gibi de düzgün çalışıyorSELECT 20. UNION SELECT null UNION SELECT 2.2 ;
Andriy M

3
Buradaki temel öngörü, MySQL'in tutabilen 2.2ancak tutulamayacak kadar dar bir veri türü seçmesidir 20. Sen değiştirerek bunu görebilirsiniz son select maddesini CAST(2.2 AS DECIMAL(10,2))verir, 20.0ilk satırda (örtük çalışan olarak CAST(20 AS DECIMAL(10,2))).
IMSoP

1
Garip olan şey sadece veri türünü dayandırarak yer etmedi 2.2. Eğer denerseniz select 20000 union select null union select 2.22Alacağınız 9999.99bir de, decimal(6,2). Her zaman bir rakam değeri tutmak için çok kısa
Mick O'Hea

1
@Evet evet. NULLOrtadaki değeri atarsanız , hassaslığı 1 kısa olarak hesaplar.
Salman A
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.