> **Simulacro de Exame: Tema 17 - Optimización e Rendemento**

**Instruccións**: Introduce un `[X]` na opción que consideres correcta e redacta a túa xustificación técnica (analizando o porqué da correcta e o erro exacto ou trampa das incorrectas) xusto debaixo de cada pregunta. Coida a terminoloxía técnica en ambos idiomas.

# Pregunta 1 (Castellano)

Disponemos de una tabla `facturas` con un índice compuesto definido exactamente sobre las columnas (`id_cliente`, `fecha_emision`, `estado`). El optimizador de consultas de nuestro motor de bases de datos relacional (RDBMS) se enfrenta a cuatro consultas diferentes. ¿En cuál de los siguientes escenarios el optimizador aplicará de forma más eficiente una operación de "Index Seek" (búsqueda indexada directa) aprovechando al máximo la estructura de árbol del índice?

- A) Cuando la cláusula contiene `WHERE id_cliente = 105 AND estado = 'Pendiente'`, ya que el optimizador reordena dinámicamente los componentes del índice compuesto mediante un algoritmo de emparejamiento predictivo intermedio, garantizando que la omisión de columnas intermedias no penalice en absoluto el coste de I/O de la consulta.
- B) Cuando la cláusula contiene `WHERE id_cliente = 105 AND fecha_emision >= '2026-01-01' AND estado = 'Pagada'`.
- C) Cuando la cláusula contiene `WHERE fecha_emision = '2026-06-26' AND estado = 'Pagada'`, puesto que el motor realiza una transposición interna de la clave izquierda basándose en las estadísticas de dispersión de la densidad del índice, permitiendo omitir la primera columna si la selectividad de las dos siguientes es críticamente alta.
- D) Cuando la cláusula contiene `WHERE id_cliente = 105 AND fecha_emision = '2026-06-26'`, aprovechando el principio del prefijo más a la izquierda (*left-most prefix*), deteniendo la evaluación estructural del índice en la columna `fecha_emision` al estar todas bajo operadores de igualdad estricta.

## Respuesta

Mucho cuidado con esta pregunta. En muchos motores de datos relacionales no se admite más de una columna como ID, lo que no quita que puedan tener un índice por Unique de más de una columna o por referencias foráneas, aunque cada una será un índice individual. En este caso interpreto que es un índice de valores únicos que permite organizar la información y ésta ser buscada internamente en ese orden. Por otro lado, tenemos cuatro consultas distintas, las cuales muestran resultados distintos aunque vinculados en el árbol de histórico de cambios en la factura. Entendiendo todo esto, en este caso, al ser Index Seek, entiendo que va orientado a un resultado concreto, por tanto, la **D** sería la correcta.

Si miramos las consultas en crudo, sin índice, la más libiana es la última por ser un entero y sobre los resultados del entero, una fecha, que no deja de ser un valor numérico limitado, a diferencia de un `varchar`, más complejo, además de tener como valor de comparación el igual; y el más lento serí la B por el hecho de que integra también el `varchar`, pese a estar limitado por el resultado del cliente y un rango cercano de fechas, además, que el mayor o menor que condiciona de forma más lenta, aunque siempre mejorada por el sistema de índices por el orden estructural del mismo.

Si lo miramos por posición dentro del índice, la A sería la más eficiente, pero el tipado rompe esa dinámica al ser un `varchar`.

Eso sí, si nos basamos íntegramente en el Index Seek, ignorando el tipado y su peso en búsqueda, el orden afecta a la construcción del índice y a donde apunta para buscar, por tanto, la B sería la más eficiente.

## Corrección

- **Tu respuesta**: D (Correcta)
- **Análisis técnico**: ¡Exacto! Diste en el clavo con el principio del **prefijo más a la izquierda** (*left-most prefix*). Al filtrar por `id_cliente` y `fecha_emision` con el operador de igualdad (`=`), el optimizador realiza un *Index Seek* perfecto escalando por los dos primeros niveles del árbol. La opción B cojea porque el operador de rango (`>=`) en la segunda columna hace que la tercera (`estado`) ya no se pueda buscar de forma estructural directa, pasando a ser un filtro residual.
- **Matiz de base de datos**: Como apunte a tu reflexión, todos los RDBMS modernos (SQL Server, Oracle, PostgreSQL, MySQL) permiten perfectamente crear Claves Primarias (PK) compuestas por varias columnas, no solo índices únicos.
- **Léxico/Ortografía**: Se te coló "*libian*" (lo correcto es **liviana**).

# Pregunta 2 (Gallego)

Dins do ámbito do deseño físico de bases de datos e a optimización de consultas, o optimizador de tarefas decide substituír unha operación de "Table Scan" (escaneo de táboa completa) por unha de "Index Scan" ou "Index Seek". Respecto ao comportamento mecánico destas operacións nunha estrutura en árbore B+ (B-Tree), sinale a opción CORRECTA:

- A) Unha operación de "Index Scan" realiza sempre e en calquera circunstancia un percorrido estritamente vertical desde a páxina raíz (Root Page) ata as páxinas folla (Leaf Pages) mediante búscas binarias consecutivas, logrando un custo operacional constante de O(1) que invalida a necesidade de avaliar a fragmentación física do disco subxacente.
- B) O optimizador escollerá sistematicamente executar un "Index Scan" sobre un "Index Seek" cando o predicado de filtrado da consulta (`WHERE`) teña unha selectividade moi baixa, é dicir, cando a consulta deba recuperar e retornar un volume estimado superior ao 92% da totalidade dos rexistros indexados na táboa.
- C) Unha operación de "Index Seek" utiliza os punteiros e as claves das páxinas de nodos intermedios da árbore para navegar de forma directa cara aos rexistros específicos que cumpren o criterio, reducindo drasticamente o número de lecturas de páxinas lóxicas en memoria.
- D) A execución dun "Index Scan" implica de xeito obrigatorio que o motor de bases de datos indexou previamente a táboa de forma agrupada (Clustered Index), garantindo por definición que a orde física dos datos no subsistema de almacenamento coincida milimetricamente coa orde lóxica determinada pola clave do índice secundario.

## Resposta

Aquí diría que a **C** pero a A non queda lonxe. A fragmentación do disco afecta por mor da lectura física nun disco mecánico, a diferencia dos SSD, polo tanto, a máis correcta viría a ser a C. Nas consultas `where`, se hay posibilidade de facer un Index Seek prevalecerá por rendemento.

## Corrección

- **Tu resposta**: C (Correcta)
- **Análisis técnico**: Impecable. El *Index Seek* navega verticalmente desde la raíz hasta las páginas hoja usando los nodos intermedios, minimizando las lecturas lógicas en memoria (I/O). Tu apunte sobre los discos mecánicos (HDD) frente a los SSD es totalmente acertado en la práctica, aunque para la teoría de oposiciones, el *Seek* siempre reduce el coste algorítmico respecto al *Scan*.
- **Olo ao léxico/ortografía**:
    - Escribiches "*se hay posibilidade*". En galego non existe o verbo "haber" con "y". O correcto é "**se hai posibilidade**".

# Pregunta 3 (Castellano)

Un administrador de bases de datos detecta problemas de rendimiento en una consulta que busca registros históricos. El campo `fecha_registro` dispone de un índice b-tree secundario no agrupado. ¿Cuál de las siguientes construcciones en la cláusula `WHERE` mantiene la condición de consulta "SARGable" (Search Argument Able), garantizando que el motor pueda realizar una búsqueda directa por índice?

- A) `WHERE DATEPART(yy, fecha_registro) = 2026 AND DATEPART(mm, fecha_registro) = 06`, dado que las funciones deterministas aplicadas sobre las columnas indexadas permiten al optimizador precalcular el plano de ejecución en la fase de parseo sintáctico sin alterar la cardinalidad de la estructura del árbol.
- B) `WHERE fecha_registro >= '2026-01-01' AND fecha_registro < '2027-01-01'`.
- C) `WHERE CAST(fecha_registro AS VARCHAR(10)) LIKE '2026-%'`, ya que la conversión implícita o explícita a tipos de datos de cadena de caracteres (strings) optimiza el proceso de comparación binaria a nivel de página del índice, transformando el escaneo en una operación secuencial síncrona de alta velocidad.
- D) `WHERE fecha_registro + 0 = '2026-06-26'`, puesto que las operaciones aritméticas neutras (como sumar cero o multiplicar por uno) no alteran el valor semántico subyacente del dato y el motor las descarta automáticamente antes de evaluar el uso del índice secundario no agrupado.

## Respuesta

Esta es descardísimamente la **B**. En cuanto se integra una función u operación al `where`, éste pierde el poder del uso de los Index Seek.

## Corrección

- **Tu respuesta**: B (Correcta)
- **Análisis técnico**: "*Descaradísimamente la B*", tal cual. Has interiorizado la regla de oro de las consultas **SARGables**: en el momento en que le encasquetes una función (`DATEPART`, `CAST`), una operación aritmética (`+ 0`) o un comodín al inicio de un string a la columna indexada, obligas al motor a evaluar fila por fila, destruyendo el *Index Seek* y provocando un costoso *Scan*.
- **Léxico/Ortografía**: Un pequeño error de tecleo: "*descardísimamente*" por **descaradísimamente**.

# Pregunta 4 (Gallego)

No monitoreo de rendemento dun sistema xestor de bases de datos relacionais (RDBMS), estamos analizando as métricas extraídas durante un pico de carga de traballo. Observamos que unha consulta específica presenta un indicador de "Physical Reads" (lecturas físicas) moi elevado en comparación cos "Logical Reads" (lecturas lóxicas). Que diagnóstico e acción correctiva técnica son os máis axeitados ante este escenario?

- A) O rácio de acertos da caché (Cache Hit Ratio) do Buffer Pool atópase nun estado óptimo próximo ao 99.8%, o que indica que os datos están totalmente residentes en memoria RAM e que o verdadeiro pescozo de botella débese a unha saturación de fíos de execución na CPU provocada por un exceso de ordenacións de tipo *Sort Merge*.
- B) O elevado número de lecturas físicas indica que os datos solicitados non se atopan no Buffer Pool (memoria caché) e o motor vese obrigado a ir buscalos directamente ao subsistema de disco, polo que a acción correctiva debe orientarse a optimizar a estratexia de indexación ou aumentar o tamaño da memoria asignada ao Buffer Pool.
- C) A métrica reflicte que a táboa sufriu un proceso de fragmentación lóxica masiva debido a un valor de "Fill Factor" (factor de recheo) configurado estritamente no 100%, o que obriga ao motor a executar operacións de *Page Split* síncronas en bucles infinitos durante a fase de lectura dos datos.
- D) O problema reside nun illamento transaccional de tipo *Serializable* que bloquea as páxinas de datos na caché, provocando que calquera consulta de lectura deba ser desviada de forma obrigatoria ao ficheiro de intercambio temporal (*TempDB* ou *Swap*) situado no almacenamento físico non volátil.

## Resposta

Esta creo que é sinxela e viría a ser a **B** por mor de que as lecturas físicas van contra a Base de Datos mentres que as lecturas lóxicas van sobre a caché. Se a carga sobre as físicas é elevada é por falta de resultados sobre a caché.

## Corrección

- **Tu resposta**: B (Correcta)
- **Análisis técnico**: Lo entendiste a la perfección. Lectura lógica = RAM (Buffer Pool). Lectura física = Disco duro. Si las lecturas físicas se disparan, significa que tenemos un problema de *Cache Miss* (los datos no están en memoria y hay que ir a buscarlos al almacenamiento lento). Las soluciones son ampliar el Buffer Pool o meter índices quirúrgicos para leer menos páginas de datos.

# Pregunta 5 (Castellano)

El optimizador de consultas debe elegir un algoritmo de combinación para resolver un `INNER JOIN` entre dos tablas: `usuarios` (150 registros, con un índice único en `id_usuario`) y pedidos (4.000.000 de registros, con un índice secundario no agrupado en `id_usuario`). ¿Qué algoritmo de combinación es el idóneo para este escenario y bajo qué condiciones mecánicas operará?

- A) Un algoritmo de Hash Join, configurando la tabla `pedidos` como la tabla de entrada interna (*Build Input*) para construir una tabla hash en la memoria RAM compartida, recorriendo posteriormente la tabla `usuarios` como entrada externa (*Probe Input*) para garantizar una correspondencia asíncrona de baja latencia.
- B) Un algoritmo de *Nested Loops Join*, donde la tabla `usuarios` actúa como bucle externo (*Outer Loop*) y, por cada uno de sus 150 registros, el motor realiza una búsqueda indexada rápida (*Index Seek*) directa en el bucle interno (*Inner Loop*) sobre el índice de la tabla `pedidos`.
- C) Un algoritmo de *Merge Join*, el cual destaca como la opción óptima por defecto para este volumen de datos debido a que no requiere que ninguna de las dos fuentes de datos de entrada esté previamente ordenada por la clave de combinación, realizando un escaneo paralelo concurrente de ambas estructuras.
- D) Un algoritmo de *Adaptive Loop Join*, que conmuta dinámicamente el plano de ejecución cada 1.000 registros procesados, reescribiendo los metadatos de los índices en caliente para equilibrar de forma simétrica el consumo de operaciones de I/O de lectura entre ambas tablas.

## Respuesta

Aquí diría que por lógica sería la **B**: coger la vista/tabla con menos registros/ágil y compararla aprobechando el índice de la clave foránea contra la pesada haciendo uso del Index Seek.

## Corrección

- **Tu respuesta**: B (Correcta)
- **Análisis técnico**: Excelente lectura mecánica de los algoritmos de combinación. Para un escenario de "Asimetría Masiva" (una tabla minúscula de 150 filas frente a una gigantesca de 4 millones con índice), el **Nested Loops** es el rey indiscutible. El motor recorre las 150 filas del bucle externo (*Outer*) y, por cada una, hace un tiro rápido de flecha (*Index Seek*) en el bucle interno (*Inner*) de la tabla pesada. Un *Hash Join* ahí habría sido un suicidio de memoria RAM si indexa la grande.
- **Léxico/Ortografía**: Ojo con el bilingüismo involuntario: escribiste "*aprobechando*" (lo correcto en castellano es **aprovechando**, con "v", mientras que en galego é **aproveitando**).