如何从sql查询中获取第一个和最后一个记录?

问题描述:

我在PostgreSQL中有一个表,我用几个条件运行查询,返回多个行,按其中一列排序。一般来说它是:如何从sql查询中获取第一个和最后一个记录?

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

现在我只获得第一,并从该查询的最后一行感兴趣。我可以在数据库之外获得它们(这是我实际做的),但是想知道为了获得更好的性能,我不应该从数据库中只获得我实际上感兴趣的2条记录。

如果是这样,我该如何修改我的查询?

+2

使用集合函数MIN&MAX:http://www.postgresql.org/docs/8.2/static/tutorial-agg.html – 2009-09-28 04:24:19

+2

@rexem:min&max不会在几列上工作 - 它们将工作只有在您按此专栏订购时才可以在单栏中显示。 – 2009-09-28 04:57:32

+0

您可能还想看看'SELECT DISTINCT ON(...)... ORDER BY ...'。参见[PostgreSQL文档](https://www.postgresql.org/docs/current/static/sql-select.html)。 – RhinoDevel 2016-08-11 09:42:27

[警告:可能不是这样做的最有效的方法]:

(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

我认为'Top'关键字仅适用于SQL Server,MySQL/Postgre使用'Limit' – Robo 2009-09-28 04:28:06

+1

你是对的。我会编辑我的并投票给你。 – 2009-09-28 04:30:21

+2

使用UNION ALL会使这一点稍微更快,因为它会删除对重复项的检查。如果第一行和最后一行当然是相同的,UNION将只返回一行,UNION ALL将两次返回同一行,它在工作方式上会有所不同。 – 2009-09-28 08:16:12

第一条记录:

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

最后一个记录:

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

其他评论中提到的UNION ALL方法肯定会比发出两个查询更快。 – 2009-09-28 08:16:45

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) 

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

PostgreSQL没有一个'rowid',这就是所谓的'ctid'存在(并没有Oracle的ROWID也不PostgreSQL的CTID保证任何顺序) – 2012-08-26 21:13:50

+4

为什么不把这个简单: 'SELECT * FROM TABLE_NAME WHERE ROWID =(SELECT MIN (rowid)FROM TABLE_NAME) OR rowid =(SELECT MAX(rowid)FROM TABLE_NAME)' – 2012-10-11 13:53:03

你可能想试试这个,可能比做两个查询速度快:

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

有趣。我没有意识到这一点。只是测试过它,当然,这都是真的。感谢您的洞察力。 – DrFriedParts 2016-03-12 09:19:17

最后一条记录:

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

第一个记录:

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

这对于PostgreSQL来说是无效的SQL(它使用标准双引号'''来引用对象名称 - 在这里它们根本不需要) – 2012-09-03 06:32:10

+0

Works,thanks!有人知道这是多么高效吗? – Souleiman 2013-04-20 21:50:56

+0

@souleiman每个查询查询计划器将尽可能快地使用适当的索引并返回O(log(N))...但是,在2个单独的查询中这样做会比一个查询更慢和/或效率更低如果你总是*想要*两者* OP的第一个和最后一个记录表示,只需在两个查询之间使用UNION ALL(更快)(如果你不想重复,则使用UNION)。 – DrFriedParts 2016-03-12 05:16:38

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})); 

为什么不使用order by asc极限1和反向排序通过desc限制1 ..

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

SELECT   
    MAX(Column) 
FROM    
    TableName 
WHERE   
    (Filter) 

UNION ALL 

SELECT   
    MIN(Column) 
FROM    
    TableName AS Tablename1 
WHERE   
    (Filter) 
ORDER BY 
    Column 

-- 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 
); 

得到它从这里: https://wiki.postgresql.org/wiki/First/last_(aggregate)

在做,直到如今,都要经过扫描两次,一个是首先暴露的方式第一行和第一行。

使用窗口函数“ROW_NUMBER()OVER(...)”加上“WITHQUERY”,您只能扫描一次并获取这两个项目。

窗口功能: https://www.postgresql.org/docs/9.6/static/functions-window.html

使用查询

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

例子:

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; 

在这样你会做关系的过滤和数据操作一次。

在两种方式上尝试一下EXPLAIN ANALYZE。