基于varchar的SQL索引

基于varchar的SQL索引,sql,performance,postgresql,indexing,Sql,Performance,Postgresql,Indexing,我有一个表,它的列是varchar(50)和float。我需要(很快)查找与给定字符串关联的浮点。即使使用索引,这也是相当缓慢的 但是,我知道每个字符串都与一个整数相关联,我在查找时就知道这个整数,因此每个字符串映射到一个唯一的整数,但每个整数并不映射到一个唯一的字符串。有人可能会认为它是一个树结构 将此整数添加到表中,对其进行索引,并使用如下查询,是否可以获得任何结果: SELECT floatval FROM mytable WHERE phrase=givenstring AND asso

我有一个表,它的列是
varchar(50)
float
。我需要(很快)查找与给定字符串关联的浮点。即使使用索引,这也是相当缓慢的

但是,我知道每个字符串都与一个整数相关联,我在查找时就知道这个整数,因此每个字符串映射到一个唯一的整数,但每个整数并不映射到一个唯一的字符串。有人可能会认为它是一个树结构

将此整数添加到表中,对其进行索引,并使用如下查询,是否可以获得任何结果:

SELECT floatval FROM mytable WHERE phrase=givenstring AND assoc=givenint

这是Postgres,如果你不知道,我对数据库的经验很少。

通过在
(phrase,assoc,floatval)
上声明一个索引,你将得到一个“覆盖索引”,它允许在问题中发布的查询在不访问表的情况下执行。假设
phrase
assoc
单独是高度选择性的(没有多少行共享该字段的相同值),单独在该字段上创建索引应该会产生几乎相同的性能


通常,您会希望将索引的数量限制为使频繁查询达到所需性能的最小集合。对于您添加到表中的每个索引,您需要支付一些磁盘空间,但更重要的是,您需要付出让DBMS在表中的每个
插入
上做更多工作的代价。

尝试添加int并在int上创建索引不会有什么坏处,varchar和include float—这将是一种覆盖性的、相当有效的方法—不确定Postgres是否包含列—如果它不简单地将其添加到索引本身的话

您还可以研究其他几种技术(我不熟悉Postgres的所有功能,因此我将按SQL Server名称给出它们):

索引视图-您可以有效地具体化一个连接多个表的视图-因此您可以将varchar连接到int,并将索引放在int、varchar和float上


包含列-您可以在索引中包含列以确保索引覆盖-即,在varchar include(float)上有一个索引-如果您的索引不覆盖,查询优化器仍然必须使用索引,然后进行书签查找以获取剩余数据。

VARCHAR
列上的键可能非常长,这会导致每页记录更少,深度更高(在
B-树中的级别更高)。较长的索引也会增加缓存未命中率

平均有多少字符串映射到每个整数

如果相对较少,则只能在整数列上创建索引,
PostgreSQL
将对记录进行精细筛选:

CREATE INDEX ix_mytable_assoc ON mytable (assoc);

SELECT  floatval
FROM    mytable
WHERE   assoc = givenint
        AND phrase = givenstring

您也可以考虑在字符串散列上创建索引:

CREATE INDEX ix_mytable_md5 ON mytable (DECODE(MD5(phrase), 'HEX'));

SELECT  floatval
FROM    mytable
WHERE   DECODE(MD5(phrase), 'HEX') = DECODE(MD5('givenstring'), 'HEX')
        AND phrase = givenstring -- who knows when do we get a collision?

每个散列只有
16
字节长,因此索引键将短得多,同时几乎完美地保留了选择性。

我建议只使用散列索引:

create index mytable_phrase_idx on mytable using hash(phrase);
这样你就可以

select floatval from mytable where phrase='foo bar';
会很快的。测试这一点:

create temporary table test ( k varchar(50), v float);
insert into test (k, v) select 'foo bar number '||generate_series(1,1000000), 1;
create index test_k_idx on test using hash (k);
analyze test;
explain analyze select v from test where k='foo bar number 634652';
查询计划 ----------------------------------------------------------------------------------------------------------------- 在测试中使用test_k_idx进行索引扫描(成本=0.00..8.45行=1宽度=8)(实际时间=0.201..0.206行=1循环=1) 索引条件:((k)::text='foo bar number 634652'::text) 总运行时间:0.265毫秒 (3排)
简短回答:是的,会有很多收获。至少只要你没有太多的更新,但是即使有更新,开销也很可能不会引起注意。

PostgreSQL
不支持索引视图或包含的列,但它支持基于函数的索引(你不必具体化表达式来索引它).PostgreSQL没有覆盖索引,因此该索引肯定会丢失。@Magnus:那么,即使索引覆盖了回答查询所需的所有字段,PostgreSQL也必须访问实际表才能检索值?你对此有什么参考资料吗?我有点好奇,想知道为什么:)从9.2开始,PostgreSQL现在只有索引扫描:在那篇文章的顶部有关于为什么以前没有的详细信息:对于PostgreSQL索引,“无法直接确定当前事务是否可以看到任何给定的元组”。使用varchar比较索引键也要昂贵得多,因为他们知道现场情况。整数索引肯定会比其他任何选项快得多。@Magnus:比较应该只进行
log(n)
次,所以我不会说这“昂贵得多”,但你是对的,它确实也增加了一些
CPU
周期。在这个测试表中,我看不到btree和hash之间的区别。 QUERY PLAN ----------------------------------------------------------------------------------------------------------------- Index Scan using test_k_idx on test (cost=0.00..8.45 rows=1 width=8) (actual time=0.201..0.206 rows=1 loops=1) Index Cond: ((k)::text = 'foo bar number 634652'::text) Total runtime: 0.265 ms (3 rows)