Jak utworzyć skrypt przetwarzania QGIS, który dodaje sekwencję do unikalnej kolumny identyfikatora w PostGIS?

10

Czy ktoś może mi pomóc w utworzeniu skryptu przetwarzania QGIS, który dodaje sekwencję do istniejącej unikalnej kolumny identyfikatora (typ: liczba całkowita) w PostGIS?

Byłoby to bardzo pomocne, np. Jako obejście błędu 6798 . Niestety nie mam doświadczenia w Pythonie.

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

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
źródło
1
Chciałbym zapytać, dlaczego w twoim przepływie pracy oraz w przepływie pracy opisanym w błędzie nie zarządzasz danymi PostgreSQL przy użyciu PGAdmin lub innych podstawowych narzędzi administracyjnych dla postgresql? Nie wiem, dlaczego wysiłek włożony w to, by działał w QGIS, kiedy narzędzia administracyjne robią to dobrze!
DPSSpatial
Dla mnie zarządzanie tabelami w QGIS DB-Manager jest dość intuicyjne. Jednak jestem również zainteresowany, aby skrypt przetwarzania mógł uruchamiać zapytania PostGIS.
eclipsed_by_the_moon
3
Dla nas PGAdmin i okno SQL są bardziej naszym „GIS” niż QGIS! QGIS jest tylko wizualnym klientem dla naszych danych przestrzennych i danych wyjściowych - wszystkie prace, w tym przetwarzanie geo, skrypty itp. Są wykonywane poza QGIS ... istniejące narzędzia do tego celu są już dopracowane i naprawdę, przepływ pracy przy użyciu tych narzędzi innych niż QGIS z danymi PostgresSQL / PostGIS jest lepszą praktyką ...
DPSSpatial

Odpowiedzi:

2

Warto zauważyć, że moduł Pythona psycopg2nie wydaje się automatycznie COMMITtransakcją (jak robią to inni klienci, tacy jak QGIS DB Manager lub pgAdmin), dlatego COMMITinstrukcja musi być częścią sqlciągu w skrypcie.

Nie ma to znaczenia w przypadku SELECTinstrukcji, ponieważ w tych przypadkach a COMMITjest oczywiście przeprowadzane przy uzyskiwaniu wyników cur.fetchall().

To jest przerobiona wersja skryptu z mojej powyższej odpowiedzi:

#!/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
źródło
6

Pod warunkiem, że twoja instrukcja SQL generuje prawidłowe wyniki, poniższe skrypty powinny robić to, czego szukasz. Niestety nie mam nic pod ręką, aby to przetestować, ale możesz spróbować wyrazić opinię.

Próbowałem to skomentować dla wygody, w zasadzie skrypt wykonuje trzy kroki:

  • uzyskaj parametry połączenia z bazą danych dla wybranej warstwy (powinno być postgres)
  • wypełnij parametry połączenia w ciągu instrukcji sql
  • wykonać instrukcję sql

Zanotuj wynik protokołu skryptu.

#!/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
źródło
Przetestowałem skrypt i dziennik przetwarzania mówi unexpected indent (, line 32) See log for more details. Czy coś robię źle? Instrukcja SQL działa w DB-Managerze.
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
Tak, moja wina. tryOświadczenie miał zły wcięcia. Właśnie to naprawiłem.
Jochen Schwarze,
Dziękuję za naprawienie tego, ale pojawia się błąd Python podczas uruchamiania skryptu.
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

Wydaje się, że już istnieje podobna wtyczka (chociaż tworzy dla ciebie nowe unikalne pole identyfikatora, zamiast tworzyć sekwencję).

Zakłada się, że masz już unikalne pole identyfikatora (nie musi to być wartość liczbowa), ale zamiast tego potrzebujesz prostego identyfikatora numerycznego (1,2,3 ..)

W przyborniku Przetwarzanie przejdź do Skrypty> Narzędzia> Pobierz skrypty z Internetu ...

Rozwiń „Niezainstalowany” i wybierz „EquivalentNumField”. Pamiętaj, aby kliknąć pole wyboru przed kliknięciem OK. To mnie złapało ... ;-)

wprowadź opis zdjęcia tutaj

Aby szybko go znaleźć, wpisz „Equiv” na pasku wyszukiwania przetwarzania i powinieneś być w stanie kliknąć go dwukrotnie.

wprowadź opis zdjęcia tutaj

Oto przykład. Te lasy miały unikalne pole (osm_id), ale wtyczka dodała NUM_FIELD z prostymi wartościami liczbowymi

wprowadź opis zdjęcia tutaj

Steven Kay
źródło
Steve, to jest przydatny skrypt, ale szukam czegoś innego.
eclipsed_by_the_moon
@ eclipsed_by_the_moon dlaczego ta odpowiedź nie jest tym, czego szukasz? Wygląda na to, że ostatecznie rozwiązuje problem konieczności posiadania unikalnej kolumny identyfikacyjnej.
kttii