점이있는 이메일에서 FTS가 예상대로 작동하지 않습니다


9

더 큰 시스템의 일부로 검색을 개발 중입니다.

우리는 Microsoft SQL Server 2014 - 12.0.2000.8 (X64) Standard Edition (64-bit)이 설정을 가지고 있습니다 :

CREATE TABLE NewCompanies(
    [Id] [uniqueidentifier] NOT NULL,
    [Name] [nvarchar](400) NOT NULL,
    [Phone] [nvarchar](max) NULL,
    [Email] [nvarchar](max) NULL,
    [Contacts1] [nvarchar](max) NULL,
    [Contacts2] [nvarchar](max) NULL,
    [Contacts3] [nvarchar](max) NULL,
    [Contacts4] [nvarchar](max) NULL,
    [Address] [nvarchar](max) NULL,
    CONSTRAINT PK_Id PRIMARY KEY (Id)
);
  1. Phone 구조화 된 쉼표로 구분 된 숫자 문자열입니다. "77777777777, 88888888888"
  2. Email같은 쉼표로 이메일 문자열을 구성되어 있습니다 "email1@gmail.com, email2@gmail.com"(같은 모든에서 쉼표 나없이 "email1@gmail.com")
  3. Contacts1, Contacts2, Contacts3, Contacts4사용자가 연락처 정보를 자유 형식으로 지정할 수있는 텍스트 필드입니다. "John Smith +1 202 555 0156"또는 처럼 "Bob, +1-999-888-0156, bob@company.com". 이 입력란에는 추가로 검색하려는 이메일과 전화가 포함될 수 있습니다.

여기서 우리는 전체 텍스트 물건을 만듭니다

-- FULL TEXT SEARCH
CREATE FULLTEXT CATALOG NewCompanySearch AS DEFAULT;  
CREATE FULLTEXT INDEX ON NewCompanies(Name, Phone, Email, Contacts1, Contacts2, Contacts3, Contacts4, Address)
KEY INDEX PK_Id

데이터 샘플입니다

INSERT INTO NewCompanies(Id, Name, Phone, Email, Contacts1, Contacts2, Contacts3, Contacts4) 
VALUES ('7BA05F18-1337-4AFB-80D9-00001A777E4F', 'PJSC Azimuth', '79001002030, 78005005044', 'regular@hotmail.com, s.m.s@gmail.com', 'John Smith', 'Call only at weekends +7-999-666-22-11', NULL, NULL)

실제로 우리는 약 10 만 개의 그러한 기록을 가지고 있습니다.

사용자는 "@ gmail.com"과 같은 이메일 부분을 지정할 수 있으며 모든 Email, Contacts1, Contacts2, Contacts3, Contacts4필드 에 Gmail 이메일 주소가있는 모든 행을 반환해야 합니다.

전화 번호도 동일합니다. 사용자는 "70283"과 같은 패턴을 검색 할 수 있으며이 숫자가 포함 된 전화를 쿼리로 반환해야합니다. Contacts1, Contacts2, Contacts3, Contacts4검색하기 전에 먼저 숫자와 공백 문자를 제외한 모든 문자를 제거해야하는 자유 형식 필드 의 경우에도 마찬가지입니다 .

우리 LIKE는 약 1500 레코드가 있었을 때 검색 에 사용 했지만 정상적으로 작동했지만 이제는 많은 레코드가 있으며 LIKE검색 결과가 무한대로 나타납니다.

이것이 우리가 거기에서 데이터를 얻는 방법입니다.

SELECT * FROM NewCompanies WHERE CONTAINS((Email, Contacts1, Contacts2, Contacts3, Contacts4), '"s.m.s@gmail.com*"') -- this doesn't get the row
SELECT * FROM NewCompanies WHERE CONTAINS((Phone, Contacts1, Contacts2, Contacts3, Contacts4), '"6662211*"') -- doesn't get anything
SELECT * FROM NewCompanies WHERE CONTAINS(Name, '"zimuth*"') -- doesn't get anything

5
왜 모든 열이 nvarchar(MAX)여기에 있습니까? 이름이 10 억 ~자인 사람에 대해 들어 본 적이 없거나 만나 본 적이 없습니다. 이 답변 에 따르면 이메일 주소는 254자를 초과 할 수 없습니다. 그래서 당신은 또한 10 억 ~ 낭비 문자가 있습니다.
Larnu

2
전체 텍스트 검색의 단어 분리기와 싸우는 것처럼 들립니다. 문자가 단어 분리기 @gmail.com이므로 검색어로 사용하는 것을 찾을 수 없습니다 @. 당신은 SQL Server 버전 따라 즉, 인덱스의 단어에 대한 user@gmail.com중 (A)입니다 user, gmailcom또는 (B) user, user@gmail.com, gmailcom. REF는 : 행동은 전체 텍스트 검색에 변경
AlwaysLearning

1
"하지만 해당 필드에서 이메일과 전화 이외의 것을 검색하고 싶지 않다" 는 말은 앞서 언급 한 것처럼 적절한 열에 저장해야합니다. 해당 데이터에 대한 열이 있으며 정규화해야합니다. 단어 분리기는 인스턴스 / 데이터베이스 수준에서 설정됩니다. 따라서 제거해야 할 중대한 변화가 될 것 .입니다.
라르 누

1
모든 전화, 전자 메일 등의 레코드에 대해 표를 1-M로 정규화하려고합니다. 두 번째 옵션은 열을 분할하는 것입니다 (외부 적용과 함께 열 _ 문자열 (이메일, ',') 사용). 사용자가 가질 수있는 이메일 수에 대한 이론적 제한을 지정한 후 다음과 같이 검색을 SELECT * FROM NewCompanies WHERE Id IN (SELECT ID from .... where MyOuterApply.EmailCol1 LIKE '%'+@SearchString+'%') OR Id IN (SELECT ID from .... where MyOuterApply.EmailCol2 LIKE '%'+@SearchString+'%')작성하십시오. 각 필드에 약 5 개의 개별 색인을 작성하고 기본 키를 포함하십시오.
starbyone

2
@TheDudeWithHat하지 않을 것을 의미하지는 않습니다. OP에 문제가있는 이유는 정규화가 없기 때문입니다.
Larnu

답변:


2

실제로 요청

SELECT [...] CONTAINS ([...], ' "6662211 *"')-아무것도 얻지 못합니다

에 대한 'Call only at weekends +7-999-666-22-11'

SELECT [...] CONTAINS (Name, ' "zimuth *"')-아무것도 얻지 못했습니다

에 맞서 'PJSC Azimuth'

이렇게 예상대로 일을 . 접두사 용어를
참조하십시오 . 때문에 하지 않은 것입니다 접두사 의 뿐만 아니라이 되지 접두사6662211*+7-999-666-22-11zimuth*Azimuth

에 관해서

SELECT [...] CONTAINS ([...], ' "sms@gmail.com*"')-행을 얻지 못함

이것은 항상 주석에서 지적한 단어 분리기 때문일 것입니다 . 보다 단어 차단기를

나는 당신의 작업에 전체 텍스트 검색이 적용 가능하다고 생각하지 않습니다.

LIKE 연산자와 동일한 작업에서 FTS를 사용해야하는 이유는 무엇입니까? LIKE 쿼리에 대해 더 나은 색인 유형이 있다면 완전히 다른 기술과 구문이 아니라 더 나은 색인 유형 이 있습니다 .
그리고 "6662211*""666 some random char 22 some random char 11" 과 일치시키는 데 도움이되지 않습니다 .
전체 텍스트 검색은 정규 표현식에 대한 "6662211*"것이 아니며 작업에 대한 올바른 표현조차 아닙니다- "임의의 임의의 문자"부분에 대한 것은 없습니다. 동의어, 단어 형식 등에 관한 것입니다.

그러나 부분 문자열을 효과적으로 검색하는 것이 가능합니까?

네 그렇습니다. 자신의 검색 엔진을 작성하는 것과 같은 전망을 떠나면 어떻게해야 SQL합니까?

우선, 데이터를 정리하는 것이 필수적입니다! 사용자가 입력 한 정확한 문자열을 사용자에게 반환하려면

사용자는 연락처를 자유 형식으로 지정할 수 있습니다

... 그대로 저장하고 그대로 둘 수 있습니다.
그런 다음 자유 형식 텍스트에서 데이터 를 추출 하고 (이메일 및 전화 번호로는 그렇게 어렵지 않습니다) 데이터를 정식 형식으로 저장해야합니다. 이메일의 경우, 당신이 정말로해야 할 유일한 일은 모두 소문자 나 대문자로 만들거나 (중요하지 않음) @노래로 나눌 수 있습니다. 그러나 전화 번호에는 숫자 만 남겨 두어야합니다
(... 그리고 숫자 로 저장할 수도 있습니다 . 공간과 시간을 절약 할 수는 있지만 검색이 다를 수 있습니다 ... 지금은 더 간단하게 뛰어 봅시다. 문자열을 사용하는 보편적 인 솔루션.)

MatthewBaker가 언급했듯이 접미사 테이블을 만들 수 있습니다. 그럼 당신은 그렇게 검색 할 수 있습니다

SELECT DISTINCT * FROM NewCompanies JOIN Sufficies ON NewCompanies.Id = Sufficies.Id WHERE Sufficies.sufficies LIKE 'some text%'

와일드 카드 %는 끝에 만 배치해야합니다 . 또는 접미사 테이블의 이점은 없습니다.

예를 들어 전화 번호를 보자

+ 7-999-666-22-11

폐 문자를 없애면 11 자리가됩니다. 전화 번호 하나에 11 개의 접미사가 필요할 것입니다.

           1
          11
         211
        2211
       62211
      662211
     6662211
    96662211
   996662211
  9996662211
 79996662211

따라서이 솔루션의 공간 복잡도는 선형 적입니다. 그렇게 나쁘지는 않습니다. 하지만 ... 레코드 수의 복잡성입니다. 그러나 기호에는 ... N(N+1)/2모든 접미사를 저장하는 기호 가 필요합니다. 즉, 2 차 복잡성입니다 ... 좋지 않습니다 ...하지만 100 000가까운 미래에 수백만 건의 기록이 있고 계획이 없다면-이와 함께 갈 수 있습니다 해결책.

공간 복잡성을 줄일 수 있습니까?

아이디어 만 설명하고 구현하려면 약간의 노력이 필요합니다. 그리고 아마도 우리는 경계를 넘어서야 할 것입니다SQL

2 개의 행 NewCompanies과 2 개의 자유 형식 텍스트 문자열 이 있다고 가정 해 봅시다 .

    aaaaa
    11111

접미사 테이블은 얼마나 커야합니까? 분명히, 우리는 단지 2 개의 레코드 만 필요합니다.

다른 예를 들어 봅시다. 또한 검색 할 2 개의 행, 2 개의 무료 텍스트 문자열. 그러나 지금은 :

    aa11aa
    cc11cc

현재 몇 개의 접미사가 필요한지 살펴 보겠습니다.

         a // no need, LIKE `a%`  will match against 'aa' and 'a11aa' and 'aa11aa'
        aa // no need, LIKE `aa%` will match against 'aa11aa'
       1aa
      11aa
     a11aa
    aa11aa
         c // no need, LIKE `c%`  will match against 'cc' and 'c11cc' and 'cc11cc'
        cc // no need, LIKE `cc%` will match against 'cc11cc'
       1cc
      11cc
     c11cc
    cc11cc

그렇게 나쁘지는 않지만 너무 좋지도 않습니다.

우리는 무엇을 더 할 수 있습니까?

사용자가 "c11"검색 필드에 입력한다고 가정 해 봅시다 . 그런 다음 성공 LIKE 'c11%'하려면 ' c11 cc'접미사가 필요합니다 . 뿐만 경우 대신 검색 "c11"우리가 처음 검색 "c%"을 위해 다음, "c1%"등등? 첫 번째 검색은 에서 하나의 행 으로 제공됩니다 NewCompanies. 그리고 후속 검색이 필요하지 않습니다. 그리고 우리는 할 수 있습니다

       1aa // drop this as well, because LIKE '1%' matches '11aa'
      11aa
     a11aa // drop this as well, because LIKE 'a%' matches 'aa11aa'
    aa11aa
       1cc // same here
      11cc
     c11cc // same here
    cc11cc

그리고 우리는 단지 4 개의 접미사로 끝납니다

      11aa
    aa11aa
      11cc
    cc11cc

이 경우 공간 복잡성이 무엇인지 말할 수는 없지만 수용 가능한 것처럼 느낍니다.


1

이 전체 텍스트 검색과 같은 경우에는 이상적이지 않습니다. 나는 당신과 같은 보트에있었습니다. 검색이 너무 느리 듯이 전체 텍스트 검색은 용어를 포함하지 않고 용어로 시작하는 단어를 검색합니다.

우리는 몇 가지 솔루션을 시도했습니다. 순수한 SQL 옵션 중 하나는 자신의 전체 텍스트 검색 버전, 특히 역 인덱스 검색을 빌드하는 것입니다. 우리는 이것을 시도했지만 성공했지만 많은 공간을 차지했습니다. 부분 검색 용어에 대한 보조 보류 테이블을 작성하고 이에 대한 전체 텍스트 색인 작성을 사용했습니다. 그러나 이것은 동일한 사본을 여러 개 반복해서 저장했음을 의미합니다. 예를 들어 "longword"를 Longword, ongword, ngword, gword ... 등으로 저장했습니다. 따라서 포함 된 구는 항상 색인 용어의 시작 부분에있게됩니다. 결함으로 가득 찬 끔찍한 해결책이지만 효과가있었습니다.

그런 다음 조회를 위해 별도의 서버를 호스팅하는 것을 보았습니다. 인터넷 검색 Lucene과 elastisearch는 이러한 패키지에 대한 유용한 정보를 제공합니다.

결국, 우리는 SQL과 함께 실행되는 자체 검색 엔진을 자체 개발했습니다. 이를 통해 음성 검색 (이중 메타 폰)을 구현 한 다음 sideex와 함께 levenshtein 계산을 사용하여 관련성을 설정할 수있었습니다. 많은 솔루션을 위해 과잉하지만 우리의 사용 사례에서 노력할 가치가 있습니다. 우리는 지금 cuda 검색을 위해 Nvidia GPU를 활용할 수있는 옵션을 가지고 있지만 이것은 완전히 새로운 두통과 잠 못 이루는 밤을 나타냅니다. 이 모든 것들의 관련성은 검색이 얼마나 자주 수행되는지, 얼마나 반응이 필요한지에 달려 있습니다.


1

전체 텍스트 인덱스에는 여러 가지 제한이 있습니다. 색인에서 찾은 단어가 전체 "부분"이라는 단어에 와일드 카드를 사용할 수 있지만 단어의 끝 부분으로 제한됩니다. 그렇기 때문에 사용할 수는 CONTAINS(Name, '"Azimut*"')있지만CONTAINS(Name, '"zimuth*"')

Microsoft 설명서에서 :

접두어 용어가 구인 경우 구를 구성하는 각 토큰은 별도의 접두사 용어로 간주됩니다. 접두사 용어로 시작하는 단어가있는 모든 행이 반환됩니다. 예를 들어 접두사 "light bread *"는 "light breaded", "lighted breaded"또는 "light bread"라는 텍스트가있는 행을 찾지 만 "약간 구운 빵"을 반환하지는 않습니다.

제목에 표시된 이메일의 점은 주요 문제가 아닙니다. 예를 들어 다음과 같이 작동합니다.

SELECT * FROM NewCompanies 
WHERE CONTAINS((Email, Contacts1, Contacts2, Contacts3, Contacts4), 's.m.s@gmail.com') 

이 경우 색인은 전체 이메일 문자열과 "gmail"및 "gmail.com"을 모두 유효한 것으로 식별합니다. "sms"만 유효하지 않습니다.

마지막 예는 비슷합니다. 전화 번호의 일부는 색인화되어 있지만 (예 : 666-22-11 및 999-666-22-11) 하이픈을 제거해도 색인에서 알 수있는 문자열이 아닙니다. 그렇지 않으면 이것이 작동합니다.

SELECT * FROM NewCompanies 
WHERE CONTAINS((Phone, Contacts1, Contacts2, Contacts3, Contacts4), '"666-22-11*"')
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.