SQL逻辑不起作用

时间:2014-03-24 17:44:06

标签: sql sql-server

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

我在SQL语句中的尝试如下所示,并且测试用例表明它不起作用。我的方法是从字符串中拆分最右边的字符,进行适当的转换,然后将它串起来并转换为整数。

问:如何修复我的SQL表达式以正确转换,或者可以使用哪种SQL表达式进行转换?

详情

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

例如,字符串值'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语句进行转换,但对于只有常规数字字符的字符串,它无法正常工作。 (上面的示例表是应转换为整数值的其他字符串的示例。

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

问:为什么我的SQL语句为字符串184返回46而不是'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

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

提前谢谢!

4 个答案:

答案 0 :(得分: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 :(得分: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 INT)替换CAST( x AS SIGNED),或者我们可以执行加法操作以隐式转换为数字。


我不太习惯从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进行替换:

 SELECT aa
      , CASE
        WHEN z.zdigit IS NOT NULL
        THEN CONCAT(LEFT(aa,CHAR_LENGTH(aa)-1),z.rdigit) * z.rsign
        ELSE aa + 0
        END AS Answervalue
   FROM Punch# t
   LEFT
   JOIN _convert_zoned_decimal z
     ON z.zdigit = RIGHT(t.aa,1)

答案 2 :(得分: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中的值相同,因此第一部分乘以第三部分,即{J} AA='046' =184,您可以看到&#1}} 39; 023'转到46等等所有三个字符都是数字的任何值最后2位乘以第二位数。

如果您使用的是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

答案 3 :(得分:1)

此代码设置一个本地表来测试过冲解码Case语句。 Fld是超弹的字段,输出被转换为货币并除以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 
相关问题