将字符串拆分为多列的查询

问题描述 投票:0回答:3

我需要一个将字符串拆分为多个列的查询,如下所示:

[text](
DECLARE @TABLE TABLE ([vDescricao] VARCHAR(100))
    
    INSERT INTO @TABLE
    SELECT 
        [descricao]
    FROM 
    (
        VALUES
        ('[A/3] - 7'),
        ('[B/3] - 50'),
        ('[C/2] - 1.3.1'),
        ('[D/1] - 1.4'),
        ('[E/3] - 33'),
        ('[F/4f] - 1.2.1.2.1'),
        ('[GG/4] -  1.2.1.4.2')
    )D([descricao]);)

想要的结果是:

我创建了以下代码:

SELECT 
    [vDescricao]

    ,[vTipo] = SUBSTRING(
        vDescricao,
        CHARINDEX('[', vDescricao)+1,
        CHARINDEX('/', vDescricao) - CHARINDEX('[', vDescricao)-1
        )

    ,[vNivel] = SUBSTRING(
        vDescricao,
        CHARINDEX('/', vDescricao)+1,
        CHARINDEX(']', vDescricao) - CHARINDEX('/', vDescricao)-1
        )

    ,[vCodigo] = SUBSTRING(
        vDescricao,
        CHARINDEX(' - ', vDescricao)+3,
        LEN(vDescricao) - CHARINDEX(' - ', vDescricao)
        )

FROM @TABLE 

但在我看来这是一个不干净的代码(多次重复,没有设计模式)。它运作良好,但对我来说看起来不太好。

有人对此案有更好的解决方案吗?

非常感谢

也许我可以使用一些带有变量的函数...

SELECT 
    [vDescricao]

    ,[vTipo] = SUBSTRING(
        vDescricao,
        CHARINDEX('[', vDescricao)+1,
        CHARINDEX('/', vDescricao) - CHARINDEX('[', vDescricao)-1
        )

    ,[vNivel] = SUBSTRING(
        vDescricao,
        CHARINDEX('/', vDescricao)+1,
        CHARINDEX(']', vDescricao) - CHARINDEX('/', vDescricao)-1
        )

    ,[vCodigo] = SUBSTRING(
        vDescricao,
        CHARINDEX(' - ', vDescricao)+3,
        LEN(vDescricao) - CHARINDEX(' - ', vDescricao)
        )

FROM @TABLE 
sql sql-server subquery substring
3个回答
1
投票

只是使用一点 JSON 的另一种选择

Select A.*
      ,Pos1 = trim(JSON_VALUE(JS,'$[0]'))
      ,Pos2 = trim(JSON_VALUE(JS,'$[1]'))
      ,Pos3 = trim(JSON_VALUE(JS,'$[2]'))
 From  @TABLE A
 Cross Apply ( values ( replace(replace(replace([vDescricao],'[',''),']',''),'-','/') ) ) B(CleanString)
 Cross Apply (values ('["'+replace(CleanString,'/','","')+'"]') ) C(JS)

结果


1
投票

您的代码有效并且似乎与您的用例相关。

如果你想避免重复自己,你可以将一些表达式移动到子查询 - 或者横向连接 - 尽管这是一种权衡,也会增加查询的复杂性:

select t.vDescricao,
    substring(t.vDescricao, i.bracket_open + 1, i.slash - i.bracket_open  - 1) vTipo,
    substring(t.vDescricao, i.slash + 1,        i.bracket_close - i.slash - 1) vNivel,
    substring(t.vDescricao, i.dash + 3,         len(vDescricao) - i.dash     ) vCodigo
from @table t
cross apply ( 
    values (
        charindex('[',   vDescricao), 
        charindex(']',   vDescricao),
        charindex('/',   vDescricao),
        charindex(' - ', vDescricao)
    ) 
) i (bracket_open, bracket_close, slash, dash)
v描述 v蒂波 v尼维尔 vCodigo
[A/3] - 7 A 3 7
[B/3] - 50 3 50
[C/2] - 1.3.1 C 2 1.3.1
[D/1] - 1.4 D 1 1.4
[E/3] - 33 E 3 33
[F/4f] - 1.2.1.2.1 F 4f 1.2.1.2.1
[GG/4] - 1.2.1.4.2 GG 4 1.2.1.4.2

小提琴


0
投票

请尝试以下解决方案。

它使用 XML 和 XQuery 来标记字符串。

SQL

-- DDL and sample data population, start
DECLARE @tbl TABLE (vDescricao VARCHAR(100));
INSERT @tbl (vDescricao) VALUES
('[A/3] - 7'),
('[B/3] - 50'),
('[C/2] - 1.3.1'),
('[D/1] - 1.4'),
('[E/3] - 33'),
('[F/4f] - 1.2.1.2.1'),
('[GG/4] -  1.2.1.4.2');
-- DDL and sample data population, end

SELECT t.*
    , vTipo = c.value('(/root/r[1]/text())[1]','VARCHAR(20)')
    , vNivel = c.value('(/root/r[2]/text())[1]','VARCHAR(20)')
    , vCodigo = c.value('(/root/r[4]/text())[1]','VARCHAR(20)')
FROM @tbl AS t
    CROSS APPLY (SELECT TRY_CAST('<root><r><![CDATA[' + 
        REPLACE(TRANSLATE(TRIM('[' FROM vDescricao),']-','//'),'/', ']]></r><r><![CDATA[') + 
        ']]></r></root>' AS XML)) AS t1(c);

输出

v描述 v蒂波 v尼维尔 vCodigo
[A/3] - 7 A 3 7
[B/3] - 50 3 50
[C/2] - 1.3.1 C 2 1.3.1
[D/1] - 1.4 D 1 1.4
[E/3] - 33 E 3 33
[F/4f] - 1.2.1.2.1 F 4f 1.2.1.2.1
[GG/4] - 1.2.1.4.2 GG 4 1.2.1.4.2
© www.soinside.com 2019 - 2024. All rights reserved.