And*_*y M 9 sql-server t-sql pattern-matching sql-server-2014 string-searching
我正在 T-SQL † 中编写自定义 JSON 解析器。
出于解析器的目的,我使用了PATINDEX从标记列表中计算标记位置的函数。在我的情况下,令牌都是单个字符,它们包括以下内容:
{ } [ ] : ,
通常,当我需要找到几个给定字符中的任何一个的(第一个)位置时,我会使用这样的PATINDEX函数:
PATINDEX('%[abc]%', SourceString)
Run Code Online (Sandbox Code Playgroud)
然后,该函数将给我aorb或 or的第一个位置c——以最先找到的为准—— in SourceString。
现在我的问题似乎与]角色有关。一旦我在字符列表中指定它,例如像这样:
PATINDEX('%[[]{}:,]%', SourceString)
Run Code Online (Sandbox Code Playgroud)
我的预期模式显然被破坏了,因为该函数从未找到匹配项。看起来我需要一种方法来转义第一个,]以便PATINDEX将其视为查找字符之一而不是特殊符号。
我发现这个问题询问了一个类似的问题:
但是,在这种情况下,]不需要在方括号中简单地指定,因为它只是一个字符,并且可以在不使用方括号的情况下指定。使用转义的替代解决方案仅适用于LIKE而不适用于PATINDEX,因为它使用一个ESCAPE子条款,由前者支持而不是由后者支持。
所以,我的问题是,有没有什么办法去寻找一个]与PATINDEX使用[ ]通配符?或者有没有办法使用其他 Transact-SQL 工具模拟该功能?
这是我需要使用PATINDEX上述[…]模式的查询示例。这里的模式有效(虽然有点),因为它不包含]字符。我也需要它来工作]:
WITH
data AS (SELECT CAST('{"f1":["v1","v2"],"f2":"v3"}' AS varchar(max)) AS ResponseJSON),
parser AS
(
SELECT
Level = 1,
OpenClose = 1,
P = p.P,
S = SUBSTRING(d.ResponseJSON, 1, NULLIF(p.P, 0) - 1),
C = SUBSTRING(d.ResponseJSON, NULLIF(p.P, 0), 1),
ResponseJSON = SUBSTRING(d.ResponseJSON, NULLIF(p.P, 0) + 1, 999999)
FROM
data AS d
CROSS APPLY (SELECT PATINDEX('%[[{]%', d.ResponseJSON)) AS p (P)
UNION ALL
SELECT
Level = ISNULL(d.OpenClose - 1, 0) + d.Level + ISNULL(oc.OpenClose, 0),
OpenClose = oc.OpenClose,
P = d.P + p.P,
S = SUBSTRING(d.ResponseJSON, 1, NULLIF(p.P, 0) - 1),
C = c.C,
ResponseJSON = SUBSTRING(d.ResponseJSON, NULLIF(p.P, 0) + 1, 999999)
FROM
parser AS d
CROSS APPLY (SELECT PATINDEX('%[[{}:,]%' COLLATE Latin1_General_BIN2, d.ResponseJSON)) AS p (P)
CROSS APPLY (SELECT SUBSTRING(d.ResponseJSON, NULLIF(p.P, 0), 1)) AS c (C)
CROSS APPLY (SELECT CASE WHEN c.C IN ('[', '{') THEN 1 WHEN c.C IN (']', '}') THEN 0 END) AS oc (OpenClose)
WHERE 1=1
AND p.P <> 0
)
SELECT
*
FROM
parser
OPTION
(MAXRECURSION 0)
;
Run Code Online (Sandbox Code Playgroud)
我得到的输出是:
{ } [ ] : ,
您可以看到]包含S在其中一行中。该Level列表示嵌套的级别,即括号和大括号的嵌套。如您所见,一旦级别变为 2,它就永远不会返回到 1。如果我可以将其PATINDEX识别]为令牌,它就会恢复。
上面例子的预期输出是:
PATINDEX('%[abc]%', SourceString)
Run Code Online (Sandbox Code Playgroud)
您可以在 db<>fiddle 处使用此查询。
†我们使用的是 SQL Server 2014,不太可能很快升级到原生支持 JSON 解析的版本。我可以编写一个应用程序来完成这项工作,但解析的结果需要进一步处理,这意味着应用程序中的工作不仅仅是解析——这种工作会更容易,而且可能更高效,使用一个 T-SQL 脚本,如果我能将它直接应用于结果就好了。
我不太可能使用 SQLCLR 作为这个问题的解决方案。但是,我不介意是否有人决定发布 SQLCLR 解决方案,因为这可能对其他人有用。
我自己的解决方案更像是一种解决方法,包括指定一个字符范围,该范围包括]和 使用该范围以及[ ]通配符中的其他字符。我使用了基于 ASCII 表的范围。根据该表,该]角色位于以下社区:
十六进制字符 --- --- ---- … 5A 90 Z 5B 91 [ 5C 92 \ 5D 93] 5E 94 ^ 5F 95 _ …
因此,我的范围采用 的形式[-^,即它包括四个字符:[、\、]、^。我还指定模式使用二进制排序规则,以精确匹配 ASCII 范围。结果PATINDEX表达式最终看起来像这样:
PATINDEX('%[[-^{}:,]%' COLLATE Latin1_General_BIN2, MyJSONString)
Run Code Online (Sandbox Code Playgroud)
这种方法的明显问题是模式开头的范围包含两个不需要的字符,\并且^. 该解决方案对我有用,因为额外的字符永远不会出现在我需要解析的特定 JSON 字符串中。当然,这在一般情况下不可能是真的,所以我仍然对其他方法感兴趣,希望比我的更通用。
| 归档时间: |
|
| 查看次数: |
4714 次 |
| 最近记录: |