Почему SQL Server возвращает числа, такие как 0,759999, а MySQL возвращает 0,76? - PullRequest
1 голос
/ 11 октября 2019

У меня есть таблица базы данных SQL Server, которая имеет три столбца. Например, одно из значений в этом столбце может быть 0,76. Этот столбец данных с именем «paramvalue» определяется как реальный. Когда я использую команду fetchall () модуля pyodbc, я получаю число, подобное 0,7599999904632568 вместо 0,76. Я использую Visual Studio 2017 и инструменты Python для Visual Studio. Я также попробовал модуль pypyodbc, но получил ту же проблему.

Таблица состоит из трех столбцов и определяется следующим образом:

pconfig_id [int] IDENTITY (41,1)NOT NULL, paramname nvarchar NOT NULL, paramvalue [real] NULL

import pyodbc
cnxn = pyodbc.connect('DRIVER={SQL 
Server};SERVER=SERVERNAME;DATABASE=DBNAME;UID=USER;PWD=PASSWORD;Connect 
Timeout=15')
cursor = cnxn.cursor()
dict = {}
rows = cursor.execute("SELECT * FROM mytable")
for row in cursor.fetchall() :
    if not row[1] in dict.keys():
        dict[row[1]] = {}
        dict[row[1]][row[2]] = row[0]

В приведенном выше примере строка [2] для типичной строки имеет значение 0,7599999904632568 вместо 0,75, как и ожидалось.

Ответы [ 2 ]

2 голосов
/ 11 октября 2019

Это связано с округлением - тип Real является приблизительным.

https://docs.microsoft.com/en-us/sql/t-sql/data-types/float-and-real-transact-sql?view=sql-server-2017

1 голос
/ 15 октября 2019

Я должен упомянуть, что эта база данных была перенесена из базы данных MySQL, в этом случае тип был float. Когда я использовал модуль MySQLdb для извлечения данных из этой таблицы MySQL, он получил 0,76, как и ожидалось.

Это не проблема, вызванная pyodbc. Разница между MySQL и MSSQL заключается в том, что числа с плавающей запятой отображаются .

0,76 не является значением, которое может быть представлено точно как 32-разрядная с плавающей запятой ("одинарная точность "). Как этот сайт (и другие) скажет вам, наиболее точное представление этого числа - 7,599999904632568359375E-1, так что именно это хранят обе базы данных (внутренне представленные как 0x3F428F5C).

При получении значения MSSQL возвращает фактическое значение, которое было записано в базу данных. Вот почему он возвращается как 0,7599999904632568.

С другой стороны, MySQL возвращает самую короткую строку, представляющую значение с плавающей запятой , что приведет к заданному сохраненному значению . Как описано в документации :

F -> D преобразование выполняется с максимально возможной точностью, возвращая D как самую короткую строку, которая возвращает F при чтении обратно и округляется досамое близкое значение в собственном двоичном формате, как определено IEEE.

Итак, MySQL выполняет циклическое переключение 0,76, потому что это просто самое короткое значение, соответствующее значению с плавающей запятой, внутреннее представление которого равно 0x3F428F5C. Это можно проиллюстрировать, протестировав число, очень близкое к 0,76, но не совсем равное:

is_mssql = (cnxn.getinfo(pyodbc.SQL_DRIVER_NAME) == 'msodbcsql17.dll')

crsr = cnxn.cursor()

test_value = '0.7599999905'
if is_mssql:
    crsr.execute("CREATE TABLE #foo (x real)")
    crsr.execute(f"INSERT INTO #foo (x) VALUES ('{test_value}')")
    result = crsr.execute("SELECT x FROM #foo").fetchval()
else:
    crsr.execute("CREATE TEMPORARY TABLE foo (x float(23))")
    crsr.execute(f"INSERT INTO foo (x) VALUES ('{test_value}')")
    result = crsr.execute("SELECT x FROM foo").fetchval()
print(f'{"MSSQL" if is_mssql else "MySQL"} returned {result}')

Несмотря на то, что 0,7599999905 является «фактическим» значением, MySQL по-прежнему возвращает 0,76, а MSSQL по-прежнему возвращает 0,7599999904632568.

...