Come creare uno script di elaborazione QGIS che aggiunge una sequenza a una colonna identificatore univoco in PostGIS?


10

Qualcuno può aiutarmi a creare uno script di elaborazione QGIS che aggiunge una sequenza a una colonna identificatore univoco esistente (tipo: intero) in PostGIS?

Ciò sarebbe abbastanza utile, ad esempio come soluzione alternativa al bug # 6798 . Sfortunatamente, non ho alcuna esperienza con Python.

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

CREATE SEQUENCE /*input_schema*/./*input_table*/_/*uic*/_seq OWNED BY /*input_schema*/./*input_table*/./*uic*/;
SELECT SETVAL('/*input_schema*/./*input_table*/_/*uic*/_seq', (SELECT MAX(/*uic*/) FROM /*input_schema*/./*input_table*/));
ALTER TABLE /*input_schema*/./*input_table*/
ALTER COLUMN /*uic*/ SET DEFAULT nextval('/*input_schema*/./*input_table*/_/*uic*/_seq'::regclass);

1
Vorrei chiedere perché nel tuo flusso di lavoro e nel flusso di lavoro descritto nel bug, non gestisci i tuoi dati PostgreSQL usando PGAdmin o altri strumenti di amministrazione di base per postgresql? Non so perché ci siano sforzi per far funzionare questo in QGIS quando gli strumenti di amministrazione lo fanno bene!
DPSSpatial

Per me, la gestione delle tabelle in QGIS DB-Manager è abbastanza intuitiva. Tuttavia, sono anche interessato a vedere come uno script di elaborazione può eseguire query PostGIS.
eclipsed_by_the_moon

3
Per noi PGAdmin e la finestra SQL sono più dei nostri "GIS" che di QGIS! QGIS è solo il client visivo per i nostri dati e output spaziali: tutto il lavoro, inclusi l'elaborazione "geo", gli script, ecc., Viene svolto al di fuori di QGIS ... gli strumenti esistenti per farlo sono già perfetti, e davvero, il flusso di lavoro dell'uso di questi strumenti non QGIS con i dati PostgresSQL / PostGIS è una pratica migliore ...
DPSSpatial

Risposte:


2

Vale la pena notare che il modulo python psycopg2non sembra automaticamente COMMITuna transazione (come fanno altri client come QGIS DB Manager o pgAdmin), quindi l' COMMITistruzione deve essere parte della sqlstringa nello script.

Questo non ha importanza con le SELECTdichiarazioni perché in questi casi a COMMITviene ovviamente eseguito un quando si ottengono risultati tramite cur.fetchall().

Questa è una versione rielaborata dello script dalla mia risposta sopra:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

#--------- define Interface
##[my_scripts]=group
##Add Serial to PostgreSQL Table=name
##Postgres_Table=vector
##Unique_identifier_column=field Postgres_Table

from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4 import *

from qgis.core import *
from qgis.gui import *
from qgis.utils import *

import psycopg2

#get the parameters for the tpg table into a directory
#get the table
pg_table = processing.getObject(Postgres_Table)
#create empty dictionary for key/value pairs of the tables connection parameters
db_params = {}
db_params['uic'] = Unique_identifier_column
#iterate over connection string
progress.setInfo(20*'-' + '  Connection parameters')
for param in pg_table.dataProvider().dataSourceUri().split(' '):
    key_val = param.split('=')
    progress.setInfo(str(key_val))
    try:
        #set key/value pair
        db_params[key_val[0]] = key_val[1]
    except:
        pass

#generate the sql statement string
#the text in round brackets are the keys from the db_params dictionary created above
#the values belonging to the keys are inserted into the string
progress.setInfo(20*'-' + '  SQL statement')
sql = """CREATE SEQUENCE %(table)s_%(uic)s_seq OWNED BY %(table)s.%(uic)s;
SELECT SETVAL('%(table)s_%(uic)s_seq', (SELECT MAX(%(uic)s) FROM %(table)s)); 
ALTER TABLE %(table)s ALTER COLUMN %(uic)s SET DEFAULT nextval('%(table)s_%(uic)s_seq'::regclass);
COMMIT;""" % db_params
#remove double quotes
sql = sql.replace('"','') 
progress.setInfo(sql)

#make connection string
constr = """dbname=%(dbname)s host=%(host)s port=%(port)s user=%(user)s     password=%(password)s""" % db_params
progress.setInfo(20*'-' + '  DB Connection string')
progress.setInfo(constr)
#make db connection
con = psycopg2.connect(constr)
cur = con.cursor()
#execute the above created sql statement
progress.setInfo(20*'-' + '  Executing SQL statement ...')
cur.execute(sql)
progress.setInfo(20*'-' + '  ... done.')

6

A condizione che la tua istruzione SQL produca risultati validi, gli script seguenti dovrebbero fare ciò che cerchi. Purtroppo non ho nulla a portata di mano per testarlo, ma potresti provare a dare un feedback.

Ho provato a commentarlo per comodità, in sostanza lo script esegue tre passaggi:

  • ottenere i parametri di connessione al database per il livello selezionato (dovrebbe essere postgres)
  • riempire i parametri di connessione nella stringa dell'istruzione sql
  • eseguire l'istruzione sql

Nota l'output del protocollo dello script.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

#--------- define Interface
##[my_scripts]=group
##Add Serial to PostgreSQL Table=name
##Postgres_Table=vector
##Unique_identifier_column=string replace_this_with_your_uic

from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4 import *

from qgis.core import *
from qgis.gui import *
from qgis.utils import *

import psycopg2

#get the parameters for the tpg table into a directory
#get the table
pg_table = processing.getObject(Postgres_Table)
#create empty dictionary for key/value pairs of the tables connection parameters
db_params = {}
db_params['uic'] = Unique_identifier_column
#iterate over connection string
progress.setInfo(20*'-' + '  Connection parameters')
for param in pg_table.dataProvider().dataSourceUri().split(' '):
    key_val = param.split('=')
    progress.setInfo(str(key_val))
    try:
        #set key/value pair
        db_params[key_val[0]] = key_val[1]
    except:
        pass

#generate the sql statement string
#the text in round brackets are the keys from the db_params dictionary created above
#the values belonging to the keys are inserted into the string
progress.setInfo(20*'-' + '  SQL statement')
sql = """CREATE SEQUENCE %(table)s_%(uic)s_seq OWNED BY %(table)s.%(uic)s;
            SELECT SETVAL(%(table)s_%(uic)s_seq, (SELECT MAX(%(uic)s) FROM %(table)s));
            ALTER TABLE %(table)s
            ALTER COLUMN %(uic)s SET DEFAULT nextval(%(table)s_%(uic)s_seq::regclass);""" % db_params
#remove double quotes
sql = sql.replace('"','') 
progress.setInfo(sql)

#make connection string
constr = """dbname=%(dbname)s host=%(host)s port=%(port)s user=%(user)s     password=%(password)s""" % db_params
progress.setInfo(20*'-' + '  DB Connection string')
progress.setInfo(constr)
#make db connection
con = psycopg2.connect(constr)
cur = con.cursor()
#execute the above created sql statement
progress.setInfo(20*'-' + '  Executing SQL statement ...')
cur.execute(sql)
progress.setInfo(20*'-' + '  ... done.')

Ho testato lo script e il registro di elaborazione dice unexpected indent (, line 32) See log for more details. C'è qualcosa che sto facendo di sbagliato? L'istruzione SQL funziona nel DB-Manager.
eclipsed_by_the_moon

File "C:/Users/abc/.qgis2/python/plugins\processing\core\GeoAlgorithm.py", line 230, in execute self.processAlgorithm(progress) File "C:/Users/abc/.qgis2/python/plugins\processing\script\ScriptAlgorithm.py", line 298, in processAlgorithm exec((script), ns) File "<string>", line 32 try: ^
eclipsed_by_the_moon

Sì, colpa mia. La trydichiarazione aveva un rientro errato. Ho appena risolto questo problema.
Jochen Schwarze,

Grazie per aver risolto questo problema, ma viene visualizzato un errore Python durante l'esecuzione dello script.
eclipsed_by_the_moon

Traceback (most recent call last): File "C:/Users/abc/.qgis2/python/plugins\processing\gui\AlgorithmDialog.py", line 219, in accept if runalg(self.alg, self): File "C:/Users/abc/.qgis2/python/plugins\processing\gui\AlgorithmExecutor.py", line 51, in runalg alg.execute(progress) File "C:/Users/abc/.qgis2/python/plugins\processing\core\GeoAlgorithm.py", line 244, in execute unicode(e) + self.tr('\nSee log for more details')) UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 44: ordinal not in range(128)
eclipsed_by_the_moon

3

Sembra che ci sia già un plug-in simile (anche se crea un nuovo campo ID univoco per te, piuttosto che creare una sequenza).

Ciò presuppone che tu abbia già un campo ID univoco (non è necessario che sia numerico), ma desideri un semplice ID numerico (1,2,3 ..)

Nella casella degli strumenti di elaborazione, vai a Script> Strumenti> Ottieni script online ...

Espandi "Non installato" e scegli "EquivalentNumField". Ricorda di fare clic sulla casella di controllo prima di fare clic su OK. Mi ha sorpreso ... ;-)

inserisci qui la descrizione dell'immagine

Per trovarlo rapidamente, digita "Equiv" nella barra di ricerca dell'elaborazione e dovresti essere in grado di fare doppio clic da lì.

inserisci qui la descrizione dell'immagine

Ecco un esempio Questi legni avevano un campo univoco (osm_id) ma il plugin ha aggiunto invece NUM_FIELD con semplici valori numerici

inserisci qui la descrizione dell'immagine


Steve, questa è una sceneggiatura utile, ma sto cercando qualcosa di diverso.
eclipsed_by_the_moon

@eclipsed_by_the_moon come è questa risposta non quello che stai cercando? Sembra in definitiva risolvere il problema di aver bisogno di una colonna identificatore univoco.
kttii,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.