Здравствуйте, Ziaw, Вы писали:
Z>Здравствуйте, MasterZiv, Вы писали:
MZ>>Индексы такие ( в первом приближении ):
MZ>>f1, f2, f3 MZ>>f1, f3 MZ>>f2, f3 MZ>>f3
Z>выделенные индексы можно заменить на один, (f3, f1) без ущерба для любых запросов.
Овощ пишет:
> "здравому смыслу". Фактически при сравнении /векторов данных (пара > значений Cl, C2 > задает вектор/) /*порядок столбцов не имеет значения*/.
Так, давайте отбросим все эти премудрости про деревья и индексы,
и будем рассуждать совсем по-тупому.
Вот есть у нас индекс на поля
MyTable (Cl, C2)
Он может использоваться для ускорения поиска по полям
(Cl и C2)
или
C1.
Т.е. будет полезен потенциально в запросах типа
select * from MyTable where Cl = ? and C2 = ?
и
select * from MyTable where Cl = ?
Напомним, если кто забыл, что для запроса
типа
select * from MyTable where C2 = ?
данный индекс будет безполезен.
Теперь предположим, что С1 у нас содержит классически трудное
для индексации поле -- пол человека с двумя значениями 'М' и 'Ж'.
Потенциально для выполения запроса
select * from MyTable where C1 = 'М'
наш индекс применим, потому что первое поле в нём -- наше.
Но такой запрос будет возвращать примерно половину нашей таблицы,
и любой ценовой оптимизатор не будет рассматривать данный индекс
для выполнения этого запроса. Потому что ему придётся читать
и индек (половину), и таблицу (половину). Легче просто прочитать
всю таблицу.
А если бы мы поменяли местами поля в индексе, то
для запроса
select * from MyTable where C2 = ?
наш индекс был бы применим, как и для запроса
select * from MyTable where Cl = ? and C2 = ?
(при предположении, что C2 содержит много разных возможных
значений).
Отчасти этот ваш Кайт конечно прав, потому что если такое поле
C1 типа пола человека переместить на второе место в индексе,
оно тоже не будет особенно помогать искать, т.е. для такого
случая в общем-то всё равно, на каком месте стоит поле C1.
Но совсем не всё равно, на каком месте стоит селективное поле C2.
Здравствуйте, MasterZiv, Вы писали:
MZ>Вот есть у нас индекс на поля MZ>MyTable (Cl, C2)
MZ>Напомним, если кто забыл, что для запроса MZ>типа MZ>select * from MyTable where C2 = ? MZ>данный индекс будет безполезен.
Это не всегда верно. Например в Oracle реализована операция INDEX SKIP SCAN именно для такого случая, иногда бывает весьма полезна. Наверняка и в других СУБД есть аналоги.
Здравствуйте, Sinclair, Вы писали:
S>we can infer from the execution plans that Oracle is internally generating multiple queries, thereby satisfying the query with multiple sub-queries
Это чушь, можно не обращать внимания.
S>Ну, то есть на самом деле используется оптимизация index scan — когда мы "перепрыгиваем" между значениями первого ключа.
Примерно так.
S>Правда, не очень понятно, зачем добавлять в индекс первую колонку со столь низкой селективностью. Но, наверное, это имеет смысл в каких-то особенных случаях.
Ты имел в виду "с низкой каридинальностью"? Селективность может быть и высокой, например, в случае сильно неравномерного распределения значений.
Еще случай, когда это имеет смысл — если мы хотим создать сжатый индекс.
Друзья! Подскажите, плиз, как лучше индексы организовать. Есть три поля f1, f2, f3, комбинация которых определяет условие SQL-запроса, т.е. грубо говоря может быть вот так:
WHERE f1
WHERE f2
WHERE f3
WHERE f1, f2
WHERE f1, f3
WHERE f2, f3
WHERE f1, f2, f3
Хочется обойтись минимальным количеством индексов, чтобы сэкономить время на инсертах. Записей будет много. Какие лучше сделать индексы и сколько? БД, если важно, SQLite.
Здравствуйте, cargo, Вы писали:
C>Друзья! Подскажите, плиз, как лучше индексы организовать. Есть три поля f1, f2, f3, комбинация которых определяет условие SQL-запроса, т.е. грубо говоря может быть вот так: C>Хочется обойтись минимальным количеством индексов, чтобы сэкономить время на инсертах. Записей будет много. Какие лучше сделать индексы и сколько? БД, если важно, SQLite.
Задачи ортогональные, чтобы сделать максимально быстрый поиск нужно 6 индексов:
f1, f2, f3
f1, f3, f2
f2, f1, f3
f2, f3, f1
f3, f2, f1
f3, f1, f2
Теперь, для ускорения инсертов из них следует выкинуть те, которые будут давать наименьшую пользу. Потом, по результатам, убрать третье и возможно второе поле из оставшихся.
Здравствуйте, Ziaw, Вы писали:
Z>Задачи ортогональные, чтобы сделать максимально быстрый поиск нужно 6 индексов:
Что-то я ступил достаточно трех, остальные рекомендации в силе
f1, f2, f3
f2, f3
f3, f1
cargo пишет:
> > WHERE f1 > WHERE f2 > WHERE f3 > WHERE f1, f2 > WHERE f1, f3 > WHERE f2, f3 > WHERE f1, f2, f3 > > Хочется обойтись минимальным количеством индексов, чтобы сэкономить > время на инсертах. Записей будет много. Какие лучше сделать индексы и > сколько? >
Индексы такие ( в первом приближении ):
f1, f2, f3
f1, f3
f2, f3
f3
Но вообще ещё нужно знать данные какие. А именно,
какие значения есть (или будут) в каждом поле и
насколько они различны.
Например, если в поле f1 будет всего два или три возможных
значения, то индекс (f1, f2, f3) лучше переделать на
(f2, f3, f1) или даже (f2, f3).
Вообще, в индексе поля лучше располагать таким образом,
чтобы поля с наиболее большим кол-вом возможных значений
шли бы в начале.
Здравствуйте, MasterZiv, Вы писали:
MZ>Например, если в поле f1 будет всего два или три возможных MZ>значения, то индекс (f1, f2, f3) лучше переделать на MZ>(f2, f3, f1) или даже (f2, f3).
MZ>Вообще, в индексе поля лучше располагать таким образом, MZ>чтобы поля с наиболее большим кол-вом возможных значений MZ>шли бы в начале.
А зачем? Чего хочем добиться?
У Кайта говорится, что это миф.
На эффективность выборки по индексу это не будет сказываться, потому что при поиске значения в индексе сравнение идет сразу целым вектором (f1, f2, f3), а не по каждому полю в отдельности.
В Oracle наоборот — иногда имеет смысл указывать в индексе первыми полями те, которые имеют наименьшее количество значений. Это позволяет более эффективно использовать возможность сжатия ключа индекса, т.е. уменьшить физический размер индекса на диске.
Здравствуйте, Овощ, Вы писали:
О>А зачем? Чего хочем добиться? О>У Кайта говорится, что это миф.
У Кайта о другом, а именно: при условии where f1, f2, индекс по (f1, f2) также эффективен, как и (f2, f1) — вне зависимости от селективности f1 и f2.
Но это неверно, например, при where f2.
Чтобы индекс лучше работал и давал большую селективность записей.
> У Кайта говорится, что это миф.
Я такого не знаю.
> На эффективность выборки по индексу это не будет сказываться, потому что > при поиске значения в индексе сравнение идет сразу целым вектором (f1, > f2, f3), а не по каждому полю в отдельности.
Да, целым вектором. Но в дереве -то ты рыщешь от корня к листам,
если пол-таблицы будет в записях "Ж", то тебе все эти записи придётся
просмотреть (бинарным поиском, конечно), пока не найдёшь "Надежда" и "Иванова".
Это медленнее.
Ну и если поиск будет только по одному полю, f1, индекс будет практически
безполезен.
(это конечно если индекс B+tree).
> В Oracle наоборот — иногда имеет смысл указывать в индексе первыми > полями те, которые имеют наименьшее количество значений. Это позволяет > более эффективно использовать возможность сжатия ключа индекса, т.е. > уменьшить физический размер индекса на диске.
Глупость IMHO. Сжатие индексов кстати очень много где есть.
cargo пишет:
> А возможно обойтись одним индексом f1, f2, f3 и искусственно вводить > недостающие поля, например для
А что , они у тебя все имеют константные значения ?
Тогда зачем они тебе нужны ?
> > WHERE f3 > > делать > > WHERE f1 IS NOT NULL AND f2 IS NOT NULL AND f3 = value ?
При таком условии B+tree индекс (f1, f2, f3) применяться
для позиционирования по индексу не может.
> > Я забыл уточнить, в выражении логика всегда AND.
Это понятно, иначе индекс тебе бы вообще не помог (составной).
Здравствуйте, cargo, Вы писали:
C>Друзья! Подскажите, плиз, как лучше индексы организовать. Есть три поля f1, f2, f3, комбинация которых определяет условие SQL-запроса, т.е. грубо говоря может быть вот так:
C>WHERE f1 C>WHERE f2 C>WHERE f3 C>WHERE f1, f2 C>WHERE f1, f3 C>WHERE f2, f3 C>WHERE f1, f2, f3
Добавь в эту табличку еще относительную частоту запросов и процент строк, удовлетворяющих данному условию. Тогда можно будет давать советы.
А поскольку сделать это до начала эксплуатации проблематично , то начинай без индексов. Когда количество данных в таблице станет существенно большим, можно вернуться к этому вопросу.
Здравствуйте, MasterZiv, Вы писали:
>> На эффективность выборки по индексу это не будет сказываться, потому что >> при поиске значения в индексе сравнение идет сразу целым вектором (f1, >> f2, f3), а не по каждому полю в отдельности.
MZ>Да, целым вектором. Но в дереве -то ты рыщешь от корня к листам, MZ>если пол-таблицы будет в записях "Ж", то тебе все эти записи придётся MZ>просмотреть (бинарным поиском, конечно), пока не найдёшь "Надежда" и "Иванова". MZ>Это медленнее.
Не так. Сравнение идет целым вектором, т.е. включая сразу все поля индекса и все поля из запроса.
Если ищется в индексе (Surname, FirstName) значение ("Иванова", "Надежда"), то это не означает, что вначале ищется значение "Иванова", а лишь найдя его переходим к поиску "Надежда".
Все поля ("Иванова", "Надежда") — это одно значение. И мы его можем целиком, с учетом порядка полей, сравнить с другими подобными значениями (например взятыми из индекса):
("Иванова", "Надежда") < ("Петрова", "Евгения"). (потому что Иванова < Петрова).
("Иванова", "Надежда") > ("Иванова", "Евгения"). (потому что Иванова = Иванова, но Надежда > Евгения).
Алгоритм поиска по дереву индекса по одному полю совпадает с алгоритмом поиска по дереву индеска с несколькими полями. Разница будет лишь в момент сравнения. В первом случае нужно просто сравнить два простых значения, во втором случае также идет сравнение двух значений, но комплексных. Но сравниваются они сразу целиком.
Именно благодаря такому "сравнению вектором", нам безразличен порядок следования полей в индексе (естественно для случая когда мы ищем по ключу, содержащему все проиндексированные поля).
Индексы (Surname, FirstName) и (FirstName, Surname) будут в данном случае одинаково эффективны.
MZ>Глупость IMHO. Сжатие индексов кстати очень много где есть.
Только оно имеет смысл тогда, когда у соседних строк в индексе совпадают префиксы (начальные поля).
И чем больше идет подряд строк с одинаковым префиксом — тем лучше для сжатия.
И наилучший резултат будет тогда, когда мы вынесем на первое место поля, с наименьшим количеством значений.
Овощ пишет:
> Не так. Сравнение идет целым вектором, т.е. включая сразу все поля > индекса и все поля из запроса. > Если ищется в индексе (Surname, FirstName) значение ("Иванова", > "Надежда"), то это не означает, что вначале ищется значение "Иванова", а > лишь найдя его переходим к поиску "Надежда".
Ну, и да, и нет. Ты B+tree представляешь себе ?
Если представляешь, то хорошо.
Одно могу сказать, что НЕ использовать неселективные поля
в индексе, а особенно в первых полях индекса -- это общая
расхожая рекомендация.
Здравствуйте, MasterZiv, Вы писали:
MZ>Да в общем-то всё равно, и какая СУБД, и какая версия. MZ>Главное, чтобы индекс был B+tree.
Ладно, до определенной степени все равно, если рассуждать о структуре B+tree и алгоритмах на ней, но фантазировать о деталях реализации, вроде "сравнения целым вектором", совершенно некорректно и по-дилетантски.
Другое дело, например, взять конкретную реализацию, протестить разные варианты, и сделать выводы, подкрепленные экспериментом. Это, по крайней мере, будет иметь практическую ценность.
Здравствуйте, wildwind, Вы писали:
W>Здравствуйте, MasterZiv, Вы писали:
MZ>>Да в общем-то всё равно, и какая СУБД, и какая версия. MZ>>Главное, чтобы индекс был B+tree.
W>Ладно, до определенной степени все равно, если рассуждать о структуре B+tree и алгоритмах на ней, но фантазировать о деталях реализации, вроде "сравнения целым вектором", совершенно некорректно и по-дилетантски.
W>Другое дело, например, взять конкретную реализацию, протестить разные варианты, и сделать выводы, подкрепленные экспериментом. Это, по крайней мере, будет иметь практическую ценность.
Привет. У тебя наверняка есть под рукой одна из книжек Кайта по внутреннему устройству Oracle. Там в конце главы о индексах есть пункт:
Миф: столбцы с максимальным количеством
разных значений должны указываться
первыми
Кажется, это следует из соображений здравого смысла. Если предполагается созда-
ние индекса по столбцам С1, С2 таблицы со 100000 строк, при этом столбец С1 имеет
100000 уникальных значений, а столбец С2 — 25000, индекс создается по столбцам
Т(С1,С2). Это означает, что столбец С1 должен указываться первым, что соответствует
"здравому смыслу". Фактически при сравнении векторов данных (пара значений Cl, C2
задает вектор) порядок столбцов не имеет значения.
Там же есть и тесты, подтверждающие высказывание.
Говорить в общем случае "о сравнение целым вектором" я считаю вполне возможным, поскольку это относится не к конкретной реализации, а к фундаментальным алгоритмам для структуры данных B+tree.
Или все же ты знаешь пример современной субд, где используются другие алгоритмы?
Здравствуйте, Овощ, Вы писали:
О>Привет. У тебя наверняка есть под рукой одна из книжек Кайта по внутреннему устройству Oracle. Там в конце главы о индексах есть пункт:
Вот так и надо писать — Кайт мол в такой-то книге пишет то-то (цитата), касается это Oracle такой-то версии.
О>Говорить в общем случае "о сравнение целым вектором" я считаю вполне возможным, поскольку это относится не к конкретной реализации, а к фундаментальным алгоритмам для структуры данных B+tree.
Здравствуйте, wildwind, Вы писали:
W>Здравствуйте, Овощ, Вы писали:
О>>Привет. У тебя наверняка есть под рукой одна из книжек Кайта по внутреннему устройству Oracle. Там в конце главы о индексах есть пункт:
W>Вот так и надо писать — Кайт мол в такой-то книге пишет то-то (цитата), касается это Oracle такой-то версии.
О>>Говорить в общем случае "о сравнение целым вектором" я считаю вполне возможным, поскольку это относится не к конкретной реализации, а к фундаментальным алгоритмам для структуры данных B+tree.
W>Неверно.
В таком случае нужно назвать субд и версию в которой это не так (желательно с ссылкой на доку). Или хотя бы пояснить теорию, зачем может понадобится использовать неэффективные алгоритмы вместо эффективных.
Здравствуйте, cargo, Вы писали:
C>Друзья! Подскажите, плиз, как лучше индексы организовать. Есть три поля f1, f2, f3, комбинация которых определяет условие SQL-запроса, т.е. грубо говоря может быть вот так:
C>WHERE f1 C>WHERE f2 C>WHERE f3 C>WHERE f1, f2 C>WHERE f1, f3 C>WHERE f2, f3 C>WHERE f1, f2, f3
C>Хочется обойтись минимальным количеством индексов, чтобы сэкономить время на инсертах. Записей будет много. Какие лучше сделать индексы и сколько? БД, если важно, SQLite.
"Базюлька" конкретно какая? Тогда могу сказать... Oracle, MS SQL, MySQL или что-то другое... Типы данных по колонкам еще уточни, плиз! Что есть f(i+j)?
Здравствуйте, _d_m_, Вы писали:
MZ>>>f1, f2, f3 MZ>>>f1, f3 MZ>>>f2, f3 MZ>>>f3
Z>>выделенные индексы можно заменить на один, (f3, f1) без ущерба для любых запросов.
___>Нет.
Какой запрос пострадает? Селективность, как я уже говорил, не учитываем. Или имеется ввиду именно размер самого индекса?
Здравствуйте, Ziaw, Вы писали:
Z>Здравствуйте, _d_m_, Вы писали:
MZ>>>>f1, f2, f3 MZ>>>>f1, f3 MZ>>>>f2, f3 MZ>>>>f3
Z>>>выделенные индексы можно заменить на один, (f3, f1) без ущерба для любых запросов.
___>>Нет.
Z>Какой запрос пострадает? Селективность, как я уже говорил, не учитываем. Или имеется ввиду именно размер самого индекса?
Здравствуйте, wildwind, Вы писали:
W>Это не всегда верно. Например в Oracle реализована операция INDEX SKIP SCAN именно для такого случая, иногда бывает весьма полезна. Наверняка и в других СУБД есть аналоги.
Конечно есть. Но бескомпромиссные DBA счиают выигрыш от index scan (вместо table scan) пренебрежимо малым по сравнению с выигрышем от index seek.
Потому, что index scan отличается только константой перед N, а index seek — это log(N) по весьма большому основанию.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Sinclair wrote:
> Конечно есть. Но бескомпромиссные DBA счиают выигрыш от index scan > (вместо table scan) пренебрежимо малым по сравнению с выигрышем от index > seek.
+1
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, wildwind, Вы писали:
W>>Это не всегда верно. Например в Oracle реализована операция INDEX SKIP SCAN именно для такого случая, иногда бывает весьма полезна. Наверняка и в других СУБД есть аналоги. S>Конечно есть. Но бескомпромиссные DBA счиают выигрыш от index scan (вместо table scan) пренебрежимо малым по сравнению с выигрышем от index seek. S>Потому, что index scan отличается только константой перед N, а index seek — это log(N) по весьма большому основанию.
index skip scan — это не index full scan (когда читается весь индекс в порядке сортировки строк в этом индексе).
index skip scan — это не index fast full scan (когда индекс используется просто как несортированная (heap) таблица).
index skip scan — это как раз и есть доступ с использованием b-tree структуры индекса, но с "фичей" не только искать нужные записи в этом индексе, но и пропускать не нужные.
По эффективности он может быть как не отличим от "классического" index seek, так и быть медленнее full (table) scan`a.
Как раз для его эффективности имеет значение кардинальность отдельных колонок в индексе (ведущих).
Вот пример:
создаем большую таблицу
SQL> create table t as select mod(rownum, 2) c_low, rownum c_high from dual connect by level <= 10000000;
Table created.
поля в ней имеют весьма различную кардинальность
SQL> select count(*), count(distinct c_low), count(distinct c_high) from t;
COUNT(*) COUNT(DISTINCTC_LOW) COUNT(DISTINCTC_HIGH)
---------- -------------------- ---------------------
10000000 2 10000000
это "плохой" индекс
SQL> create index i1_clow_chigh on t(c_low, c_high);
Index created.
а это вроде как хороший
SQL> create index i2_chigh on t(c_high);
Index created.
статистика для оптимизатора
SQL> exec dbms_stats.gather_table_stats(ownname=>user, cascade=>true, tabname=>'T');
PL/SQL procedure successfully completed.
SQL> set autotrace on
доступ по "плохому" индексу по "плохому" предикату. однако же Cost=4
SQL> select/*+ index(t i1_clow_chigh) */count(*) from t where c_high = 999;
COUNT(*)
----------
1
Execution Plan----------------------------------------------------------Plan hash value: 2275081951
----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 6 | 4 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 6 | | |
|* 2 | INDEX SKIP SCAN| I1_CLOW_CHIGH | 1 | 6 | 4 (0)| 00:00:01 |
----------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("C_HIGH"=999)
filter("C_HIGH"=999)
доступ по хорошему индексу по хорошему предикату. Cost=3. Лишь чуть меньше чем в предыдущем "плохом" случае.
SQL> select/*+ index(t i1_chigh) */count(*) from t where c_high = 999;
COUNT(*)
----------
1
Execution Plan----------------------------------------------------------Plan hash value: 2726032430
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 6 | 3 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 6 | | |
|* 2 | INDEX RANGE SCAN| I2_CHIGH | 1 | 6 | 3 (0)| 00:00:01 |
------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("C_HIGH"=999)
полный скан таблицы. с ужасным Cost=4494.
SQL> select/*+ full(t) */count(*) from t where c_high = 999;
COUNT(*)
----------
1
Execution Plan----------------------------------------------------------Plan hash value: 2966233522
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 6 | 4494 (9)| 00:00:54 |
| 1 | SORT AGGREGATE | | 1 | 6 | | |
|* 2 | TABLE ACCESS FULL| T | 1 | 6 | 4494 (9)| 00:00:54 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("C_HIGH"=999)
Так что в данном случае получаем что index skip scan (cost=4) оказался таким же быстрым как и index seek (cost=3), и намного лучше full scan (cost=4494).
В других случаях все может быть и по другому.
Такая вот чудная операция, позволяющая в индексе по полям (c1, c2) искать с условиями только по (с2).
Здравствуйте, Овощ, Вы писали:
О>index skip scan — это не index full scan (когда читается весь индекс в порядке сортировки строк в этом индексе). О>index skip scan — это не index fast full scan (когда индекс используется просто как несортированная (heap) таблица). О>index skip scan — это как раз и есть доступ с использованием b-tree структуры индекса, но с "фичей" не только искать нужные записи в этом индексе, но и пропускать не нужные.
Я не понимаю, как это работает. О>Так что в данном случае получаем что index skip scan (cost=4) оказался таким же быстрым как и index seek (cost=3), и намного лучше full scan (cost=4494). О>В других случаях все может быть и по другому. О>Такая вот чудная операция, позволяющая в индексе по полям (c1, c2) искать с условиями только по (с2).
Прикольно.
What Oracles does not mention is that the cardinality of the leading column has a direct impact on the speed of the index skip scan. In our example, the first column, sex has two columns
While Oracle does not publish the internals of the index skip scan, we can infer from the execution plans that Oracle is internally generating multiple queries, thereby satisfying the query with multiple sub-queries
Ну, то есть на самом деле используется оптимизация index scan — когда мы "перепрыгиваем" между значениями первого ключа.
Правда, не очень понятно, зачем добавлять в индекс первую колонку со столь низкой селективностью. Но, наверное, это имеет смысл в каких-то особенных случаях.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, wildwind, Вы писали: S>>Правда, не очень понятно, зачем добавлять в индекс первую колонку со столь низкой селективностью. Но, наверное, это имеет смысл в каких-то особенных случаях. W>Ты имел в виду "с низкой каридинальностью"?
В данном примере именно селективность получилась крайне неудачной — отбрасываем всего половину значений.
Хуже только
create table t as select42 c_low, rownum c_high from dual connect by level <= 10000000;
W> Селективность может быть и высокой, например, в случае сильно неравномерного распределения значений.
Ну да. Это я и хотел сказать — при существенной неравномерности такой индекс может давать хороший результат для запросов по любому из ключей.
В вырожденном случае имеем, например, 1000 значений high для low1, и по одному значению high для low1-low99.
Что ж, век живи — век учись.
W>Еще случай, когда это имеет смысл — если мы хотим создать сжатый индекс.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.