我有一个零件数据库,我将不断查询报价系统。零件数据库中有1,400,000多条记录。用户只是开始键入部件号,他们希望系统只能在几个字符后找到,所以我需要能够进行通配符搜索,例如:
SELECT NeededFields FROM Parts WHERE PartNumber LIKE 'ML%'
我是否可以执行任何类型的优化以尝试从这种类型的查询中获得最大的性能?我将PartNumber字段编入索引,但我不确定这是否是我能得到的最好的。我愿意考虑将数据库中内置的备用索引结构与SQL索引分开。主键是Guid,但我需要这个用于复制,因为我使用的是特定的数据结构。
答案 0 :(得分:4)
大多数(好的)优化器都会尝试使用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可能会有一些警告。
请注意,如果有另一个提供选择性的索引,则可以优先使用该索引来提供对通配符搜索的访问。
答案 1 :(得分:4)
我猜你的主键(GUID)可能有一个聚簇索引。您可能需要考虑使主键不被群集。相反,您可以聚集为PartNumber创建的索引。 (每个表只能有一个聚簇索引)
您还应该考虑在查询中添加TOP谓词,以便只返回前100行(或左右)。我在想......如果用户第一次输入M,可能会有几十万个匹配,加载速度很慢。通过限制行数,您应该获得更好的性能。
答案 2 :(得分:1)
如何在partnumber字段上对表进行分区。您可以将表拆分为不同的卷。
A卷持有a-m
卷B保持n-z
编辑顺便说一句,从来没有这样做过。
答案 3 :(得分:1)
此查询看起来很好!如果该字段已编入索引并且您正在执行LIKE 'term%'
查询,那么通配符在最后,您应该获得优化的执行计划。
根据您的DBMS,您可以检查优化程序对EXPLAIN关键字的真正作用。
答案 4 :(得分:1)
通过使用部件号的前2或3个字符对表进行分区来进行实验。 试验分区本地索引与全局索引。
答案 5 :(得分:1)
我很好奇,
您可以扩展您的问题以包含以下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%'
如果事实证明第一个/第二个查询比最后一个查询慢一点,您可以看一下引入一个维护这些情况的缓存表(用触发器或作业更新它)
另外,我想我只是注意到了一些事情,你的指数是否完全覆盖?如果不是,您可能会在结果计数达到阈值时获得表扫描。
答案 6 :(得分:1)
我会使用全文搜索。通过这种查询,您的结果几乎是即时的。
答案 7 :(得分:1)
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)
此查询需要多更长时间。
答案 8 :(得分:0)
考虑不使用SQL。
为您的用户界面创建一些您需要的数据转储。一百万条记录不是很多 - 考虑制作26
个文件 - 每个“首字母”一个并存储信息。它包括最受欢迎的条目。
如果您想要更具体的特性(或用户开始滚动),您可以为每个文件26*26
,AB
,AC
等提供AD
个文件剩余的记录。
如果您坚持使用SQL,请对其进行概要分析。尝试为前导字符创建索引,例如
CREATE INDEX partno_idx ON parts (SUBSTRING(partnumber,0,1))
(或者您的本地SQL方言)然后使用WHERE SUBSTRING(partnumber,0,1)='M'
之类的查询。结果将避免前缀搜索,这些搜索很少比整个字符串(或整数)索引(可能使用哈希表或跳过列表)快。
分析这一点非常重要:将字符转换为ASCII码可能会更快。您的数据集以及SQL服务器软件和版本将具有高度相关性。
答案 9 :(得分:0)
如果您使用的是mysql,请考虑创建部分索引:
mysql> CREATE INDEX part_of_name ON customer (name(10));
索引只会匹配10个第一个字符