TSQL для определения длинных значений Float - PullRequest
1 голос
/ 09 июля 2009

Я имею дело с унаследованной системой, где мне нужно идентифицировать некоторые неверные записи на основе столбца с типом данных Float.

Хорошие записи имеют значение ...

1
2
1.01
2.01

Плохие записи - это что-то типа ..

1.009999999999999
2.003423785643000
3.009999990463260

Я пробовал несколько операторов выбора, в которых я конвертировал в десятичную и приводил к типу varchar и использовал функцию LEN (), но это не сработало, поскольку хорошие записи, которые равны 1.01, становятся 1.0100000000000000

- Изменить Сейчас я немного ближе, поскольку обнаружил, что могу это сделать (Вес * 100), и все хорошие записи становятся целыми числами, такими как 101,201,265,301,500 и т. Д. и плохие, такие как 2.00999999046326, становятся 200.999999046326

Ответы [ 6 ]

1 голос
/ 09 июля 2009

Задумывались ли вы об использовании интеграции CLR и использовании .net для обработки проверки см. эту ссылку Основы использования сборки .NET в MS SQL - Пользовательские функции

в основном вы используете методы .net как пользовательскую функцию для проверки; .NET лучше работает с числами.

1 голос
/ 09 июля 2009

как то так, может быть? (конечно, настройте точность / масштаб в предложении where)

select val from mytable WHERE CONVERT(decimal(5,2), val) <> val
1 голос
/ 09 июля 2009

Это работает на моей БД SQL Server 2005:

select len(cast(cast(1.01 as float) as varchar))

Результат:

4

На самом деле, он даже позволяет мне пропустить явное приведение типа varchar, если я хочу:

select len(cast(1.011 as float))

Результат:

5


Обновление: Прежде всего, мне все еще нужен был приведение к varchar. Думать иначе было неправильно. Тем не менее, у меня было все это работает с использованием строк и собирался опубликовать как. Затем вы обновили мултиплинг на 100 и поняли, что это путь. Итак, вот мой код для тестирования обоих способов:

declare @test table ( c float)
insert into @test 
    select * from 
        ( select 14.0100002288818 as c union
          select 1.01 union
          select 2.00999999046326 union
          select 14.01
         ) t

select c,
  case when c = cast(cast(c as varchar) as float) AND LEN(cast(c as varchar))<=5 then 1 else 0 end,
  case when c * 100 = floor(c * 100) then 1 else 0 end
from @test
0 голосов
/ 09 июля 2009

Запустите это:

DECLARE @d FLOAT;
SET @d = 1.23;
SELECT ABS(CAST(@d AS DECIMAL(10,2)) - CAST(@d AS DECIMAL(15,8)));
SET @d = 1.230000098;
SELECT ABS(CAST(@d AS DECIMAL(10,2)) - CAST(@d AS DECIMAL(15,8)));

Используйте некоторый порог, такой как:

ABS(CAST(@d AS DECIMAL(10,2)) - CAST(@d AS DECIMAL(15,8)))<0.00001
0 голосов
/ 09 июля 2009

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

это также зависит от того, где вы запускаете что-то вроде следующего

select convert(float,1.33)

в анализаторе запросов вывод 1.3300000000000001 в SSMS вывод составляет 1,33

при преобразовании в десятичное значение необходимо указать масштаб и точность

так что если вы сделаете

select convert(decimal(10,6),convert(float,1.33))

вы получите 1.330000, потому что вы указали шкалу 6

вы могли бы сделать что-то вроде этого, где после преобразования в десятичное число вы отбросите конечные 0s

select replace(rtrim(replace(convert(varchar(30),
    (convert(decimal(10,6),convert(float,1.33)))),'0',' ')),' ','0')

для значения 3,00999999046326 вам нужна шкала не менее 14

select replace(rtrim(replace(convert(varchar(30),
    (convert(decimal(30,14),convert(float,3.00999999046326)))),'0',' ')),' ','0')
0 голосов
/ 09 июля 2009

Вы можете сделать что-то вроде этого:

SELECT * 
FROM YourTable
WHERE CAST(YourFloatField AS DECIMAL(15,2)) <> YourFloatField

Я предполагаю, что все, что "плохо", имеет более двух знаков после запятой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...