2010-04-13 2 views
12

У меня есть таблица с столбцами varchar(50) и float. Мне нужно (очень быстро) посмотреть получить float, связанный с данной строкой. Даже с индексированием это довольно медленно.SQL-индексирование на varchar

Я знаю, однако, что каждая строка связана с целым числом, которое я знаю во время поиска, так что каждая строка сопоставляется с уникальным целым числом, но каждое целое не сопоставляется с уникальной строкой. Можно подумать об этом как о древовидной структуре.

Есть ли что-нибудь, чтобы быть получен путем добавления этого целого числа в таблице, индексирование на нем, и используя запрос типа:

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

Это Postgres, и если вы не могли сказать, у меня очень мало опыт работы с базами данных.

ответ

14

Ключи на VARCHAR столбцы могут быть очень длинными, что приводит к меньшему количеству записей на странице и большей глубине (больше уровней в B-Tree). Более длинные индексы также увеличивают коэффициент пропуска кеша.

Сколько строк в средней карте для каждого целого?

Если есть относительно мало, вы можете создать индекс только на целочисленном столбце и 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 байт, поэтому указательные клавиши будут намного короче, сохраняя при этом селективность почти идеально.

+0

Сравнение указательных клавиш также намного дороже с varchar, поскольку они знают локаль. Целочисленный индекс определенно будет намного быстрее, чем любой другой вариант. –

+0

@Magnus: сравнение должно быть сделано только «log (n)» раз, поэтому я бы не назвал это «намного» более дорогостоящим, но вы правы, он также добавляет некоторые циклы «CPU». – Quassnoi

-1

При объявлении индекса на (phrase, assoc, floatval) вы получите «индекс покрытия», который позволяет выполнить запрос, отправленный в вопросе, даже не обращаясь к таблице. Предполагая, что либо phrase, либо assoc один очень избирательно (не так много строк имеют одинаковое значение для поля), создание индекса только в этом поле должно давать почти такую ​​же производительность.

Как правило, вам нужно ограничить количество индексов наименьшим набором, которое получает ваши частые запросы до желаемой производительности. Для каждого индекса, который вы добавляете в таблицу, вы платите некоторое дисковое пространство, но, что более важно, вы платите цену за то, что СУБД больше работают над каждым INSERT в таблицу.

+0

PostgreSQL не имеет индексов покрытия, поэтому индекс определенно будет потерей. –

+0

@Magnus: Итак, даже если индекс охватывает все поля, необходимые для ответа на запрос, PostgreSQL должен получить доступ к фактической таблице для получения значений? У вас есть какая-то ссылка на это? Мне любопытно узнать * почему * :) –

+0

Начиная с 9.2, PostgreSQL теперь имеет только индексированные проверки: https://wiki.postgresql.org/wiki/Index-only_scans#Covering_indexes Подробности в верхней части сообщения почему он этого не делал ранее: с индексами PostgreSQL «невозможно напрямую определить, является ли какой-либо данный кортеж видимым для текущей транзакции». – jwadsack

-1

Не помешает попробовать добавить int и сделать свой индекс на int, varchar и включить float - это будет охватывать и довольно эффективно - не уверен, что Postgres включил столбцы - если он не просто добавляет его к самому индексу.

Есть несколько других методов, которые Вы можете посмотреть в (я не знаком со всеми функциями Postgres, так что я дам их по имени SQL Server):

индексированные представления - вы можете эффективно материализовать представление, которое присоединяется к нескольким таблицам - так что вы можете присоединиться к вашему varchar к вашему int и иметь свой индекс на int и varchar и float

Включенные столбцы - вы можете включать столбцы в индекс, чтобы гарантировать, что индекс покрывает - то есть иметь индекс на varchar include (float) - если ваш индекс не покрывает, оптимизатору запросов все равно придется использовать индекс, а затем выполнить поиск по закладкам, чтобы получить оставшиеся данные.

+1

'PostgreSQL' не поддерживает индексированные представления или включенные столбцы, но поддерживает индексы на основе функций (вам не нужно материализовывать выражение для индексации). – Quassnoi

3

Я бы рекомендовал просто индекс хэш:

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'; 
 
                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) 
+1

В этой тестовой таблице я не вижу разницы между btree и хешем. – hiroshi

0

Короткий ответ: да, там будет много выиграть. По крайней мере, пока у вас мало обновлений, но вполне вероятно, что накладные расходы даже не будут заметны.

Смежные вопросы