SQL 选择指定单词之前的子字符串

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

我正在尝试

SELECT
从一个较大的字符串中提取一个子字符串,该子字符串应该在“STORE”或“PROTECT”之前结束。

问题在于,在某些情况下,单词“STORE”或“PROTECT”可以在字符串中出现在彼此之前或之后,我们需要提取出现在这两个单词之前的子字符串。

示例字符串:

  1. “无标签 a ABCD EFGH IJKLM FOO BAR,储存于 15-30°C。” - 这里没有“PROTECT”,但存在“STORE”一词,所以我们应该在“STORE”之前获取一个子字符串。

  2. “最高储存 30°C (86°F)。防潮” - 在此字符串中,第一次出现单词“STORE”之前没有字符串,因此子字符串将为 BLANK

  3. “MEPO BKHGT FOO BARFOO BAGK 小瓶。避光,保存在 X°C 至 Y°C 之间。” - 查询应返回单词“PROTECT”之前的子字符串。

  4. “纸箱内含 1 X 瓶 SB245063 100 毫克冻干粉末,用于重构。储存于 2°C - 8°C。避光。” - 在此字符串中,“STORE”一词存在于“PROTECT”之前,因此查询应返回“STORE”一词之前的子字符串。

  5. NULL
    - 当字符串为空时,查询应返回空值而不是错误。

SQL查询:

DECLARE @TestVariable AS VARCHAR(MAX)='UNLAB ABCFG DRETFG FOO BARFOO 3990MG TABLETS, STORE AT 15-30°C.'

SELECT case when CHARINDEX('STORE', upper(@TestVariable)) > CHARINDEX('PROTECT', upper(@TestVariable))
then SUBSTRING(@TestVariable, 1, CHARINDEX('PROTECT', upper(@TestVariable))-1)
else SUBSTRING(@TestVariable, 1, CHARINDEX('STORE', upper(@TestVariable))-1)
end pack_description

如果字符串中没有“PROTECT”或“STORE”的单词且字符串值为

NULL
,则查询返回错误。

根据上面的示例,应该对该查询进行哪些更改才能满足我们的要求?

sql sql-server substring charindex
1个回答
0
投票

SQL Server 是一个很差的文本解析平台,因为它的模式匹配功能非常有限。这在支持正则表达式(正则表达式)的应用程序中会更好,因为正则表达式可以使用正则表达式(如

/\bSTORE\b/
)更好地检查上下文(例如单词边界)。

但是,如果您需要在 SQL Server 中执行此操作,则可以添加

LEN()
作为第三个位置选择器,并添加
LEAST()
来选择最左侧的修剪位置。
NULLIF()
函数还可以用于将未找到的0位置映射为null,这样它们就会被忽略。

SELECT
    LEFT(D.String, LEAST(
        NULLIF(CHARINDEX('STORE', D.String), 0) - 1,
        NULLIF(CHARINDEX('PROTECT', D.String), 0) - 1,
        LEN(D.String)
        )) AS Result
FROM Data D

纯粹为了美观,可以使用 A

CROSS APPLY
将位置计算与最终选择分开。

SELECT
     LEFT(D.String, A.NewLen) AS Result
FROM DATA D
CROSS APPLY (
    SELECT LEAST(
        NULLIF(CHARINDEX('STORE', D.String), 0) - 1,
        NULLIF(CHARINDEX('PROTECT', D.String), 0) - 1,
        LEN(D.String)
        ) AS NewLen
) A

我们还可以将关键字放在表格中,并使用子选择来搜索任何关键字。

ISNULL()
函数用于注入
LEN()
作为默认修剪位置。

SELECT
     LEFT(D.String, A.NewLen) AS Result
FROM Data D
CROSS APPLY (
    SELECT ISNULL(MIN(P.Pos), LEN(D.String)) AS NewLen
    FROM (
        SELECT NULLIF(CHARINDEX(K.Keyword, D.String), 0) - 1 AS Pos
        FROM Keywords K
    ) P
) A

最后,如果文本包含部分匹配关键字的较大单词(例如“保护”或“恢复器”),当前匹配逻辑可能会产生错误命中。这可以通过使用

PATINDEX()
并在关键字前面加上非字母模式
[^A-Z]
来解决。

SELECT
     LEFT(D.String, A.NewLen) AS Result
FROM Data D
CROSS APPLY (
    SELECT ISNULL(MIN(P2.AdjustedPos), LEN(D.String)) AS NewLen
    FROM (
        SELECT NULLIF(PATINDEX('%[^A-Z]' + K.Keyword + '[^A-Z]%', ' ' + D.String + ' '), 0) - 2 AS Pos
        FROM Keywords K
    ) P
    CROSS APPLY (
        SELECT CASE WHEN P.Pos < 0 THEN 0 ELSE P.Pos END AS AdjustedPos
    ) P2
) A

结果示例

新长度 结果 尾巴
未标记a ABCD EFGH IJKLM FOO BAR,  储存于 15-30°C。
储存温度高达 30°C (86°F)。防潮
MEPO BKHGT FOO BARFOO BAGK 小瓶。  避光,储存在 X°C 至 Y°C 之间。
装有 1 X 瓶 SB245063 100 毫克冻干粉的纸箱,用于重构。  储存于 2°C - 8°C。避光。
这里只是一个描述
完成修复
油漆保护
香蕉。  90 天后丢弃。

请参阅 this db<>fiddle 以获取上述各项的演示。

© www.soinside.com 2019 - 2024. All rights reserved.