繁体   English   中英

SQL中的最佳LIKE搜索

[英]Optimal LIKE search in SQL

我有一个零件数据库,我将不断查询报价系统。 零件数据库中有1,400,000多条记录。 用户只是开始键入部件号,他们希望系统只能在几个字符后找到,所以我需要能够进行通配符搜索,例如:

SELECT NeededFields FROM Parts WHERE PartNumber LIKE 'ML%'

我是否可以执行任何类型的优化以尝试从这种类型的查询中获得最大的性能? 我将PartNumber字段编入索引,但我不确定这是否是我能得到的最好的。 我愿意考虑将数据库中内置的备用索引结构与SQL索引分开。 主键是Guid,但我需要这个用于复制,因为我使用的是特定的数据结构。

大多数(好的)优化器都会使用一个LIKE子句的索引,而子卡不会首先出现。 如果模式以外卡开头,那么他们可以做的就更少了。

如果索引是B-Tree索引,而不是哈希索引(ISAM系统通常使用B-Trees),则该子句的前导字符可用于约束索引搜索。 如果系统使用哈希索引,则无法轻松处理部分字符串,除非您在第一个字符上创建单独的索引,然后在前两个字符上创建单独的索引,然后是列的前三个字符.... ISAM系统可能允许您灵活性; 大多数SQL系统都没有,您必须创建包含零件号字段的前1,2,3 ...字符的1,2,3,...字符列。

补充 :评论问“哪个DBMS?”,这是公平的。 我可以在您可以使用的任何版本中担保IBM Informix Dynamic Server(IDS)和Standard Engine(SE)。 我希望IBM DB2(LUW或z / OS)能够做到这一点; 我希望Oracle能够做到这一点。 评论表明PostgreSQL 8.0及以上版本是这样做的 - 需要注意。 我无法回答自己对Sybase,Ingres,MS SQL Server,Firebird或MySQL的了解。 关于何时可以使用索引,可能存在与每个DBMS相关的警告。

请注意,如果存在另一个提供选择性的索引,则可以优先使用该索引来提供对通配符搜索的访问。

我猜你的主键(GUID)可能有一个聚集索引。 您可能需要考虑使主键不被群集。 相反,您可以聚集为PartNumber创建的索引。 (每个表只能有一个聚簇索引)

您还应该考虑在查询中添加TOP谓词,以便只返回前100行(或左右)。 我在想......如果用户第一次输入M,可能会有几十万个匹配,加载速度很慢。 通过限制行数,您应该获得更好的性能。

你如何在partnumber字段上分区表。 您可以将表拆分为不同的卷。

A卷持有上午
卷B保持nz

编辑从来没有这样做过。

请参阅此理论http://msdn.microsoft.com/en-us/library/ms345146.aspx

这个查询看起来很好! 如果该字段已编制索引并且您正在执行LIKE 'term%'查询(通配符在最后),则应获得优化的执行计划。

根据您的DBMS,您可以检查优化器对EXPLAIN关键字的真正作用。

通过使用部件号的前2或3个字符对表进行分区来进行实验。 试验分区本地索引与全局索引。

我很好奇,

您可以扩展您的问题以包括以下4个查询的持续时间:

SELECT top 100 NeededFields FROM Parts WHERE PartNumber LIKE '%'
SELECT top 100 NeededFields FROM Parts WHERE PartNumber LIKE 'M%'
SELECT top 100 NeededFields FROM Parts WHERE PartNumber LIKE 'ML%'
SELECT top 100 NeededFields FROM Parts WHERE PartNumber LIKE 'ML0833%'

如果事实证明第一个/第二个查询比最后一个查询慢一点,您可以看一下引入一个维护这些情况的缓存表(用触发器或作业更新它)

另外,我想我刚刚注意到了一些事情,你的指数是否完全被覆盖? 如果不是,您可能会在结果计数达到阈值时获得表扫描。

我会使用全文搜索。 通过这种查询,您的结果几乎是即时的。

当通配符结束时, PostgreSQL似乎无法优化LIKE查询。

这里,表区域在列名称上编制索引:

CREATE INDEX index_zones_name ON Zones(name);

索引用于=查询:

EXPLAIN SELECT id FROM Zones WHERE name = 'toto.fr';
                              QUERY PLAN                                   
-------------------------------------------------------------------------------
 Index Scan using index_zones_name on zones  (cost=0.00..21.06 rows=4 width=4)
   Index Cond: (name = 'toto.fr'::text)

尽管有三百万行,但查询是即时的。

但是,对于LIKE查询:

EXPLAIN SELECT id FROM Zones WHERE name LIKE 'toto%';
                   QUERY PLAN                        
---------------------------------------------------------
 Seq Scan on zones  (cost=0.00..75991.43 rows=1 width=4)
   Filter: (name ~~ 'toto%'::text)

和查询需要更长的时间。

考虑不要使用SQL。

为您的用户界面创建一些您需要的数据转储。 一百万条记录不是很多 - 考虑制作26 - 每个“首字母”一个并存储信息。 它包括最受欢迎的条目。

如果您想要更多特异性(或用户开始滚动),您可以为每个剩余记录多制作26*26个文件( ABACAD等)。

如果您坚持使用SQL,请对其进行概要分析。 尝试主角创建一个索引,例如

CREATE INDEX partno_idx ON parts (SUBSTRING(partnumber,0,1))

(或者你的本地SQL方言)然后使用像WHERE SUBSTRING(partnumber,0,1)='M'这样的查询。 结果将避免前缀搜索,这些搜索很少比整个字符串(或整数)索引(可能使用哈希表或跳转列表)更快。

分析这一点非常重要:将字符转换为ASCII码可能会更快。 您的数据集以及SQL服务器软件和版本将具有高度相关性。

如果您使用的是mysql,请考虑创建部分索引:

mysql> CREATE INDEX part_of_name ON customer (name(10));

索引仅匹配10个第一个字符

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM