2017-02-16 75 views
2

我有一个People(Id, first_name, last_name),其中主键是id。我希望能够查到(last_name, first_name, Id)订购的表格中的前N个人。在某些情况下,我需要查找下N个人,等等。我想有效地做到这一点。做这个的最好方式是什么?如何实现分页?

回答

3

主要有两种方式:

  • 使用LIMITOFFSET
  • 使用LIMIT和关键的,以前的页面

的OFFSET策略让您阅读任意网页,但因为每次查询运行时它都是无效的,所以它必须读取所有先前页面中的行。这是最容易实现的,并且可以成为可接受的策略(特别是如果您只需要前几页),但通常不推荐。前一页的关键策略确实需要按顺序读取页面,但效率更高,因为每个页面只读取所需的行。

因此,让我们先从原来的查询来获取从表中的结果通过(LastName, FirstName, Id)下令:

SELECT 
    t.id, 
    t.first_name, 
    t.last_name 
FROM 
    People as t 
ORDER BY 
    t.last_name, 
    t.first_name, 
    t.id 
LIMIT 
    @limit_rows 

你可能会想,以确保您的查询的所有查看数据库数据的一致性快照,所以你”我们希望确保你的查询序列总是从相同的时间戳中读取。完成此操作的最简单方法是将您的第一个查询设置为returnReadTimestamp设置为true的ReadOnly事务。然后,您的后续查询也可以是ReadOnly事务,并且它们应该使用由原始查询返回的相同时间戳作为它们的readTimestamp。请注意,无论您选择哪种方法,ORDER BY条款对于确保您的查询序列中的一致结果至关重要。 假设返回的最后一行是(1709, "John", "Smith")。然后你在查询第一次尝试,得到的结果的下一页可能是这样的:

SELECT 
    t.id, 
    t.first_name, 
    t.last_name 
FROM 
    People as t 
WHERE 
    t.last_name > "Smith" 
    OR 
    (t.last_name = "Smith" and t.first_name > "John") 
    OR 
    (t.last_name = "Smith" and t.first_name = "John" AND t.id > 1709) 
ORDER BY 
    t.last_name, 
    t.first_name, 
    t.id 
LIMIT 
    @limit_rows 

中间WHERE条款是新的。但是编写这个谓词比你想象的要复杂。您可能需要处理NULL值。您必须处理有多个名为John Smith的人使用不同的id值的情况。而且您需要非常小心浮点数和NaN值。 Cloud Spanner的Read API在这种情况下也很有用,因为它可以更容易地对表格上的范围扫描进行分页。

+0

嗨迈克,感谢张贴这个。如果您使用快照并提供时间戳,为什么需要添加所有约束条件。你不能说t.id> 1709吗?或者你是否试图掩盖快照因垃圾收集而过期的情况? – Bradford

+0

垃圾收集在这里是无关紧要的。额外约束的原因是因为问题表示我们希望按照(姓氏,名字,ID)的顺序返回查询结果,这与主键顺序不同。请注意,可能有一个结果,其t.id小于1709,这仍然是一个需要的结果(例如“Wilkes”,“Bob”,805) –

0

MySQL和PostgreSQL的支持非常酷的功能,称为偏移通常用LIMIT子句中使用。

LIMIT子句用于限制SQL语句中返回结果的数量。所以,如果你有一台1000行,但只想要回第10位,你会做这样的事情:

SELECT column FROM table LIMIT 10 

这是类似于Microsoft SQL Server上的TOP子句。然而LIMIT子句总是在MySQL和PostgreSQL的查询结尾。

现在假设你想显示结果11-20。随着关键字抵消其一样简单,下面的查询将做到:

SELECT column FROM table LIMIT 10 OFFSET 10 

这可以很容易地编写多页结果或分页与SQL。通常使用的方法是选择所有记录,然后在应用程序服务器层上进行筛选,而不是直接在数据库上进行筛选。就像你会想象在数据库上这样做会产生更好的性能。