Supongamos que queremos obtener el identificador de departamento y nombre de la tabla de departamentos ordenados por el nombre de manera descendente. La sentencia SQL sería algo así:
SQL> SELECT department_id, department_name 2 FROM DEPARTMENTS 3 ORDER BY department_name DESC;
DEPARTMENT_ID DEPARTMENT_NAME
------------- ------------------------------
120 Treasury
50 Shipping
150 Shareholder Services
80 Sales
250 Retail Sales
260 Recruiting
30 Purchasing
70 Public Relations
270 Payroll
200 Operations
220 NOC
DEPARTMENT_ID DEPARTMENT_NAME
------------- ------------------------------
20 Marketing
170 Manufacturing
210 IT Support
230 IT Helpdesk
60 IT
40 Human Resources
240 Government Sales
100 Finance
90 Executive
130 Corporate Tax
140 Control And Credit
DEPARTMENT_ID DEPARTMENT_NAME
------------- ------------------------------
190 Contracting
180 Construction
160 Benefits
10 Administration
110 Accounting
27 filas seleccionadas. Si queremos obtener sólo las primeras 11 filas según el mismo orden, una primera posibilidad sería algo como esto:
SQL> SELECT department_id, department_name
2 FROM DEPARTMENTS
3 WHERE ROWNUM <= 11
4 ORDER BY department_name DESC;
DEPARTMENT_ID DEPARTMENT_NAME
------------- ------------------------------
50 Shipping
80 Sales
30 Purchasing
70 Public Relations
20 Marketing
60 IT
40 Human Resources
100 Finance
90 Executive
10 Administration
110 Accounting
11 filas seleccionadas. ROWNUM es una pseudocolumna que contiene un número que indica el orden en que Oracle selecciona la filas de la consulta, empezando por 1. El problema es que el ROWNUM de cada fila se determina antes de hacer la ordenación. Por ese motivo la consulta no funciona bien.
Se nos podría ocurrir solucionarlo mediante la encapsulación la consulta dentro de otra consulta y filtrar por el ROWNUM de la consulta externa:
Se nos podría ocurrir solucionarlo mediante la encapsulación la consulta dentro de otra consulta y filtrar por el ROWNUM de la consulta externa:
SQL> SELECT *
2 FROM (
3 SELECT department_id, department_name
4 FROM DEPARTMENTS
5 ORDER BY department_name DESC
6 ) WHERE ROWNUM <= 11;
DEPARTMENT_ID DEPARTMENT_NAME
------------- ------------------------------
120 Treasury
50 Shipping
150 Shareholder Services
80 Sales
250 Retail Sales
260 Recruiting
30 Purchasing
70 Public Relations
270 Payroll
200 Operations
220 NOC
11 filas seleccionadas. Parece que funciona bien, pero existe un problema: ¡si usamos el operador >= con ROWNUM no funcionará! Tampoco si queremos buscar una fila concreta, pero sólo si es superior a la 1ª (¿?). Ejemplos:
SQL> SELECT *
2 FROM (
3 SELECT department_id, department_name
4 FROM DEPARTMENTS
5 ORDER BY department_name DESC
6 ) WHERE ROWNUM = 1;
DEPARTMENT_ID DEPARTMENT_NAME
------------- ------------------------------
120 Treasury
SQL> SELECT *
2 FROM (
3 SELECT department_id, department_name
4 FROM DEPARTMENTS
5 ORDER BY department_name DESC
6 ) WHERE ROWNUM = 2;
ninguna fila seleccionada
SQL> SELECT *
2 FROM (
3 SELECT department_id, department_name
4 FROM DEPARTMENTS
5 ORDER BY department_name DESC
6 ) WHERE ROWNUM >= 2;
ninguna fila seleccionada La solución definitiva consiste en volver a encapsular la consulta anterior, renombrando la pseudocolumna ROWNUM interior para poder referenciarla desde fuera:
SQL> SELECT department_id, department_name
2 FROM (
3 SELECT department_id, department_name, ROWNUM numfila
4 FROM (
5 SELECT *
6 FROM DEPARTMENTS
7 ORDER BY department_name DESC
8 )
9 ) WHERE numfila = 2;
DEPARTMENT_ID DEPARTMENT_NAME
------------- ------------------------------
50 Shipping Conociendo esto ya podemos realizar una consulta con resultados ordenados de manera que podamos seleccionar un rango de filas a mostrar, por ejemplo:
SQL> SELECT department_id, department_name 2 FROM ( 3 SELECT department_id, department_name, ROWNUM numfila 4 FROM ( 5 SELECT * 6 FROM DEPARTMENTS 7 ORDER BY department_name DESC 8 ) 9 ) WHERE numfila BETWEEN 11 AND 20;
Muy util, estaba teniendo problemas al obtener la página deseada.
ResponderEliminarComo mejora a la consulta anterior solo añadir un nuevo campo para obtener el número total de resultados que devolvería la consulta de no ser paginada.
Esto es muy útil para no tener que hacer 2 consultas una para contar el número de resultado y otra para obtenerlos.
Siguiendo con el ejemplo la cosa quedaría:
SELECT department_id, department_name, NUM_TOTAL_REGISTROS
FROM (
SELECT department_id, department_name, ROWNUM numfila, NUM_TOTAL_REGISTROS
FROM (
SELECT dep.*, SELECT COUNT (*) OVER () NUM_TOTAL_REGISTROS
FROM DEPARTMENTS dep
ORDER BY department_name DESC
)
) WHERE numfila BETWEEN 11 AND 20;
Gracias Martín. Muy útil tu aportación para conocer el uso de cláusula OVER.
ResponderEliminarEse resultado quiero verlo en una forma como puedo hacerlo
ResponderEliminarPuedes basar tu formulario en una vista de base de datos creada siguiendo los ejemplos anteriores.
ResponderEliminar1) Crear la vista:
CREATE OR REPLACE VIEW DEPARTMENTS_ORDERED_BY_NAME AS
SELECT d.*, ROWNUM NUMFILA
FROM (
SELECT *
FROM hr.DEPARTMENTS
ORDER BY department_name
) d
2) Crear un bloque basado en tabla o vista con el asistente de Oracle Forms, y le indicas tu vista.
De esta manera la columna NUMFILA se comporta como si fuera una fila más de la tabla.
Por otra parte, si tu bloque debe permitir inserciones o actualizaciones tendrías que implementar uno o más triggers de base de datos sobre la vista que gestionen estas operaciones (también es posible hacerlo con los triggers de Forms).
Un saludo.