2014-03-24 100 views
0

我将字符数据存储在从数据文件导入的列中。字符数据表示一个整数值,但是..最后(最右边的)字符并不总是一个数字字符。我试图使用SQL表达式将字符数据转换为整数值,但它不起作用。SQL逻辑不起作用

我在下面显示了一条SQL语句的尝试,以及一个演示它不工作的测试用例。我的做法是从字符串中分离最右边的字符,进行适当的转换,然后将它们串起来并转换为整数。

问:如何修复我的SQL表达式以正确地转换它,或者可以使用什么SQL表达式来执行转换?

详情

字符串中的最右边的字符可以是在下面的“代码”列中的值中的一个。 “Digit”列显示由字符表示的实际整数值,“Sign”列显示整个字符串是否被解释为负值或正值。

例如,字符串值'023N'表示整数值+235。 (最右边的'N'字符表示数字值为5,带有正号)。字符串值'104}'表示整数值-1040。 (最右边的'}' charcacter表示数字值为'0',并且使整体值为负。)

下面是显示所需转换的表格。

Code Digit Sign 
'}' '0' - 
'J' '1' - 
'K' '2' - 
'L' '3' - 
'M' '4' - 
'N' '5' - 
'O' '6' - 
'P' '7' - 
'Q' '8' - 
'R' '9' - 
'{' '0' + 
'A' '1' + 
'B' '2' + 
'C' '3' + 
'D' '4' + 
'E' '5' + 
'F' '6' + 
'G' '7' + 
'H' '8' + 
'I' '9' + 

这里的例子值的表:

Create Table #Punch 
(
aa varchar(20) 
) 

Insert Into #Punch values ('046') 
Insert into #Punch values ('027') 
Insert into #Punch values ('004') 
Insert into #Punch values ('020') 
Insert into #Punch values ('090') 

这是SQL语句不进行转换,但它不具有只是普通的数字字符的字符串正常工作。 (上面的示例表是应该被转换为整数值的其它字符串的例子。

该SQL语句的字符串046返回的184一个整数值,当我希望它返回46

问:为什么我的SQL语句的字符串'046'

 select 
     aa, Answervalue = 
     (cast(
    substring(aa, 1, len(aa)-1) + 
    case 
     when right(aa,1) in ('{','}','0') then '0'   
     when right(aa,1) between 'A' and 'I' then cast(ascii(right(aa,1))-64 as char(1)) 
     when right(aa,1) between 'J' and 'R' then cast(ascii(right(aa,1))-73 as char(1)) 
     else '' 
    end 
    as int) * 
    case 
    when right(aa,1) in ('{','0') or right(aa,1) between 'A' and 'I' then 1 
    when right(aa,1) in ('}') or right(aa,1) between 'J' and 'R' then -1 
    when aa in (aa) then aa 
    end) 
from 
(
select aa from #Punch 
) bb 

01返回的 184,而不是 46的整数值?

对于给定的插入值,“046”的结果为“184”。它应该是“46”。对于“004”,结果是“0”。它应该是“4”。除了这些问题,逻辑工作正常。如果列值aa是数字,并且值中没有代码\字符(例如{,A,N,B等),我想将其作为原始值。所以如果它是046那么值应该是46.

谢谢你提前!

+0

你需要描述你的逻辑,只是因为你的代码不会产生你期望的结果,我们无法猜测代码的逻辑部分失败 – 2014-03-24 18:09:44

+0

对于给定的插入值,“046”的结果为“184”。它应该是“46”。对于“004”,结果是“0”。它应该是“4”。 – ETLUser

+3

为什么?你只是说了你的期望,但现在你如何到达那里。 – 2014-03-24 18:18:01

回答

1

我无法编辑我原来的(我错过了你的关于数字点),所以这里要再次重申:

它看起来像你对我插错数据插入表中。

尝试:

Insert Into #Punch values ('04O') 
Insert into #Punch values ('02P') 
Insert into #Punch values ('00D') 
Insert into #Punch values ('02{') 
Insert into #Punch values ('09}') 

至于检查,看看是否值是数字,那是另一个问题。尝试使用:

 select 
     aa, Answervalue = CASE WHEN IsNumeric(aa) = 1 THEN aa ELSE 
     (cast(
    substring(aa, 1, len(aa)-1) + 
    case 
     when right(aa,1) in ('{','}','0') then '0'   
     when right(aa,1) between 'A' and 'I' then cast(ascii(right(aa,1))-64 as char(1)) 
     when right(aa,1) between 'J' and 'R' then cast(ascii(right(aa,1))-73 as char(1)) 
     else '' 
    end 
    as int) * 
    case 
    when right(aa,1) in ('{','0') or right(aa,1) between 'A' and 'I' then 1 
    when right(aa,1) in ('}') or right(aa,1) between 'J' and 'R' then -1 
    when aa in (aa) then aa 
    end) END 
from 
(
select aa from #Punch 
) bb 
1

它看起来像你的问题之一就是这条线:

when aa in (aa) then aa 

随着'046'值,最左边的两个字符'04',乘上'046',你会得到整数值184.

我会对最右边的字符进行一次测试,只有一个CASE表达式,而不是在多个CASE表达式中检查同一个事物,并执行多重折叠。

原来的陈述只是太多的工作来弄清楚它在做什么,用多个CASE表达式和乘法和CAST。

使用单个CASE表达式可以创建更直接的SQL语句;通过将返回表达式作为单个表达式来获得,即使它意味着重复一些类似的代码,也更容易解密。

对于SQL Server,一个有点冗长的表达会更容易破译,并且将使它更容易为读者了解什么表情是这样做的:

SELECT aa 
     , Answervalue = 
     CASE RIGHT(aa,1) 
     WHEN '{' THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),'0') AS INT) 
     WHEN 'A' THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),'1') AS INT) 
     WHEN 'B' THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),'2') AS INT) 
     WHEN 'C' THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),'3') AS INT) 
     WHEN 'D' THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),'4') AS INT) 
     WHEN 'E' THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),'5') AS INT) 
     WHEN 'F' THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),'6') AS INT) 
     WHEN 'G' THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),'7') AS INT) 
     WHEN 'H' THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),'8') AS INT) 
     WHEN 'I' THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),'9') AS INT) 
     WHEN '}' THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),'0') AS INT) * -1 
     WHEN 'J' THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),'1') AS INT) * -1 
     WHEN 'K' THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),'2') AS INT) * -1 
     WHEN 'L' THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),'3') AS INT) * -1 
     WHEN 'M' THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),'4') AS INT) * -1 
     WHEN 'N' THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),'5') AS INT) * -1 
     WHEN 'O' THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),'6') AS INT) * -1 
     WHEN 'P' THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),'7') AS INT) * -1 
     WHEN 'Q' THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),'8') AS INT) * -1 
     WHEN 'R' THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),'9') AS INT) * -1 
     ELSE CAST(aa AS INT) 
     END 
    FROM Punch# 

或者,你可以这样做这样的:

SELECT aa 
     , Answervalue = 
     CASE 
     WHEN RIGHT(aa,1) IN ('{') 
      THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),'0') AS INT) 
     WHEN RIGHT(aa,1) BETWEEN 'A' AND 'I' 
      THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),CAST(ASCII(RIGHT(aa,1))-64 AS CHAR(1))) AS INT) 
     WHEN RIGHT(aa,1) IN ('}') 
      THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),'0') AS INT) * -1 
     WHEN RIGHT(aa,1) BETWEEN 'J' AND 'R' 
      THEN CAST(CONCAT(LEFT(aa,LEN(aa)-1),CAST(ASCII(RIGHT(aa,1))-73 AS CHAR(1))) AS INT * -1 
     ELSE 
      CAST(aa AS INT) 
     END 
    FROM Punch# 

对于MySQL,这将是这个样子:

SELECT aa 
     , CASE 
     WHEN RIGHT(aa,1) IN ('{') 
      THEN CONCAT(LEFT(aa,CHAR_LENGTH(aa)-1),'0') + 0 
     WHEN RIGHT(aa,1) BETWEEN 'A' AND 'I' 
      THEN CONCAT(LEFT(aa,CHAR_LENGTH(aa)-1),CAST(ASCII(RIGHT(aa,1))-64 AS CHAR(1))) + 0 
     WHEN RIGHT(aa,1) IN ('}') 
      THEN CONCAT(LEFT(aa,CHAR_LENGTH(aa)-1),'0') * -1 + 0 
     WHEN RIGHT(aa,1) BETWEEN 'J' AND 'R' 
      THEN CONCAT(LEFT(aa,CHAR_LENGTH(aa)-1),CAST(ASCII(RIGHT(aa,1))-73 AS CHAR(1))) * -1 + 0 
     ELSE aa + 0 
     END AS Answervalue 
    FROM Punch# 

注意:在MySQL中,我们可以用CAST(x AS SIGNED)替换CAST(x AS INT),或者我们可以执行一个加法操作来导致隐式转换为数字。


我不完全适合从ASCII值中减去64或73。 (因为我还没有测试过,以确保所有字符集都能正常工作。)

我真的很想设置查找表,并使用外部联接操作。事情是这样的:

CREATE TABLE _convert_zoned_decimal 
(`zdigit` CHAR(1) NOT NULL PRIMARY KEY 
, `rdigit` CHAR(1) NOT NULL 
, `rsign` TINYINT NOT NULL 
); 

INSERT INTO _convert_zoned_decimal VALUES 
('}','0',-1),('J','1',-1),('K','2',-1),('L','3',-1),('M','4',-1) 
,('N','5',-1),('O','6',-1),('P','7',-1),('Q','8',-1),('R','9',-1) 
,('{','0',+1),('A','1',+1),('B','2',+1),('C','3',+1),('D','4',+1) 
,('E','5',+1),('F','6',+1),('G','7',+1),('H','8',+1),('I','9',+1) 
; 

与该表,我可以用一个外连接操作,并做了更换,这样的事情对MySQL:

1

我已经向您查询,并把它分成了零件看它的部分看看发生了什么。

SELECT aa 
     ,Answervalue = (CAST(SUBSTRING(aa, 1, LEN(aa) - 1) + CASE WHEN RIGHT(aa, 1) IN ('{', '}', '0') THEN '0' 
                    WHEN RIGHT(aa, 1) BETWEEN 'A' AND 'I' 
                    THEN CAST(ASCII(RIGHT(aa, 1)) - 64 AS CHAR(1)) 
                    WHEN RIGHT(aa, 1) BETWEEN 'J' AND 'R' 
                    THEN CAST(ASCII(RIGHT(aa, 1)) - 73 AS CHAR(1)) 
                    ELSE '' 
                  END AS INT) 
         * CASE WHEN RIGHT(aa, 1) IN ('{', '0') 
            OR RIGHT(aa, 1) BETWEEN 'A' AND 'I' THEN 1 
           WHEN RIGHT(aa, 1) IN ('}') 
            OR RIGHT(aa, 1) BETWEEN 'J' AND 'R' THEN -1 
           WHEN aa IN (aa) THEN aa 
          END) 
     ,PartOne = SUBSTRING(aa, 1, LEN(aa) - 1) 
     ,PartTwo = CASE WHEN RIGHT(aa, 1) IN ('{', '}', '0') THEN '0' 
         WHEN RIGHT(aa, 1) BETWEEN 'A' AND 'I' THEN CAST(ASCII(RIGHT(aa, 1)) - 64 AS CHAR(1)) 
         WHEN RIGHT(aa, 1) BETWEEN 'J' AND 'R' THEN CAST(ASCII(RIGHT(aa, 1)) - 73 AS CHAR(1)) 
         ELSE '' 
        END 
     ,PartThree = CASE WHEN RIGHT(aa, 1) IN ('{', '0') 
           OR RIGHT(aa, 1) BETWEEN 'A' AND 'I' THEN 1 
         WHEN RIGHT(aa, 1) IN ('}') 
           OR RIGHT(aa, 1) BETWEEN 'J' AND 'R' THEN -1 
         WHEN aa IN (aa) THEN aa 
        END 
    FROM (
      SELECT aa 
      FROM #Punch 
     ) bb 

的结果如下

enter image description here

逻辑是CAST(PartOne + PartTwo as INT)*PartThree

正如你所看到的条款的最后一部分产生相同的值,你必须在aa因此第一部分是什么得到乘以第三部分,你得到AA='046' =184,你可以看到'023'变成46等等任何值,其中所有三个字符都是数字最后2位数得到mu以第二位数字表示。

如果您使用的是SQL Server 2012,则可以将代码修改为以下内容。

SELECT aa 
     ,Answervalue = CASE WHEN TRY_CAST(aa AS INT) IS NOT NULL THEN aa 
          ELSE (CAST(SUBSTRING(aa, 1, LEN(aa) - 1) 
            + CASE WHEN RIGHT(aa, 1) IN ('{', '}', '0') THEN '0' 
             WHEN RIGHT(aa, 1) BETWEEN 'A' AND 'I' 
             THEN CAST(ASCII(RIGHT(aa, 1)) - 64 AS CHAR(1)) 
             WHEN RIGHT(aa, 1) BETWEEN 'J' AND 'R' 
             THEN CAST(ASCII(RIGHT(aa, 1)) - 73 AS CHAR(1)) 
             ELSE '' 
            END AS INT) * CASE WHEN RIGHT(aa, 1) IN ('{', '0') 
                  OR RIGHT(aa, 1) BETWEEN 'A' AND 'I' THEN 1 
                 WHEN RIGHT(aa, 1) IN ('}') 
                  OR RIGHT(aa, 1) BETWEEN 'J' AND 'R' THEN -1 
                 WHEN aa IN (aa) THEN aa 
                END) 
         END 
    FROM #Punch 

在逻辑启动之前,我添加的所有内容都是CASE WHEN TRY_CAST(aa AS INT) IS NOT NULL THEN aa ELSE。这样你可以避免做逻辑,如果它是整数,它会给你想要的结果。

结果:

AA  AnswerValue 
023  23 
046  46 
027  27 
004  4 
020  20 
090  90 
10}  -100 
45A  451 
03}  -30 
1

此代码设置一个本地表来测试过冲解码case语句。 FLD是overpunch场和输出强制转换为金钱和除以100作为最后2位数字解码领域都是便士(或分)在我们的例子:

Declare @MyTable as table 
(
Fld  varchar(20) 
) 

Insert into @MyTable values ('00056i') 

Select Fld from @MyTable 

Select 
    Case 
    when isnull(Fld,'')='' then null 
    when len(isnull(Fld,''))=0 then null 
    when right(Fld,1)='}' then cast('-' + left(Fld,(len(Fld)-1))+'0' as money)/100 
    when (ascii(right(upper(Fld),1))>=74 and ascii(right(upper(Fld),1))<=82) then cast('-' + left(Fld,(len(Fld)-1))+char(ascii(right(upper(Fld),1))-25) as money)/100 
    when right(Fld,1)='{' then cast(left(Fld,(len(Fld)-1))+'0' as money)/100 
    when (ascii(right(upper(Fld),1))>=65 and ascii(right(upper(Fld),1))<=73) then cast(left(Fld,(len(Fld)-1))+char(ascii(right(upper(Fld),1))-16) as money)/100 
    End as FldDecode 

    from @MyTable