sql - FTS无法正常处理带有点的电子邮件

标签 sql sql-server indexing full-text-search full-text-indexing

我们正在将搜索作为更大系统的一部分进行开发。

我们的安装程序具有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)
);
  • Phone是一个结构化的逗号分隔数字字符串,例如"77777777777, 88888888888"
  • Email是结构化的电子邮件字符串,带有逗号,例如"email1@gmail.com, email2@gmail.com"(或根本没有逗号)"email1@gmail.com")
  • 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字段,我们在搜索之前都应该首先删除除数字和空格字符之外的所有字符。

    当我们有大约1500条记录时,我们曾经使用LIKE进行搜索,但是效果很好,但是现在我们有很多记录,并且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
    

    最佳答案

    实际要求

    SELECT [...] CONTAINS([...], '"6662211*"') -- doesn't get anything


    反对'Call only at weekends +7-999-666-22-11'

    SELECT [...] CONTAINS(Name, '"zimuth*"') -- doesn't get anything


    反对'PJSC Azimuth'像预期的那样完成的工作。
    参见Prefix Term。因为6662211*不是+7-999-666-22-11前缀,而且zimuth*不是Azimuth前缀
    至于

    SELECT [...] CONTAINS([...], '"s.m.s@gmail.com*"') -- this doesn't get the row


    这可能是由于alwayslearning在注释中指出的断字符。参见word-breakers
    我认为全文搜索不适用于您的任务。
    为什么要在与LIKE运算符完全相同的任务中使用FTS?如果对于LIKE查询有更好的索引类型...那么将有更好的索引类型
    ,而不是完全不同的技术和语法。
    而且绝不会帮助您将"6662211*"与“666 一些任意char 22 一些任意char 11”进行匹配。
    全文搜索与正则表达式无关(而"6662211*"甚至不是该工作的正确表达-与“任意字符”部分无关),它与同义词,词形等有关。
    但是,是否有可能有效地搜索子字符串?
    是的。除了编写自己的搜索引擎这样的前景之外,我们在SQL中还能做什么?
    首先-必须清理您的数据!
    如果您想向用户返回他们输入的确切字符串

    users can specify contact details in free form


    ...您可以按原样保存它们...并保留它们。
    然后,您需要从自由格式文本中提取数据(对于电子邮件和电话号码来说并不难)并以某种规范形式保存数据。
    对于电子邮件,您真正需要做的唯一一件事-将它们全部转换为小写或大写(没关系),然后拆分然后在@上唱歌。但是在电话号码中,您只需要保留数字
    (...然后您甚至可以将它们存储为数字。这样可以节省一些时间和空间。但是搜索将有所不同...现在让我们进入使用字符串的更简单通用的解决方案中。)
    作为MatthewBaker mentioned,您可以创建后缀表。
    然后,您可以像这样搜索
    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符号来存储所有后缀-这是二次复杂性...不好...但是如果您现在拥有100 000记录并且在不久的将来没有数百万的计划-您可以用这个解决方案。
    我们可以降低空间复杂度吗?
    我只会描述这个想法,要实现它需要一些努力。可能我们需要跨越SQL的边界
    假设您在NewCompanies中有2行,其中有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
    
    最后只有四个后缀
          11aa
        aa11aa
          11cc
        cc11cc
    
    我不能说这种情况下的空间复杂度如何,但是感觉可以接受。

    关于sql - FTS无法正常处理带有点的电子邮件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60277261/

    相关文章:

    sql - 联合、排除或拦截以在选择中使用默认数据?

    sql - 基于层次结构树将员工关联到部门

    sql - 获取列和关联列名称之间的最大值

    sql-server - Django 模型选择 : IntegerField vs CharField

    mysql - 查询将键值表转换为人类可读的表?

    mysql存储过程问题: insert runs first and always whatever If condition I put

    mysql - 一个属性可以同时是pk和fk吗?

    mysql - sql 如何正确使用 HAVING 选择结果

    python - pandas 从日期时间索引中删除秒

    elasticsearch - elasticsearch 如何从其索引中获取 AND 运算符查询