2009-09-28 114 views
45

Tengo una tabla en PostgreSQL, ejecuto una consulta sobre ella con varias condiciones que devuelve varias filas ordenadas por una de las columnas. En general es:¿Cómo obtener el primer y último registro de una consulta sql?

SELECT <some columns> 
FROM mytable 
<maybe some joins here> 
WHERE <various conditions> 
ORDER BY date DESC 

Ahora sólo estoy interesado en conseguir la primera y la última fila de la consulta. Podría obtenerlos fuera del DB, dentro de mi aplicación (y esto es lo que realmente hago) pero me preguntaba si para un mejor rendimiento no debería obtener de la base de datos solo esos 2 registros que realmente me interesan.

Y si es así, ¿cómo modifico mi consulta?

+2

Utilice las funciones agregadas MIN & MAX: http://www.postgresql.org/docs/8.2/static/tutorial-agg.html –

+2

@rexem: min & max no funcionará en varias columnas, y funcionarán en una sola columna solo si está ordenando por esta columna. –

+0

Es posible que también desee ver 'SELECT DISTINCT ON (...) ... ORDER BY ...'. Ver [documentación de PostgreSQL] (https://www.postgresql.org/docs/current/static/sql-select.html). – RhinoDevel

Respuesta

55

[Advertencia: Puede que no sea la manera más eficiente de hacerlo]:

(SELECT <some columns> 
FROM mytable 
<maybe some joins here> 
WHERE <various conditions> 
ORDER BY date DESC 
LIMIT 1) 

UNION ALL 

(SELECT <some columns> 
FROM mytable 
<maybe some joins here> 
WHERE <various conditions> 
ORDER BY date ASC  
LIMIT 1) 
+7

Creo que la palabra clave 'Arriba' es sólo para el servidor SQL, MySQL/Postgre usa 'Límite' – Robo

+1

Tiene razón. Editaré el mío y votaré el tuyo. –

+2

El uso de UNION ALL hará que esto sea marginalmente más rápido, ya que elimina un cheque de duplicados. Diferirá en cómo funciona si la primera y la última fila son las mismas, por supuesto: UNION devolverá solo una fila, UNION ALL devolverá la misma fila dos veces. –

19

Primer registro:

SELECT <some columns> FROM mytable 
<maybe some joins here> 
WHERE <various conditions> 
ORDER BY date ASC 
LIMIT 1 

Último registro:

SELECT <some columns> FROM mytable 
<maybe some joins here> 
WHERE <various conditions> 
ORDER BY date DESC 
LIMIT 1 
+0

El método UNION ALL mencionado en el otro comentario definitivamente será más rápido que emitir dos consultas. –

4
SELECT * FROM TABLE_NAME WHERE ROWID=(SELECT MIN(ROWID) FROM TABLE_NAME) 
UNION 
SELECT * FROM TABLE_NAME WHERE ROWID=(SELECT MAX(ROWID) FROM TABLE_NAME) 

o

SELECT * FROM TABLE_NAME WHERE ROWID=(SELECT MIN(ROWID) FROM TABLE_NAME) 
          OR ROWID=(SELECT MAX(ROWID) FROM TABLE_NAME) 
+7

PostgreSQL no tiene un 'rowid', se llama' ctid' allí (y rowid es ni Oracle ni garantía ctid de PostgreSQL cualquier orden) –

+4

Por qué no hacer esto más simple: 'SELECT * DE TABLE_NAME DONDE rowid = (SELECT MIN (rowid) FROM TABLE_NAME) O rowid = (SELECCIONE MAX (rowid) FROM TABLE_NAME) ' –

18

Es posible que desee probar esto, lo que potencialmente podría ser más rápido que hacer dos consultas:

select <some columns> 
from (
    SELECT <some columns>, 
      row_number() over (order by date desc) as rn, 
      count(*) over() as total_count 
    FROM mytable 
    <maybe some joins here> 
    WHERE <various conditions> 
) t 
where rn = 1 
    or rn = total_count 
ORDER BY date DESC 
+0

Interesante. No me di cuenta de eso. Acabo de probarlo y es, por supuesto, todo cierto. Gracias por la visión. – DrFriedParts

12

último registro:

SELECT * FROM `aboutus` order by id desc limit 1 

primer registro:

SELECT * FROM `aboutus` order by id asc limit 1 
+1

SQL no válido para PostgreSQL (utiliza las comillas dobles estándar '" 'para citar los nombres de los objetos, y no se necesitan en absoluto aquí) –

+0

¡Funciona, gracias! ¿Alguien sabe cuán eficiente es esto? – Souleiman

+0

@souleiman Cada consulta es tan rápido como sea posible. El planificador de consultas usará el índice apropiado y lo devolverá lo más rápido posible O (log (N)) ... sin embargo, hacer esto en 2 consultas separadas será más lento y/o menos eficiente que una consulta si * siempre * quiere * ambos * el primer y último registro como indica el OP. Simplemente use UNION ALL (más rápido) entre las 2 consultas (o UNION si no desea duplicados). – DrFriedParts

1
select * 
from {Table_Name} 
where {x_column_name}=(
    select d.{x_column_name} 
    from (
     select rownum as rno,{x_column_name} 
     from {Table_Name})d 
     where d.rno=(
      select count(*) 
      from {Table_Name})); 
-1

por qué no usar ordenar por asc limit 1 y reverse - ordena por límite desc 1 ...

?

0
SELECT 
    MIN(Column), MAX(Column), UserId 
FROM 
    Table_Name 
WHERE 
    (Conditions) 
GROUP BY 
    UserId DESC 

o

SELECT   
    MAX(Column) 
FROM    
    TableName 
WHERE   
    (Filter) 

UNION ALL 

SELECT   
    MIN(Column) 
FROM    
    TableName AS Tablename1 
WHERE   
    (Filter) 
ORDER BY 
    Column 
0
-- Create a function that always returns the first non-NULL item 
CREATE OR REPLACE FUNCTION public.first_agg (anyelement, anyelement) 
RETURNS anyelement LANGUAGE SQL IMMUTABLE STRICT AS $$ 
     SELECT $1; 
$$; 


-- And then wrap an aggregate around it 
CREATE AGGREGATE public.FIRST (
     sfunc = public.first_agg, 
     basetype = anyelement, 
     stype = anyelement 
); 

-- Create a function that always returns the last non-NULL item 
CREATE OR REPLACE FUNCTION public.last_agg (anyelement, anyelement) 
RETURNS anyelement LANGUAGE SQL IMMUTABLE STRICT AS $$ 
     SELECT $2; 
$$; 

-- And then wrap an aggregate around it 
CREATE AGGREGATE public.LAST (
     sfunc = public.last_agg, 
     basetype = anyelement, 
     stype = anyelement 
); 

¡Gracias desde aquí: https://wiki.postgresql.org/wiki/First/last_(aggregate)

3

En todas las formas expuestas de hacer hasta ahora, debe pasar por escaneo de dos veces, una para el primer fila y uno para la última fila.

Usando la función de ventana "ROW_NUMBER() OVER (...)" más "WITH Queries", puede escanear solo una vez y obtener ambos elementos.

ventana Función: https://www.postgresql.org/docs/9.6/static/functions-window.html

con consultas: https://www.postgresql.org/docs/9.6/static/queries-with.html

Ejemplo:

WITH scan_plan AS (
SELECT 
    <some columns>, 
    ROW_NUMBER() OVER (ORDER BY date DESC) AS first_row, /*It's logical required to be the same as major query*/ 
    ROW_NUMBER() OVER (ORDER BY date ASC) AS last_row /*It's rigth, needs to be the inverse*/ 
FROM mytable 
<maybe some joins here> 
WHERE <various conditions> 
ORDER BY date DESC) 

SELECT 
    <some columns> 
FROM scan_plan 
WHERE scan_plan.first_row = 1 OR scan_plan.last_row = 1; 

En ese camino que va a hacer relaciones, filtraciones y manipulación de datos sólo una vez.

Pruebe EXPLAIN ANALYZE en ambos sentidos.

Cuestiones relacionadas