Postgresql 偏移量与行数()

Postgresql 偏移量与行数(),postgresql,Postgresql,正如我们所知,Postgresql的偏移量要求它扫描所有的行,直到到达您请求的位置,这使得它对于通过大量结果集进行分页毫无用处,随着偏移量的增加,速度越来越慢 PG 8.4现在支持窗口功能。而不是: SELECT * FROM table ORDER BY somecol LIMIT 10 OFFSET 500 你可以说: SELECT * FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY somecol ASC) AS rownum FROM table

正如我们所知,Postgresql的偏移量要求它扫描所有的行,直到到达您请求的位置,这使得它对于通过大量结果集进行分页毫无用处,随着偏移量的增加,速度越来越慢

PG 8.4现在支持窗口功能。而不是:

SELECT * FROM table ORDER BY somecol LIMIT 10 OFFSET 500
你可以说:

SELECT * FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY somecol ASC) AS rownum FROM table) AS foo
WHERE rownum > 500 AND rownum <= 510
后一种方法对我们有帮助吗?或者我们必须继续使用标识列和临时表进行大分页吗?

对于大的结果集使用临时表,速度会快得多。对于较小的结果,设置偏移量的限制可以很好地进行施工,但它有其局限性


行号是一件好事,但不用于分页。由于顺序扫描,您的性能很差。

我构建了一个比较偏移量、游标和行号的测试。我对ROW_NUMBER的印象是,无论你在结果集中的哪个位置,它的速度都是一致的,这是正确的。然而,这种速度比偏移量或光标要慢得多,正如我的印象一样,偏移量或光标的速度几乎相同,两者的速度都会随着结果的结束而降低

结果:

offset(100,100): 0.016359
scroll(100,100): 0.018393
rownum(100,100): 15.535614

offset(100,480000): 1.761800
scroll(100,480000): 1.781913
rownum(100,480000): 15.158601

offset(100,999900): 3.670898
scroll(100,999900): 3.664517
rownum(100,999900): 14.581068
测试脚本使用sqlalchemy设置表和1000000行测试数据。然后,它使用一个psycopg2游标执行每个SELECT语句,并使用三种不同的方法获取结果

from sqlalchemy import *

metadata = MetaData()
engine = create_engine('postgresql://scott:tiger@localhost/test', echo=True)

t1 = Table('t1', metadata,
    Column('id', Integer, primary_key=True),
    Column('d1', String(50)),
    Column('d2', String(50)),
    Column('d3', String(50)),
    Column('d4', String(50)),
    Column('d5', String(50))
)

if not engine.has_table('t1'):
    conn = engine.connect()
    t1.create(conn)

    # 1000000 rows
    for i in range(100):
        conn.execute(t1.insert(), [
            dict(
                ('d%d' % col, "data data data %d %d" % (col, (i * 10000) + j))
                for col in range(1, 6)
            ) for j in xrange(1, 10001)
        ])

import time

def timeit(fn, count, *args):
    now = time.time()
    for i in xrange(count):
        fn(*args)
    total = time.time() - now
    print "%s(%s): %f" % (fn.__name__, ",".join(repr(x) for x in args), total)

# this is a raw psycopg2 connection.
conn = engine.raw_connection()

def offset(limit, offset):
    cursor = conn.cursor()
    cursor.execute("select * from t1 order by id limit %d offset %d" % (limit, offset))
    cursor.fetchall()
    cursor.close()

def rownum(limit, offset):
    cursor = conn.cursor()
    cursor.execute("select * from (select *, "
                    "row_number() over (order by id asc) as rownum from t1) as foo "
                    "where rownum>=%d and rownum<%d" % (offset, limit + offset))
    cursor.fetchall()
    cursor.close()

def scroll(limit, offset):
    cursor = conn.cursor('foo')
    cursor.execute("select * from t1 order by id")
    cursor.scroll(offset)
    cursor.fetchmany(limit)
    cursor.close()

print 

timeit(offset, 10, 100, 100)
timeit(scroll, 10, 100, 100)
timeit(rownum, 10, 100, 100)

print 

timeit(offset, 10, 100, 480000)
timeit(scroll, 10, 100, 480000)
timeit(rownum, 10, 100, 480000)

print 

timeit(offset, 10, 100, 999900)
timeit(scroll, 10, 100, 999900)
timeit(rownum, 10, 100, 999900)

当你进行分页时,游标是否不能正常工作?当连接结束时,它们不会消失吗?嗯,是的,这是一个网络应用程序,所以光标不是一个选项。是的,在连接结束后,光标确实会消失,这很好。它仍然比任何其他解决方案快得多。使用限制偏移量或行号的查询也会出现。我不知道为什么光标不能在网络应用程序中使用,它在这里工作得很好。开始测试,看看你能得到多少。或者为什么光标不是一个选项?它在一个大的结果集上运行速度非常快,并且不会对您或您的代码造成伤害。在PHP中运行良好。游标在其他类型的应用程序中工作得更好,这是真的,但不要担心Web应用程序,也不要限制自己。是否启用了服务器端游标?如果没有,上面的代码就没有创建数据库游标,也就是您要查找的对象。使用名称调用游标时,将调用psycopg2服务器端游标,如中所示。如果从对游标的调用中删除name参数,则上面的scroll函数每次调用大约需要10秒钟——这是因为如果不打开服务器端游标,psycopg2将完全加载结果集,在上述情况下,它会在连线上拖出1百万行。