Sql-Server

使用 ’ 將 ](右方括號)與 PATINDEX 匹配’萬用字元

  • October 1, 2018

我正在用 T-SQL †編寫自定義 JSON 解析器。

出於解析器的目的,我正在使用PATINDEX從標記列表中計算標記位置的函式。在我的例子中,標記都是單個字元,它們包括:

{ } [ ] : ,

通常,當我需要找到幾個給定字元中的任何一個的(第一個)位置時,我會使用如下PATINDEX函式:

PATINDEX('%[abc]%', SourceString)

然後,該函式將為我提供aorb或的第一個位置c——以最先找到的為準——在SourceString.

現在我的問題似乎與]角色有關。一旦我在字元列表中指定它,例如這樣:

PATINDEX('%[[]{}:,]%', SourceString)

我的預期模式顯然被破壞了,因為該函式永遠找不到匹配項。看起來我需要一種方法來逃避第一個],以便PATINDEX將其視為查找字元之一而不是特殊符號。

我發現這個問題詢問了類似的問題:

但是,在這種情況下,]不需要在括號中指定,因為它只是一個字元,並且可以在沒有括號的情況下指定。確實使用轉義的替代解決方案僅適用於LIKE而不適用於PATINDEX,因為它使用ESCAPE由前者支持而不是後者支持的子條款。

所以,我的問題是,有沒有辦法**使用萬用字元來****尋找 a ?]``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)
;

我得到的輸出是:

Level  OpenClose  P   S      C   ResponseJSON
-----  ---------  --  -----  --  ---------------------------
1      1          1          {   "f1":["v1","v2"],"f2":"v3"}
1      null       6   "f1"   :   ["v1","v2"],"f2":"v3"}
2      1          7          [   "v1","v2"],"f2":"v3"}
2      null       12  "v1"   ,   "v2"],"f2":"v3"}
2      null       18  "v2"]  ,   "f2":"v3"}
2      null       23  "f2"   :   "v3"}
2      0          28  "v3"   }   

您可以看到]被包含S在其中一行中。該Level列表示嵌套的級別,表示括號和大括號的嵌套。如您所見,一旦級別變為2,它就永遠不會回到1。如果我可以將其PATINDEX辨識]為令牌,它將具有。

上述範例的預期輸出為:

Level  OpenClose  P   S     C   ResponseJSON
-----  ---------  --  ----  --  ---------------------------
1      1          1         {   "f1":["v1","v2"],"f2":"v3"}
1      NULL       6   "f1"  :   ["v1","v2"],"f2":"v3"}
2      1          7         [   "v1","v2"],"f2":"v3"}
2      NULL       12  "v1"  ,   "v2"],"f2":"v3"}
2      0          17  "v2"  ]   ,"f2":"v3"}
1      NULL       18        ,   "f2":"v3"}
1      NULL       23  "f2"  :   "v3"}
1      0          28  "v3"  }

您可以在 db<>fiddle使用此查詢。


†我們使用的是 SQL Server 2014,不太可能很快升級到原生支持 JSON 解析的版本。我可以編寫一個應用程序來完成這項工作,但解析的結果需要進一步處理,這意味著應用程序中的工作不僅僅是解析——這種工作會更容易,而且可能更有效,完成一個 T-SQL 腳本,如果我能將它直接應用到結果中就好了。

我不太可能使用 SQLCLR 作為這個問題的解決方案。但是,我不介意有人決定發布 SQLCLR 解決方案,因為這可能對其他人有用。

我自己的解決方案,這更像是一種解決方法,包括指定一個字元範圍,其中包括]並使用該範圍以及[ ]萬用字元中的其他字元。我使用了一個基於 ASCII 表的範圍。根據該表,該]角色位於以下街區:

十六進制十進製字元
--- --- ----
…
5A 90Z
5B 91 [
5C 92\
**5D 93]**
5E 94 ^
5F 95 _
…

因此,我的範圍採用 的形式[-^,即它包括四個字元:[\]^。我還指定該模式使用二進制排序規則,以完全匹配 ASCII 範圍。結果PATINDEX表達式最終看起來像這樣:

PATINDEX('%[[-^{}:,]%' COLLATE Latin1_General_BIN2, MyJSONString)

這種方法的明顯問題是模式開頭的範圍包括兩個不需要的字元,\^. 該解決方案對我有用,因為額外的字元永遠不會出現在我需要解析的特定 JSON 字元串中。當然,一般來說這不可能是真的,所以我仍然對其他方法感興趣,希望比我的更普遍。

引用自:https://dba.stackexchange.com/questions/206481