如何从 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
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 查询中获取分组记录的第一条和最后一条记录?