我正在尝试
SELECT
从一个较大的字符串中提取一个子字符串,该子字符串应该在“STORE”或“PROTECT”之前结束。
问题在于,在某些情况下,单词“STORE”或“PROTECT”可以在字符串中出现在彼此之前或之后,我们需要提取出现在这两个单词之前的子字符串。
示例字符串:
“无标签 a ABCD EFGH IJKLM FOO BAR,储存于 15-30°C。” - 这里没有“PROTECT”,但存在“STORE”一词,所以我们应该在“STORE”之前获取一个子字符串。
“最高储存 30°C (86°F)。防潮” - 在此字符串中,第一次出现单词“STORE”之前没有字符串,因此子字符串将为 BLANK
“MEPO BKHGT FOO BARFOO BAGK 小瓶。避光,保存在 X°C 至 Y°C 之间。” - 查询应返回单词“PROTECT”之前的子字符串。
“纸箱内含 1 X 瓶 SB245063 100 毫克冻干粉末,用于重构。储存于 2°C - 8°C。避光。” - 在此字符串中,“STORE”一词存在于“PROTECT”之前,因此查询应返回“STORE”一词之前的子字符串。
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 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 以获取上述各项的演示。