在电话字段的前 3 个字符(区号)上创建索引?

Posted

技术标签:

【中文标题】在电话字段的前 3 个字符(区号)上创建索引?【英文标题】:Create index on first 3 characters (area code) of phone field? 【发布时间】:2013-10-08 18:58:14 【问题描述】:

我有一个 Postgres 表,其中电话字段存储为 varchar(10),但我们经常搜索区号,例如:

select * from bus_t where bus_phone like '555%'

我想创建一个索引来促进这些搜索,但尝试时出错:

CREATE INDEX bus_ph_3 ON bus_t USING btree (bus_phone::varchar(3));

ERROR: 42601: syntax error at or near "::"

我的第一个问题是,我该如何做到这一点,但我也想知道对字段的前 X 个字符进行索引是否有意义,或者对整个字段进行索引是否同样有效。

【问题讨论】:

如果你打算创建索引,不妨只索引整个字段。 对于 phone # 字段,索引整个内容可能有意义,但对于更大的字段,构建和维护该索引的成本实际上可能会损害性能。 【参考方案1】:

实际上,对于LIKE (~~) 或正则表达式 (~) 的模式匹配,普通 B 树索引 通常无用,即使使用左锚定模式,如果您的安装在"C" 以外的任何其他语言环境上运行,这是典型情况。这是overview over pattern matching and indices in a related answer on dba.SE

使用varchar_pattern_ops 运算符类(匹配您的varchar 列)创建索引,并确保使用read the chapter on operator classes in the manual。

CREATE INDEX bus_ph_pattern_ops_idx ON bus_t (bus_phone <b>varchar_pattern_ops</b>);

您的原始查询可以使用此索引:

... WHERE bus_phone LIKE '555%'

functional index on the first 3 characters as described in the answer by @a_horse 在这种情况下的性能几乎相同。

-> SQLfiddle demo.

一般来说,在相关前导字符上使用functional index是个好主意,但您的列只有 10 个字符。考虑overhead per tuple is already 28 bytes。节省 7 个字节不足以产生重大影响。添加函数调用的成本和xxx_pattern_ops are generally a bit faster. 在 Postgres 9.2 或更高版本中,完整列上的索引也可以用作 covering index in index-only scans.

但是,列中的字符越多,功能索引的好处就越大。You may even have to resort to a prefix index (or some other kind of hash) if the strings get too long. There is a maximum length for indices.

如果您决定使用功能索引,请考虑使用xxx_pattern_ops 变体以获得额外的性能优势。请务必阅读in the manual 和Peter Eisentraut's blog entry 中的优缺点:

CREATE INDEX bus_ph_3 ON bus_t (left(bus_phone, 3) varchar_pattern_ops);

解释错误信息

您必须将standard SQL cast syntax 用于功能索引。这会起作用 - 很像带有left() 的那个,但像@a_horse 我更喜欢left()

CREATE INDEX bus_ph_3 ON bus_t USING btree (cast(bus_phone AS varchar(3));

【讨论】:

你是对的,我应该提到text_pattern_ops来支持左锚定LIKE条件【参考方案2】:

当使用like '555%' 时,也会使用完整列上的索引。无需仅索引前三个字符。

如果您只想索引前 3 个字符(例如,为了节省空间),那么您可以使用 left() 函数:

CREATE INDEX bus_ph_3 ON bus_t USING btree (left(bus_phone,3));

但为了使用该索引,您需要在 where 子句中使用该表达式:

where left(bus_phone,3) = '555';

但又一次:这很可能是矫枉过正,完整列上的索引就足够了,也可以用于其他查询,例如bus_phone = '555-1234' 前三个字符的索引不会。

【讨论】:

因为某些用户可能在使用where bus_phone::varchar(3) 或其他变体,我认为您对整个列进行索引的建议最有意义 - 谢谢! @user35581: bus_phone::varchar(3) 没有任何意义。为什么会有人想这样做? 不要切线,但是转换为varchar(3)有什么问题? @user35581:对我来说似乎相当可疑 - 但这可能是个人喜好。我更喜欢说明我的意图是什么。在这种情况下,我认为使用substr()left() 可以明确最终目标是什么。类型转换主要是改变一个值的 type,而不是它的长度。对我来说这是代码味道(但同样:这可以看作是个人偏好或编码风格) @user35581:我为您收到的错误消息添加了解释。但我同意 a_horse 并更喜欢 left() 作为功能索引。

以上是关于在电话字段的前 3 个字符(区号)上创建索引?的主要内容,如果未能解决你的问题,请参考以下文章

mysql之字符串字段添加索引

MYSQL之索引配置方法分类

jQuery Validation 突出显示两个字段的一个标签

用 HIVEQL 将电话区号与电话分开

js验证电话号码(包括手机,固定电话,带区号,不带区号))

将电话号码与地址簿中的确定性匹配