Вопрос:
У меня есть пространственная таблица (дорожные линии), хранящаяся с использованием SDE.ST_GEOMETRY
пользовательского типа данных ESRI в базе геоданных Oracle 12c . Я хочу перечислить вершины линий, чтобы в конечном итоге получить доступ и обновить их координаты. Если бы я использовал SDO_GEOMETRY / Oracle Locator, я бы использовал эту
SDO_UTIL.GETVERTICES
функцию. Но я не использую SDO_GEOMETRY / Oracle Locator, и нет эквивалентной функции в SDE.ST_GEOMETRY
. Единственные SDE.ST_GEOMETRY
функции, которые я могу найти, которые относятся к вершинам, это ST_PointN
и ST_NumPoints
.
Я пришел с запросом, который успешно все это делает - получает вершины линий в виде строк (вдохновлено этой страницей ):
1 SELECT a.ROAD_ID
2 ,b.NUMBERS VERTEX_INDEX
3 ,a.SDE.ST_X(SDE.ST_PointN(a.SHAPE, b.NUMBERS)) AS X
4 ,a.SDE.ST_Y(SDE.ST_PointN(a.SHAPE, b.NUMBERS)) AS Y
5 FROM ENG.ROADS a
6 CROSS JOIN ENG.NUMBERS b
7 WHERE b.NUMBERS <= SDE.ST_NumPoints(a.SHAPE)
8 --removed to do explain plan: ORDER BY ROAD_ID, b.NUMBERS
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5996 | 1545K| | 262 (1)| 00:00:01 |
| 1 | MERGE JOIN | | 5996 | 1545K| | 262 (1)| 00:00:01 |
| 2 | INDEX FULL SCAN | R23715_SDE_ROWID_UK | 30 | 90 | | 1 (0)| 00:00:01 |
|* 3 | SORT JOIN | | 3997 | 1018K| 2392K| 261 (1)| 00:00:01 |
| 4 | TABLE ACCESS FULL| ROAD | 3997 | 1018K| | 34 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
" 3 - access(""B"".""NUMBERS""<=""SDE"".""ST_NumPoints""(""A"".""SHAPE""))"
" filter(""B"".""NUMBERS""<=""SDE"".""ST_NumPoints""(""A"".""SHAPE""))"
Это CROSS JOINS
строки в ROADS
таблице к NUMBERS
таблице (и ограничивает результаты количеством вершин в каждой строке).
Статистика: (обновлено)
- Каждая строка имеет максимум 30 вершин (в среднем 4,38 вершин на линию)
- ДОРОГ имеет 3,997 линий
- ЧИСЛО имеет 30 строк (порядковые номера, начинающиеся с 1)
- Результирующий набор содержит 17 536 строк
Тем не менее, производительность плохая (40 секунд), и я не могу не думать - есть ли более элегантный способ сделать это? Для меня использование таблицы чисел и перекрестного соединения кажется небрежным подходом. Есть ли способ лучше?
Условия Layman будут оценены; Я парень из общественных работ, а не администратор.
Обновление № 1:
Если я удаляю строки 3 и 4 (строки функций, связанных с X & Y) из запроса, он выполняется мгновенно. Но, конечно, я не могу просто удалить эти строки, мне нужны столбцы X & Y. Так что это наводит меня на мысль, что низкая производительность как-то связана с функциями X & Y.
Однако, если я экспортирую точки в статическую таблицу, а затем запусту на ней функции X & Y, это также будет выполнено мгновенно.
Итак, означает ли это, что низкая производительность вызвана функциями X & Y, кроме, ну, нет, это не так? Я смущен.
Обновление № 2:
Если я выведу X и Y из запроса, добавлю их во внешний запрос и добавлю ROWNUM к внутреннему запросу, то это будет намного быстрее (16 секунд - обновлено):
SELECT
ROWNUM
,ROAD_ID
,VERTEX_INDEX
,SDE.ST_X(ST_POINT) AS X
,SDE.ST_Y(ST_POINT) AS Y
FROM
(
SELECT
ROWNUM
,a.ROAD_ID
,b.NUMBERS VERTEX_INDEX
,SDE.ST_PointN(a.SHAPE, b.NUMBERS) AS ST_POINT
FROM ENG.ROAD a
CROSS JOIN ENG.NUMBERS b
WHERE b.NUMBERS <= SDE.ST_NumPoints(a.SHAPE)
)
--removed to do explain plan: ORDER BY ROAD_ID, VERTEX_INDEX
-------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5996 | 322K| | 262 (1)| 00:00:01 |
| 1 | COUNT | | | | | | |
| 2 | VIEW | | 5996 | 322K| | 262 (1)| 00:00:01 |
| 3 | COUNT | | | | | | |
| 4 | MERGE JOIN | | 5996 | 1545K| | 262 (1)| 00:00:01 |
| 5 | INDEX FULL SCAN | R23715_SDE_ROWID_UK | 30 | 90 | | 1 (0)| 00:00:01 |
|* 6 | SORT JOIN | | 3997 | 1018K| 2392K| 261 (1)| 00:00:01 |
| 7 | TABLE ACCESS FULL| ROAD | 3997 | 1018K| | 34 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
" 6 - access(""B"".""NUMBERS""<=""SDE"".""ST_NumPoints""(""A"".""SHAPE""))"
" filter(""B"".""NUMBERS""<=""SDE"".""ST_NumPoints""(""A"".""SHAPE""))"
Джастин Кейв объясняет, почему ROWNUM помогает повысить производительность: почему добавление ROWNUM в запрос повышает производительность?
Хотя это улучшение производительности и хорошо, но пока недостаточно. И я не могу не думать, что до сих пор не до конца понимаю, как работает запрос или почему он такой медленный, как он есть.
Вопрос все еще стоит: есть ли лучший путь?
источник
Ответы:
Я немного знаю о производительности Oracle и почти ничего не знаю о пользовательских типах данных, но постараюсь дать вам план по повышению производительности.
1) Убедитесь, что вы не можете получить план объяснения.
Можно получить планы объяснения, даже если у вас нет сложного программного обеспечения базы данных. Что произойдет, если вы выполните
set autotrace on explain
?Вы также можете попробовать DBMS_XPLAN . Сначала сохраните план, добавив в ваш запрос несколько дополнительных ключевых слов:
Затем выполните это:
SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY());
Возможно, что ни один из них не сработает, и вы действительно не сможете получить план объяснения. Я просто хотел проверить это, потому что с планом объяснения сообществу будет намного легче помочь вам.
2) Учитывайте требования.
Вы сказали, что 20 секунд недостаточно. Вы или кто-то еще определил, что именно достаточно хорошо? Есть ли место для переговоров? Ваш запрос должен быть точно одним запросом SELECT? Не могли бы вы заполнить глобальную временную таблицу за один шаг и выбрать результаты, которые вы хотели в следующем? Не могли бы вы создать хранимую процедуру, которая возвращает набор результатов и вызвать его?
3) Установите нижнюю границу времени, необходимого для завершения запроса.
Я предлагаю выполнить простой запрос, который «обманывает», чтобы выяснить, как будет выглядеть хорошо оптимизированный запрос. Например, сколько времени занимает этот запрос, который получает только первые вершины?
Я подозреваю, что даст вам 4000 строк. Если вы умножите время ответа этого запроса на 17,5 / 4, это может дать вам хорошую нижнюю границу для общего времени выполнения.
Если ваша нижняя граница для общего времени выполнения длиннее, чем вы установили на шаге 2, вам нужно либо проявить творческий подход к своей модели данных, заранее рассчитав результаты и сохранив их в таблицах, либо пересмотреть необходимое время отклика.
4) Тест, чтобы выяснить, какие функции вносят больше всего в ваше время выполнения.
С обновлением № 1 вы были на правильном пути, но вам нужно попытаться контролировать объем выполняемой работы. Например, можно ли написать группу относительно простых запросов, которые выполняют каждую функцию ровно 10000 раз? Как сравниваются времена отклика?
5) Иди на работу.
В зависимости от требований, установленных на шаге 2, и того, что вы нашли на шаге 4, попробуйте любой прием, который вы можете придумать, чтобы сократить время выполнения запроса. Вы можете предварительно рассчитать результаты и сохранить их? Если проблема связана с тем, сколько раз выполнялись функции, то недокументированная подсказка материализации может оказаться полезной. Это заставляет Oracle создавать скрытые временные таблицы за кулисами для хранения результатов. Я не знаю, совместимо ли это со специальными типами данных, которые вы используете.
Например, может быть, что-то подобное работает лучше? Извинения, если он не компилируется, но у меня нет возможности проверить.
Если вы все еще застряли после всего этого, я подозреваю, что это по крайней мере даст вам дополнительную информацию, которую вы можете отредактировать в вопросе. Удачи!
источник
Я попытался использовать CONNECT BY (и DUAL), чтобы увидеть, будет ли это быстрее, но это не так (примерно так же).
Я получил идею из этого поста: Как рассчитать диапазоны в Oracle?
источник
Результаты и ответ на ответ Джо Оббиша :
Примечание. С этого момента я буду ссылаться на запрос в Обновлении № 2 как на «запрос»; Я не буду ссылаться на запрос в оригинальном вопросе.
1) Убедитесь, что вы не можете получить план объяснения.
Я не могу выполнить
set autotrace on explain
. Я получаю эту ошибку:ORA-00922: missing or invalid option (#922)
Но я могу выполнить
DBMS_XPLAN
. Я предполагал, что я не смогу сделать это. К счастью, я был не прав. Я сейчас бегу объяснять планы.2) Учитывайте требования.
Ваш запрос должен быть точно одним запросом SELECT?
Я думаю , что вопрос действительно должен быть точно один запрос. Программное обеспечение, которое я использую, очень ограничено и не позволяет использовать несколько операторов выбора.
Вы точно определили свои требования?
3) Установите нижнюю границу времени, необходимого для завершения запроса.
Запрос первой вершины выполняется за 3,75 секунды (возвращает 3805 строк, как и ожидалось).
3.75 sec * (16495 total / 3805 lines) = 16.25 sec
Результат: нижняя граница для общего времени выполнения длиннее, чем я установил на шаге 2 (5 секунд). Поэтому я думаю, что решение состоит в том, чтобы «... проявить творческий подход к моей модели данных, заранее рассчитав результаты и сохранив их в таблице» (требуемое время ответа не подлежит обсуждению). Другими словами, сделайте материализованное представление.
Кроме того, нижняя граница в 16,25 секунды соответствует общему времени выполнения запроса в Обновлении № 2 (16 секунд). Я думаю, это доказывает, что мой запрос полностью оптимизирован, учитывая функции и данные, с которыми мне приходится работать.
4) Тест, чтобы выяснить, какие функции вносят больше всего в ваше время выполнения.
Я создал две таблицы (обе содержат 10000 строк):
ROADS_BM
иROADS_STARTPOINT_BM
. Я выполнил простые запросы к таблицам, используя каждую из задействованных функций. Вот результаты:Документация по функциям: ST_X , ST_Y , ST_NumPoints , ST_PointN
Результат?
ST_PointN
это проблема. Время отклика составляет 9,5 секунд, это ужасно по сравнению с другими функциями. Я предполагаю, что это имеет смысл, хотя.ST_PointN
возвращаетST_POINT
тип данных геометрии, который должен быть довольно сложным по сравнению с другими функциями, которые возвращают простое число.5) Иди на работу.
Основываясь на шагах 2, 3 и 4, вот мои выводы:
ST_PointN
функции. Это медленно. Я не думаю, что я могу с этим многое сделать. Кроме попыток полностью перепрограммировать / воссоздать функцию в надежде, что я смогу добиться большего успеха, чем специалисты, которые ее сделали. Не совсем практично.ST_PointN
, в этом тоже виноват. Запрос CTE не был пустой тратой; Я многому научился, просто используя его.6. Заключение.
Я удовлетворен тем, что максимально оптимизировал запрос. Я настрою предварительный расчет и перейду к следующему. Большое спасибо Джо Оббишу; Я узнал тонну из шагов, которые он обеспечил.
источник