使用“[ ]”通配符将 ](右方括号)与 PATINDEX 匹配

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 解决方案,因为这可能对其他人有用。

And*_*y M 7

我自己的解决方案更像是一种解决方法,包括指定一个字符范围,该范围包括]和 使用该范围以及[ ]通配符中的其他字符。我使用了基于 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 字符串中。当然,这在一般情况下不可能是真的,所以我仍然对其他方法感兴趣,希望比我的更通用。