ผมเขียนเอง JSON parser ใน T-SQL †
สำหรับวัตถุประสงค์ในการแยกวิเคราะห์ฉันใช้PATINDEX
ฟังก์ชันที่คำนวณตำแหน่งของโทเค็นจากรายการโทเค็น โทเค็นในกรณีของฉันเป็นอักขระเดี่ยวทั้งหมดและรวมไว้ด้วย:
{} []:,
โดยปกติเมื่อฉันต้องการค้นหาตำแหน่ง (แรก) ของตัวละครที่กำหนดหลายตัวฉันจะใช้PATINDEX
ฟังก์ชั่นดังนี้:
PATINDEX('%[abc]%', SourceString)
ฟังก์ชั่นแล้วจะให้ฉันตำแหน่งแรกของa
หรือb
หรือc
- SourceString
แล้วแต่จำนวนใดจะเกิดขึ้นจะพบแรก
ตอนนี้ปัญหาในกรณีของฉันดูเหมือนจะเชื่อมต่อกับ]
ตัวละคร ทันทีที่ฉันระบุไว้ในรายการตัวละครเช่นนี้
PATINDEX('%[[]{}:,]%', SourceString)
ดูเหมือนว่ารูปแบบที่ฉันตั้งใจไว้จะพังเพราะฟังก์ชั่นไม่เคยพบคู่ที่ตรงกัน ดูเหมือนว่าฉันต้องการวิธีที่จะหลบหนีแรก]
เพื่อให้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)
;
ผลลัพธ์ที่ฉันได้รับคือ:
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" }
คุณสามารถเล่นกับแบบสอบถามนี้ในฐานข้อมูล <> ซอ
†เรากำลังใช้ SQL Server 2014 และไม่น่าจะอัพเกรดเป็นเวอร์ชั่นที่รองรับการแยกวิเคราะห์ JSON โดยกำเนิด ฉันสามารถเขียนแอปพลิเคชันเพื่อทำงาน แต่ผลลัพธ์ของการแยกวิเคราะห์จำเป็นต้องดำเนินการเพิ่มเติมซึ่งหมายถึงการทำงานในแอปพลิเคชันมากกว่าการแยกวิเคราะห์ - ประเภทของงานที่จะง่ายขึ้นมากและอาจมีประสิทธิภาพมากขึ้นด้วย สคริปต์ T-SQL หากฉันสามารถนำไปใช้กับผลลัพธ์ได้โดยตรง
ไม่น่าเป็นไปได้มากที่ฉันจะใช้ SQLCLR เป็นวิธีแก้ปัญหาสำหรับปัญหานี้ได้ อย่างไรก็ตามฉันไม่รังเกียจหากมีคนตัดสินใจโพสต์โซลูชัน SQLCLR เนื่องจากอาจเป็นประโยชน์ต่อผู้อื่น
["foo]bar”]
อย่างไร?