“[]”와일드 카드를 사용하여 PATINDEX와] (닫는 대괄호) 일치


9

T-SQL 에서 사용자 지정 JSON 파서를 작성 중 입니다.

파서의 목적을 PATINDEX위해 토큰 목록에서 토큰의 위치를 ​​계산하는 함수를 사용하고 있습니다. 필자의 경우 토큰은 모두 단일 문자이며 다음을 포함합니다.

{} [] :,

일반적으로 주어진 여러 문자 중 첫 번째 위치를 찾아야 할 때 다음 PATINDEX과 같은 기능을 사용합니다 .

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

이 기능은 다음 날의 첫 번째 위치 줄 것이다 a또는 bc에 - 먼저 발견 될 일 중을 - 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"  }

이 쿼리 를 db <> fiddle에서 재생할 수 있습니다 .


우리는 SQL Server 2014를 사용하고 있으며 JSON 구문 분석을 기본적으로 지원하는 버전으로 곧 업그레이드하지 않을 것입니다. 작업을 수행하기 위해 응용 프로그램을 작성할 수는 있지만 구문 분석 결과를 더 처리해야합니다. 이는 구문 분석보다 응용 프로그램에서 더 많은 작업을 의미합니다. 훨씬 쉽고 효율적으로 수행 할 수있는 작업 T-SQL 스크립트 (결과에만 직접 적용 할 수있는 경우)

이 문제에 대한 해결책으로 SQLCLR을 사용할 수있을 가능성은 거의 없습니다. 그러나 누군가 SQLCLR 솔루션을 게시하기로 결정한 경우 다른 사람에게 유용 할 수 있으므로 신경 쓰지 않습니다.


json은 어떻게 생겼 ["foo]bar”]습니까?
살만 A

@SalmanA : 이러한 시나리오는 무시해도됩니다.
Andriy M

답변:


6

해결 방법 인 내 자신의 솔루션 ][ ]와일드 카드 의 다른 문자와 함께 해당 범위 를 포함 하고 사용 하는 문자 범위를 지정하는 것으로 구성되었습니다 . ASCII 테이블을 기반으로 범위를 사용했습니다. 이 표에 따르면 ]캐릭터는 다음과 같은 이웃에 있습니다.

16 월 12 월 Char
--- --- ----
…
5A 90 Z
5B 91 [
5C 92 \
5D 93]
5E 94 ^
5F 95 _
…

내 범위는, 그러므로,의 형태를했다 [-^, 그것은 네 개의 문자를 포함 즉 : [, \, ], ^. 또한 패턴이 이진 데이터 정렬을 사용하여 ASCII 범위와 정확히 일치하도록 지정했습니다. 결과 PATINDEX표현식은 다음과 같이 보입니다.

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

이 접근 방식의 명백한 문제는 패턴의 시작 부분의 범위는이 개 원치 않는 문자가 포함되어 있다는 것입니다 \^. 구문 분석 해야하는 특정 JSON 문자열에서 추가 문자를 절대로 사용할 수 없기 때문에 솔루션이 저에게 효과적이었습니다. 당연히, 이것은 일반적으로 사실이 될 수 없으므로, 나는 여전히 다른 방법에 관심이 있습니다.


4

많은 문자열 분할을 수행해야 할 때이 작업을 다시 시작해야 할 것입니다.

알려진 문자 집합이 있으면 표를 만드십시오.

CREATE TABLE dbo.characters ( character CHAR(1) NOT NULL PRIMARY KEY CLUSTERED );

INSERT dbo.characters ( character )
SELECT *
FROM (
        SELECT '[' UNION ALL
        SELECT ']' UNION ALL
        SELECT '{' UNION ALL
        SELECT '}' UNION ALL
        SELECT ',' 
) AS x (v)

그런 다음 마법 CROSS APPLY을 다음 과 함께 사용하십시오 CHARINDEX.

SELECT TOP 1000 p.Id, p.Body, ca.*
FROM dbo.Posts AS p
CROSS APPLY (
    SELECT TOP 1 CHARINDEX(c.character, p.Body) AS first_things_first
    FROM dbo.characters AS c
    ORDER BY CHARINDEX(c.character, p.Body) ASC
) AS ca
WHERE ca.first_things_first > 0

당신이해야 할 일에 대해 분명한 것을 놓치고 있다면, lemme은 알고 있습니다.


4

검색하기 전에 문제가되는 문자를 대체하고 나중에 다시 넣는 접근 방식이 과거에 나타났습니다.

이 경우 다음과 같은 작업을 수행 할 수 있습니다.

DECLARE @test NVARCHAR(MAX);
DECLARE @replacementcharacter CHAR(1) = CHAR(174);

SET @test = 'Test[]@String'

SELECT PATINDEX('%[[' + @replacementcharacter + '@]%', REPLACE(@test,']',@Replacementcharacter))

이 코드는 5를 올바르게 반환합니다. ¬ 문자를 사용하지 않는 것 같습니다. 사용하지 않을 ASCII 문자가 없으면이 솔루션이 작동하지 않습니다.

그러나 이상하게도 귀하의 질문에 대한 직접적인 대답은 아니오입니다-PATINDEX가 ']'을 (를) 검색하도록 할 수는 없지만 교체하면 필요하지 않습니다.

동일한 예이지만 변수 사용법이 없습니다.

DECLARE @test NVARCHAR(MAX);

SET @test = 'Test[]@String'

SELECT PATINDEX('%[[' + CHAR(174) + '@]%', REPLACE(@test,']',CHAR(174)))

코드에서 위의 솔루션을 사용하면 필요한 결과를 얻을 수 있습니다.

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('%[[{'+ CHAR(174) + ']%', REPLACE(d.ResponseJSON,']',CHAR(174)))) 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('%[[{}:,'+ CHAR(174) + ']%' COLLATE Latin1_General_BIN2, REPLACE(d.ResponseJSON,']',CHAR(174)))) 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)
;

4

]경우에만 특별 하므로 , 외부로 이동 [...]하여 PATINDEX두 번 사용할 수 있습니다 . 모두 평가 하고 . 한 결과가 0이면 다른 결과를 가져옵니다. 그렇지 않으면 두 값 중 작은 값을 사용하십시오.][...]PATINDEX('%[[{}:,]%', SourceString)PATINDEX('%]%', SourceString)

귀하의 예에서 :

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 + ISNULL(p.P, 0),
      S             = SUBSTRING(d.ResponseJSON, 1, p.P - 1),
      C             = c.C,
      ResponseJSON  = SUBSTRING(d.ResponseJSON, p.P + 1, 999999)
    FROM
      parser AS d
      CROSS APPLY (VALUES (NULLIF(PATINDEX('%[[{}:,]%', d.ResponseJSON), 0), NULLIF(PATINDEX('%]%', d.ResponseJSON), 0))) AS p_ (a, b)
      CROSS APPLY (VALUES (CASE WHEN p_.a < p_.b OR p_.b IS NULL THEN p_.a ELSE p_.b END)) AS p (P)
      CROSS APPLY (SELECT SUBSTRING(d.ResponseJSON, p.P, 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)
;

https://dbfiddle.uk/?rdbms=sqlserver_2014&fiddle=66fba2218d8d7d310d5a682be143f6eb


-4

왼쪽 '['의 경우 :

PATINDEX('%[[]%',expression)

올바른 ']'의 경우 :

PATINDEX('%]%',expression)

1
이것은 여는 대괄호 또는 닫는 대괄호를 검색하는 방법을 지정합니다. OP는 닫는 대괄호를 포함하여 여러 문자 중 하나를 찾고 있습니다 (해당 문자를 대괄호로 묶음으로 표시).
RDFozz
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.