2014-11-20 2 views
0

У меня есть несколько столбцов, которые имеют значения либо в дробных строках (например, 6 11/32), либо в виде десятичных знаков (1.5). Есть ли вызов CAST или CONVERT, который может конвертировать их последовательно десятичным знакам?Преобразование дробной строки в десятичную.

Ошибка:

Msg 8114, уровень 16, состояние 5, строка 1 Ошибка преобразования типа данных VARCHAR в числовой.

Могу ли я избежать какого-либо разбора?

Спасибо!

P.S. Я работаю в SQL Server Management Studio 2012.

ответ

2
CREATE FUNCTION ufn_ConvertToNumber(@STR VARCHAR(50)) 
RETURNS decimal(18,10) 
AS 
BEGIN 
     DECLARE @L VARCHAR(50) = '' 
     DECLARE @A DECIMAL(18,10) = 0 
     SET @STR = LTRIM(RTRIM(@STR)); -- Remove extra spaces 
     IF ISNUMERIC(@STR) > 0 SET @A = CONVERT(DECIMAL(18,10), @STR) -- Check to see if already real number 
     IF CHARINDEX(' ',@STR,0) > 0 
     BEGIN 
      SET @L = SUBSTRING(@STR,1,CHARINDEX(' ',@STR,0) - 1) 
      SET @STR = SUBSTRING(@STR,CHARINDEX(' ',@STR,0) + 1 ,50) 
      SET @A = CONVERT(DECIMAL(18,10), @L) 
     END 
     IF CHARINDEX('/',@STR,0) > 0 
     BEGIN 
      SET @L = SUBSTRING(@STR,1,CHARINDEX('/',@STR,0) - 1) 
      SET @STR = SUBSTRING(@STR,CHARINDEX('/',@STR,0) + 1 ,50) 
      SET @A = @A + (CONVERT(DECIMAL(18,10), @L)/CONVERT(DECIMAL(18,10), @STR) ) 
     END 
     RETURN @A 
END 
GO 

Затем к нему доступ через выберите dbo.ufn_ConvertToNumber («5 9/5»)

+0

Немного более прочный, чем мой. +1 –

0

Мне неизвестно любая система базы данных, или структура кода в этом отношении, поддерживающие строки, такие как 6 11/32 изначально. Лучше всего добавить столбец в соответствующую таблицу и денормализовать фактическое значение там со сценарием или создать над ним верх над этим, что делает это автоматически. Однако это потребует некоторого сложного кода, и, вероятно, это не совсем так, как в SQL.

+0

Excel поддерживает фракцию :) –

+0

Хотя Я когда-то кажется, что некоторые плохие веб-приложения используют Excel в качестве своего уровня данных, я бы точно не назвал его «системой базы данных»;) Excel - это инструмент конечного пользователя, поэтому имеет смысл поддерживать нотации конечных пользователей. –

+0

Класс фракций часто задается как упражнение в начальных классах программирования ООП. Это довольно легко сделать, и его можно легко найти, ища «класс фракции C#» или «класс Java Fraction». –

1

Вам нужно будет разобрать. Как говорит Нильс, это не очень хорошая идея; но это можно сделать довольно просто с помощью скалярной функции T-SQL.

CREATE FUNCTION dbo.FracToDec (@frac VARCHAR(100)) 
RETURNS DECIMAL(14, 6) 
AS 
    BEGIN 
     RETURN CASE 
      WHEN @frac LIKE '% %/%' 
       THEN CAST(LEFT(@frac, CHARINDEX(' ', @frac, 1) -1) AS DECIMAL(14,6)) + 
        (CAST(SUBSTRING(@frac, CHARINDEX(' ', @frac, 1) + 1, CHARINDEX('/', @frac, 1)-CHARINDEX(' ',@frac,1)-1) AS DECIMAL(14,6)) 
        /CAST(RIGHT(@frac, LEN(@frac) - CHARINDEX('/', @frac, 1)) AS DECIMAL(14,6))) 
      WHEN @frac LIKE '%/%' 
       THEN CAST(LEFT(@frac, CHARINDEX('/', @frac, 1) - 1) AS DECIMAL(14,6))/CAST(RIGHT(@frac, LEN(@frac) - CHARINDEX('/', @frac, 1)) AS DECIMAL(14,6)) 
      ELSE 
       CAST(@frac AS DECIMAL(14,6)) 
      END 
    END 
GO 

-- Test cases 
SELECT dbo.FracToDec('22/7'), dbo.fracToDec('3.117'), dbo.fracToDec('7 3/4') 

-- Output 
-- 3.142857 3.117000 7.750000 

Обратите внимание, что это не сработает, если содержимое прошло на самом деле не соответствуют формам «мм/пп», «хх мм/пп» или реальный десятичное.

1

А вот решение без функций и хранимых процедур - просто для удовольствия. Во-первых, вы должны создать новый столбец (я называю это decimal), а затем заполнить его значения, преобразованных из исходного столбца смешанного формата (так называемого inconsistent) с помощью следующего запроса:

UPDATE "my_table" 
SET  "decimals" = CASE WHEN CHARINDEX('/', "inconsistent") > 0 
          THEN CAST(CASE WHEN CHARINDEX(' ', 
                 RTRIM(LTRIM("inconsistent"))) > 0 
             THEN LEFT(RTRIM(LTRIM("inconsistent")), 
                CHARINDEX(' ', 
                  RTRIM(LTRIM("inconsistent"))) 
                - 1) 
             ELSE '0' 
            END AS FLOAT) 
           + CAST(SUBSTRING(RTRIM(LTRIM("inconsistent")), 
               CHARINDEX(' ', 
                  RTRIM(LTRIM("inconsistent"))) 
               + 1, 
               CHARINDEX('/', 
                  RTRIM(LTRIM("inconsistent"))) 
               - 1 - CHARINDEX(' ', 
                   RTRIM(LTRIM("inconsistent")))) AS FLOAT) 
          /CAST(RIGHT(RTRIM(LTRIM("inconsistent")), 
              LEN(RTRIM(LTRIM("inconsistent"))) 
              - CHARINDEX('/', 
                 RTRIM(LTRIM("inconsistent")))) AS FLOAT) 
          ELSE CAST(RTRIM(LTRIM("inconsistent")) AS FLOAT) 
        END 
+0

kudos для нечитаемости :) Почему же обратные запросы? –

+0

И я на самом деле думал, что кто-то найдет его изящным :)) О, жестокий мир! :)) Анекдоты, конечно же! Что касается обратных шагов: привык к MySQL (в точном соответствии с PhpMyAdmin), нуждающимся в тех, кто правильно справляется с именами в базе данных/таблице/столбцах (не уверен, что это сработало без них - возможно, это было бы - но у меня была такая привычка как-то) , Поймите, что речь идет о MS SQL, но старые привычки умеют тяжело (плюс все равно должны работать). Некоторая история здесь для ya :) –

+1

Собственно, backquotes do * not * работают в MS SQL, просто попробовали. Либо «двойные кавычки», либо [скобки]. И MS SQL не имеет конструкции 'IF()'; это 'CASE WHEN cond THEN val1 ELSE val2 END'.Извините :( –

Смежные вопросы