|
Как оптимизировать выборку по диапазону?
|
|||
---|---|---|---|
#18+
LiYing Partition pruning при беглом ознакомлении показалось многообещающим, но обнаружилась печалька А вот тут надо хорошо в затылке почесать, что важнее. FK ведь можно и сэмулировать на триггерах. ... |
|||
:
Нравится:
Не нравится:
|
|||
14.02.2022, 23:07 |
|
Как оптимизировать выборку по диапазону?
|
|||
---|---|---|---|
#18+
Akina А вот тут надо хорошо в затылке почесать, что важнее. FK ведь можно и сэмулировать на триггерах. Это что-то типа такого? https://stackoverflow.com/questions/47230968/how-to-make-a-trigger-work-like-a-foreign-key Если нет, то можно примерчик такого эмулирования? ... |
|||
:
Нравится:
Не нравится:
|
|||
15.02.2022, 08:24 |
|
Как оптимизировать выборку по диапазону?
|
|||
---|---|---|---|
#18+
LiYing Это что-то типа такого? Угу, именно. То, что обычно делается системно, выполняется вручную триггерами. Конечно, полной эмуляции так не получить, но случаи, когда эмуляция невозможна, достаточно редки и экзотичны. Другой вопрос - надо ли реально, ведь поддержка такой эмуляции намного сложнее, и надо иметь очень серьёзные основания для её применения. Есть и другие способы ускорения. Например, предрасчёт - предварительная агрегация. Например, посуточная, по дате и контрагенту. И соответственно запрос слать в предрасчётную таблицу, где будет явно поменьше записей. ... |
|||
:
Нравится:
Не нравится:
|
|||
15.02.2022, 11:23 |
|
Как оптимизировать выборку по диапазону?
|
|||
---|---|---|---|
#18+
авторLiYing, SELECT d.period p, COUNT(DISTINCT c.id_mkd) c1, COUNT(c.id) c2 FROM data_sgrc d INNER JOIN contragents c ON d.id_contragent=c.id WHERE d.period='2021-10-01' GROUP BY p Я бы для конкретно этого запроса сделал: либо индекс data_sgrc по колонокам period + id_contragent, а не наоборот, как тут предлагали. Но индекс такой нужен только при условии, что в реальном запросе из этой таблицы берется только дата и id_contragent, а другие колонки не берутся. Тогда после сканирования куска индекса мы в таблицу вообще не лезем. Либо партиционирование по дате. При этом внешние ключи, как мне кажется, не должны мешать, так как с точки зрения здравого смысла делают ссылку в таблице с данными на таблицу справочника. А партиционирование мы делаем в таблице с данными, а не в справочнике, поэтому внешний ключ вполне можно будет создать. Если в MySQL даже это нельзя сделать, то это будет очень странно. В любом случае, постепенно базы уходят от внешних ключей с целью повышения производительности. Предполагая, что целостность поддерживается логикой программы. И не требуется дополнительно строить ключи. В OLAP внешние ключи даже если и делают, то только для видимости, и тут же их отключают. В MS SQL Server я под такое сделал не партиционирование, а кластеризованный индекс по дате + ID, то есть таблица сама является индексом посортированным по дате + ID. Поэтому все запросы за любой период - день, месяц, год - всегда сканируют только нужную часть таблицы. Это было просто чудом. Есть ли такой в MySQL сомневаюсь. Вам тогда партиционирование по дате, как некий аналог этого. Если уж совсем надо быстро, то я бы переписал запрос так, чтобы сначала просуммировать исходную таблицу, а уже потом на маленьком просуммированном наборе присоединять справочник. SELECT d.period p, COUNT(DISTINCT c.id_mkd) c1, sum(c3) c2 from ( SELECT d1.period p, d1.id_contragent, count(*) as c3 FROM data_sgrc d1 WHERE d1.period='2021-10-01' group by d1.period, d1.id_contragent ) d INNER JOIN contragents c ON d.id_contragent=c.id GROUP BY d.period ... |
|||
:
Нравится:
Не нравится:
|
|||
20.02.2022, 16:28 |
|
Как оптимизировать выборку по диапазону?
|
|||
---|---|---|---|
#18+
VDeltsov, спасибо за советы! Протестировал предложенный запрос: №1 для одного месяца Код: sql 1. 2. 3. 4. 5. 6. 7. 8. 9.
idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra1PRIMARY<derived2>nullALLnullnullnullnull110843100.00Using where; Using filesort1PRIMARYcnulleq_refPRIMARYPRIMARY4d.id_contragent1100.00null2DERIVEDd1nullindexid_contr_perid_contr_per9null110843610.00Using where; Using index №2 для трех месяцев Код: sql 1. 2. 3. 4. 5. 6. 7. 8. 9.
idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra1PRIMARY<derived2>nullALLnullnullnullnull123147100.00Using where; Using filesort1PRIMARYcnulleq_refPRIMARYPRIMARY4d.id_contragent1100.00null2DERIVEDd1nullindexid_contr_perid_contr_per9null110843611.11Using where; Using index; Using temporary №1 > Query Time: 1,08s №2 > Query Time: 2,627s Т.е. запросы выполняются еще дольше. Первый, для периода в 1 месяц, в 2 раза дольше, а второй - незначительно, но все равно дольше. ... |
|||
:
Нравится:
Не нравится:
|
|||
21.02.2022, 08:36 |
|
Как оптимизировать выборку по диапазону?
|
|||
---|---|---|---|
#18+
LiYing, увидел, что у вас тут миллион клиентов, поэтому быстрее чем за секунду вы тут вряд ли получите. Самый быстрый вариант - это исключительно для тестов сделать два индекса, чтобы узнать самое минимальное время выполнения: data_sgrc по колонкам period + id_contragent contragents по колонкам id + id_mkd При этом таблицы вообще не будут считываться, только соединение индексов. Посмотреть, какое будет минимальное время второго выполнения, когда нужные куски индекса будут уже в памяти. Если это итак будет в районе 1 секунды, то будет ясно, что это - предел. И останется только подсказками попробовать два варианта соединения - hash join (и full index scan на таблицу клиентов) и nested loop (на таблицу клиентов). Как в MySQL делать подсказки - не знаю. Кстати, в MySQL есть подсказка типа PARALLEL 8, как в ORACLE, или option (maxdop 8), как в MS SQL? Следующий вариант, боюсь посоветовать, так как за колоночные таблицы в соседних ветках меня Ролин Хун затролллил. Ровно аналогичный запрос к колоночным таблицам MS SQL Server, где в таблице фактов 440 млн строк, в таблице клиентов 1,8 млн. В результате фильтра по дате (за год) получаю 60 млн строк, в результате первой группировки получают 1 миллион строк (миллион сочетаний дата+клиент), затем соединяю на клиентов и группирую по дням, делаю distinct id_mkd - получают 335 строк. И всё это за 0,5 секунд при максимальной параллельности на 12 ядер процессора. Если брать за один большой день (31 декабря), где 700 000 строк данных, то результат будет 0,090 секунд. На одном старом процессоре (2006 года выпуска) с отключенной параллельностью: 2 секунды (для 60 млн строк) и 0,260 секунд (для 700 тыс строк). При этом никакого партиционирования, никаких дополнительных индексов вообще не требуется. Все данные с сжатом в 20 раз виде итак помещаются в память. Но это всё в MS SQL Server в колоночных таблицах. В MySQL колоночные таблицы есть только в MariaDB в виде отдельно подключаемого модуля, и только в Линуксе, и еще и ним надо научиться работать. Но зато OLAP-запросы будут супер быстрыми. При условии, что вставляете данные пачками, а не по одной. Если попробуете их - расскажите тут или в моей ветке https://www.sql.ru/forum/1342038/ ... |
|||
:
Нравится:
Не нравится:
|
|||
22.02.2022, 13:53 |
|
|
start [/forum/topic.php?fid=47&startmsg=40134108&tid=1827770]: |
0ms |
get settings: |
7ms |
get forum list: |
7ms |
check forum access: |
1ms |
check topic access: |
1ms |
track hit: |
47ms |
get topic data: |
3ms |
get forum data: |
1ms |
get page messages: |
127ms |
get tp. blocked users: |
0ms |
others: | 2318ms |
total: | 2512ms |
0 / 0 |