SQL Server:从一个具有可变长度的varchar解析两个值

时间:2017-04-11 19:59:26

标签: sql sql-server

我在表格中有一个varchar列。在本专栏中,结构是一致的,但长度可能略有不同。

以下是我可以处理的一些例子。请注意值如何符合相同的模式,但(策略编号和两个帐号)中包含的数值的长度可能略有不同。

DECLARE @historyDescription1 VARCHAR(50)
DECLARE @historyDescription2 VARCHAR(50)
DECLARE @historyDescription3 VARCHAR(50)

SET @historyDescription1 = 'Policy ZZ 998560-17 transferred from Account 0028397267 to Account 192135980'
SET @historyDescription2 = 'Policy ZZ 9985601-17-00 transferred from Account 128397267 to Account 792135980'
SET @historyDescription3 = 'Policy ZZ 998560789-17-00 transferred from Account 228397267 to Account 0192135980'

对于每条记录,我需要选择第一个帐号和第二个帐号。总会有两个在场。

像...一样的东西。

SELECT [first parsed value]+','+[second parsed value]
FROM myTable
WHERE [...]

这样我得到的结果就像......

0028397267,192135980  
128397267,792135980  
228397267,0192135980

我一直在玩SUBSTRINGCHARINDEXLEFTRIGHT无济于事。任何帮助表示赞赏。

5 个答案:

答案 0 :(得分:2)

我试过这个,它似乎适用于你的样本值:

select substring(hist, pos1 + len1, pos2 - pos1 - len1 -1), substring(hist, pos2 + len2, lenhist - pos2 - len2 +1)
from (
select 
      pos1 = charindex('from Account', hist) 
    , pos2 = charindex('to Account', hist)
    , len1 = len('from Account')
    , len2 = len('to Account')
    , lenhist = len(hist)
    , *
from t
    ) x

我使用派生表来保持表达式的简单。

A demo can be found here

答案 1 :(得分:1)

你在这里发生了一些事情,这使得它看起来比它看起来更具挑战性。首先,您需要拆分输入字符串以分隔值。然后你需要再次将它们重新组合在一起。我在sql server central.com上使用Jeff Moden的字符串拆分器。你可以在这里找到他的文章,代码和关于这个主题的讨论。 http://www.sqlservercentral.com/articles/Tally+Table/72993/

既然我们可以有效地分割字符串,我们只需要将它重新组合在一起。我们可以使用STUFF和FOR XML技巧来做到这一点。

这适用于您发布的示例数据。如果值并非始终位于字符串中的位置,则面临挑战。

create table #Something (HistoryDescription varchar(200))

insert #Something
select 'Policy ZZ 998560-17 transferred from Account 0028397267 to Account 192135980' union all
select 'Policy ZZ 9985601-17-00 transferred from Account 128397267 to Account 792135980' union all
select 'Policy ZZ 998560789-17-00 transferred from Account 228397267 to Account 0192135980'

with parsedValues as
(
    select *
    from #Something s
    cross apply dbo.DelimitedSplit8K(s.HistoryDescription, ' ')
    where ItemNumber in (7, 10)
)

select HistoryDescription, 
    STUFF((select ',' + Item
        from parsedValues pv2
        where pv2.HistoryDescription = pv.HistoryDescription
        order by pv2.ItemNumber
        for xml path('')), 1, 1, '')
from parsedValues pv
group by pv.HistoryDescription

答案 2 :(得分:1)

charindex用于了解“帐户”一词的起始位置。 对于第二个“帐户”字样,您需要一个大于第一个的起始索引。

create table #test(
valor varchar(max)
)

insert into #test values('Policy ZZ 998560-17 transferred from Account 0028397267 to Account 192135980')
insert into #test values('Policy ZZ 9985601-17-00 transferred from Account 128397267 to Account 792135980')
insert into #test values('Policy ZZ 998560789-17-00 transferred from Account 228397267 to Account 0192135980')

select REPLACE(substring(
                                valor,
                                CHARINDEX('Account',valor)+8,
                                CHARINDEX('Account', valor,charindex('Account',valor))
                                )
                            ,' to Account ',
                            ',') as Accounts from #test

答案 3 :(得分:0)

更新更好的方法

DECLARE @tbl TABLE(historyDescription VARCHAR(500));
INSERT INTO @tbl VALUES
 ('Policy ZZ 998560-17 transferred from Account 0028397267 to Account 192135980')
,('Policy ZZ 9985601-17-00 transferred from Account 128397267 to Account 792135980')
,('Policy ZZ 998560789-17-00 transferred from Account 228397267 to Account 0192135980');


WITH SplitAtAccount AS
(
    SELECT historyDescription
          ,CAST('<x>' + REPLACE(
                          REPLACE(
                             REPLACE(historyDescription,' transferred from Account ','SplitThisHere'
                                    ),' to Account ','SplitThisHere'
                                  ),'SplitThisHere','</x><x>') + '</x>' AS XML) Casted 
    FROM  @tbl 
)
SELECT Casted.value('(x/text())[1]','nvarchar(max)') AS Part1 
      ,Casted.value('(x/text())[2]','nvarchar(max)') AS Part2 
      ,Casted.value('(x/text())[3]','nvarchar(max)') AS Part3 
FROM SplitAtAccount 

结果

Part1                     Part2       Part3
Policy ZZ 998560-17       0028397267  192135980
Policy ZZ 9985601-17-00   128397267   792135980
Policy ZZ 998560789-17-00 228397267   0192135980

答案 4 :(得分:0)

这不是一个比其他方法更好的方法,但它只是一个解析字符串的教育练习,使用CTE简化一些字符串表达式,并得出你想要的一般结果。这是一个更简单的,如果是“强力”方法,不依赖于STUFF / FOR XML技巧。它会进一步解析各个部分,包括政策编号,以防您出于其他目的而需要。

CREATE TABLE #Something (HistoryDescription varchar(200))
INSERT #Something
SELECT 'Policy ZZ 998560-17 transferred from Account 0028397267 to Account 192135980' union all
SELECT 'Policy ZZ 9985601-17-00 transferred from Account 128397267 to Account 792135980' union all
SELECT 'Policy ZZ 998560789-17-00 transferred from Account 228397267 to Account 0192135980'

WITH HistoryDescriptionFragments AS (
SELECT REVERSE(SUBSTRING(HistoryDescription, 1, CHARINDEX(' transferred', HistoryDescription) - 1)) AS ReversedPolicyFragment,
       SUBSTRING(HistoryDescription, CHARINDEX('from Account ', HistoryDescription) + LEN('from Account ') + 1, LEN(HistoryDescription)) AS FromAccountFragment, 
       SUBSTRING(HistoryDescription, CHARINDEX('to Account ', HistoryDescription) + LEN('to Account ') + 1, LEN(HistoryDescription)) AS ToAccountFragment, 
       *
FROM #Something
), HistoryDescriptionParts AS (
SELECT REVERSE(SUBSTRING(ReversedPolicyFragment, 1, CHARINDEX(' ', ReversedPolicyFragment) - 1)) AS Policy, 
       SUBSTRING(FromAccountFragment, 1, CHARINDEX(' ', FromAccountFragment) - 1) AS FromAccount,
       ToAccountFragment AS ToAccount, 
       HistoryDescription AS OriginalText
FROM HistoryDescriptionFragments
)
SELECT Policy, FromAccount, ToAccount, FromAccount + ',' + ToAccount AS DesiredOutput
FROM HistoryDescriptionParts

这给出了结果:

Policy           FromAccount  ToAccount   DesiredOutput
---------------  -----------  ----------  --------------------
998560-17        0028397267   192135980   0028397267,192135980
9985601-17-00    128397267    792135980   128397267,792135980
998560789-17-00  228397267    0192135980  228397267,0192135980

作为解释......第一个CTE只是简化解析策略号所必需的。它将字符串拆分为包含您感兴趣的每个部分的片段,反转策略片段,以便通过仅搜索第一个空格更容易选择数字。第二个CTE优化了解析以获得所需的数据。最终的SELECT只是从CTE中选择,然后将你感兴趣的两件作品加入到最终的“Desired Output”中。