Ho costruito un test che confronta OFFSET, cursori e ROW_NUMBER(). La mia impressione di ROW_NUMBER(), che sarebbe coerente in termini di velocità indipendentemente da dove ti trovi nel set di risultati, è corretta. Tuttavia, quella velocità è notevolmente più lenta di OFFSET o CURSOR, che, come è stata anche la mia impressione, sono più o meno le stesse in termini di velocità, entrambi decrescenti di velocità man mano che vai alla fine del risultato.
Risultati:
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
Lo script di test utilizza sqlalchemy per impostare tabelle e 1000000 righe di dati di test. Quindi utilizza un cursore psycopg2 per eseguire ciascuna istruzione SELECT e recuperare i risultati con i tre diversi metodi.
from sqlalchemy import *
metadata = MetaData()
engine = create_engine('postgresql://scott:[email protected]/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)