LIKE N '% %'를 검색하면 유니 코드 문자가 일치하고 = N' '가 많은 문자와 일치하는 이유는 무엇입니까?


20
DECLARE @T TABLE(
  Col NCHAR(1));

INSERT INTO @T
VALUES      (N'A'),
            (N'B'),
            (N'C'),
            (N'Ƕ'),
            (N'Ƿ'),
            (N'Ǹ');

SELECT *
FROM   @T
WHERE  Col LIKE N'%�%'

보고

Col
A
B
C
Ƕ
Ƿ
Ǹ

SELECT *
FROM   @T
WHERE  Col = N'�' 

보고

Col
Ƕ
Ƿ
Ǹ

아래의 가능한 모든 2 바이트 "문자"를 생성하면 =버전이 21,229와 LIKE N'%�%'모두 일치한다는 것을 알 수 있습니다 (동일한 결과로 이진이 아닌 데이터 정렬을 시도했습니다).

WITH T(I, N)
AS 
(
SELECT TOP 65536 ROW_NUMBER() OVER (ORDER BY @@SPID),
                 NCHAR(ROW_NUMBER() OVER (ORDER BY @@SPID))
FROM master..spt_values v1, 
     master..spt_values v2
)
SELECT I, N 
FROM T
WHERE N = N'�'  

여기서 무슨 일이 일어나고 있는지 밝힐 수 있습니까?

COLLATE Latin1_General_BIN그런 다음 사용 은 단일 문자와 일치 NCHAR(65533)하지만 문제는 다른 규칙에서 사용하는 규칙을 이해하는 것입니다. 21,229 자에 해당하는 특수 문자는 무엇 =이고 왜 모든 문자가 와일드 카드와 일치합니까? 나는 내가 누락 된 그 뒤에 어떤 이유가 있다고 생각합니다.

nchar(65534)[그리고 21k 명의 다른 사람들]은 마찬가지로 잘 작동 nchar(65533)합니다. 이 질문은 다음 nchar(502) 과 같이 똑같이 사용되어 표현 될 수 있습니다 .- LIKE N'%Ƕ%'( 모두 일치) 모두와 동일하게 동작합니다 =. 아마 큰 단서 일 것입니다.

SELECT마지막 쿼리에서를 변경하면 SELECT I, N, RANK() OVER(ORDER BY N)SQL Server가 문자 순위를 지정할 수 없음 을 나타냅니다. 데이터 정렬로 처리되지 않은 문자는 동등한 것으로 간주됩니다.

Latin1_General_100_CS_AS데이터 정렬이 있는 데이터베이스 는 5840 개의 일치 항목을 생성합니다. Latin1_General_100_CS_AS아래 컷 =일치 아주 상당히 있지만, 변경되지 않는 LIKE행동을. 나중에 데이터 정렬이 더 작아 져 와일드 카드 LIKE검색 에서 무시되는 문자 모음이있는 것처럼 보입니다 .

SQL Server 2016을 사용하고 있습니다.이 기호 는 유니 코드 대체 문자이지만 UCS-2 인코딩에서 유효하지 않은 문자는 55296-57343 AFAIK이며이 N'Ԛ'범위에없는 것과 같은 완벽하게 유효한 코드 포인트와 명확하게 일치합니다 .

이 모든 문자는 LIKE및 의 빈 문자열처럼 작동합니다 =. 심지어 동등한 것으로 평가합니다. N'' = N'�'사실 LIKE이며 단일 공간 LIKE '_' + nchar(65533) + '_'을 비교 하지 않고 놓을 수 있습니다 . LEN비교는 다른 결과를 산출하므로 아마도 특정 문자열 함수 일뿐입니다.

LIKE이 경우 에는 동작이 올바른 것 같습니다. 그것은 알 수없는 값처럼 행동합니다 (아무것도 될 수 있음). 이 다른 캐릭터들도 마찬가지입니다 :

  • nchar(11217) (불확실성 표시)
  • nchar(65532) (개체 교체 문자)
  • nchar(65533) (교체 캐릭터)
  • nchar(65534) (캐릭터 아님)

따라서 등호로 불확실성을 나타내는 모든 문자를 찾으려면과 같은 보조 문자를 지원하는 데이터 정렬을 사용합니다 Latin1_General_100_CI_AS_SC.

이것들은 문서, 데이터 정렬 및 유니 코드 지원 에서 언급 된 "무가 중 문자"그룹이라고 생각합니다 .

답변:


8

하나의 "문자"(여러 코드 포인트로 구성 될 수 있음 : 서로 게이트 쌍, 문자 결합 등)가 다른 규칙과 비교되는 방식은 다소 복잡한 규칙을 기반으로합니다. 유니 코드 사양에 표시된 모든 언어에서 발견되는 다양한 (때로는 "엉뚱한") 규칙을 모두 고려해야하기 때문에 너무 복잡 합니다. 이 시스템은 모든 NVARCHAR데이터와 VARCHARSQL Server 데이터 정렬이 아닌 Windows 데이터 정렬을 사용하는 데이터 (로 시작하는 데이터)에 대해 이진이 아닌 데이터 정렬에 적용됩니다 SQL_. 이 시스템은 VARCHAR간단한 매핑을 사용하므로 SQL Server 데이터 정렬을 사용하는 데이터 에는 적용되지 않습니다 .

대부분의 규칙은 UCA (Unicode Collation Algorithm)에 정의되어 있습니다. 이러한 규칙 중 일부와 UCA에서 다루지 않은 규칙은 다음과 같습니다.

  1. allkeys.txt파일에 제공된 기본 주문 / 무게 (아래에 표시)
  2. 어떤 민감도 및 옵션이 사용되고 있습니까 (예 : 대소 문자를 구분합니까? 아니면 대소 문자를 구분합니까?, 민감한 경우 대문자가 먼저 소문자입니까?)
  3. 모든 로캘 기반 재정의
  4. 유니 코드 표준 버전이 사용 중입니다.
  5. "인간"요소 (즉, 유니 코드는 소프트웨어가 아닌 사양이므로이를 구현하기 위해 각 공급 업체에 맡겨집니다)

휴먼 팩터에 관한 최종 요점은 SQL Server가 사양에 따라 항상 100 % 동작 할 것으로 기 대해서는 안된다는 점을 분명히하기 위해 강조했습니다.

여기서 가장 중요한 요소는 각 코드 포인트에 부여 된 가중치와 여러 코드 포인트가 동일한 가중치 사양을 공유 할 수 있다는 사실입니다. 여기에서 기본 가중치 (로케일 별 재정의 없음)를 찾을 수 있습니다 ( 100일련의 데이터 정렬은 유니 코드 v 5.0- Microsoft Connect 항목 에 대한 주석의 비공식적 인 확인 이라고 생각합니다 ).

http://www.unicode.org/Public/UCA/5.0.0/allkeys.txt

해당 코드 포인트 – U + FFFD –는 다음과 같이 정의됩니다.

FFFD  ; [*0F12.0020.0002.FFFD] # REPLACEMENT CHARACTER

이 표기법은 UCA의 9.1 Allkeys 파일 형식 섹션에 정의되어 있습니다 .

<entry>       := <charList> ';' <collElement>+ <eol>
<charList>    := <char>+
<collElement> := "[" <alt> <weight> "." <weight> "." <weight> ("." <weight>)? "]"
<alt>         := "*" | "."

Collation elements marked with a "*" are variable.

우리가보고있는 코드 포인트에는 실제로 "*"로 시작하는 사양이 있으므로 마지막 줄이 중요합니다. 3.6 가변 가중치 섹션 에는 직접 액세스 할 수없는 데이터 정렬 구성 값을 기반으로 정의 된 네 가지 가능한 동작이 있습니다 (대소 문자 구분이 소문자인지 먼저 사용되는지와 같이 각 데이터 정렬의 Microsoft 구현에 하드 코딩 됨). 먼저 대문자를 VARCHAR사용합니다. SQL_데이터 정렬을 사용하는 데이터와 다른 모든 변형 이 다른 속성 ).

나는 어떤 경로가 취해 졌는지에 대한 완전한 연구를 수행하고 더 확실한 증거가 제공되도록 어떤 옵션이 사용되고 있는지 추론 할 시간이 없지만, 각 코드 포인트 사양 내에서 무언가에 관계없이 말할 수 있습니다. "동일한"것으로 간주되는 것은 항상 전체 사양을 사용하지는 않습니다. 이 경우, 우리는 "0F12.0020.0002.FFFD"를 가지고 있으며 아마도 사용중인 레벨 2와 3 일 것입니다 (예 : .0020.0002. ). 메모장에서 ".0020.0002"에 "카운트"를 수행합니다. 12,581 개의 일치 항목을 찾습니다 (아직 처리하지 않은 보조 문자 포함). "[*"에서 "Count"를 수행하면 4049 개의 일치 항목이 리턴됩니다. 패턴을 사용하여 RegEx "찾기"/ "카운트"수행\[\*\d{4}\.0020\.0002832 개의 일치 항목을 반환합니다. 따라서이 조합의 어딘가에 내가 볼 수없는 다른 규칙과 Microsoft 특정 구현 세부 정보 가이 동작에 대한 전체 설명입니다. 분명히 말하면, 규칙이 적용되면 모두 동일한 가중치를 갖기 때문에 모든 일치하는 문자에 대해 동작이 동일합니다. 반드시 Mr. ).

COLLATE아래 쿼리를 통해 두 가지 버전의 데이터 정렬에서 다양한 감도가 어떻게 작동하는지 쿼리 아래 결과에 따라 절을 변경할 수 있습니다 .

;WITH cte AS
(
  SELECT     TOP (65536) ROW_NUMBER() OVER (ORDER BY (SELECT 0)) - 1 AS [Num]
  FROM       [master].sys.columns col
  CROSS JOIN [master].sys.objects obj
)
SELECT cte.Num AS [Decimal],
       CONVERT(VARBINARY(2), cte.Num) AS [Hex],
       NCHAR(cte.Num) AS [Character]
FROM   cte
WHERE  NCHAR(cte.Num) = NCHAR(0xFFFD) COLLATE Latin1_General_100_CS_AS_WS --N'�'
ORDER BY cte.Num;

다른 데이터 정렬에서 일치하는 다양한 문자 수는 다음과 같습니다.

Latin1_General_100_CS_AS_WS   =   5840
Latin1_General_100_CS_AS      =   5841 (The "extra" character is U+3000)
Latin1_General_100_CI_AS      =   5841
Latin1_General_100_CI_AI      =   6311

Latin1_General_CS_AS_WS       = 21,229
Latin1_General_CS_AS          = 21,230
Latin1_General_CI_AS          = 21,230
Latin1_General_CI_AI          = 21,537

위에 나열된 모든 데이터 정렬 N'' = N'�'에서 true로 평가됩니다.

최신 정보

좀 더 연구 할 수 있었고 여기에 내가 찾은 것이 있습니다.

"아마도"작동하는 방법

ICU 데이터 정렬 데모를 사용하여 로케일을 "en-US-u-va-posix"로 설정하고 강도를 "기본"으로 설정하고 "정렬 키"표시를 확인한 다음 위의 쿼리 결과 ( Latin1_General_100_CI_AICollation 사용) :

�
Ԩ
ԩ
Ԫ

그리고 그것은 다음을 반환합니다 :

Ԫ
    60 2E 02 .
Ԩ
    60 7A .
ԩ
    60 7A .
�
    FF FD .

그런 다음 http://unicode.org/cldr/utility/character.jsp?a=fffd 에서 " "의 문자 특성을 확인하고 레벨 1 정렬 키 (즉, FF FD)가 "uca"특성과 일치하는지 확인하십시오. 해당 "uca"속성을 클릭하면 검색 페이지 ( http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3Auca%3DFFFD%3A%5D)로 이동 합니다. 또한 allkeys.txt 파일에서 레벨 1 정렬 가중치는로 표시되며 이에 0F12대한 일치 항목은 1 개뿐입니다.

우리가 제대로 동작을 해석하고 있는지 확인하기 위해, 내가 다른 문자 보았다 : 그리스어 대문자 오 미크론 함께 VARIA 에서 http://unicode.org/cldr/utility/character.jsp?a=1FF8 하는있는 "UCA"( 즉, 레벨 1 정렬 중량 / 배열 요소) 5F30. "5F30"을 클릭하면 검색 페이지로 이동합니다 – http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3Auca%3D5F30%3A%5D – 30 개 일치, 20 개 중 20 개 표시 그것들은 0-65535 범위에 있습니다 (예 : U + 0000-U + FFFF). Code Point 1FF8 에 대한 allkeys.txt 파일을 살펴보면 레벨 1 정렬 가중치가입니다 . 메모장에서 "카운트"하기12E012E0. 30 개의 일치 항목을 표시합니다 (이 파일은 Unicode v 5.0 용이며 사이트에서 Unicode v 9.0 데이터를 사용하므로 보장되지는 않지만 Unicode.org의 결과와 일치 함).

SQL Server에서 다음 쿼리는 10 개의 보충 문자를 제거 할 때 Unicode.org 검색과 동일한 20 개의 일치 항목을 반환합니다.

;WITH cte AS
(
  SELECT TOP (65535) ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS [Num]
  FROM   [master].sys.columns col
  CROSS JOIN [master].sys.objects obj
)
SELECT cte.Num AS [Decimal],
       CONVERT(VARCHAR(50), CONVERT(VARBINARY(2), cte.Num), 2) AS [Hex],
       NCHAR(cte.Num) AS [Character]
FROM cte
WHERE NCHAR(cte.Num) = NCHAR(0x1FF8) COLLATE Latin1_General_100_CI_AI
ORDER BY cte.Num;

그리고 ICU 데이터 정렬 데모 페이지로 돌아가서 "입력"상자의 문자를 SQL Server의 20 개 결과 목록에서 가져온 다음 3 개의 문자로 바꾸십시오.


𝜪

실제로는 모두 동일한 5F 30레벨 1 정렬 가중치를 갖습니다 (캐릭터 속성 페이지의 "uca"필드와 일치).

따라서이 특정 캐릭터가 다른 것과 일치 해서는 안되는 것처럼 보입니다 .

실제로 작동하는 방식 (적어도 Microsoft-land)

SQL Server와 달리 .NET에는 CompareInfo.GetSortKey 메서드 를 통해 문자열의 정렬 키를 표시하는 방법이 있습니다. 이 방법을 사용하고 U + FFFD 문자 만 전달하면 정렬 키가 반환 0x0101010100됩니다. 그런 다음 0-65535 범위의 모든 문자를 반복하여 0x01010101004529 개의 일치 하는 정렬 키가있는 문자를 확인하십시오 . 이것은 Latin1_General_100_CS_AS_WS데이터 정렬을 사용할 때 SQL Server에서 반환 된 5840과 정확히 일치하지 않지만 유니 코드 v를 사용하는 Windows 10 및 .NET Framework 버전 4.6.1을 실행 중이라면 지금 가장 가깝습니다. CharUnicodeInfo 클래스 의 차트에 따른 6.3.0( "설명"섹션의 "발신자 메모"). 현재 SQLCLR 함수를 사용하고 있으므로 대상 Framework 버전을 변경할 수 없습니다. 기회가되면 콘솔 응용 프로그램을 만들고 유니 코드 v 5.0을 사용하는 대상 Framework 버전 4.5를 사용합니다 .100 버전은 Collations와 일치해야합니다.

이 테스트는 U + FFFD에 대해 .NET과 SQL Server간에 정확히 같은 수의 일치 항목이 없어도 이것이 SQL Server 관련 동작 이 아니며 구현에 대한 의도적이든 감독이든 확실 하지 않음 을 보여줍니다. Microsoft에서 U + FFFD 문자는 유니 코드 사양에 맞지 않아도 실제로 몇 문자와 일치합니다. 그리고이 문자가 U + 0000 (null)과 일치하면 가중치가 누락 된 것일 수 있습니다.

또한

=쿼리와 쿼리의 동작의 차이 LIKE N'%�%'와 관련하여 와일드 카드 및 이러한 � Ƕ Ƿ Ǹ문자 (예 :)의 누락 된 가중치와 관련이 있습니다. LIKE조건이 단순히 상태로 변경 되면 조건 LIKE N'�'과 동일한 3 행을 리턴합니다 =. 와일드 카드 관련 문제가 "누락 된"가중치로 인한 것이 아닌 경우 ( btw에 0x00의해 정렬 키가 반환 되지 않음) 정렬 키 CompareInfo.GetSortKey가 컨텍스트 (예 : 주변 문자)에 따라 달라질 수있는 특성을 가진 문자가 잠재적으로있을 수 있습니다. ).


감사합니다-allkeys.txt에 링크 된 것과 동일한 가중치가 부여되지 않은 것처럼 보입니다 FFFD( *0F12.0020.0002.FFFD하나만 검색 하면 결과가 반환 됨). @Forrest의 관찰에서 그것들은 모두 빈 문자열과 일치하고 주제에 대한 조금 더 많은 독서는 다양한 비 이진 데이터 정렬에서 공유하는 가중치처럼 보입니다.
Martin Smith

1
@MartinSmith 사용하여 몇 가지 조사 했 ICU 정렬 데모를 하고, 퍼팅 � A a \u24D0과 5839 경기에 있던 몇 가지 다른 설정을 발생합니다. 첫 번째 가중치를 건너 뛸 수없는 것 같습니다.이 대체 문자는로 시작하는 유일한 문자입니다 0F12. 다른 많은 사람들도 고유 한 첫 가중치를 가졌으며 많은 사람들이 allkeys 파일에서 완전히 빠져있었습니다. 따라서 이것은 인적 오류로 인한 구현 버그 일 수 있습니다. 데이터 정렬 차트의 유니 코드 사이트에있는 "지원되지 않는"그룹에서이 문자를 보았습니다. 내일 더 보일 것입니다.
Solomon Rutzky

Rextester는 4.5를 사용합니다. 실제로 해당 버전 (3385)에서 일치하는 항목이 더 적습니다. 어쩌면 내가 당신과 다른 옵션을 설정하고 있습니까? rextester.com/JBWIN31407
마틴 스미스

BTW의 정렬 키 01 01 01 01 00여기에 언급되어 archives.miloush.net/michkap/archive/2007/09/10/4847780.html (외모처럼 CompareInfo.InternalGetSortKey호출 LCMapStringEx)
마틴 스미스

@MartinSmith 나는 약간의 차이를 가지고 있지만 아직 차이점이 무엇인지 확실하지 않습니다. .NET이 실행되는 OS가 고려됩니다. 시간이 있으면 내일 더 잘 보일 것입니다. 일치하는 수에 관계없이, 이것은 적어도 동작의 이유를 확인하는 것으로 보입니다. 특히 연결된 블로그와 연결된 다른 블로그 덕분에 정렬 키 구조에 대한 통찰력이 생겼습니다. 내가 연결된 CharUnicodeInfo 페이지는 여기 내 제안의 기초가되는 기본 데이터 정렬 호출을 언급 : connect.microsoft.com/SQLServer/feedback/details/2932336을 :-)
솔로몬 Rutzky
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.