|
Не работает coalesce() для поля типа TIMESTAMP в Query метода репозитория
|
|||
---|---|---|---|
#18+
Здравствуйте. В Query метода репозитория (extends JpaRepository<..., Long>) не получается заставить работать функцию coalesce() в условии для поля типа TIMESTAMP. В запросе (БД Oracle 12.2.0.1.0) есть условие: Код: plsql 1.
Но с таким условием не работает индекс и план запроса очень плохой (работает медленно). Чтобы индекс заработал пришлось поменять условие на: Код: plsql 1.
Т.е. для NULL значений поля типа TIMESTAMP возвращаем значение определённой даты и выбираем их же по условию равенства с этой датой. При этом индекс заработал и план запроса стал значительно лучше (смотрю это в DataGrip). Но когда пытаюсь перенести этот код в Query метода репозитория (привёл "to_timestamp()" для примера) Код: java 1. 2. 3. 4. 5. 6.
то при сборке проекта получаю ошибку: Код: java 1.
Проще говоря, в HQL Query нужно заменить условие "timestamp is null" на условие, которое бы заменяло NULL значения поля типа TIMESTAMP на постоянное значение и сравнивало бы с этим же значением. Может кто-то сталкивался с подобной задачей или есть более корректное решение? ... |
|||
:
Нравится:
Не нравится:
|
|||
21.06.2021, 13:21 |
|
Не работает coalesce() для поля типа TIMESTAMP в Query метода репозитория
|
|||
---|---|---|---|
#18+
upd Ошибка была из-за того, что в Query неправильно имя поля указал: было " and coalesce(bp.master.close_date,...", а нужно " and coalesce(bp.master.closeDate,". Но главный вопрос темы пока открыт. ... |
|||
:
Нравится:
Не нравится:
|
|||
21.06.2021, 14:01 |
|
Не работает coalesce() для поля типа TIMESTAMP в Query метода репозитория
|
|||
---|---|---|---|
#18+
А как выглядит DDL для создания индекса? И как так получилось что с coalesce(close_date...) не выбрасывало ту же ошибку? Это был нативный запрос? Было бы здорово еще увидеть план запроса. ... |
|||
:
Нравится:
Не нравится:
|
|||
21.06.2021, 14:46 |
|
Не работает coalesce() для поля типа TIMESTAMP в Query метода репозитория
|
|||
---|---|---|---|
#18+
Stanislav Bashkyrtsev, DDL для создания индекса (для двух полей timestamp) самый элементарный: Код: plsql 1.
Как временное решение пока решил использовать: Код: java 1. 2. 3. 4. 5. 6.
т.к., учитывая миллисекунды, подобных значений в поле с текущим current_timestamp должно быть немного (если они вообще попадутся), а значений с NULL в поле бОльшая часть. Зато используется индекс и Cost значительно ниже. План запроса без использования индекса: используется условие "... and ddm.open_date <= current_date and ddm.close_date is null" Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21.
План запроса c использованием индекса: используется условие "... and ddm.open_date <= current_date and coalesce(ddm.close_date, current_timestamp) = current_timestamp" Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24.
P.S. "И как так получилось что с coalesce(close_date...) не выбрасывало ту же ошибку?" Эта форма записи работает в DataGrip, но не в Java-коде (там поля Entity называются не так, как поля таблицы). Если есть замечания или советы, как ещё ускорить выполнение запроса, буду только рад. ... |
|||
:
Нравится:
Не нравится:
|
|||
21.06.2021, 15:55 |
|
Не работает coalesce() для поля типа TIMESTAMP в Query метода репозитория
|
|||
---|---|---|---|
#18+
Хм. Если эти два запроса должны выдавать один и тот же результат, почему результирующее кол-во строк выдает очень разное: 72322 vs 836? ... |
|||
:
Нравится:
Не нравится:
|
|||
21.06.2021, 16:59 |
|
Не работает coalesce() для поля типа TIMESTAMP в Query метода репозитория
|
|||
---|---|---|---|
#18+
Stanislav Bashkyrtsev, Странно, что так в планах запросов отображается, но оба запроса возвращают одинаковое кол-во строк (проверил с count(*)) и оно даже на 250 больше, чем в первом плане отображено. ... |
|||
:
Нравится:
Не нравится:
|
|||
21.06.2021, 17:42 |
|
Не работает coalesce() для поля типа TIMESTAMP в Query метода репозитория
|
|||
---|---|---|---|
#18+
Ну это видимо выводится предполагаемое кол-во записей, основанное на статистике. Почему-то Oracle считает что если close_date is null, то не использовать индекс по open_date бесполезно. Странно, возможно это из-за того что под open_date условие подпадает слишком много строк и Оракл считает что по индексу идти бесполезно. Наверняка Ораклу можно как-то сказать пересчитать эту статистику. Может это его заставит передумать. Так-то запрос с coalesce() быстрей работать не может. ... |
|||
:
Нравится:
Не нравится:
|
|||
21.06.2021, 18:17 |
|
Не работает coalesce() для поля типа TIMESTAMP в Query метода репозитория
|
|||
---|---|---|---|
#18+
В плане продолжают оставаться Table Access Full и Hash Join т.ч. Ваше утверждение про "индекс заработал" полное лукавство Для Вашего запроса должно быть Nested Loop's и два Index'а create index IDX_DEPARTMENT_DOC_MASTER_OPENDT_CLOSEDT on DEPARTMENT_DOC_MASTER(open_date, close_date); Для оптимизации выражения может использоваться только ОДИН индекс. По логике запроса, доступ должен отбирать данные по полю departmentType , т.ч. именно это поле и должно быть в индексе. Т.е. правильный индекс: department_type, open_date, close_date Все остальное, т.е. любые планы, где есть Hash Join и/или Table Access Full - это порнография. Еще большая порнография, только если Вы увидете в планах Index to Bitmap conversion и прочие другие слова со словами Bitmap. Это будет уже не просто порно, а хард порно для особых ценителей извращений с сервером. Если трогать существующие индексы запрещает архитектор - пишете докладную записку начальству о проф. непригодности Ваших коллег и невозможности в связи с этим реализовать поставленные Вам задачи. После чего забиваете нано-болт и на работе читаете анекдот.ру, смотрите ру-тубе или занимаетесь чем-то подобным. IMHO Проще говоря, в HQL Query нужно заменить условие "timestamp is null" на условие, которое бы заменяло NULL значения поля... НЕ нужно. Нужен нормальный индекс содержащий department_type (именно по нему и будет идти B-tree поиск) и дополнительно поля с датами для оптимизации чтения данных из таблицы (AFAIK скорее всего B-Tree поиск по ним идти не будет, просто будет налагаться filter, но будет не нужно лишнее случайное обращение к таблице за данными для наложения filter) AFAIK ... |
|||
:
Нравится:
Не нравится:
|
|||
21.06.2021, 18:19 |
|
Не работает coalesce() для поля типа TIMESTAMP в Query метода репозитория
|
|||
---|---|---|---|
#18+
Stanislav Bashkyrtsev https://www.sql.ru/forum/1336929/medlennaya-rabota-zaprosa-na-odnom-iz-znacheniy-parametra На мой взгляд, "нормальный" план это Nested Loop по master в качестве главной таблице с отбором по индексу по полю departmentType (плюс остальные поля для оптимизации) дальше уходим в detail с отбором по индексу по полю DEPARTMENT_DOC_MASTER_ID Скорее всего статистика и распределение данных такая, что Oracle (скорее всего осмысленно!) считает, что ПОСЛЕДОВАТЕЛЬНЫЙ full table scan по таблице будет БЫСТРЕЕ чем СЛУЧАЙНЫЙ доступ по RowId из индекса. И на индексы в лучшем случае забивает нано-болт (full table scan), а в худшем, начинает глючить и воспаленная нейронная сеть оптимизатора генерирует разную порнографию. Тот, кто проектировал базу, не подумал, что при исторической таблице хорошо бы еще в качестве дополнительных полей даты добавить. Самое простое, нарезать более широкие индексы. Где будут и истинно "ключевые" поля (поиск по B-tree) и дополнительные поля с датами, что бы случайный доступ для наложения фильтра был НЕ НУЖЕН. По b-tree будет быстро находится departmentType , потом ПОСЛЕДОВАТЕЛЬНЫМ чтением из ИНДЕКСА будут извлекаться даты, накладываться фильтр и лишь для полностью подходящих под фильтр записей будет случайный доступ к таблице. IMHO, могу ошибаться. Но скорее всего это поможет. ... |
|||
:
Нравится:
Не нравится:
|
|||
21.06.2021, 18:30 |
|
Не работает coalesce() для поля типа TIMESTAMP в Query метода репозитория
|
|||
---|---|---|---|
#18+
В общем я еще поискал немного инфы: похоже что когда мы используем функции (в данном случае coalesce()), Oracle не может расчитывать статистику. Хотя его можно заставить это делать с помощью каких-то средств в DBMS_STATS - некие Extended Statistics. Видимо из-за этого он и использует индекс. А в случае когда функции нет, он опирается на статистику, а та неверная и говорит ему что будет выбрано очень много записей из таблиц. Поэтому оптимизатор считает что индексы использовать бессмысленно. Но это все академический интерес, а чтоб запрос выполнялся на самом деле быстро, нужно следовать советам от Leonid Kudryavtsev (если я правильно понимаю его эмоциональные выплески): 1. Нужен индекс на те поля которые участвуют в поиске 2. Нужен индекс на колонку из внутренней таблицы по которой те объединяются (department_doc_master.id) потому как по этой колоне будет поиск во время join'a. Если создать индекс где используется эта join колонка, а также те колонки по которым мы отфильтровуем записи во внутренней таблице, то наверно (не проверял) по этому одному индексу поиск и пройдет. И тогда вообще в табличный файл СУБД пойдет только когда все что нужно отфильтровали. Однако возможно поля с низкой мощностью (cardinality) не имеет смысл добавлять в этот индекс (подозреваю что это department_type, и может даже close_date раз там одни null'ы). Потому как в B+ Tree он не особо поможет. По таким колонкам строят Bitmap индексы, однако их не получится одновременно смешивать с деревьями. Т.е. нужно все-таки протестировать разницу и точно узнать стоит ли department_type и close_date включать в индекс. PS: т.к. заведено две темы - не понятно теперь в какую постить. Это очень неудобно, больше так не делай плз. ... |
|||
:
Нравится:
Не нравится:
|
|||
22.06.2021, 13:26 |
|
Не работает coalesce() для поля типа TIMESTAMP в Query метода репозитория
|
|||
---|---|---|---|
#18+
Stanislav Bashkyrtsev По таким колонкам строят Bitmap индексы, однако их не получится одновременно смешивать с деревьями. "Вредные советы" ( C ) Маршак 1) Bitmap для OLAP. В OLTP приложении может сделать очень-очень плохо . Начиная от блокировок при update и заканчивая совершенно дурацкими планами "новомодного" оптимизатора 2) Современный Oracle умеет их смешивать . Мало того, иногда, когда несколько B-Tree по разным колонкам, он B-Tree преобразует в Bitmap и начинает смешивать - но с точки зрения плана и скорости, обычно это полная порнография и от таких планов все встает колом AFAIK. Я бы нафиг это фичу бы отключал, но я не админ, и обычно удается обойтись хинтами ))). Stanislav Bashkyrtsev Однако возможно поля с низкой мощностью (cardinality) не имеет смысл добавлять в этот индекс Нужно. Вы не поняли главной идеи. Дело не в кардинальности, дело в том, что для наложения фильтра будет НЕ нужен СЛУЧАЙНЫЙ (дорогой) доступ в таблицу. Все поля для фильтрации уже будут в индексе и будут браться ПОСЛЕДОВАТЕЛЬНЫМ ЧТЕНИЕМ (быстрым) из индекса, исключив СЛУЧАЙНЫЙ (медленный) доступ к таблицы Если поля будут не все, то для полей для фильтрации придется лезть в основную таблицу. И Oracle может посчитать, что последовательный Full Table Scan по таблице, быстрее чем Index + случайный (медленный) доступ в Table Я думаю, что статистика правильная, просто данные (историчность!) таковы, что индекс без учета дат не сильно помогает, а возможно даже замедляет работу. Тут дело не только в кардинальности, но например и в "размазонности" данных по таблице. Note: Вроде стоимость последовательного и случайного доступа определяется https://docs.oracle.com/cd/B19306_01/server.102/b14237/initparams144.htm#REFRN10143 Но трогать его не следует. Т.к. ясно, что индексы нарезаны крайне не оптимально. Нужно добавлять индексы. Note2: Когда работал с CC&B, вообще пришлось огромное кол-во полей в индекс включать, все поля которые были использовались в запросе, и access/filter и просто из select list'а. Распределение данных было таково, что нужный результат был "размазан" по всей таблице CI_FT, и последовательный доступ приводил к извлечению массы ненужной информации, фактически всей таблице. Единственный вариант который смогли придумать, продублировать таблицу в индексе. Оно конечно выглядело коряво, но по крайне мере стало работать (запросы с 2-3 минут , удалось довести до 5-10 сек. в худшем случае). Т.к. данные стали хранится последовательно. Пытался играться с Index organization / Cluster Table - но на нашей версии Oracle оно работало "странно". Т.ч. отказались, и когда было нужно, просто дублировали информацию из таблице в индексы. Место на диске сейчас не является проблемой. Т.ч. экономить место в индексах в ущерб производительности - я лично смысла не виж. Stanislav Bashkyrtsev 2. Нужен индекс на колонку из внутренней таблицы по которой те объединяются (department_doc_master.id) потому как по этой колоне будет поиск во время join'a. У ТС он есть, но так же не подхватился. Т.ч. только добавлять даты и по максимому. IMHO ... |
|||
:
Нравится:
Не нравится:
|
|||
22.06.2021, 14:35 |
|
Не работает coalesce() для поля типа TIMESTAMP в Query метода репозитория
|
|||
---|---|---|---|
#18+
Leonid Kudryavtsev1) Bitmap для OLAP. В OLTP приложении может сделать очень-очень плохо. Начиная от блокировок при update и заканчивая совершенно дурацкими планами "новомодного" оптимизатораНу как бы если у тебя boolean/enum колонки, то плохо-неплохо, а выбора нет. Деревья в таком случае бессмысленны (хотя в композитных индексах может и норм будет). Leonid Kudryavtsev2) Современный Oracle умеет их смешивать.Мм.. не слышал о таком. Есть ссылочка? Leonid KudryavtsevМало того, иногда, когда несколько B-Tree по разным колонкам, он B-Tree преобразует в Bitmap и начинает смешиватьНу преобразование индексов на ходу и изначальное построение bitmap+btree - эт разные вещи. Leonid KudryavtsevВы не поняли главной идеи. Дело не в кардинальности, дело в том, что для наложения фильтра будет НЕ нужен СЛУЧАЙНЫЙ (дорогой) доступ в таблицу. Все поля для фильтрации уже будут в индексе и будут браться ПОСЛЕДОВАТЕЛЬНЫМ ЧТЕНИЕМ (быстрым) из индекса, исключив СЛУЧАЙНЫЙ (медленный) доступ к таблицыИндексы - это доп структура которую нужно поддерживать. Да, мы может получим доп буст на пару миллисекунд в SELECT если создадим такой индекс, но при этом можем просесть при вставке/обновлении, а также будет занимать доп место. Плюс нужно понимать что идеальная картина нарушается еще больше если какие-то записи одновременно обновляются . Ибо для поддержания MVCC возможно базе все равно прийдется ходить по табличному файлу (в индексе будут два значения - новое и старое, однако MVCC инфа в индексах не хранится - прийдется идти в таблицу и проверять версию). Поэтому я бы рекомендовал проверить разницу в производительности. И если она окажется не существенной, то лишние поля в индекс не добавлять. Все что мы тут обсуждаем - это догадки. TC же может проверить это все на практике, зачем ему гадать. Leonid KudryavtsevЯ думаю, что статистика правильная, просто данные (историчность!) таковы, что индекс без учета дат не сильно помогает, а возможно даже замедляет работу. ТС писал выше что с индексом работает намного быстрей. И кол-во записей которые он получает намного меньше чем то что показывает статистика. ... |
|||
:
Нравится:
Не нравится:
|
|||
22.06.2021, 14:55 |
|
Не работает coalesce() для поля типа TIMESTAMP в Query метода репозитория
|
|||
---|---|---|---|
#18+
Stanislav Bashkyrtsev Ну как бы если у тебя boolean/enum колонки, то плохо-неплохо, а выбора нет. Деревья в таком случае бессмысленны (хотя в композитных индексах может и норм будет). В OLAP - да. В OLTP - нет. Т.к. битмап индекс на 2 значения, при любом update заблокирует половину таблицы. А оно такое надо? Могу ошибаться (т.к. не спец. по битмапам), но битмап это не для OLTP. Stanislav Bashkyrtsev Leonid Kudryavtsev2) Современный Oracle умеет их смешивать. http://www.dba-oracle.com/t_bitmap_conversion_to_rowid.htm И сопустствующие. Могу ошибаться. Когда такое видно в плане - тут же возникает желание похинтовать. Stanislav Bashkyrtsev Да, мы может получим доп буст на пару миллисекунд в SELECT если создадим такой индекс Нужно мерить на реальных задачах. У меня было ускорение с 2-3 и более минут, до 5-10 секунд на реальных запросах от CC&B Когда SELECT просто не работает (а у ТС он именно что НЕ работает), оптимизировать пару миллисекунд при вставке/обновлении - такое себе. А экономия места в 2021 году вообще смешно ))). Stanislav Bashkyrtsev Ибо для поддержания MVCC возможно базе все равно прийдется ходить по табличному файлу (в индексе будут два значения - новое и старое, однако MVCC инфа в индексах не хранится - прийдется идти в таблицу и проверять версию). Так глубоко не знаю. Stanislav Bashkyrtsev ТС писал выше что с индексом работает намного быстрей. И кол-во записей которые он получает намного меньше чем то что показывает статистика. У ТС Hash Join и Full Table Scan на подчиненную таблица. Т.е. текущие индексы не работают вообще. В таком случае обсуждать, быстро/медленно IMHO бессмысленно. Это как обсуждать, как быстрее руками толкать машину, в которой кончился бензин, вместо того, что бы сходить с канистрой до заправке и потом на машине доехать. IMHO Единственно правильный план, два доступа по индексу и Nested Loop. IMHO ... |
|||
:
Нравится:
Не нравится:
|
|||
22.06.2021, 17:23 |
|
Не работает coalesce() для поля типа TIMESTAMP в Query метода репозитория
|
|||
---|---|---|---|
#18+
Добрый день. Извиняюсь, что вчера не отвечал (экспериментировал с индексами). Пока остановился на таком варианте запроса: Это запрос, который Hibernate отправляет в БД Oracle. Код: plsql 1. 2. 3. 4. 5.
В ORM запросе NVL заменено COALESCE. Код: java 1. 2. 3. 4. 5.
Воспользовался советом Leonid Kudryavtsev (ещё раз благодарю) и создал индекс, который бы охватывал все поля в условиях выборки: Код: plsql 1.
Индекс отображается в плане запроса (стоимость значительно уменьшилась - было почти 2200): Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24.
Правда столкнулся со странным поведением БД - вчера стоимость запроса (после создания этого индекса) составляла те же 445, а сегодня (когда подключился заново и выполнил план запроса) была больше 900. Снёс все индексы (вчера создавал на разные комбинации полей, т.к. "ускорить" нужно было ещё пару запросов), создал указанный выше индекс заново и стоимость в плане запроса опять стала 445. ... |
|||
:
Нравится:
Не нравится:
|
|||
23.06.2021, 13:15 |
|
Не работает coalesce() для поля типа TIMESTAMP в Query метода репозитория
|
|||
---|---|---|---|
#18+
AFAIK Насколько я знаю, стоимость измеряется в попугаях. Т.ч. смотреть на нее можно, но придавать ей большое значение не стоит. ... |
|||
:
Нравится:
Не нравится:
|
|||
23.06.2021, 19:41 |
|
Не работает coalesce() для поля типа TIMESTAMP в Query метода репозитория
|
|||
---|---|---|---|
#18+
Можно сопоставлять стоимость разных планов одного и того же запроса (без учёта хинтов - подсказок для оптимизатора). Если запрос меняется, например, в части where, то это уже другой запрос, даже если по своему смыслу он должен выдать идентичный набор записей. ... |
|||
:
Нравится:
Не нравится:
|
|||
24.06.2021, 05:39 |
|
Не работает coalesce() для поля типа TIMESTAMP в Query метода репозитория
|
|||
---|---|---|---|
#18+
Basil A. Sidorov, В том-то и дело, что сам запрос не менялся вообще (условия в where тоже). Но сегодня опять подключился к БД, выполнил план запроса и вместо вчерашних Cost = 445 ( 22339104 ) и использования индексов на обеих таблицах join-а увидел это: Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23.
Причём, этот индекс сегодня не работает и на других запросах, на которых вчера значения Cost тоже были значительно лучше с его использованием. Код: sql 1.
... |
|||
:
Нравится:
Не нравится:
|
|||
24.06.2021, 13:28 |
|
Не работает coalesce() для поля типа TIMESTAMP в Query метода репозитория
|
|||
---|---|---|---|
#18+
chikaginsk Правда столкнулся со странным поведением БД - вчера стоимость запроса (после создания этого индекса) составляла те же 445, а сегодня (когда подключился заново и выполнил план запроса) была больше 900. Снёс все индексы (вчера создавал на разные комбинации полей, т.к. "ускорить" нужно было ещё пару запросов), создал указанный выше индекс заново и стоимость в плане запроса опять стала 445. Сразу после создания индексов сделай dbms_stats.gather_index_stats или *table_stats. ... |
|||
:
Нравится:
Не нравится:
|
|||
24.06.2021, 13:36 |
|
Не работает coalesce() для поля типа TIMESTAMP в Query метода репозитория
|
|||
---|---|---|---|
#18+
chikaginsk сегодня опять Код: plsql 1. 2. 3.
вчера было вчера, а сегодня - уже сегодня, оракл решил что так будет лучше ... |
|||
:
Нравится:
Не нравится:
|
|||
24.06.2021, 13:38 |
|
Не работает coalesce() для поля типа TIMESTAMP в Query метода репозитория
|
|||
---|---|---|---|
#18+
mayton, А это актуально только сразу после создания индексов? Нельзя таким методом заставить использовать уже ранее (вчера) созданный индекс? Попробовал несколько вариантов dbms_stats.gather_table_stats (с разными параметрами, методами оптимизации) и dbms_stats.gather_index_stats не удаляя индекса и результата не получил (план запроса остался прежним). ... |
|||
:
Нравится:
Не нравится:
|
|||
24.06.2021, 16:33 |
|
Не работает coalesce() для поля типа TIMESTAMP в Query метода репозитория
|
|||
---|---|---|---|
#18+
mayton, Не помогло. Вчера вечером "дропнул" индексы, создал заново Код: plsql 1. 2.
и выполнил: Код: plsql 1. 2. 3. 4.
И, как и раньше было, после создания стоимость плана запроса с использованием индекса 'IDX_DEPARTMENT_DOC_MASTER_TYPE_OPENDT_CLOSEDT_TRUNC' составляла 445, а сегодня утром опять 997. ... |
|||
:
Нравится:
Не нравится:
|
|||
25.06.2021, 09:38 |
|
|
start [/forum/topic.php?fid=59&msg=40079732&tid=2120413]: |
0ms |
get settings: |
17ms |
get forum list: |
6ms |
check forum access: |
1ms |
check topic access: |
1ms |
track hit: |
54ms |
get topic data: |
2ms |
get forum data: |
1ms |
get page messages: |
392ms |
get tp. blocked users: |
0ms |
others: | 351ms |
total: | 825ms |
0 / 0 |