我们正在将搜索作为更大系统的一部分进行开发。
我们的安装程序具有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/