¿Cómo crear un script de procesamiento QGIS que agregue una secuencia a una columna de identificador único en PostGIS?

10

¿Alguien puede ayudarme a crear un script de procesamiento QGIS que agregue una secuencia a una columna de identificador único existente (tipo: entero) en PostGIS?

Esto sería bastante útil, por ejemplo, como solución para el error # 6798 . Desafortunadamente, no tengo ninguna experiencia en Python.

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

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);
eclipsado_por_la_luna
fuente
1
Preguntaría por qué en su flujo de trabajo, y en el flujo de trabajo descrito en el error, ¿no está administrando sus datos de PostgreSQL utilizando PGAdmin u otras herramientas de administración centrales para postgresql? ¡No sé por qué hay un esfuerzo para hacer que esto funcione en QGIS cuando las herramientas de administración lo hacen bien!
DPSEspacial
Para mí, administrar tablas en QGIS DB-Manager es bastante intuitivo. Sin embargo, también me interesa ver cómo un script de procesamiento puede ejecutar consultas PostGIS.
eclipsed_by_the_moon
3
¡Para nosotros, PGAdmin y la ventana SQL son más de nuestro "SIG" que QGIS! QGIS es solo el cliente visual de nuestros datos y resultados espaciales: todo el trabajo, incluido el procesamiento 'geo', los scripts, etc., se realiza fuera de QGIS ... las herramientas que existen para hacer esto ya están perfeccionadas, y realmente, el flujo de trabajo de usar estas herramientas que no son QGIS con datos PostgresSQL / PostGIS es una mejor práctica ...
DPSSpatial

Respuestas:

2

Vale la pena señalar que el módulo de Python psycopg2no parece ser automáticamente COMMITuna transacción (como lo hacen otros clientes como QGIS DB Manager o pgAdmin), por lo tanto, la COMMITdeclaración debe ser parte de la sqlcadena en el script.

Esto no importa con las SELECTdeclaraciones porque en estos casos, COMMITobviamente, se lleva a cabo cuando se obtienen resultados a través de cur.fetchall().

Esta es una versión reelaborada del script de mi respuesta anterior:

#!/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.')
Jochen Schwarze
fuente
6

Siempre que su declaración SQL produzca resultados válidos, los siguientes scripts deben hacer lo que busca. Lamentablemente, no tengo nada a mano para probar esto, pero podrías intentar dar tu opinión.

Traté de comentarlo por conveniencia, básicamente el script lleva a cabo tres pasos:

  • obtener los parámetros de conexión de la base de datos para la capa seleccionada (deben ser postgres)
  • rellene los parámetros de conexión en la cadena de instrucción sql
  • ejecutar la instrucción sql

Tenga en cuenta la salida del protocolo de la secuencia de comandos.

#!/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.')
Jochen Schwarze
fuente
He probado el script y el registro de procesamiento dice unexpected indent (, line 32) See log for more details. ¿Hay algo que estoy haciendo mal? La instrucción SQL está funcionando en el 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í, mi culpa. La trydeclaración tenía una sangría incorrecta. Acabo de arreglar esto.
Jochen Schwarze
Gracias por arreglar esto, pero recibo un error de Python cuando ejecuto el 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

Parece que ya hay un complemento similar (aunque crea un nuevo campo de ID único para usted, en lugar de crear una secuencia).

Esto supone que ya tiene un campo de identificación único (esto no necesita ser numérico), sino que desea una identificación numérica simple (1,2,3 ..)

En la caja de herramientas Procesamiento, vaya a Scripts> Herramientas> Obtener scripts en línea ...

Expanda "No instalado" y elija "EquivalentNumField". Recuerde hacer clic en la casilla de verificación antes de hacer clic en Aceptar. Eso me sorprendió ... ;-)

ingrese la descripción de la imagen aquí

Para encontrarlo rápidamente, escriba "Equiv" en la barra de búsqueda de procesamiento, y debería poder hacer doble clic desde allí.

ingrese la descripción de la imagen aquí

Aquí hay un ejemplo. Estas maderas tenían un campo único (osm_id) pero el complemento ha agregado un NUM_FIELD con valores numéricos simples.

ingrese la descripción de la imagen aquí

Steven Kay
fuente
Steve, este es un guión útil, pero estoy buscando algo diferente.
eclipsed_by_the_moon
@eclipsed_by_the_moon ¿cómo es esta respuesta no es lo que estás buscando? Parece que finalmente resuelve su problema de necesitar una columna de identificador único.
kttii