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

Posted

技术标签:

【中文标题】如何从 sql 查询中获取第一条和最后一条记录?【英文标题】:How to get First and Last record from a sql query? 【发布时间】:2009-09-28 04:16:31 【问题描述】:

我在PostgreSQL 中有一个表,我对它运行了一个查询,其中包含几个返回多行的条件,按其中一列排序。一般是:

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

现在我只对从此查询中获取第一行和最后一行感兴趣。我可以将它们放在数据库之外,在我的应用程序中(这就是我实际所做的),但我想知道是否为了获得更好的性能,我不应该只从数据库中获取我真正感兴趣的那两条记录。

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

【问题讨论】:

使用聚合函数 MIN & MAX:postgresql.org/docs/8.2/static/tutorial-agg.html @rexem:最小值和最大值不适用于多列 - 只有当您按此列排序时,它们才适用于单列。 您可能还想看看SELECT DISTINCT ON (...) ... ORDER BY ...。见PostgreSQL documentation。 【参考方案1】:

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

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

【讨论】:

我认为 'Top' 关键字仅适用于 SQL server,mysql/Postgre 使用 'Limit' 使用 UNION ALL 会稍微加快速度,因为它消除了对重复项的检查。当然,如果第一行和最后一行相同,它的工作方式会有所不同 - UNION 将只返回一行,UNION ALL 将返回同一行两次。 @Magnus Hagander:我不确定当最多有 2 行时它会更快。当然,我通常会区分 UNION 和 UNION ALL。 按原样运行查询会在 UNION 附近出现语法错误,可能是因为必须只有一个限制和排序依据。我解决了它用括号括起来的查询,比如(SELECT ... LIMIT 1) UNION ALL (SELECT ... LIMIT 1) 谁能解释为什么这可能没有效率?【参考方案2】:

您可能想试试这个,可能比执行两个查询更快:

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

【讨论】:

【参考方案3】:

第一条记录:

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

【讨论】:

另一条评论中提到的UNION ALL方法肯定比发出两个查询要快。【参考方案4】:

最后一条记录:

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

第一条记录:

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

【讨论】:

这对于 PostgreSQL 来说是无效的 SQL(它使用标准双引号 " 来引用对象名称 - 无论如何这里都不需要它们) @souleiman 每个查询都尽可能快。查询规划器将使用适当的索引并尽可能快地返回 O(log(N))...但是如果您总是 i> 想要 both 如 OP 所示的第一条和最后一条记录。只需在 2 个查询之间使用 UNION ALL(更快)(如果您不想重复,则使用 UNION)。【参考方案5】:

到目前为止所有暴露的do方式,都必须扫描两次,第一行一次,最后一行一次。

使用窗口函数“ROW_NUMBER() OVER (...)”加上“WITH Queries”,您可以只扫描一次并获得两项。

窗口功能: 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。

【讨论】:

感谢您也提供对关键概念的参考 上面的count(*) over () as total_count 性能更高一些,因为它只使用了一个WindowAgg,而且数据集也只排序了一次。【参考方案6】:
SELECT <rows> FROM TABLE_NAME WHERE ROWID=(SELECT MIN(ROWID) FROM TABLE_NAME) 
UNION
SELECT <rows> 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)

【讨论】:

PostgreSQL 没有rowid,它在那里被称为ctid(Oracle 的 rowid 和 PostgreSQL 的 ctid 都不保证任何排序) 为什么不让这个更简单:SELECT * FROM TABLE_NAME WHERE rowid=(SELECT MIN(rowid) FROM TABLE_NAME) OR rowid=(SELECT MAX(rowid) FROM TABLE_NAME)【参考方案7】:

我知道这是一个有 7 年历史的线程,但问题几乎相同,并且接受的答案是我开始使用并最终优化为以下内容,在我的情况下,它始终返回 85ms +-5ms 是一个索引 int 字段。

note1:已接受答案中的 UNION ALL 示例也有效,但在我的情况下性能较差,时间为 300 毫秒 +-20 毫秒。

note2:下一个最受好评的答案(行计数器示例)也有效,但在我的情况下性能最低,时间为 800 毫秒 +-70 毫秒。

select
  (select <some_column> from <some_table>
    order by <some_field> limit 1)        as oldest,
  (select <some_column> from <some_table> 
    order by <some_field> desc limit 1)   as newest
;

我确实注意到 op 引用了可能的连接。我不需要为了我自己的目的而包含连接(只是在相当动态的视图中获取当前的低 ID 和高 ID),但是使用这个模型,最旧和最新的子查询应该能够成为完整的查询。尚未测试,因此不确定它是否有效或最佳。

我确实测试过这个模型(上面可能也已经建议过),它可能更容易加入,但性能只是比上面示例的一半少一点,始终返回 220 毫秒 +在我的情况下为 -10 毫秒。

select oldest.<some_field> as old, 
       newest.<some_field> as new  
from
  (select <some_column> from <some_table>
    order by <some_field> limit 1)        as oldest,
  (select <some_column> from <some_table> 
    order by <some_field> desc limit 1)   as newest
;

【讨论】:

那么两个代码 sn-ps 中的哪一个是 85ms 的那个?【参考方案8】:

在某些情况下,WINDOW 函数 FIRST_VALUE() 和 LAST_VALUE() 很有用。主要优势 - 此查询是可读的,仅对数据进行一次排序,并且它只有一个查询可用于多列。

 SELECT
    FIRST_VALUE(timestamp) over w as created_dt,
    LAST_VALUE(timestamp) over w as last_update_dt,
    LAST_VALUE(action) over w as last_action
FROM events
WINDOW w as (ORDER BY timestamp ASC)

它可以用于通过某个ID获取第一行和最后一行

SELECT DISTINCT
    order_id,
    FIRST_VALUE(timestamp) over w as created_dt,
    LAST_VALUE(timestamp) over w as last_update_dt,
    LAST_VALUE(action) over w as last_action
    
FROM events as x
WINDOW w as (PARTITION BY order_id ORDER BY timestamp ASC)

【讨论】:

为避免重复窗口函数,您可以对每个窗口行为使用WINDOW 子句,然后在OVER 中引用它:WINDOW w as (PARTITION BY order_id ORDER BY timestamp ASC)【参考方案9】:
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));

【讨论】:

【参考方案10】:
-- 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)

【讨论】:

【参考方案11】:
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

【讨论】:

【参考方案12】:

为什么不使用order by asc limit 1 和相反的order by desc limit 1

【讨论】:

【参考方案13】:

如何在c#中获取DB的第一条和最后一条记录。

SELECT TOP 1 * 
  FROM ViewAttendenceReport 
 WHERE EmployeeId = 4 
   AND AttendenceDate >='1/18/2020 00:00:00' 
   AND AttendenceDate <='1/18/2020 23:59:59'
 ORDER BY Intime ASC
 UNION
SELECT TOP 1 * 
  FROM ViewAttendenceReport 
 WHERE EmployeeId = 4 
   AND AttendenceDate >='1/18/2020 00:00:00' 
   AND AttendenceDate <='1/18/2020 23:59:59' 
 ORDER BY OutTime DESC; 

【讨论】:

【参考方案14】:

我认为这段代码是一样的并且更容易阅读。

SELECT <some columns> 
FROM mytable
<maybe some joins here>
WHERE date >= (SELECT date from mytable)
OR date <= (SELECT date from mytable);

【讨论】:

虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的额外上下文可提高其长期价值。

以上是关于如何从 sql 查询中获取第一条和最后一条记录?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用聚合函数在 MySQL 查询中获取分组记录的第一条和最后一条记录?

mysql中如何查询表的第一条和最后一条记录

sql数据库查询,只能查出第一条记录?

postgresql某数据表中有多天的记录存在,我想取某天的第一条和最后一条记录,数据库查询语句怎么写

如何获取SQL查询当前数据上一条和下一条的记录?

获取表中存在的每个日期的第一条和最后一条记录号