在电话字段的前 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 个字符(区号)上创建索引?的主要内容,如果未能解决你的问题,请参考以下文章