使用 ’ 將 ](右方括號)與 PATINDEX 匹配’萬用字元
我正在用 T-SQL †編寫自定義 JSON 解析器。
出於解析器的目的,我正在使用
PATINDEX
從標記列表中計算標記位置的函式。在我的例子中,標記都是單個字元,它們包括:{ } [ ] : ,
通常,當我需要找到幾個給定字元中的任何一個的(第一個)位置時,我會使用如下
PATINDEX
函式:PATINDEX('%[abc]%', SourceString)
然後,該函式將為我提供
a
orb
或的第一個位置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 字元串中。當然,一般來說這不可能是真的,所以我仍然對其他方法感興趣,希望比我的更普遍。