获取第 3 次连字符 (-) 后的所有字符

Get all the characters after the 3rd hypen (-)

提问人:user3933327 提问时间:10/26/2023 最后编辑:Thom Auser3933327 更新时间:10/27/2023 访问量:118

问:

我有列 TrackNo,其值如下

8070444981-010023-013123-INBBTC-C
601724-072923-078923-INAAAX-B

我需要从第三个连字符示例中获取值

-INBBTC-C
-INAAAX-B

我尝试了substring,charindex,但它不适用于这两种情况

select substring( Trackno,
charindex('-', TrackNo , (charindex('-', TrackNo , 1))
+charindex('-', TrackNo, (charindex('-', TrackNo , 1))+1)),20) 
from table
sql-server 子字符串 charindex

评论

0赞 Yitzhak Khabinsky 10/26/2023
在提出问题时,您需要提供一个最小的可重现示例:(1) DDL 和示例数据填充,即 CREATE 表和 INSERT T-SQL 语句。(2) 需要执行的操作,即逻辑和代码尝试在 T-SQL 中实现它。(3) 期望的输出,基于上面 #1 中的示例数据。(4) 您的 SQL Server 版本 (SELECT @@version;)。
1赞 Charlieface 10/26/2023
dbfiddle.uk/Hv-a1Ggr第二行上有一个杂散。(charindex('-', TrackNo , 1)) +
0赞 Jonas Metzler 10/27/2023
我不明白为什么这么多人在SQL中做如此奇怪的字符串操作。通常,SQL,尤其是 SQL Server 在这些事情上并不擅长,并且没有为这些事情做。最好将字符串分开并将它们保存在不同的列中,或者在应用程序中执行字符串操作。
1赞 siggemannen 10/27/2023
@JonasMetzler 如果子字符串和透视问题消失,StackOverflow 上的 SQL 人员会如何处理他们的时间

答:

-2赞 Yitzhak Khabinsky 10/26/2023 #1

请尝试以下基于 XML 和 XQuery 的解决方案。

XPath 谓词正在为你的方案执行工作:The XPath predicate is doing the job for your scenario:/root/r[position() ge 4]

获取第 3 次连字符 (-) 后的所有字符

SQL算法

-- DDL and sample data population, start
DECLARE @tbl TABLE (ID INT IDENTITY PRIMARY KEY, Trackno VARCHAR(100));  
INSERT INTO @tbl (Trackno) VALUES 
('8070444981-010023-013123-INBBTC-C'),
('601724-072923-078923-INAAAX-B');
-- DDL and sample data population, end

DECLARE @separator CHAR(1) = '-';

SELECT t.* 
    , REPLACE(c.query('data(/root/r[position() ge 4])')
        .value('text()[1]', 'VARCHAR(100)'), SPACE(1), @separator) AS result
FROM @tbl AS t
    CROSS APPLY (SELECT TRY_CAST('<root><r><![CDATA[' + 
        REPLACE(Trackno, @separator, ']]></r><r><![CDATA[') + 
        ']]></r></root>' AS XML)) AS t1(c);

输出

编号 轨道编号 结果
1 8070444981-010023-013123-INBBTC-C INBBTC-C型
2 601724-072923-078923-英纳AX-B INAAAX-B型

评论

0赞 user3933327 10/27/2023
这给出了结果,但是你能分享我吗/是否有任何简短的解决方案,因为它用于 SP 的 select 语句中,其中包含具有多个连接的大量数据
0赞 Yitzhak Khabinsky 10/27/2023
建议的解决方案将在 SP 中起作用:(1) 在实际表下添加联接,(2) 添加到子句中。CROSS APPLYAdd REPLACE(...) as resultSELECT
1赞 Xedni 10/27/2023 #2

如果您有 SQL 2022,则可以使用可选的 ordinal 参数,并获取所需的任何部分(拆分后)。string_split-

除此之外,您可以使用嵌套调用来暴力破解该部分,就像您所做的那样。你只需要确保你正确地偏移了每个块,这样你就不会在错误的地方切片。charindex

;with trackingNumbers (TrackNo) as
(
    select '8070444981-010023-013123-INBBTC-C'
    union all select '601724-072923-078923-INAAAX-B'
), b as
(
    select
        TrackNo,
        FirstIndex = charindex('-', TrackNo),
        SecondIndex = charindex
            (
                '-', 
                TrackNo, 
                charindex
                (
                    '-', 
                    TrackNo
                ) + 1
            ),
        ThirdIndex = charindex
            (
                '-', 
                TrackNo, 
                charindex
                (
                    '-', 
                    TrackNo, 
                    charindex
                    (
                        '-', 
                        TrackNo
                    ) + 1
                ) + 1
            )
    from trackingNumbers
)
select *,
LastTwo = substring(TrackNo, ThirdIndex + 1, 8000)
from b

如果需要,还可以通过替换字符串中的各种值来将其转换为JSON或XML数据的形式,还有其他方法可以做到这一点,但是为了解决您最初尝试的方法,我将避免使用这些方法。

编辑作为奖励,如果你想走这条路,这里有一些你可以通过连续交叉应用来做到这一点。

;with trackingNumbers (TrackNo) as
(
    select '8070444981-010023-013123-INBBTC-C'
    union all select '601724-072923-078923-INAAAX-B'
)
select 
    LastTwo = substring(TrackNo, d.ThirdIndex + 1, 8000),
    FirstIndex,
    SecondIndex,
    ThirdIndex
from trackingNumbers a
cross apply
(
    select FirstIndex = charindex('-', TrackNo)
) b
cross apply
(
    select SecondIndex = charindex('-', TrackNo, b.FirstIndex + 1)
) c
cross apply
(
    select ThirdIndex = charindex('-', TrackNo, c.SecondIndex + 1)
) d

评论

0赞 Stuck at 1337 10/27/2023
而不是只做 4000 或 8000 或其他什么。没有理由让其中一个 CPU 关闭并计算每行的长度。len(TrackNo)
0赞 Charlieface 10/27/2023
@Stuckat1337长度存储在前缀中,它不仅仅是以 null 结尾的 C 字符串。
0赞 Charlieface 10/27/2023
可以通过将重复的计算放入一系列CROSS APPLY (VALUES
0赞 Stuck at 1337 10/27/2023
@Charlieface 与常数相比,您是说 LEN() 是零成本吗?无论是实际计算长度还是从页面中检索长度,它仍然不是免费的,除了可能保护您免受未来数据类型更改的影响外,它不会给你带来任何东西。
0赞 Charlieface 10/27/2023
@Stuckat1337 不,我是说它不能做任何事情,比如读取整个字符串。长度已在内存中,它存储在字符串的其余部分(如 BSTR 或 .Net 字符串)旁边。这不是零成本,但成本并没有那么高。我同意没有必要。
-2赞 John Cappelletti 10/27/2023 #3

只是另一个选项,使用 和string_split()string_agg()

Declare @YourTable Table ([SomeCol] varchar(50))  Insert Into @YourTable Values 
 ('8070444981-010023-013123-INBBTC-C')
,('601724-072923-078923-INAAAX-B')
 
Select *
from @YourTable
Cross Apply ( Select NewVal =string_agg(value,'-')  within group (order by ordinal)
                 From string_split(SomeCol,'-',1)
                 Where ordinal>3
                 ) B

结果

SomeCol                             NewVal
8070444981-010023-013123-INBBTC-C   INBBTC-C
601724-072923-078923-INAAAX-B       INAAAX-B

评论

1赞 Xedni 10/27/2023
作为对 OP 的说明,这仅在运行 SQL Server 2022、Azure SQL 数据库、Azure SQL 托管实例或 Azure Synapse Analytics 时有效。
0赞 Xabi 10/27/2023 #4
with [table] (Trackno) as (
select '8070444981-010023-013123-INBBTC-C' union
select '601724-072923-078923-INAAAX-B')

select substring(Trackno,
            0 + charIndex('-', TrackNo,
            1 + charIndex('-', TrackNo,
            2 + charIndex('-', Trackno)))
            , 20) as [Result]
from [table]

db<>小提琴