【问题标题】:How to optimise DB model for fast LIKE query with wildcards如何使用通配符优化数据库模型以实现快速 LIKE 查询
【发布时间】:2017-05-30 10:46:58
【问题描述】:

我想知道最有效的方法是优化 SQL Server 中的数据库模型,以便在开始时使用通配符进行 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+ 条记录。

我需要编写一个选择语句来查找包含 搜索字符串的记录(仅在 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 次插入。我可以强制索引只更新一次吗?

最后,由于复杂性,我宁愿不使用“三元组”解决方案。因此,任何提示或技巧都非常受欢迎。

提前谢谢

史蒂文

【问题讨论】:

标签: sql sql-server performance sqlperformance


【解决方案1】:

如果没有全文搜索,则需要进行完整的索引扫描。关于优化领先通配符扫描性能的唯一想法是使用旧版 SQL_* 排序规则而不是 Windows 排序规则。由于比较规则更简单但不太健壮,旧版排序规则的开销更少。

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

【讨论】:

  • 此时使用BINARY排序规则会更快
  • @StevenQ,同意二进制排序规则将是最快的。如果需要使用二进制排序规则进行不区分大小写的搜索,则需要以一致的大小写存储名称片段(如示例数据的大写)并以相同的大小写指定搜索字符串。
【解决方案2】:

一般来说,全文搜索(FTS)的主要目的是:

  1. 针对特定语言的词干(按词根搜索、丢弃前缀/后缀、变形等);
  2. 二进制格式(例如 DOC/X、PDF 和其他类似文件格式的文本)的索引。

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)时,您可能希望引入分区以将单个部分的大小保持在合理的范围内。现在只是小事一桩,所以不用担心这个或任何文件系统问题。

【讨论】:

  • 感谢您的回复。事实上,我没有提到 FTS,因为我已经遇到了这些限制。我的问题的目的更多是为了获得关于索引的提示和技巧,以及用于 sql server 微调的文件系统优化。
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-09
    • 1970-01-01
    • 2015-04-25
    • 2010-11-22
    • 2019-09-18
    • 2012-03-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多