Como criar um script de processamento QGIS que adiciona uma sequência a uma coluna identificadora exclusiva no PostGIS?

10

Alguém pode me ajudar a criar um script de processamento QGIS que adicione uma sequência a uma coluna identificadora exclusiva existente (tipo: número inteiro) no PostGIS?

Isso seria bastante útil, por exemplo, como uma solução alternativa para o bug # 6798 . Infelizmente, eu não tenho nenhuma experiência em Python.

insira a descrição da imagem aqui

insira a descrição da imagem aqui

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);
eclipsed_by_the_moon
fonte
1
Gostaria de perguntar por que, no seu fluxo de trabalho e no descrito no bug, você não está gerenciando seus dados do PostgreSQL usando o PGAdmin ou outras ferramentas administrativas básicas para o postgresql? Não sei por que há esforço gasto para fazer esse trabalho no QGIS quando as ferramentas administrativas o fazem muito bem!
DPSSpatial
Para mim, o gerenciamento de tabelas no QGIS DB-Manager é bastante intuitivo. No entanto, também estou interessado em ver como um script de processamento pode executar consultas PostGIS.
eclipsed_by_the_moon
3
Para nós, PGAdmin e a janela SQL são mais nossos "GIS" do que QGIS! O QGIS é apenas o cliente visual dos nossos dados e saídas espaciais - todo o trabalho, incluindo 'geo'-processing, scripts, etc., é feito fora do QGIS ... as ferramentas que existem para fazer isso já estão aperfeiçoadas e, na verdade, o fluxo de trabalho do uso dessas ferramentas não-QGIS com os dados do PostgresSQL / PostGIS é uma prática melhor ...
DPSSpatial

Respostas:

2

Vale ressaltar que esse módulo python psycopg2não parece automaticamente COMMITuma transação (como outros clientes, como o QGIS DB Manager ou o pgAdmin), portanto, a COMMITinstrução deve fazer parte da sqlcadeia de caracteres do script.

Isso não importa com as SELECTdeclarações, porque nesses casos a COMMITé obviamente realizada ao obter resultados via cur.fetchall().

Esta é uma versão reformulada do script da minha resposta acima:

#!/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
fonte
6

Desde que sua instrução SQL produza resultados válidos, os scripts abaixo devem fazer o que você procura. Infelizmente, não tenho nada em mãos para testar isso, mas você pode tentar dar um feedback.

Tentei comentar por conveniência, basicamente o script executa três etapas:

  • obter parâmetros de conexão com o banco de dados para a camada selecionada (deve ser postgres)
  • preencha os parâmetros de conexão na string de instrução sql
  • executar a instrução sql

Observe a saída do protocolo do 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.')
Jochen Schwarze
fonte
Eu testei o script e o log de processamento diz unexpected indent (, line 32) See log for more details. Existe algo que estou fazendo errado? A instrução SQL está funcionando no 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
Sim, minha culpa. A trydeclaração teve um recuo errado. Apenas consertei isso.
Jochen Schwarze
Thx por corrigir isso, mas recebo um erro de Python ao executar o 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 já haver um plug-in semelhante (embora ele crie um novo campo de ID exclusivo para você, em vez de criar uma sequência.)

Isso pressupõe que você já tenha um campo de ID exclusivo (não precisa ser numérico), mas deseja um ID numérico simples (1,2,3 ..)

Na caixa de ferramentas Processando, vá para Scripts> Ferramentas> Obter scripts de online ...

Expanda "Não instalado" e escolha "EquivalentNumField". Lembre-se de clicar na caixa de seleção antes de clicar em OK. Isso me pegou de fora ... ;-)

insira a descrição da imagem aqui

Para encontrá-lo rapidamente, digite "Equiv" na barra de pesquisa de processamento e você poderá clicar duas vezes nele.

insira a descrição da imagem aqui

Aqui está um exemplo. Essas madeiras tinham um campo exclusivo (osm_id), mas o plug-in adicionou um NUM_FIELD com valores numéricos simples

insira a descrição da imagem aqui

Steven Kay
fonte
Steve, este é um script útil, mas estou procurando algo diferente.
eclipsed_by_the_moon
@eclipsed_by_the_moon como essa resposta não é o que você está procurando? Parece resolver o problema de precisar de uma coluna identificadora exclusiva.
kttii