2015-05-24 12 views
7

Dalla documentazione psycopg2:Python cursori psycopg2

Quando viene eseguita una query di database, il cursore psycopg di solito recupera tutti i record restituiti dal backend, trasferendole il processo client. Se la query ha restituito un'enorme quantità di dati, una quantità proporzionalmente grande di memoria verrà allocata dal client. Se il set di dati è troppo grande per essere praticamente gestito dal lato client, è possibile creare un cursore sul lato server.

Vorrei interrogare una tabella con forse migliaia di righe e fare qualche azione per ognuna. I normali cursori porteranno effettivamente l'intero set di dati sul client? Non sembra molto ragionevole. Il codice è qualcosa sulla falsariga di:

conn = psycopg2.connect(url) 
cursor = conn.cursor() 
cursor.execute(sql) 
for row in cursor: 
    do some stuff 
cursor.close() 

Mi aspetto che questa sia un'operazione di streaming. E una seconda domanda riguarda l'ambito dei cursori. All'interno del mio ciclo mi piacerebbe fare un aggiornamento di un altro tavolo. Devo aprire un nuovo cursore e chiudere ogni volta? Ogni aggiornamento di articolo dovrebbe essere nella sua stessa transazione in quanto potrebbe essere necessario eseguire un rollback.

for row in cursor: 
    anotherCursor = anotherConn.cursor() 
    anotherCursor.execute(update) 
    if somecondition: 
     anotherConn.commit() 
    else: 
     anotherConn.rollback 
cursor.close() 

======== EDIT: La mia risposta a PRIMA PARTE ========

Ok, cercherò di rispondere alla prima parte della mia domanda. I normali cursori portano effettivamente l'intero set di dati non appena si chiama execute, prima ancora di iniziare a iterare il set di risultati. È possibile verificarlo verificando l'impronta di memoria del processo in ogni fase. Ma la necessità di un cursore sul lato server è in realtà dovuta al server Postgres e non il cliente, ed è documentato qui: http://www.postgresql.org/docs/9.3/static/sql-declare.html

Ora, questo non è immediatamente evidente dalla documentazione, ma tali cursori in realtà può essere creato temporaneamente per la durata della transazione. Non è necessario creare in modo esplicito una funzione che restituisce un riferimento al database, con l'istruzione SLQ specifica, ecc. Con psycopg2 è necessario fornire un nome mentre si ottiene il cursore e verrà creato un cursore temporaneo per quella transazione. Così, invece di:

cursor = conn.cursor() 

si solo bisogno di a:

cursor = conn.cursor('mycursor') 

Questo è tutto e funziona. Presumo che la stessa cosa sia fatta sotto le copertine quando si usa JDBC, quando si imposta fetchSize. È solo un po 'più trasparente. Vedere i documenti qui: https://jdbc.postgresql.org/documentation/head/query.html#query-with-cursor

È possibile verificare che questo funzioni interrogando la vista di pg_cursors all'interno della stessa transazione. Il cursore sul lato server viene visualizzato dopo aver ottenuto il cursore sul lato client e scompare dopo aver chiuso il cursore sul lato client. Quindi, la linea di fondo: sono felice di fare quel cambiamento al mio codice, ma devo dire che questo è stato un grande trucchetto per qualcuno che non ha esperienza con postgres.

+0

È possibile evitare tutto il traffico e il compito del cursore eseguendo la selezione e l'aggiornamento nella stessa query. Pubblica le query reali ed è molto probabile che otterrai una risposta molto migliore. –

+0

Immagino tu intenda con una frase di "aggiornamento dove", giusto? Nel mio caso d'uso, l'elaborazione era molto più complessa di quella. –

+0

Intendo una query CTE. Qualunque sia la complessità. –

risposta

0

In realtà, hai già risposto alla domanda;).

  1. Sì è necessario utilizzare il cursore sul lato server per ottenere i record in streaming http://initd.org/psycopg/docs/usage.html#server-side-cursors

Da docs:

CREATE FUNCTION reffunc(refcursor) RETURNS refcursor AS $$ 
BEGIN 
    OPEN $1 FOR SELECT col FROM test; 
    RETURN $1; 
END; 
$$ LANGUAGE plpgsql; 

E in codice:

cur1 = conn.cursor() 
cur1.callproc('reffunc', ['curname']) 

cur2 = conn.cursor('curname') 
for record in cur2:  # or cur2.fetchone, fetchmany... 
    # do something with record 
    pass 
  1. Sì, dovresti aprire una nuova cursore, se vuoi ottenere le righe con il cursore sul lato server.
+0

Quindi con i normali cursori non appena eseguo sql, l'intero set di risultati verrà caricato in memoria? Cosa succede se non ho accesso al database per creare un oggetto lato server? Non c'è qualcosa come un set di risultati JDBC? Grazie –

+0

Vedere l'esempio nel driver JDBC in cui non è necessario creare alcun cursore esplicito sul lato server: https://jdbc.postgresql.org/documentation/head/query.html#query-with-cursor –

+0

doc: quando viene eseguita una query del database , il cursore Psycopg solitamente recupera tutti i record restituiti dal back-end, trasferendoli al processo client. Se la query ha restituito un'enorme quantità di dati, una quantità proporzionalmente grande di memoria verrà allocata dal client. – kwarunek