2011-02-15 76 views
2

我正在導入到SQL Server(2008 R2)範圍內的原始源表中的一些髒的輸入數據。輸入提供者定義爲十進制(9,2)或十進制(4,2)的字段以字符串形式出現,但字符串並不總是符合數據定義(請參閱圖!)。StringToDecimal功能 - T-SQL問題

我們進口從平面文件數據到原始表,然後塗抹一些轉換腳本的「乾淨」的數據插入到分配給列的正確數據類型的表。

例如:

raw_table 
TotalAmount varchar(12) 

clean_table 
TotalAmount decimal(9,2) 

現在,我的問題是這樣的。如果我想要做一些這方面的「基本」的清理,我想要做它的線沿線的一個功能:

CREATE FUNCTION [dbo].[StringToDecimal] 
(
    @conversionString VARCHAR(12) 
) 
RETURNS DECIMAL(9,2) 
AS 
BEGIN 

    DECLARE @rsp DECIMAL(9,2) 

    IF ISNUMERIC(LTRIM(RTRIM(REPLACE(@conversionString,' ','')))) = 1 
     BEGIN 
      SET @rsp = ISNULL(CONVERT(decimal(17,6), NULLIF(LTRIM(RTRIM(REPLACE(@conversionString,' ',''))),'')), 0) 
     END 
    ELSE 
     BEGIN 
      SET @rsp = 0 -- or we can return NULL here 
     END 

    RETURN @rsp 
END 

然而,一個人如何能去在這個組合支持各種尺寸的小數?有沒有辦法參數化響應類型?我認爲只是返回一個我們通常看到的最大尺寸的小數,然後在另一端再次轉換它,但是,您遇到算術溢出問題。

希望任何想法/洞察解決這一個!

+0

什麼是髒數據實際上是什麼樣子?你是否必須在數據庫中執行此操作?外部的Perl/Python /無論腳本或.NET程序會更容易嗎?你能否通過參數化響應類型來闡明你的意思;你正在轉換_to_不同的數據類型? – Pondlife 2011-02-15 15:50:00

回答

2

有沒有辦法參數化響應類型?

它比你想象的更簡單。只需返回VARCHAR並從VARCHAR轉換爲十進制(x,y)即可。你甚至都不需要投 - 你可以直接(只要其持有有效的十進制數據)分配一個VARCHAR到小數列/變量。

我將創建2個功能來代替。 StringToDecimal2執行實際轉換,但返回6個「錯誤代碼」之一。你可以用它來檢查why一個字符串是否無效。或者使用包裝器dbo.StringToDecimal,它只是將無效代碼變成NULL。

CREATE FUNCTION [dbo].[StringToDecimal2] 
(
    @conversionString VARCHAR(12), 
    @precision int, -- total digits 
    @scale int -- after decimal point 
) 
RETURNS VARCHAR(100) 
AS 
BEGIN 
    -- remove spaces, we'll allow this error. no need to trim 
    set @conversionString = REPLACE(@conversionString,' ','') 
    -- note: 1,234.56 (thousands separated) will be invalid, so will 1,234,56 (European decimals) 
    -- well, ok, let's clean up the thousands separators. BUT! It will incorrectly scale European decimals 
    set @conversionString = REPLACE(@conversionString,',','') 

    -- we don't support scientific notation either, so 1e4 (10,000) is out 

    if @conversionString like '%[^0-9.+-]%' return 'INVALID1' -- only digits and decimal are valid (plus +-) 
    if @conversionString like '%.%.%' return 'INVALID2' -- too many decimals 
    if @conversionString like '_%[+-]%' return 'INVALID3' -- +- symbol not in the first position 
    if @conversionString like '[.+-]' return 'INVALID4' -- a single character from "+-." 
    if @conversionString like '[+-].' return 'INVALID5' -- symbol and decimal only 

    -- add a decimal place so it is easier to work with below 
    if @conversionString not like '%.%' 
     set @conversionString = @conversionString + '.' 

    -- allow decimal places to go only as far as scale 
    set @conversionString = left(@conversionString, charindex('.', @conversionString)[email protected]) 

    -- ensure the data is within precision number of digits in total 
    if charindex('.', @conversionString) > @precision - @scale + 1 
     return 'INVALID6' -- too many digits before decimal 

    RETURN @conversionString 
END 
GO 

CREATE FUNCTION [dbo].[StringToDecimal] 
(
    @conversionString VARCHAR(12), 
    @precision int, -- total digits 
    @scale int -- after decimal point 
) 
RETURNS VARCHAR(100) 
AS 
BEGIN 
RETURN case when [dbo].[StringToDecimal2](@conversionString, @precision, @scale) like 'INVALID%' 
then null else [dbo].[StringToDecimal2](@conversionString, @precision, @scale) end 
END 
GO 

一些測試:

select [dbo].[StringToDecimal2]('12342342', 9,2) 

select convert(decimal(9,2),[dbo].[StringToDecimal]('1234234', 9,2)) 
select convert(decimal(9,2),[dbo].[StringToDecimal]('12342342', 9,2)) 
select convert(decimal(9,2),[dbo].[StringToDecimal]('123423.3333', 9,2)) 
select convert(decimal(20,10),[dbo].[StringToDecimal]('123423sd.3333', 20,10)) 
select convert(decimal(20,10),[dbo].[StringToDecimal]('123423sd..3333', 20,10)) 
select convert(decimal(20,10),[dbo].[StringToDecimal]('-123423.3333', 20,10)) 
select convert(decimal(20,10),[dbo].[StringToDecimal]('+123423..3333', 20,10)) 
0

感謝您的額外信息。這聽起來像你有三個步驟:

  1. 取下字符串不是數字或小數點(?你有沒有過多個點在一個字符串)
  2. 轉換爲所有字符(9,5)或(4,1)爲適當的(你是怎麼決定的?有沒有四捨五入?確實10X.781成爲10.78100或10.7或10.8?)基於點
  3. 插入/更新的最終價值的地方

1,我會立即避免TSQL,並考慮外部腳本或CLR過程。一個CLR函數可以解析,但你仍然有返回不同數據類型的問題。

因爲這似乎是某種ETL任務的,在我的環境,我可能會實現它在SSIS包的腳本組件。該組件將執行解析並將乾淨的數據發送到不同的輸出以供進一步處理。如果這是一次性任務,我將使用Python腳本來解析輸入數據並生成INSERT或UPDATE語句。

我不知道是否有這些解決方案都適合你,但也許它會給你一些想法。你應該避免使用ISNUMERIC()函數。搜索這個網站或谷歌找到一些它認爲是數字的「奇怪」輸入。