Comment créer un script de traitement QGIS qui ajoute une séquence à une colonne d'identifiant unique dans PostGIS?

10

Quelqu'un peut-il m'aider à créer un script de traitement QGIS qui ajoute une séquence à une colonne d'identifiant unique existante (type: entier) dans PostGIS?

Ce serait très utile, par exemple comme solution de contournement pour le bogue # 6798 . Malheureusement, je n'ai aucune expérience Python.

entrez la description de l'image ici

entrez la description de l'image ici

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
la source
1
Je demanderais pourquoi dans votre flux de travail et le flux de travail décrit dans le bogue, vous ne gérez pas vos données PostgreSQL à l'aide de PGAdmin ou d'autres outils d'administration de base pour postgresql? Je ne sais pas pourquoi des efforts sont déployés pour faire ce travail dans QGIS alors que les outils d'administration le font très bien!
DPSSpatial
Pour moi, la gestion des tables dans QGIS DB-Manager est assez intuitive. Cependant, je suis également intéressé de voir comment un script de traitement peut exécuter des requêtes PostGIS.
eclipsed_by_the_moon
3
Pour nous, PGAdmin et la fenêtre SQL sont plus de notre "SIG" que QGIS! QGIS n'est que le client visuel de nos données spatiales et de nos sorties - tout le travail, y compris le `` géo '', les scripts, etc. sont effectués en dehors de QGIS ... les outils qui existent pour le faire sont déjà perfectionnés, et vraiment, le flux de travail de l'utilisation de ces outils non-QGIS avec des données PostgresSQL / PostGIS est une meilleure pratique ...
DPSSpatial

Réponses:

2

Il convient de noter que le module python psycopg2ne semble pas automatiquement COMMITune transaction (comme le font d'autres clients comme QGIS DB Manager ou pgAdmin), par conséquent, l' COMMITinstruction doit faire partie de la sqlchaîne dans le script.

Cela n'a pas d'importance avec les SELECTdéclarations car dans ces cas, un COMMITest évidemment effectué lors de l'obtention des résultats via cur.fetchall().

Ceci est une version retravaillée du script de ma réponse ci-dessus:

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

À condition que votre instruction SQL produise des résultats valides, les scripts ci-dessous devraient faire ce que vous recherchez. Malheureusement, je n'ai rien à portée de main pour tester cela, mais vous pouvez essayer de donner votre avis.

J'ai essayé de le commenter pour plus de commodité, le script effectue essentiellement trois étapes:

  • obtenir les paramètres de connexion à la base de données pour la couche sélectionnée (devrait être postgres)
  • remplir les paramètres de connexion dans la chaîne d'instruction sql
  • exécuter l'instruction sql

Notez la sortie de protocole du 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
la source
J'ai testé le script et le journal de traitement indique unexpected indent (, line 32) See log for more details. Y a-t-il quelque chose que je fais mal? L'instruction SQL fonctionne dans 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
Oui, ma faute. La trydéclaration avait une indentation erronée. Je viens de corriger ça.
Jochen Schwarze
Thx pour avoir corrigé cela, mais j'obtiens une erreur Python lors de l'exécution du 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

Il semble déjà y avoir un plugin similaire (bien qu'il crée un nouveau champ ID unique pour vous, plutôt que de créer une séquence.)

Cela suppose que vous avez déjà un champ d'ID unique (cela n'a pas besoin d'être numérique), mais que vous voulez un ID numérique simple à la place (1,2,3 ..)

Dans la boîte à outils Traitement, accédez à Scripts> Outils> Obtenir des scripts en ligne ...

Développez "Non installé" et choisissez "EquivalentNumField". N'oubliez pas de cocher la case avant de cliquer sur OK. Cela m'a rattrapé ... ;-)

entrez la description de l'image ici

Pour le trouver rapidement, tapez "Equiv" dans la barre de recherche de traitement, et vous devriez pouvoir double-cliquer dessus à partir de là.

entrez la description de l'image ici

Voici un exemple. Ces bois avaient un champ unique (osm_id) mais le plugin a ajouté un NUM_FIELD avec des valeurs numériques simples à la place

entrez la description de l'image ici

Steven Kay
la source
Steve, c'est un script utile, mais je cherche quelque chose de différent.
eclipsed_by_the_moon
@eclipsed_by_the_moon comment cette réponse ne correspond-elle pas à ce que vous recherchez? Il semble finalement résoudre votre problème d'avoir besoin d'une colonne d'identifiant unique.
kttii