none
Access, неверные данные столбца RRS feed

  • Вопрос

  • Добрый вечер! Сегодня выявил ошибку в своем приложении. В базе данных есть числовое поле "Двойное с плавающей точкой". Программа сохраняет туда данные даты в виде OADate. Получается число - "44233,7245479977". Проблема появилась на тех компьютерах, где стоит формат времени "Английский (США)". Как только выставляется этот формат времени в базе данных убирается запятая у числа. И программа получает уже число, которое не возможно перевести обратно в дату. Самое интересное, при смене формата времени в числе сразу появляется запятая. То есть она как бы есть...

    Как решить эту проблему?

    6 февраля 2021 г. 18:50

Ответы

  • Значения данных в запрос в любом случае нужно вставлять параметрами, не важно, строка это это или число. Да, если значение числовое, его нужно перевести в число с помощью той же культуры, с помощью которой его преобразовали в текст на сервере. Лучше использовать CultureInfo.InvariantCulture вместо ru-RU, так как загрузка русского языка может потребовать дополнительных ресурсов на не русскоязычной ОС, или вообще упасть с ошибкой, если какого-то пакета не хватает. Правильный подход именно такой.
    • Предложено в качестве ответа Maksim MarinovMicrosoft contingent staff, Moderator 9 февраля 2021 г. 9:19
    • Отменено предложение в качестве ответа Siompc 9 февраля 2021 г. 9:29
    • Помечено в качестве ответа Siompc 10 февраля 2021 г. 19:12
    9 февраля 2021 г. 3:34

Все ответы

  • В формате США запятая - это разделитель тысяч, а разделитель десятичной части - точка. Сам Access эти форматы обрабатывает отлично. Ошибка где-то при преобразовании между текстом и числом в вашем коде.
    8 февраля 2021 г. 3:45
  • Я понял. Значение OADate получает сервер. И отправляет его клиенту. Отправляет число с запятой в виде строки. Access сохраняет его, соответственно, как число не дробное.

    Получается при добавлении или обновлении данных таблицы нужно всегда проверять, является ли столбец типа DBTYPE_R8 и потом проверять регион, делать замену точки на запятую или обратно, затем только сохранять... Более простого способа нет? Например указать в таблице БД принудительно разделитель числа. Или еще как-нибудь?

    И как проверить программно текущий разделитель дробных чисел?

    8 февраля 2021 г. 9:44
  • Получить разделитель нашел как:

    Dim tmp = Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator

    Но вот костыль с Replace это не есть хорошо...

    8 февраля 2021 г. 9:51
  • Этот подход в корне неверен. Access при использовании и ODBC, и OleDB умеет принимать сразу числовое значение нужного типа, никакого преобразования в строку вообще не должно фигурировать. Либо, если оно есть, оно должно осуществляться с предопределенным CultureInfo, так что бы разделитель был известен.
    8 февраля 2021 г. 10:18
  • Можно попробовать изменить тип данных на строку и сохранять в базу с запятой, а в самом приложении изменить CultureInfo при запуске, что бы переводило обратно В дату без ошибок. Но вот поле типа строка будет занимать значительно больше, чем число...
    8 февраля 2021 г. 10:19
  • Нет, это еще хуже. Надо передавать правильный CultureInfo в тот метод, который осуществяляет преобразование (Double.Parse, Double.ToString, ...), а не устанавливать глобально. В каких-то других методах может понадобиться именно системная культура.
    8 февраля 2021 г. 11:05
  • Ох как много кода придется переписать...

    А что если сохранить сперва системную культуру в переменную, а потом при запуске приложения в Application.xaml изменить ее:

    Private Sub Application_Startup(sender As Object, e As StartupEventArgs)
            Thread.CurrentThread.CurrentCulture = New Globalization.CultureInfo("ru-RU")
    End Sub
    И если понадобится системная - брать ее из переменной? Такой способ сработает? И какие будут в дальнейшем подводные камни?

    8 февраля 2021 г. 13:39
  • Это в каждом новом потоке нужно указывать новую культуру? 0_0 Да тут теперь месяц сидеть нужно, что бы все переделать

    Опять же не получается. Вот приходит с сервера строка данных. У сервера разделитель запятая. В строке путь будет "123,45". Я выполняю запрос "INSERT INTO `t1` (`Число`) VALUES ('" & SERVDATA & "')"

    В базе в итоге число "12345", потому что база опирается на системную культуру... Получается перед запросом нужно сперва преобразовать строку в число согласно культуре указанной в потоке, затем обратно в строку и выполнить запрос? Это жестоко

    • Изменено Siompc 8 февраля 2021 г. 13:54
    8 февраля 2021 г. 13:46
  • Этот код нужно выкинуть и переписать по нормальному. Здесь не преобразование в строку нужно, а использование параметризованных запросов: https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/configuring-parameters-and-parameter-data-types

    Если запрос будет "INSERT INTO `t1` (`Число`) VALUES ('?')", а значение передается через параметр (cmd.Parameters.AdddWithValue("param",SERVDATA)),то проблемы со знаками разделителей не будет.

    Как я уже написал, Access поддерживает передачу числовых значений напрямую, без преобразования в строку, нужно лишь правильно ей воспользоваться

    8 февраля 2021 г. 16:13
  • Прошу мнение экспертов по костылю. Пока что ничего умнее не придумал. Сейчас данные идут только с сервера, на стороне клиента при сохранении в БД проверяется тип столбца и "правильно" составляется запрос...

    If TypeColown(TableName, entryData.Key) = "DBTYPE_R8" Then
                            tmpQueryData2 &= ",'" & Double.Parse(entryData.Value, New Globalization.CultureInfo("ru-RU")).ToString() & "'"
                        Else
                            tmpQueryData2 &= ",'" & entryData.Value & "'"
    End If

    А потом уже все потоки используют системную Culture для работы с данными



    • Изменено Siompc 8 февраля 2021 г. 16:25 я тупой
    8 февраля 2021 г. 16:23
  • Странно, не видел этого ответа ранее. Только сейчас загрузился. Спасибо за информацию.

    Наверное так тоже не получится. Потому что передать в качестве параметров нужно именно Double

    Дело в том, что данные с сервера уже приходят в виде строки. Они могут быть как текстом, так и числом. Клиент просто принимает строку и разбивает ее на множество пар "заголовок-значение". Поэтому этот текст в любом случае придется сперва правильно перевести в число, а потом передавать в качестве параметра. Так ведь?

    8 февраля 2021 г. 22:21
  • Значения данных в запрос в любом случае нужно вставлять параметрами, не важно, строка это это или число. Да, если значение числовое, его нужно перевести в число с помощью той же культуры, с помощью которой его преобразовали в текст на сервере. Лучше использовать CultureInfo.InvariantCulture вместо ru-RU, так как загрузка русского языка может потребовать дополнительных ресурсов на не русскоязычной ОС, или вообще упасть с ошибкой, если какого-то пакета не хватает. Правильный подход именно такой.
    • Предложено в качестве ответа Maksim MarinovMicrosoft contingent staff, Moderator 9 февраля 2021 г. 9:19
    • Отменено предложение в качестве ответа Siompc 9 февраля 2021 г. 9:29
    • Помечено в качестве ответа Siompc 10 февраля 2021 г. 19:12
    9 февраля 2021 г. 3:34
  • Спасибо за помощь! Будем использовать полученные знания! :)
    9 февраля 2021 г. 9:29
  • Можно еще вопрос? Что бы использовать CultureInfo.InvariantCulture для преобразования числа, сервер тоже должен его отправлять в CultureInfo.InvariantCulture? Если сервер использует ru-RU, тогда ничего не получится?
    10 февраля 2021 г. 14:46
  • Попробуйте сами, и увидите, получиться или нет. InvariantCulture - это почти то же самое, что en-us, только для нее гарантируется, что она всегда доступна и ее параметры не зависят от настроек системы и обновлений. То есть для передачи данных по сети это как раз то, что нужно. С использованием другой культуры может получиться, но до поры до времени.
    10 февраля 2021 г. 16:40