La pregunta:
Tengo una tabla espacial (líneas de ruta), almacenada usando el SDE.ST_GEOMETRY
tipo de datos definido por el usuario de ESRI en una geodatabase Oracle 12c . Quiero enumerar los vértices de las líneas para poder acceder y actualizar sus coordenadas. Si estuviera usando SDO_GEOMETRY / Oracle Locator, entonces usaría la
SDO_UTIL.GETVERTICES
función. Pero no estoy usando SDO_GEOMETRY / Oracle Locator, y no hay una función equivalente en SDE.ST_GEOMETRY
. Las únicas SDE.ST_GEOMETRY
funciones que puedo encontrar que pertenecen a los vértices son ST_PointN
y ST_NumPoints
.
Se me ocurrió una consulta que hace todo esto con éxito: obtiene los vértices de la línea como filas (inspirados en esta página ):
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""))"
Es CROSS JOINS
las líneas en la ROADS
tabla a una NUMBERS
tabla (y limita los resultados al número de vértices en cada línea).
Estadísticas: (actualizado)
- Cada línea tiene un máximo de 30 vértices (promedio de 4.38 vértices por línea)
- CARRETERAS tiene 3,997 líneas
- NÚMEROS tiene 30 filas (números secuenciales que comienzan en 1)
- El conjunto de resultados tiene 17.536 filas.
Sin embargo, el rendimiento es pobre (40 segundos), y no puedo evitar pensar: ¿hay una manera más elegante de hacer esto? Para mí, usar una tabla de números y una unión cruzada parece un enfoque descuidado. ¿Hay una mejor manera?
Los términos del laico serían apreciados; Soy un tipo de Obras Públicas, no un DBA.
Actualización n. ° 1:
Si elimino las líneas 3 y 4 (cadena de funciones relacionadas con X e Y) de la consulta, se ejecuta instantáneamente. Pero, por supuesto, no puedo simplemente eliminar estas líneas, necesito las columnas X e Y. Entonces esto me lleva a creer que el rendimiento lento tiene algo que ver con las funciones X e Y.
Sin embargo, si exporto los puntos a una tabla estática y luego ejecuto las funciones X e Y, también se ejecuta instantáneamente.
Entonces, ¿esto significa que el rendimiento lento es causado por las funciones X e Y, excepto, bueno, no, no lo es? Estoy confundido.
Actualización n. ° 2:
Si saco las X e Y de la consulta, las pongo en una consulta externa y agrego ROWNUM a la consulta interna, entonces es mucho más rápido (16 segundos - actualizado):
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""))"
Justin Cave explica por qué ROWNUM ayuda al rendimiento aquí: ¿por qué agregar ROWNUM a una consulta mejora el rendimiento?
Si bien esta mejora del rendimiento es buena, aún no es lo suficientemente buena. Y no puedo evitar pensar que todavía no entiendo completamente cómo funciona la consulta o por qué es tan lenta como es.
La pregunta sigue en pie: ¿hay una mejor manera?
fuente
Respuestas:
Sé un poco sobre el rendimiento de Oracle y casi nada sobre los tipos de datos personalizados, pero intentaré darle un plan para mejorar el rendimiento.
1) Verifique que no pueda obtener un plan de explicación.
Es posible obtener planes explicativos incluso si no tiene un software de base de datos sofisticado. ¿Qué pasa si ejecutas
set autotrace on explain
?También puedes probar DBMS_XPLAN . Primero guarde el plan envolviendo su consulta con algunas palabras clave adicionales:
Entonces ejecute esto:
SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY());
Es posible que ninguno de los dos funcione y usted realmente no puede obtener un plan explicativo. Solo quería verificar eso porque con un plan de explicación será mucho más fácil para la comunidad ayudarlo.
2) Considerar los requisitos.
Dijiste que 20 segundos no son lo suficientemente buenos. ¿Usted o alguien más ha definido exactamente qué es lo suficientemente bueno? ¿Hay espacio para la negociación? ¿Su consulta debe ser exactamente una consulta SELECT? ¿Podría completar una tabla temporal global en un paso y seleccionar los resultados que desea en el siguiente? ¿Podría crear un procedimiento almacenado que devuelva un conjunto de resultados y llamarlo?
3) Establezca un límite inferior para el tiempo requerido para completar la consulta.
Sugiero ejecutar una consulta simple que "engañe" para descubrir cómo sería una consulta bien optimizada. Por ejemplo, ¿cuánto dura esta consulta que solo obtiene los primeros vértices?
Sospecho que eso te dará 4000 filas. Si multiplica el tiempo de respuesta de esa consulta por 17.5 / 4, podría obtener un límite inferior bueno para el tiempo total de ejecución.
Si su límite inferior para el tiempo total de ejecución es más largo que el establecido en el paso 2, entonces necesita ser creativo con su modelo de datos calculando los resultados con anticipación y almacenándolos en tablas o necesita renegociar el tiempo de respuesta requerido.
4) Punto de referencia para descubrir qué funciones están contribuyendo más a su tiempo de ejecución.
Estaba en el camino correcto con la Actualización n. ° 1, pero debe intentar controlar la cantidad de trabajo que se realiza. Por ejemplo, ¿es posible escribir un grupo de consultas relativamente simples que ejecuten cada función exactamente 10000 veces? ¿Cómo se comparan los tiempos de respuesta?
5) Ve a trabajar.
Dependiendo de los requisitos establecidos en el paso 2 y de lo que encontró en el paso 4, intente cualquier truco que se le ocurra para reducir el tiempo de ejecución de la consulta. ¿Eres capaz de calcular previamente los resultados y guardarlos? Si el problema se relaciona con la cantidad de veces que se ejecutan las funciones, entonces la sugerencia de materialización no documentada puede ser útil. Eso obliga a Oracle a crear una tabla temporal oculta detrás de escena para almacenar los resultados. No sé si es compatible con los tipos de datos especiales que está utilizando.
Por ejemplo, ¿tal vez algo como esto funciona mejor? Disculpas si no se compila pero no tengo forma de probar.
Si todavía está atrapado después de todo esto, sospecho que al menos le dará información adicional que puede editar en la pregunta. ¡Buena suerte!
fuente
Intenté usar CONNECT BY (y DUAL) para ver si sería más rápido, pero no lo es (es casi lo mismo).
Se me ocurrió la idea de esta publicación: ¿Cómo calcular rangos en Oracle?
fuente
Resultados y respuesta a la respuesta de Joe Obbish :
Nota: De ahora en adelante, me referiré a la consulta en la Actualización # 2 como 'la consulta'; No me referiré a la consulta en la pregunta original.
1) Verifique que no pueda obtener un plan de explicación.
No puedo ejecutar
set autotrace on explain
. Me sale este error:ORA-00922: missing or invalid option (#922)
Pero soy capaz de ejecutar
DBMS_XPLAN
. Asumí que no podría hacer esto. Afortunadamente me equivoqué. Ahora estoy corriendo explicando planes.2) Considerar los requisitos.
¿Su consulta debe ser exactamente una consulta SELECT?
Creo que la consulta no tiene por qué ser exactamente una consulta. El software que estoy usando es muy limitado y no permite múltiples sentencias select.
¿Ha definido exactamente cuáles son sus requisitos?
3) Establezca un límite inferior para el tiempo requerido para completar la consulta.
La consulta del primer vértice se ejecuta en 3.75 segundos (devuelve 3805 filas, como se esperaba).
3.75 sec * (16495 total / 3805 lines) = 16.25 sec
El resultado: el límite inferior para el tiempo total de ejecución es más largo que el establecido en el paso 2 (5 segundos). Por lo tanto, creo que la solución es '... ser creativo con mi modelo de datos calculando los resultados con anticipación y almacenándolos en una tabla' (el tiempo de respuesta requerido no es negociable). En otras palabras, crea una vista materializada.
Además, el límite inferior de 16.25 segundos coincide con el tiempo total de ejecución de la consulta en la Actualización n. ° 2 (16 segundos). Creo que esto prueba que mi consulta está totalmente optimizada, dadas las funciones y los datos con los que tengo que trabajar.
4) Punto de referencia para descubrir qué funciones están contribuyendo más a su tiempo de ejecución.
He creado dos tablas (ambas contienen 10.000 filas):
ROADS_BM
yROADS_STARTPOINT_BM
. He ejecutado consultas simples en las tablas usando cada una de las funciones involucradas. Aquí están los resultados:Documentación de funciones: ST_X , ST_Y , ST_NumPoints , ST_PointN
¿El resultado?
ST_PointN
es el problema. Su tiempo de respuesta de 9.5 segundos es abismal en comparación con las otras funciones. Sin embargo, supongo que esto tiene un poco de sentido .ST_PointN
devuelve unST_POINT
tipo de datos de geometría, que tiene que ser bastante complejo en comparación con las otras funciones que devuelven un número simple.5) Ve a trabajar.
Basado en los pasos 2, 3 y 4, aquí están mis hallazgos:
ST_PointN
función. Es lento. Sin embargo, no creo que haya mucho que pueda hacer al respecto. Además de intentar reprogramar / recrear completamente la función con la esperanza de que pudiera hacerlo mejor que los especialistas que la hicieron. No es exactamente práctico.ST_PointN
es culpa de eso. Sin embargo, la consulta CTE no fue un desperdicio; Aprendí mucho con solo usarlo.6. Conclusión.
Estoy satisfecho de haber optimizado la consulta tanto como sea posible. Configuraré el cálculo previo y pasaré a lo siguiente. Muchas gracias a Joe Obbish; He aprendido un montón de los pasos que dio.
fuente