如何使用通配符

时间:2017-05-30 10:46:58

标签: sql sql-server performance sqlperformance

我想知道最有效的方法是在SQL服务器中优化数据库模型,以便在开头使用带有通配符的LIKE进行查询。我不是数据库专家,因此欢迎使用索引或其他优化的所有建议。

情况: 我有一个表'产品',其中包含以下列:

ShortNameEN (varchar(50))
ShortNameFR (varchar(50))
ShortNameDE (varchar(50))
ShortNameNL (varchar(50))
LongNameEN (varchar(250))
LongNameFR (varchar(250))
LongNameDE (varchar(250))
LongNameNL (varchar(250))

此表包含300000多条记录。

我需要编写一个select语句来查找包含搜索字符串的记录(仅在ShortNameEN中)。 我的问题是

SELECT * 
FROM Products 
WHERE ShortNameEN LIKE '%searchstring%'

当然,这个查询非常慢。在ShortNameEN上添加索引将无济于事,因为由于第一个通配符而不会使用它们。

问题1:将ShortNameEN列与表格的其余部分分开是否有意义?我不知道磁盘访问/行大小/页面大小以及这将如何影响性能。也许还有其他与文件系统相关的优化可以提高性能吗?

临时解决方案

我找到了一个创造性的“三元组”解决方案,但对我的模型产生了相当大的影响。为此我创建了第二个表'ProductNameFragments',它引用了我的初始表,并按以下方式分解每个ShortNameEN:

ProductId = 123,ShortNameEN ='PRINTER'

的示例
ProductId | NameFragment
123       | PRINTER
123       | RINTER
123       | INTER
123       | NTER
123       | TER
123       | ER
123       | R

Product表上的触发器将同步ProductNameFragments表。

这样我可以加入我的两个表,并在没有初始通配符的情况下进行查询。

SELECT p.* 
FROM Product p, ProductNameFragment pnf
WHERE p.Id = pnf.ProductId
AND pnf.NameFragment LIKE '%searchstring%'

首次测试表明,这大大提高了我的搜索查询效果。

问题2:我应该在ProductNameFragment上使用常规索引还是聚簇索引?在更新/删除/插入产品时,这将如何影响性能? 更新一个产品名称时,这可能会导致ProductNameFragments表中的50个删除和50个插入。我可以强制索引只更新一次吗?

最后,由于复杂性,我宁愿不使用'trigram'解决方案。所以任何提示或技巧都非常受欢迎。

提前谢谢

史蒂芬

3 个答案:

答案 0 :(得分:2)

如果没有全文搜索,则需要进行完整的索引扫描。关于优化主要通配符扫描性能的唯一想法是使用旧版SQL_ *排序规则而不是Windows排序规则。由于比较规则更简单(尽管不那么健壮),传统的排序规则会减少开销。

我建议在ProductFragmentProductID列上使用聚簇索引来优化产品级操作。或者,ProductIDNameFragment自然键上的聚簇主键将优化插入/更新/删除操作并确保数据完整性。

答案 1 :(得分:1)

通常,全文检索(FTS)的主要目的是:

  1. 关于特定语言的词干(以root搜索,丢弃前/后缀,变形等);
  2. 二进制格式的索引(例如DOC / X,PDF和其他类似文件格式的文本)。
  3. Microsoft SQL Server附带的FTS引擎没有领先的通配符搜索,所以不要打扰。

    你提到的解决方案是AFAIK,它是唯一一款能够为领先的通配符提供任何体面性能的解决方案。此外,任何声称具有这种功能的FTS产品都将实现这种非常重要的功能。窗帘背后的算法。

    对于您自己的实现,像这样的表将是一个良好的开端:

    create table dbo.TextFragments (
      TextFragment nvarchar(...) not null, -- Maximum size depends on your data
      LanguageId int not null,
      EntityId int not null,
      RowId bigint not null,
      constraint [PK_TextFragments] primary key (TextFragment, LanguageId, EntityId, RowId)
    );
    

    我把所有语言放在一个表中;如果没有这个,为您的系统添加新语言会变得相当复杂。当然,您也需要一个语言查找表。

    EntityId字段允许您索引来自不同表的数据。如果您只有一个表并且不打算将其他表编入索引,则可以删除该字段。

    RowId字段将行的标识符存储在与该片段匹配的相应表中。当然,您可以调整数据类型或添加其他列以使其适用于您的系统。

    正如其他人所建议的那样,您可能希望使用文本片段的排序规则和大小写来优化搜索。将来,如果/当您的系统存储更多条目(比如100M)时,您可能需要引入分区以将单个部分的大小保持在合理的限制范围内。现在它是花生,所以不要担心,或者任何文件系统问题。

答案 2 :(得分:-1)

我会尝试添加一个带有回文的额外列,并用LIKE'keyword%'或LIKE'droweyk%'替换%keyword%query。这也是一个黑客