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.