我将字符数据存储在从数据文件导入的列中。字符数据表示整数值,但是..最后(最右边)字符并不总是数字字符。我正在尝试使用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。
提前谢谢!
答案 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
结果如下
逻辑是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