2010-06-26 9 views
27

जैसा कि हम जानते हैं, पोस्टग्रेस्क्ल ऑफ़ ऑफ़सेट के लिए यह आवश्यक है कि जब तक आप अनुरोध करते हैं, तब तक यह सभी पंक्तियों तक स्कैन न हो, जो इसे बड़े परिणाम सेट के माध्यम से अंकन के लिए बेकार बनाता है, धीमा हो जाता है और ऑफसेट के रूप में धीमा हो जाता है।OFFSET बनाम ROW_NUMBER()

पीजी 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 

बाद दृष्टिकोण हमें बिल्कुल भी मदद करता है? या क्या हमें बड़े पेजिनेशन के लिए कॉलम और टेम्प टेबल की पहचान करना जारी रखना है?

+0

एक कर्सर एक विकल्प क्यों नहीं होगा? यह एक बड़े परिणाम पर वास्तव में तेज़ है और यह आपको और न ही आपके कोड को नुकसान पहुंचाता है। PHP में ठीक काम करता है। एक कर्सर अन्य प्रकार के अनुप्रयोगों में भी बेहतर काम करता है, यह सच है लेकिन वेबपैप के बारे में चिंता न करें और स्वयं को सीमित न करें। –

उत्तर

22

मैंने एक परीक्षण बनाया है जो OFFSET, कर्सर, और ROW_NUMBER() की तुलना करता है। 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 कर्सर का उपयोग करता है।

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) 
+1

क्या आपने server_side_cursors को सक्षम किया था? यदि नहीं, तो उपर्युक्त कोड डेटाबेस कर्सर नहीं बना था, जो चीज़ आप खोज रहे थे। http://www.sqlalchemy.org/docs/05/reference/dialects/postgres.html?highlight=cursor –

+5

psycopg2 सर्वर साइड कर्सर तब लागू किए जाते हैं जब आप कर्सर को नाम से कॉल करते हैं, जैसे कि http://initd.org /psycopg/docs/usage.html#server-side- कर्सर। यदि आप कॉल से कर्सर() में नाम तर्क हटाते हैं, तो ऊपर दिए गए "स्क्रॉल" फ़ंक्शन में प्रति कॉल लगभग दस सेकंड लगते हैं - क्योंकि यदि आप सर्वर साइड कर्सर चालू नहीं करते हैं और उपरोक्त मामले में psycopg2 पूरी तरह से परिणाम सेट लोड करता है यह तार पर 1 एम पंक्तियों खींचता है। – zzzeek

3

एक बड़े परिणाम के लिए CURSOR का उपयोग करें, बहुत तेज़ होगा। छोटे नतीजे के लिए सेट ऑफ़सेट निर्माण ठीक काम करता है, लेकिन इसकी सीमाएं हैं।

ROW_NUMBER एक अच्छी बात है, लेकिन अंकन के लिए नहीं। अनुक्रमिक स्कैन के कारण आप खराब प्रदर्शन के साथ समाप्त होते हैं।

+0

जब आप पेजिनेशन कर रहे हों तो कर्सर अच्छी तरह से काम नहीं करेंगे? जब कनेक्शन समाप्त होता है तो वे गायब नहीं होते हैं? – Charles

+0

मिमी हाँ यह एक वेबएप के लिए है, इसलिए कर्सर एक विकल्प नहीं हैं। – zzzeek

+0

हां, आपके कनेक्शन समाप्त होने के बाद एक कर्सर विघटित हो जाता है और यह ठीक है। यह अभी भी किसी भी अन्य समाधान के बाद बहुत तेज है। LIMIT OFFSET या ROW_NUMBER का उपयोग कर एक क्वेरी भी dissappears। मुझे नहीं पता कि वेबपैप में एक कसर का उपयोग क्यों नहीं किया जा सकता है, यह यहां पर बहुत अच्छा काम करता है। परीक्षण शुरू करें और देखें कि आप कितना लाभ प्राप्त करेंगे। (या ढीला) –

संबंधित मुद्दे