robermaps / PostGIS-Apuntes

🐘🌏Apuntes de SQL para Postgre y PostGIS

Home Page:https://roberer.github.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Apuntes de Postgre / PostGIS

Por Rober J

👷‍♀️ DDL - Lenguaje de definición de datos

Introducción a las bases de datos

Una base de datos es un conjunto de información estructurada para poder acceder a ella fácilmente. En informática, la base de datos se almacena digitalmente y por medio de programas se accede a la información y se opera con ella. Su aspecto es el de una (o muchas) tabla:

Nombre del elementoBarrioDistritoFecha de instalación
FarolaAcaciasArganzuela1995
FarolaAtochaArganzuela1995
BuzónJerónimosRetiro2001

Para que la base de datos sea geográfica debe contar con información acerca de su posición en el espacio, es decir, debe representar alguna realidad territorial. Una posibilidad sería contar con dos columnas más: una para el dato de la coordenada X y otra para su coordenada Y:

Nombre del elementoBarrioDistritoFecha de instalaciónCoordenada XCoordenada Y
FarolaAcaciasArganzuela1995-3.70428140.403497
FarolaAtochaArganzuela1995-3.69254640.406163
BuzónJerónimosRetiro2001-3.68444940.407504

De este modo, con el programa adecuado es posible visualizar la información sobre un marco espacial en vez de quedarnos solo con la tabla. Bastará con conocer el sistema de referencia de las coordenadas (WGS 84 EPSG: 4326 en este caso) para que cada una de las filas se convierta en un punto posicionado en sus coordenadas, es decir, pueda representar su geometría.

El tamaño de las bases de datos puede ser tan grande que para manipular la información se recurre a filtros complejos que afinen nuestras búsquedas y operaciones. En este contexto es donde suelen usarse los lenguajes de programación, tanto en programas gestores de bases de datos (SGBD) como Acess o Postgre como en Sistemas de Información Geográfica como QGIS. Entre estos lenguajes se encuentra SQL, diseñado específicamente para ser el lenguaje común de las bases de datos.

PostgreSQL, o solo Postgre, es un software de código abierto de gestión de bases de datos que utiliza el lenguaje SQL para operar con la información, y entre sus virtudes está la de gestionar la información espacial o geometrías de los elementos almacenados en ella.

Para comenzar a usarlo, debemos descargarlo de su página principal, familiarizarnos un poco con PgAdmin, la herramienta de Postgre que proporciona interfaz gráfica a nuestras bases de datos y desde la que se realizan la mayor parte de las operaciones, y aprender a manejar los aspectos fundamentales de SQL, los cuales trato en este post.

Aspecto de pgAdmin

Sin embargo, para aplicar estas herramientas a la información geográfica para crear geodatabases y combinarlas con Sistemas de Información Geográfica como QGIS es necesario hacer uso de una extensión de Postgre llamada PostGIS que añade las funcionalidades necesarias para trabajar con esta clase de información y de las cuales hablo en otras entradas del blog.

Logo oficial de PostGIS

Por último, la documentación oficial y otros enlaces de utilidad para Postgre, SQL y PostGIS los recopilo en el siguiente post junto a otros muchos recursos:

⚠ Algunas cuestiones básicas como las claves primarias o la integridad de los datos aún no las he tratado en este post(proximamente)


Tablas

Las tablas son la estructura básica de almacenamiento de PostgreSQL y en general de los datos en las bases de datos relacionales.

Constan de dos elementos básicos: filas y columnas

IDElemento BarrioFecha
0001FarolaAcacias1995
0002FarolaAtocha1995
0003BuzónJerónimos2001
  • Las filas son cada uno de los registros de la tabla, en este caso elementos urbanos.
  • Las columnas son los distintos datos que se almacenan de cada elemento, en este caso el barrio y distrito al que pertenecen junto a su fecha de instalación.

Además las tablas cuentan también con restricciones, tanto para toda la tabla como para algunas de sus columnas.

Forman parte del lenguaje de definición de datos (DDL) de Postgre, es decir, de las herramientas SQL para armar la estructura de la base de datos como son también los esquemas o los dominios

Crear tablas

En una base de datos PostgreSQL pueden crearse tantas tablas como se necesite. La sintaxis básica para ello es:

CREATE TABLE [IF NOT EXISTS] esquema.nombre_tabla(
nombre_columna1 tipo(n) [RESTRICCIÓN],
nombre_columna2 tipo(n) [RESTRICCIÓN],
nombre_columna3 tipo(n) [RESTRICCIÓN],
[RESTRICCIÓN DE TABLA],
[RESTRICCIÓN DE TABLA]
);
  • Crea una tabla especificando un nombre junto al esquema al que van a pertenecer.
  • Con IF NOT EXISTS comprueba que el nombre no esté repetido para poder crearla.
  • Entre paréntesis se especifica el nombre de la columna, el tipo de dato y su longitud (n), y las restricciones que tendrá cada columna o toda la tabla.

Por ejemplo, para crear la tabla del inicio, haríamos lo siguiente:

CREATE TABLE miesquema.Urbanismo (
ID char(4) PRIMARY KEY,
Elemento varchar(15),
Barrio varchar(15),
Fecha date
);

Modificar tablas y columnas

Podemos cambiar las propiedades de la tabla o de algunas de sus columnas combinando ALTER TABLE con alguna sentencias según lo que queramos modificar:

Cambiar nombre a la tabla

ALTER TABLE [IF EXISTS] tabla
 RENAME TO nuevo_nombre

IF EXISTS es opcional y evita que de error en caso de no existir la tabla (lo cual detendría toda la ejecución del código)

Cambiar nombre de la columna

ALTER TABLE tabla RENAME COLUMN columna TO nuevo_nombre

Asignar un nuevo esquema a la tabla

ALTER TABLE tabla SET SCHEMA nombre_esquema

Añadir una nueva columna

ALTER TABLE tabla ADD COLUMN nombre_columna tipo(n) [RESTRICCIÓN]

Se indica su nombre, el tipo de dato y opcionalmente sus restricciones

Eliminar columnas

ALTER TABLE tabla DROP COLUMN columna [RESTRICT/CASCADE]

Con RESTRICT (por defecto) da error en caso de haber datos en las columnas.

CASCADE la borra aunque contenga datos.

Añadir y quitar expresiones asociadas a las columnas

ALTER TABLE tabla ALTER COLUMN columna SET DEFAULT expresión

Cambiar el tipo de dato que puede almacenar la columna indicada

ALTER TABLE tabla ALTER COLUMN columna [SET DATA] TYPE tipo(n)

Quitar expresión de la tabla

ALTER TABLE tabla ALTER COLUMN columna DROP DEFAULT

Aceptar o no datos nulos

ALTER TABLE tabla ALTER COLUMN columna { SET / DROP } NOT NULL

Añadir una restricción para toda la tabla

ADD CONSTRAINT nombre_restricción RESTRICCIÓN

Añadir una restricción en una columna

ADD CONSTRAINT nombre_restricción RESTRICCIÓN(columna)

Borrar restricción

DROP CONSTRAINT nombre_restricción [ RESTRICT / CASCADE ]

Con RESTRICT (por defecto) da error en caso de haber datos dependientes de la restricción

CASCADE borra la restricción junto a los datos dependientes de ella

Borrar las tablas o su contenido

Se puede tanto borrar completamente una tabla como simplemente dejarla limpia usando las siguientes cláusulas:

Borrar una o varias tablas (separadas por comas y en ese orden)

DROP TABLE [IF EXISTS] tabla [, tabla2][RESTRICT / CASCADE]

Con IF EXISTS se evita error en caso de no existir.

Por defecto se ejecuta RESTRICT para dar error en caso de existir objetos dependientes de las tablas.

Si se indica CASCADE borrará todas las tablas dependientes.

Borrar todos los registros de la tabla sin modificar su estructura

TRUNCATE TABLE tabla [RESTRICT / CASCADE]

Con RESTRICT (por defecto) se genera error en caso de existir claves externas que la hagan referencia.

CASCADE vacía todas las tablas conectadas por la clave externa.


Esquemas

Los esquemas de PostgreSQL son conjuntos de datos o tablas propiedad de uno o varios usuarios de la base de datos.

Forman parte del lenguaje de definición de datos (DDL) de Postgre, es decir, de las herramientas SQL para crear y modificar los objetos que estructuran las bases de datos.

Crear un esquema

Basta con usar CREATE SCHEMA y asignarle un nombre:

CREATE SCHEMA nombre_esquema

Modificar un esquema

ALTER SCHEMA esquema RENAME TO nuevo_nombre

Cambiar el nombre del esquema

ALTER SCHEMA esquema OWNER TO nuevo_usuario

Cambiar el propietario del esquema

[AUTHORIZATION usuario]

Especificar usuario propietario del nuevo esquema distinto al creador.

Borrar un esquema

DROP SCHEMA [IF EXISTS] esquema [RESTRICT / CASCADE]

Con IF EXISTS evitamos que se genere un error que detenga el proceso en caso de no existir el esquema.

Por defecto se ejecuta RESTRICT, generando un error en caso de existir información en el esquema.

Con CASCADE se elimina todo.


Restricciones

Las restricciones de PostgreSQL son reglas que deben cumplir los datos para poder incorporarlos a las columnas que se encuentren bajo dicha restricción.

Con ellas podemos evitar que se inserten valores nulos, geometrías que no cumplan cierto estándar de calidad o datos que se excedan de los límites que les marquemos.

Existen restricciones que afectan a las columnas y otras que se aplican a toda una tabla.

Restricciones de columna

Afectan solo a la columna indicada.

NOT NULLNo se admiten valores nulos en la columna.

Obliga a rellenar la fila con valores permitidos si quiere agregarse a la tabla.
UNIQUEEl dato de una celda no puede repetirse en la columna.

Funciona a modo de clave alternativa.
PRIMARY KEYEstablece que la columna es la clave primaria de la tabla, por lo que no podrá contener valores nulos ni valores repetidos.
REFERENCES tabla(columna)Solo pueden introducirse en esa columna datos que ya existan previamente en otra tabla.

Si no se especifica la columna por defecto buscará en la otra tabla una columna con el mismo nombre que a la que se le aplica la restricción.
CHECK (condiciones)Los valores que se introduzcan en la columna deberán cumplir las condiciones que se especifiquen mediante fórmulas matemáticas o lógicas booleanas (TRUE o FALE)

Restricciones de tabla

Afectan a toda la tabla o a varias de sus columnas.

UNIQUE (columna1,columna2…)No se permite que se repitan combinaciones de valores en las filas: si la fila 1 tiene valores ab la fila 2 tendrá que ser aa, bb, ba, bc… pero no ab.
FOREIGN KEY (columna1,columna2…) REFERENCES tabla(columna1,columna2…)Establece a las columnas indicadas como claves foráneas para otra tabla.

Si las columnas de la otra tabla se llaman igual no es necesario especificar el nombre de las columnas a las que referencia.
CHECK (condiciones)La tabla debe ajustarse a las condiciones que se especifiquen mediante fórmulas matemáticas o lógicas booleanas (TRUE o FALSE)
PRIMARY KEY (columna1,columna2…)Cuando queramos establecer como clave primaria una combinación de columnas, deberá usarse como restricción de tabla.

Crear una restricción de tabla

Existen dos maneras de crear una restricción:

Añadir restricción a una tabla

ALTER TABLE [ IF EXISTS ] tabla ADD CONSTRAINT nombre_restricción RESTRICCIÓN

Añadir restricción a una columna

ALTER TABLE [ IF EXISTS ] tabla ADD CONSTRAINT nombre_restricción RESTRICCIÓN (columna)

En ambos casos se le asigna un nombre a la restricción para poder identificarla fácilmente. Las restricciones existentes para una tabla pueden verse desde el menú lateral de pgAdmin.

Borrar una restricción de tabla

Para borrar una una restricción hay que usar la sentencia modificadora de tablas ALTER TABLE e indicar el nombre que el usuario le dio a la restricción que se va a borrar:

ALTER TABLE [ IF EXISTS ] tabla DROP CONSTRAINT nombre_restricción [ RESTRICT / CASCADE ]

Con IF EXISTS no da error en caso de no existir la tabla

Nombre_restricción es el nombre que el usuario le dio a la restricción

Con RESTRICT (por defecto) da error en caso de haber datos dependientes de la restricción. CASCADE borra la restricción junto a los datos dependientes de ella


Dominios

En PostgreSQL los dominios son conjuntos de restricciones que se aplican a una serie de datos para adaptarlos a nuestras necesidades.

Forman parte del lenguaje de definición de datos (DDL) de Postgre, es decir, de las herramientas SQL para armar la estructura de la base de datos.

Crear dominios

CREATE DOMAIN nombre_dominio [AS] tipo(n)
[DEFAULT expresión]
[CONSTRAINT nombre_restricción RESTRICCIÓN]
{ NOT NULL / NULL / CHECK (expresión) }

Estructura para crear un nuevo dominio especificando un nombre de nuestra elección y el tipo de datos (float, text, date…) en el que se basará el dominio.

Con DEFULT establecemos la expresión que defina los valores por defecto de las columnas que operen bajo el nuevo dominio creado. Los valores deben coincidir con el tipo de datos especificado arriba. Si no se especifica, el valor por defecto será NULL.

Mediante CONSTRAINT asignamos restricciones y opcionalmente un nombre a la restricción de tabla.

Con NULL y NOT NULL se indica si se aceptan valores nulos o no (por defecto los acepta)

CHECK nos permite establecer una expresión booleana (verdadero o falso) para lanzar un error en caso de que no se cumplan los requisitos establecidos al actualizar un campo de acuerdo a la restricción.

Modificar Dominos

Para modificar un dominio debe combinarse ALTER DOMAIN con alguna cláusulas en función de lo que queramos modificar:

Indicar al dominio un nuevo valor por defecto o eliminarlo

ALTER DOMAIN dominio
 { SET DEFAULT expresión / DROP DEFAULT }

No afecta a los registros ya creados.

DROP DEFAULT borra el valor por defecto.

Borra una restricción de dominio

ALTER DOMAIN dominio
 DROP CONSTRAINT [ IF EXISTS ] restricción [ RESTRICT / CASCADE]

Da opción a que salga error si hay objetos dependientes con RESTRICT o que por el contrario los borre con CASCADE.

Cambiar el nombre de una restricción

ALTER DOMAIN dominio
 RENAME CONSTRAINT restricción TO nuevo_nombre

Cambia al propietario del dominio a uno nuevo, al actual o al que inicie sesión

ALTER DOMAIN dominio
 OWNER TO { nuevo_usuario / CURRENT_USER / SESSION_USER }

Cambia el que se acepten o no valores nulos

ALTER DOMAIN dominio
 { SET / DROP } NOT NULL

Añade una nueva restricción al dominio siguiendo la estructura de CREATE DOMAIN

ALTER DOMAIN dominio
 ADD restricción

Cambia el nombre del dominio

ALTER DOMAIN dominio
 RENAME TO nuevo_nombre

Cambia el esquema al que pertenece el dominio

ALTER DOMAIN dominio
 SET SCHEMA nuevo_esquema

Borrar dominios

DROP DOMAIN [ IF EXISTS ] dominio [,dominio2] [ CASCADE / RESTRICT ]

Se pueden borrar varios dominios separándolos por comas y en ese orden

Con IF EXISTS (solo en PostgreSQL) se evita error en caso de no existir.

Por defecto se ejecuta RESTRICT para dar error en caso de existir objetos dependientes del dominio. CASCADE borra todos los objetos dependientes del dominio.


Tipos de datos

Cada columna en una base de datos de PostgreSQL puede almacenar un tipo de dato concreto que debe ser especificado en función de la clase de información que queramos introducir.

La elección del tipo de dato debe hacerse en el proceso inicial del diseño de la base de datos. Ahorrará espacio en disco, agilizará las consultas y operaciones y evitará engorrosas modificaciones a posteriori.

Caracteres y texto

TipoTamañoDescripción
char(n)n bytesReserva espacio en disco para almacenar ‘n’ caracteres (entre 1 y 8000) según la codificación usada.
varchar(n)n bytesAlmacena hasta n caracteres según la codificación sin reservar espacio para ello.
text1 – 2.147.483.647 bytesPuede almacenar texto hasta el límite en bytes permitido.
❗ este tipo de datos debe introducirse ‘entrecomillado’

Números enteros

smallint2 bytesValores entre -32.768 y 32767
int4 bytesValores entre -2.147.483.648 y 2.147.483.647
bigint8 bytesValores entre 2-63 y 263 
serial4 bytesValores autoincrementables entre -2.147.483.648 y 2.147.483.647

Números decimales

numeric(p,s)VariableAlmacena ‘p’ dígitos (precisión) de los cuales ‘s’ son decimales (escala)
Son valores exactos, ideales para el cálculo.
real4 bytesAlmacena números reales con hasta 6 posiciones decimales.
Valores inexactos.
float8
double precision
8 bytesAlmacena números reales con hasta 15 posiciones decimales.
Valores inexactos.

Fechas

date4 bytesFechas en calendario gregoriano
time8 bytesAlmacena la hora con resolución de microsegundo
timestamp8 bytesFecha y hora entre el 4713 a.C. y 294276 d.C.
timestampz8 bytesFecha y hora incluyendo zona horaria entre el 4713 a.C. y 294276 d.C.
❗ este tipo de datos debe introducirse ‘entrecomillado’

Valores lógicos (booleanos)

boolean1 bitValores que se convierten a TRUE (1, yes, t, y, true) o a FALSE (0, no, f, n, false)

Geometrías (solo con PostGIS)

geometry(tipo, SRC)VariableAlmacena objetos geométricos simples (Simple Feature)

El tipo puede ser POINT, MULTIPOINT, LINESTRING, MULTILINESTRING, POLYGON y MULTIPOLYGON

SRC es el código del sistema de referencia
topogeometry(tipo, SRC, tolerancia)VariableAlmacena objetos topogeométricos

Al igual que las geometrías, su tipo puede ser también POINT, MULTIPOINT, LINESTRING, MULTILINESTRING, POLYGON y MULTIPOLYGON y también requieren de un sistema de referencia

Sin embargo, la topogeometría requiere de una tolerancia clúster para realizar los cálculos. Su valor se dará en unidades del sistema


Operadores

Los operadores son usados en las consultas de PostgreSQL para manipular los datos de la base de datos. Permiten filtrar la información para obtener solo los registros que necesitamos y hacer operaciones con ellos.

Operadores aritméticos

Llevan a cabo operaciones matemáticas. El resultado es siempre un número.

OperadorDescripciónEjemploResultado
+Suma5 + 38
Resta2 – 20
*Multiplicación7 * 321
/División9 / 33
^Elevación6 ^ 236 (6*6)
%Módulo: resto de una división entera20 % 3
2 (20/3=18)
|/Raíz cuadrada|/ 819 (9*9=81)
||/Raíz cúbica||/1255 (5*5*5=125)
!Factorial4!24 (1*2*3*4)
!!Factorial (prefijo)!!424 (1*2*3*4)
@Valor absoluto@ -25
@25
25

Cuando varios operadores se encuentran dentro de una misma expresión, se ejecutan siguiendo un orden, denominado precedencia de operadores:

  1. Lo que se encuentre entre paréntesis ()
  2. Multiplicaciones, divisiones y módulos
  3. Sumas y restas
  4. Izquierda a derecha cuando exista igualdad de importancia

Operadores de comparación

Comprueban si dos valores son iguales o arrojan el mismo resultado. Los resultados de los operadores de comparación son valores true o false dependiendo de si se cumple la comparación o no.

OperadorDescripciónEjemplo Resultado
=Igual que 1 + 2 = 3true
!=Distinto que25 != 5 * 5FALSE
<>Distinto que (estándar)25 <> 5 * 5FALSE
<Menor que7 < 10TRUE
>Mayor que7 > 10FALSE
<=Menor o igual que7 <= 5FALSE
>=Mayor o igual que7 <= 7TRUE
!<No es menor que9 !< 8TRUE
!>No es mayor que9 !> 7FALSE

Operadores lógicos

Comprueban si una o varias expresiones son verdad o no. Su resultado son valores true o false.

Daremos los ejemplos con una tabla hipotética con los municipios de España, su población, la provincia y la comunidad autónoma a la que pertenecen.

OperadorDescripciónEjemplo Resultado
ALLTRUE cuando todas las expresiones son TRUE(provincia = Zaragoza) ALL (población < 2M)TRUE (todos los municipios de Zaragoza tienen menos de 2M de habitantes)
ANDTRUE si dos expresiones son TRUE(provincia = Zamora) AND (población > 1M)FALSE (Zamora existe pero no tiene ningún municipio con 1M de habitantes)
ANY/SOMETRUE para los valores que coinciden con la consulta10000 < ANY (población)TRUE para todos los municipios con más de 10000 habitantes
BETWEENTRUE para los valores dentro de un rangopoblación BETWEEN 1000 AND 10000TRUE para los municipios que tengan entre 1000 y 10000 habitantes
EXISTSTRUE cuando existe el valor en la base de datosEXISTS TeruelTRUE (Teruel existe)
INTRUE cuando los datos de un campo se encuentran en la listaProvincia IN (‘Jaén’,’Granada’)TRUE para todos los municipios de Jaen y Granada.
NOT TRUE cuando no se cumpla la condición. Invierte operadores como LIKE, IN, BETWEEN, EXISTS, IS…NOT (población > 10000)TRUE para todos aquellos municipios con menos de 10000 habitantes.
OR TRUE cuando al menos una expresión sea verdadera(población >= 5M) OR (provincia = Madrid)TRUE. Pese a que ningún municipio de España tiene más de 5M de habitantes, existe la provincia de Madrid.
IS NULL
TRUE cuando exista algún en el registro. Puede combinarse con NOT.Provincia IS NOT NULLTRUE porque todos los municipios tendrán asignada una provincia.

Operadores de cadenas de caracteres

Permiten operar con texto o cadenas de caracteres, llamadas string en jerga informática.

OperadorDescripciónEjemploResultado
+Concatena texto‘Corre’ + ‘caminos’Correcaminos
||Concatena texto con otros datos que no sean text‘Valor: ‘ || 200Valor: 200
substrExtrae subcadenas de texto indicando posiciones.substr(‘Correcaminos’, 0,1,2,3,4)Corre
LIKECompara fragmentos de texto y devuelve TRUE en caso de coincidenciamunicipio LIKE ‘Alm’True para municipios que contengan ‘alm’ en su nombre: Almería, Almunia…


Índices

En PostgreSQL los índices son una selección de columnas de una tabla que hacen referencia a todos sus registros. Lo que se consigue con ello es reducir los tiempos de consulta, puesto que en vez de leer toda la tabla se leerá solo el índice, y éste referenciará el resto de campos de la tabla.

Si por ejemplo tenemos una base de datos geográficos para el sistema inteligente de una gran ciudad (Smart City) y queremos encontrar los datos de un solo tipo de bombillas que supondrían solo un 5% del total de miles que puede haber repartidas por toda la ciudad, con un índice en base a los tipos de bombilla nuestra consulta no tendría que recorrer toda la tabla, sino que se centraría en la columna de los tipos de bombilla y a partir de ahí sacaría todos los datos de las que coincidiesen.

Hay que tener en cuenta que los índices ocupan espacio en disco, así que no hay que volverse loco creyendo que con muchos índices nos irá todo más rápido porque puede ir en nuestra contra, además de que deben ser actualizados.

Crear un índice

PostgreSQL crea automáticamente un índice sobre el campo asignado como Primary Key. Este índice se llamará indice primario, pero pueden crearse más índices dependiendo del campo o expresión a la que queramos tener un acceso más rápido.

La sintaxis para crear un índice en PostgreSQL es la siguiente:

CREATE INDEX [IF NOT EXISTS] nombre_del_índice 
ON tabla USING método
(columnas o expresión) 
TABLESPACE pg_default;

CREATE INDEX es la sentencia básica para crear el índice y asignarle un nombre

IF NOT EXISTS impide que salte error en caso de existir un índice con el mismo nombre que le estamos dando

ON especifica la tabla de la que se va a hacer el índice

USING asigna un método de indexación a la columna seleccionada o al resultado de una expresión que definamos

TABLESPACE identifica el lugar donde se almacenará el índice. pg_default es el predeterminado de Postgre.

Métodos de indexación

Es el algoritmo que usa PostgreSQL para crear el índice. Si no se especifica ninguno será btree por defecto, que es el más común y que se adapta a la mayoría de situaciones.

Para bases de datos espaciales el que más nos interesa es gist. Recomiendo echar un vistazo al artículo sobre indexación espacial de GeoTalleres y a este otro artículo sobre el uso de este tipo de índice, pero en resumidas cuentas para indexar geometrías gist es más rápido que b-tree y evita que en caso de existir geometrías muy complejas se supere el tamaño máximo que permite Postgre para un índice.

En la documentación oficial se detallan los distintos tipos o métodos de indexación.

Usar un índice

Una vez creado el índice, cuando hagamos consultas sobre esa tabla el propio Postgre lo usará para recorrerla.

Puede comprobarse la diferencia en tiempo de procesado de usar o no un índice mediante EXPLAIN ANALYSE, una función que devuelve el plan de ejecución de una consulta sin realizarla. Bastaría con usarlo en una consulta antes de crear el índice y volverlo a usar una vez creado.

Borrar un índice

Para borrar un índice basta con usar DROP INDEX:

DROP INDEX nombre_del_índice


Secuencias y serials

Las secuencias en PostgreSQL son una herramienta para generar números enteros sin que se repita ninguno. Estos números se asignan a campos numéricos que habitualmente son clave primaria o serials.

Si por ejemplo inventariamos los cientos de ejemplares de árbol que puede haber en un monte no vamos a darle un nombre único a cada uno, echaríamos mano de una secuencia para que a cada árbol que metamos en la base de datos se le asigne un ID único.

Crear secuencias

La sintaxis para crear secuencias es la siguiente:

CREATE SEQUENCE nombre_secuencia
[START WITH x]
[INCREMENT BY n]
[MAXVALUE y]
[MINVALUE z]
[CYCLE];
  • CREATE SEQUENCE es la sentencia para crear la secuencia y asignarle el nombre que queramos
  • START WITH asigna un valor X de tipo entero desde el que empezar
  • INCREMENT BY asigna un valor N de tipo entero que se sumará al anterior
  • MAXVALUE y MINVALUE establece los valores enteros máximo y mínimo que tendrá la secuencia
  • CYCLE señala que la secuencia se repetirá una vez llegue al máximo

Los parámetros anteriores pueden omitirse para tomar los siguientes valores por defecto:

  • START WITH 1
  • INCREMENT BY 1
  • MIN VALUE -9223372036854775808
  • MAX VALUE 9223372036854775807
  • No se repetirá una vez alcance el máximo

Se puede conocer cuál será el siguiente valor de la secuencia con NEXTVAL y usarlo para insertarlo en un nuevo registro.

Averiguar el siguiente valor de una secuencia:

SELECT NEXTVAL('nombre_secuencia');

Insertar un nuevo registro en el que el valor de la primera columna será el del siguiente de la serie:

INSERT INTO tabla
VALUES (nextval('nombre_serie'), valor_columna2...);

En este ejemplo creamos una tabla con ríos en la que a cada uno se le asigna un ID en base a una serie:

CREATE TABLE rios(
ID integer DEFAULT nextval('serie'),
nombre varchar(30),
longitud numeric
)

Modificar una secuencia

Al modificar una sentencia solo se alteran los siguientes valores que surjan de ella, no los ya creados. La sintaxis es:

ALTER SEQUENCE nombre_secuencia
[START WITH x]
[INCREMENT BY n]
[MAXVALUE y]
[MINVALUE z]
[CYCLE];

Borrar una secuencia

Para borrar una secuencia la sintaxis es la siguiente:

DROP SEQUENCE nombre_secuencia [CASCADE];


Vistas de datos

Las vistas son una herramienta de PostgreSQL para almacenar consultas de información y visualizarlas modo de tabla virtual sin alterar en ningún momento la información de la base de datos.

Son muy útiles porque permiten visualizar conjuntos de datos de forma rápida sin alterar la información, permitiendo ocultarla o mostrarla según nuestra consulta.

También sirven para almacenar esas consultas, especialmente si son complejas, y acceder a ellas en todo momento, mejorando nuestra productividad.

Por último, pueden usarse para dar permisos a otros usuarios de solo visualización, evitando así que puedan modificar los datos.

A través de las funciones SELECT y JOIN entre otras podrán mostrar tanto conjuntos de filas y columnas de una taba como de varias tablas y resúmenes estadísticos. Así mismo también pueden combinarse las vistas entre sí y con otras tablas.

Crear vistas

Tan solo hay que seleccionar un conjunto de datos con CREATE VIEW:

CREATE VIEW nombre_vista as
SELECT [sentencias select];

Se recomienda que los nombres de las vistas sean fácilmente identificables, añadiendo prefijos o sufijos como »vista» a la descripción de la misma.

Para crear una vista a partir de otra vista, bastaría con referirse a ella al momento de crearla:

CREATE VIEW nombre_vista2 as 
SELECT columna1, columna2 FROM nombre_vista1;

Visualizar datos de las vistas

Visualizarlo siempre que queramos con SELECT, ya sea toda la vista o solo ciertas columnas o registros:

-- Selecciona toda la vista
SELECT * FROM nombre_vista;
-- Selecciona las columnas 1 y 2 de la vista
SELECT columna1, columna2 FROM nombre_vista;
-- Devuelve el valor máximo de la columna 3 de la vista
SELECT MAX(columna3) FROM nombre_vista;

Borrar una vista

Las vistas se borran con DROP VIEW:

DROP VIEW nombre_vista


Funciones

Las funciones en PostgreSQL son sentencias predefinidas por el usuario para no tener que repetir el mismo código una y otra vez. Con una vez que sean definidas tan solo habrá que introducir en el futuro las nuevas variables que serán aplicadas a dicha función.

Si por ejemplo solemos consultar en nuestra geodatabase los usos de suelo de España por provincias, no será necesario andar reescribiendo o copiando y pegando la misma consulta una y otra vez, sino que podemos definir una función en la que tan solo introduciendo el nombre de la provincia que necesitemos.

PostgreSQL cuenta con funciones propias que se detallan en la documentación oficial, entre ellas las funciones de agregado que vemos en la entrada consultar y agregar atributos. Conocerlas evita tener que crearlas, pero en muchas ocasiones tendremos que hacerlo según nuestras necesidades.

Crear una función

La sintaxis para crear una función es la siguiente:

CREATE [OR REPLACE] FUNCTION nombre_función(parametros) 
RETURNS tipo_dato
AS 'expresión' 
language sql;
  • CREATE FUNCTION es la sentencia para crear funciones
  • OR REPLACE es opcional y permite reescribir una función ya existente que tenga el mismo nombre
  • Los parámetros son los nombres que se le darán a las variables de la función junto a su tipo de dato. Conviene no repetir con nombres de columna.
  • RETURNS especifica el tipo de dato que devolverá la función.
  • AS define la expresión o función que tomará las variables definidas en los parámetros
  • language sql indica que la función se ha escrito en SQL. Además de SQL pueden escribirse funciones en otros lenguajes como PL/PgSQL

Ejemplo

Podemos poner como ejemplo la función que seleccionaría los usos del suelo en función de la provincia. Para crearla usaríamos el siguiente código:

CREATE OR REPLACE FUNCTION usos_provincia(varchar) RETURNS varchar
AS 'SELECT usos FROM tabla_usos WHERE provincia = $1'
language sql;

‘varchar’ es el tipo de argumento cuyo dato es introducido por el usuario y sustituirá a $1 en el SELECT.

En este caso indica que el usuario deberá introducir una cadena de texto de tipo variable.

$1 indica que se está tomando el primer argumento de la lista, en este caso ‘nombre’. Es muy útil si la función requiere de varios argumentos.

Para usar esta función y obtener los usos en Cáceres lo haríamos de la siguiente manera:

SELECT usos_provincia('Cáceres');

Eliminar funciones

La sintaxis para borrarlas es:

DROP FUNCTION [ IF EXISTS ] nombre_función[(argumentos)][CASCADE]

Los argumentos solo habría que definirlos en caso de haber creado dos funciones distintas con el mismo nombre.

IF EXISTS evita que se genere error en caso de no existir la función

CASCADE borrará todos los elementos que dependan de la función que se va a eliminar.


Triggers

En PostgreSQL, los triggers (desencadenantes o disparadores) son códigos que se ejecutarán de forma automática cuando se produzca un evento determinado.

Podemos programarlos para que se ejecuten:

Crear un trigger

La sintaxis para crear un trigger es la siguiente:

CREATE TRIGGER nombre_trigger {BEFORE/AFTER} {INSERT/UPDATE/DELETE}
ON tabla [ FOR [ EACH ] {ROW/STATEMENT} ]
EXECUTE PROCEDURE función(argumentos);

CREATE TRIGGER es la sentencia para crear triggers y asignarles un nombre

BEFORE y AFTER indican si el trigger se ejecutará antes de o después de un INSERT, UPDATE o DELETE.

ON es para definir la tabla a la que se aplicará el trigger

FOR EACH indica que se ejecutará por cada fila ROW afectada o solo una vez junto a la sentencia STATEMENT

Tras EXECUTE PROCEDURE se referenciará la función que se ejecutará

Hay que tener en cuenta que la función que se va a ejecutar:

  • deberá estar definida antes de crear el trigger
  • debe devolver el tipo trigger
  • no puede tener argumentos
  • puede usarse en varios triggers
  • puede desencadenar otros triggers

Si esta función la definimos como PL/pgSQL podremos usar las variables especiales OLD y NEW para triggers de filas (ROW)

  • OLD contiene la fila que había antes de usar sobre ella UPDATE o DELETE
  • NEW contiene la fila resultado de aplicar un INSERT o UPDATE

Ejemplo: crear función y usarla en un trigger

A continuación vamos a crear un trigger que se active cada vez que se vaya a borrar una fila para que en su lugar se cambien los valores a NULL:

1- Crear la función

CREATE OR REPLACE FUNCTION no_borrar() RETURNS TRIGGER AS $no_borrar$
DECLARE plpgsql
BEGIN
RETURN NULL;
END;
$no_borrar$
LANGUAGE plpgsql;

no_borrar() es el nombre de la función

Con RETURNS TRIGGER indicamos que la función se usará en un trigger

AS es un requisito del lenguaje para indicar de nuevo el nombre de la función entre los símbolos $ tanto al principio como al final de la función.

Entre BEGIN y END se encuentra el código o expresión que se ejecutará. RETURN es un valor que siempre se devolverá al usar la función. También podría usarse un UPDATE o un INSERT por ejemplo.

En LANGUAGE indicamos que se usa Pl/pgSQL

2- Crear el trigger

CREATE TRIGGER trigger_borrado BEFORE DELETE
ON municipios FOR EACH ROW
EXECUTE PROCEDURE no_borrar();

Se crea un trigger llamado trigger_borrado que se ejecuta antes de usar DELETE sobre una tabla llamada municipios.

El trigger ejecutará la función no_borrar() creada anteriormente por cada fila afectada por DELETE, rellenándolas con valores nulos en vez de eliminarlas.

Borrar un trigger

La sintaxis para borrar un trigger es la siguiente:

DROP TRIGGER nombre_trigger ON tabla;


🛠 DML - Lenguaje de manipulación de datos

SELECT

Los resultados de SELECT consisten en nuevas tablas que contienen los registros que se ajustan a nuestra consulta: parámetros o condiciones que hemos establecido en una sentencia. Esta tabla deberá guardarse si queremos conservarla.

A continuación se muestran varios ejemplos de la función SELECT en los que se va añadiendo el uso de cláusulas básicas como WHERE o DISTINCT. Todas pueden combinarse entre ellas para afinar los resultados y generar búsquedas más complejas.

Seleccionar columnas por su nombre

SELECT columna1, columna2
 FROM tabla;

Seleccionar todas las columnas de una tabla

SELECT * FROM tabla;

Seleccionar columnas de una tabla específica creando una nueva columna llamada columna3 con los resultados de sumar 50 a una de esas columnas.

SELECT columna1, (columna2 + 50) AS columna3
FROM tabla;

Pueden crearse expresiones usando operadores para generar nuevas columnas a partir de los datos existentes.

Deben ir entre paréntesis.

No mostrar valores repetidos al seleccionar columnas con DISTINCT

SELECT DISTINCT columna1, columna2
 FROM tabla;

Filtrar la información mediante expresiones con WHERE

SELECT * FROM tabla 
WHERE columna1 < 100;

Deben usarse operadores

Este ejemplo selecciona los registros de una tabla cuando en la columna1 el valor es inferior a 100.

Ordenar una selección de datos con ORDER BY

SELECT columna1, columna2
FROM tabla
ORDER BY columna2 [DESC];

Si no usamos DESC se ordenan de menor a mayor

Si usamos DESC se ordenan de mayor a menor

Admite números y texto

SELECT columna1, columna2
FROM tabla
ORDER BY columna2, columna1;

En caso de existir registros con el mismo valor en la columna 2 se ordenarían según la columna 1 y sucesivamente.

Mostrar los x primeros resultados de una selección con LIMIT

SELECT * FROM tabla
LIMIT 100;

En este caso se mostrarían solo los 100 primeros registros

Obviar los primeros x resultados de una selección con OFFSET

SELECT * FROM tabla
OFFSET 50;

En este caso no se mostrarían los 50 primeros registros

SELECT * FROM tabla
LIMIT 60
OFFSET 30

Combinando LIMIT y OFFEST para este caso se seleccionarán los registros 31 al 91.

Orden de la cláusulas

El orden en el que se deben escribir las cláusulas básicas SELECT dentro de una misma sentencia es el siguiente:

  1. SELECT
  2. FROM
  3. WHERE
  4. ORDER
  5. LIMIT
  6. OFFSET


INSERT

Con INSERT se introduce nuevas filas en la base de datos especificando los datos nuevos y las columnas en las que se colocarán.

Debe prestarse especial atención a la sintaxis de los datos que introducimos: el texto debe ir entrecomillado, al igual que las fechas. Los números no deben entrecomillarse, pues los tomará como texto.

Si los valores que introducimos se asignan a columnas que no soportan su tipo de datos (introducimos texto en una columna para números) se generará error.

INSERT INTO tabla [(columna1, columna2, columna3...)]
VALUES (valor_columna1,valor_columna2,valor_columna3...);

INSERT INTO especifica el lugar donde se añadirán los nuevos registros

Con VALUES asignamos valores en el orden que hemos especificado con INSERT INTO

Si no se han especificado columnas los valores tomarán la posición original de las columnas de la tabla en la que introducimos los datos.

Ejemplos de inserciones

INSERT INTO parcelas (tamaño, provincia, precio_m2)
VALUES (200,'Barcelona',80);

Solo se introducen datos en las columnas indicadas, por lo que si alguna otra columna no acepta datos nulos se generará error

INSERT INTO parcelas 
VALUES ('54548GL',200,'Barcelona',80);

En este caso se introducen datos en todas las columnas según su orden

El primer dato sería un identificador para la parcela de tipo char(7) por lo que debe ir entrecomillado

INSERT INTO parcelas
VALUES ('54548GL',200,'Barcelona',80),
       ('65882GL',520,'Tarragona',NULL);

Puede introducirse varios registros a la vez

Si no se quiere introducir datos para alguna columna en un registro concreto y la columna lo permite se puede especificar con NULL


DELETE

DELETE selecciona columnas al igual que SELECT pero lo que hace es borrarlas.

DELETE FROM tabla
  • Elimina toda la tabla

Podemos añadir cláusulas como WHERE para eliminar selectivamente los datos:

DELETE parcelas WHERE tamaño_m2 < 1000 AND provincia = 'Salamanca'
  • Elimina los registros de la tabla parcelas aquellas parcelas de Salamanca que tengan menos de 1000 metros cuadrados.


UPDATE

UPDATE actualiza el valor de los registros seleccionados con nuevos valores.

La sintaxis básica de UPDATE es seleccionar una tabla y definir una expresión después de SET indicando columnas a actualizar y los nuevos valores que tendrán:

UPDATE tabla SET [expresión]

Ejemplos de actualización de registros

UPDATE parcelas
SET valor_m2 = 5
WHERE tipo = 'rural' AND provincia = 'Córdoba'

Actualiza el valor del m2 a 5€ cuando la parcela se encuentra en la provincia de Córdoba y está catalogada como rural

UPDATE parcelas SET valor_m2 = valor_m2 * 1.03

Aumenta el valor del m2 de las parcelas un 3% multiplicando su valor por 1.03


Consultar y agregar atributos

En PostgreSQL las sentencias SELECT se utilizan para extraer información de la base de datos y generar tablas nuevas. Es parte de lo que se conoce como DML o Data Manipulation Language que introducimos en el post sobre Select, Insert, Delete y Update.

Aquí veremos cláusulas específicas de SELECT para agregar datos. Son funciones que generan resultados únicos a partir de los valores que se encuentren en filas distintas, haciendo operaciones matemáticas sobre ellos o agrupándolos.

A continuación tenéis ejemplos de código SQL describiendo las cláusulas de agregación de datos empleadas con una hipotética tabla llamada tabla_arbolado que cuenta con las columnas id, especie, altura_m, podas y nidos_aves.

Ejemplos de agregación de atributos

SELECT especie FROM tabla_arbolado GROUP BY especie;

GROUP BY genera una nueva columna que agrupa todas las filas en valores únicos.

En este caso se han agrupado todos los árboles inventariados de un parque según su especie. A diferencia de DISTINCT, cada grupo (especie) se encuentra unido a los registros que aglutina (árboles), permitiendo operar con ellos para obtener estadísticas.

SELECT especie FROM tabla_arbolado GROUP BY especie,
HAVING altura_m > 10;

HAVING filtra los resultados de los grupos creados con GOUP BY.

Siguiendo con el ejemplo anterior, el resultado final será la agrupación de árboles por especie que midan más de 10 metros.

SELECT AVG(altura_m) FROM tabla_arbolado;

AVG devuelve la media de los valores numéricos de la columna indicada.

En este caso obtendríamos la altura media de todos los árboles de la tabla arbolado.

SELECT COUNT(id_arbol) FROM tabla_arbolado';
SELECT COUNT(id_arbol) AS recuento_acacias FROM tabla_arbolado WHERE especie = 'acacia';

COUNT devuelve el número de filas que cumplen la condición indicada.

En el primer caso nos diría cuántos árboles hay en el parque.

En el segundo caso obtendríamos el número de acacias del parque.

SELECT MAX(altura_m) FROM tabla_arbolado;

MAX devuelve el valor máximo de la columna seleccionada.

En este caso nos diría cuántos metros mide el árbol más alto del parque.

SELECT MIN(podas) FROM tabla_arbolado;

MIN devuelve el valor mínimo de la columna seleccionada.

En este caso nos diría cuál es el menor número de podas que ha tenido algún árbol en el parque.

SELECT SUM(nidos_aves) FROM tabla_arbolado;

SUM devuelve la suma de los valores numéricos de una columna seleccionada.

En este caso nos diría el número total de nidos de pájaro que se han descubierto en el parque.

SELECT ARRAY_AGG(especie) FROM tabla_arbolado;

ARRAY_AGG crea una lista con los valores de la columna indicada, incluyendo los nulos.

En este ejemplo obtendríamos una lista con todas las especies existentes en el arbolado del parque.

Las cláusulas anteriores son combinables entre si, pudiendo hacer selecciones más complejas:

SELECT especie, COUNT(nidos_aves) AS recuento_nidos, 
COUNT(podas) AS recuento_podas FROM tabla_arbolado 
GROUP BY nombre 
ORDER BY recuento_nidos DESC;

Con este código obtendríamos el número de nidos de ave y de podas que se han encontrado en cada una de las especies, ordenado de mayor a menor número de nidos.


JOIN - Unir tablas

Las bases de datos en PostgreSQL suelen estar formadas por varias tablas como consecuencia del diseño lógico en el proceso inicial de su creación.

En ocasiones necesitamos que la información dispersa en varias tablas se muestre unida en nuevas tablas, permitiendo ver la relación entre los datos y operar con ellos fácilmente. Para realizar esto en PostgreSQL se utilizan las sentencias JOIN.

CROSS JOIN

Esta sentencia une cada fila de la primera tabla con cada fila de la segunda tabla. Generará por tanto una nueva tabla con el número de filas resultado de multiplicar el número de filas de la tabla 1 por el número de filas de la tabla 2:

Si tuviésemos dos tablas, una con las provincias de España y otra con una lista de categorías de usos del suelo, obtendríamos una nueva tabla en la que cada fila correspondería a una provincia y un uso del suelo.

Las columnas que contendría la nueva tabla se especifica en SELECT. En el siguiente caso se añadirían las columnas Nombre y Uso:

SELECT provincias.Nombre, usos_del_suelo.Uso 
FROM provincias, usos_del_suelo;
SELECT provincias.Nombre, usos_del_suelo.Uso
FROM provincias
CROSS JOIN usos_del_suelo;

Resultado:

NombreUso
AlmeríaCultivo
AlmeríaIndustrial
AlmeríaForestal
AlbaceteCultivo
AlbaceteIndustrial
AlbaceteForestal

INNER JOIN

Con INNER JOIN lo que hacemos es unir en una misma fila aquellos registros de las tablas seleccionadas que compartan un atributo en común.

Pongamos que tenemos dos tablas, una con las provincias y otra con municipios. La de provincias tiene una columna ID con un identificador único para cada una y la de municipios tiene una columna con el código de la provincia en la que están.

Al hacer el INNER JOIN obtendríamos una nueva tabla en la que cada fila almacenaría tanto el nombre de la provincia como el del municipio. Debe indicarse qué columna de la primera tabla corresponde a la de la segunda, pudiendo hacer uso de abreviaturas (j y k para este ejemplo).

Las columnas que contendría la nueva tabla se especifica en SELECT. En el siguiente caso se añadirían todas las columnas al usar el asterisco (*):

SELECT * FROM provincias j 
INNER JOIN municipios k
ON j.ID = k.ID_prov;

INNER JOIN es el JOIN por defecto, por lo que puede prescindirse de escribir JOIN

SELECT * FROM provincias j 
JOIN municipios k
ON j.ID = k.ID_prov;

Resultado:

IDProvID_ProvMunicipio
1Almería1Níjar
1Almería1Gádor
1Almería1Aguadulce
2Albacete2Tinajeros
2Albacete2Barrax
2Albacete2Argamasón

OUTER JOIN

Une las tablas incluyendo las filas que no estén unidas por campos en común. Ello hace que se generen campos nulos, pero permite ver si hay registros en una tabla que no guardan la relación que deberían con la otra.

Hay tres tipos según qué tabla queremos que una todas sus filas:

LEFT OUTER JOIN

Genera una nueva tabla uniendo todas las filas de la primera tabla, aunque muchas de ellas no guarden relación con un campo en común con la otra.

Por ejemplo, si tenemos una primera tabla con las provincias de España y su ID, y otra con las inundaciones de España durante 2019 con el ID de la provincia en el que se originaron, al hacer el LEFT JOIN obtendríamos una tabla en la que conservaríamos las provincias en las que no se han producido inundaciones:

SELECT * FROM provincias j 
LEFT [OUTER] JOIN inundacion_2019 k
ON j.ID = k.ID_prov;
  • No es necesario escribir el OUTER
  • Las columnas que contendría la nueva tabla se especifica en SELECT. En este caso se añadirían todas las columnas al usar el asterisco (*)

Resultado:

IDProvID_ProvInundado
1Almería1Níjar
2Murcia2San Javier
2Murcia2Totana
2Murcia2Torre Pacheco
3Alicante3San Miguel de Salinas
3Alicante3Pilar de la Horadada
4Las PalmasNULLNULL
5Santa CruzNULLNULL

RIGHT OUTER JOIN

Hace lo mismo pero son las filas de la segunda tabla las que son todas unidas a las de la primera.

Usando el ejemplo anterior, si hiciésemos RIGHT JOIN solo obtendríamos de la tabla de provincias aquellas en las que se han producido inundaciones, conservando todos los registros sobre inundaciones (incluiría así a Ceuta y Melilla, que son Ciudades Autónomas):

SELECT * FROM provincias j 
RIGHT [OUTER] JOIN inundacion_2019 k
ON j.ID = k.ID_prov;
  • No es necesario escribir el OUTER
  • Las columnas que contendría la nueva tabla se especifica en SELECT. En este caso se añadirían todas las columnas al usar el asterisco (*)

Resultado:

IDProvID_ProvInundado
1Almería1Níjar
2Murcia2San Javier
2Murcia2Totana
2Murcia2Torre Pacheco
3Alicante3San Miguel de Salinas
3Alicante3Pilar de la Horadada
NULLNULL0Ceuta
NULLNULL0Melilla

FULL OUTER JOIN

Crea una nueva tabla con todas las filas de las tablas a unir:

SELECT * FROM provincias j 
FULL [OUTER] JOIN inundacion_2019 k
ON j.ID = k.ID_prov;
  • No es necesario escribir el OUTER
  • Las columnas que contendría la nueva tabla se especifica en SELECT. En este caso se añadirían todas las columnas al usar el asterisco (*)

Resultado:

IDProvID_ProvInundado
1Almería1Níjar
2Murcia2San Javier
2Murcia2Totana
2Murcia2Torre Pacheco
3Alicante3San Miguel de Salinas
3Alicante3Pilar de la Horadada
4Las PalmasNULLNULL
5Santa CruzNULLNULL
NULLNULL0Ceuta
NULLNULL0Melilla

JOINS MÚLTIPLES

Para unir más de dos tablas deben encadenarse un JOIN con otro. El primer JOIN une la primera y la segunda tabla. El segundo une ese resultado con una tercera tabla, el tercero unirá ese resultado con una cuarta tabla y así sucesivamente.

Como ejemplo, podríamos unir al FULL JOIN del ejemplo anterior una tercera tabla con la lista de municipios que han recibido ayudas para la reparación de los daños:

SELECT * 
FROM provincias j 
FULL [OUTER] JOIN inundacion_2019 k
ON j.ID = k.ID_prov
LEFT JOIN ayudas f
ON k.Inundado = f.Ayuda;

Resultado:

IDProvID_ProvInundadoAyuda
1Almería1NíjarNULL
2Murcia2San JavierNULL
2Murcia2TotanaTotana
2Murcia2Torre PachecoTorre Pacheco
3Alicante3San Miguel de SalinasNULL
3Alicante3Pilar de la HoradadaPilar de la Horadada
4Las PalmasNULLNULLNULL
5Santa CruzNULLNULLNULL
NULLNULL0CeutaCeuta
NULLNULL0MelillaNULL


Subconsultas

En ocasiones necesitamos hacer consultas en PostgreSQL que por su complejidad nos obligan a escribir códigos largos, teniendo que dividirlo en varias partes según el procedimiento lógico que nos haga llegar al resultado que esperamos.

En vez de encadenar instrucciones dependientes unas de otras se puede simplificar el proceso anidando una consulta dentro de otra, es decir, creando subconsultas.

Las subconsultas son sentencias SELECT metidas entre paréntesis () dentro de otras sentencias SELECT, INSERT, DELETE y UPDATE.

A continuación tienes los distintos tipos de subconsultas que existen. Les acompañarán ejemplos tomando como base una tabla ficticia en la que se almacenan municipios con su nombre, superficie, altitud media, población y provincia a la que pertenecen.

El operador siempre debe escogerse con cuidado dependiendo del resultado que queramos obtener con la subconsulta. Si el resultado esperado es un único valor o registro usaremos los operadores aritméticos o de comparación, pero si vamos a obtener varios valores habrá que echar mano de los operadores lógicos.

Subconsulta como expresión

Selecciona datos a partir de otra selección.

SELECT columna 
FROM tabla 
WHERE columna OPERADOR (subconsulta);

Ejemplos

SELECT nombre
FROM municipios
WHERE superficie > ALL (SELECT superficie FROM municipios
WHERE provincia = 'León' OR población > 10000 );

Listaríamos los municipios cuya superficie es mayor que la superficie de cualquier municipio de la provincia de León o que su población supere los 10000 habitantes.

SELECT nombre
FROM municipios
WHERE altitud < ANY (SELECT altitud FROM municipios
WHERE provincia = 'Huelva');

Seleccionaríamos los municipios cuya altitud media sea inferior a la que tenga cualquier municipio de la provincia de Huelva.

SELECT nombre
FROM municipios
WHERE provincia IN (SELECT provincia FROM municipios
GROUP BY provincia 
ORDER BY SUM(población) DESC
LIMIT 5);

Averiguaríamos cuáles son los municipios de las 5 provincias más pobladas.

IN compara la provincia a la que pertenece cada municipio con las 5 provincias que obtienen un valor más alto al sumar los valores de población de sus municipios.

Subconsultas como tabla

En este caso se usa una subconsulta para filtrar la fuente de información sobre la que se hará la consulta principal. Para ello, se usa justo después de FROM.

SELECT columna 
FROM (subconsulta_tabla1)
JOIN tabla2;

Este tipo de subconsulta resulta útil, por ejemplo, cuando vas a realizar un JOIN, ya que así los datos se procesarían más rápido.

Ejemplo

SELECT nombre FROM (SELECT nombre, población FROM municipios) 
WHERE población > 5000 

Hemos seleccionado los municipios con más de 5000 habitantes cargando solo las dos columna que íbamos a usar: nombre y población.

Subconsultas correlacionadas

SELECT (subconsulta1)(subconsulta2)
FROM tabla
WHERE columna OPERADOR (expresión);
SELECT columna
FROM tabla
WHERE columna OPERADOR (subconsulta con función_agregado);

Una de las consultas o subconsultas sobre una tabla dependen del resultado de otra consulta sobre esa misma tabla.

En el primer caso lo establecido en la subconsulta 1 sería necesario para llevar a cabo la subconsulta 2.

En el segundo caso la subconsulta contendría una función de agregado cuyo resultado alimentaría a la consulta principal.

Ejemplo

SELECT nombre 
FROM municipios
WHERE altitud > (SELECT AVG(altitud) FROM municipios);

Obtendríamos los nombres de los municipios cuya altitud supera a la media de altitud de todos los municipios.

La altitud media es un dato que no conocemos pero que tampoco ha sido necesario que lo calculáramos previamente porque se ha utilizado la función de agregado AVG (calcula la media aritmética)


🐘🌏 PostGIS

Datos geométricos

En cartografía, los elementos geográficos se abstraen, según su naturaleza, en objetos geométricos como puntos, líneas o polígonos que son posicionados en un marco de referencia espacial a través de las coordenadas de sus vértices.

Es el caso de las variables geográficas discretas, es decir, elementos que pueden individualizarse: un árbol será un punto que marque sus coordenadas, una parcela será un polígono cuyos lados recorran sus lindes, una calle recta corresponderá a una línea que una dos puntos correspondientes al inicio y fin de dicha calle etc.

Esta clase de información geográfica en forma de objetos geométricos se denomina vectorial. Existe otra denominada ráster que suele utilizarse para variables continuas del territorio como elevación o temperaturas.

En una base de datos geográficos que siga el modelo Simple Feature cada uno de los objetos vectoriales corresponderá con un registro en una tabla que contendrá información como su nombre, sus medidas, su fecha o cualquier otro valor que queramos.

Las geometrías en PostGIS se engloban dentro del tipo de dato geometry en el que se especifica qué tipo de geometría se va a almacenar:

geometry(tipo, SRC)
  • Tipo hace referencia al tipo de geometría que almacenará una columna que guarde geometrías y que serán detallados más abajo
  • SRC es el código del sistema de referencia en el que se encuentren las coordenadas de los objetos que se introduzcan

Tipos de geometría

A continuación se detallan los tipos de geometría con los que trabaja PostGIS:

POINT

Un único punto en el espacio localizado a través de sus coordenadas X e Y. Se utiliza para posicionar elementos individualizados en un único registro como una persona, un comercio o un coche.

Con los puntos se pueden hacer por ejemplo interpolaciones, selecciones espaciales o análisis de proximidad.

Cuando sean insertados en un registro deberá usarse la siguiente codificación WKT (Well Known Text) con las coordenadas del punto:

POINT (10 15)

MULTIPOINT

Se puede también almacenar en un solo registro varios puntos que cuenten con las mismas características salvo su posición en el espacio, como puede ser el caso de buzones, señales de tráfico STOP o lugares donde se han encontrado trufas.

Con ellos pueden hacerse al igual que con los POINT análisis de proximidad o selecciones espaciales.

Cuando sean insertados en un registro deberá usarse la siguiente codificación WKT con las coordenadas de cada punto separados por comas:

MULTIPOINT (10 15, 15 25, 7 35, 30 28)

LINESTRING

Cada registro almacena una sola línea. Las líneas consisten en una secuencia de puntos unidos según el orden en el que han sido introducidos. Se usan para elementos lineales del territorio como caminos, ríos, fronteras o rutas de todo tipo.

Este tipo de geometría permite medir longitudes y hacer análisis de redes entre otros geoprocesos.

Cuando sean insertados en un registro deberá usarse la siguiente codificación WKT con las coordenadas de los vértices en el orden en el que se dibujará la línea:

LINESTRING (10 30, 20 15, 40 10)

MULTILINESTRING

Un único registro también puede almacenar varias líneas que cuenten con las mismas características, como pueden ser curvas de nivel con el mismo valor de altitud o distintos tramos de una misma carretera.

Cuando sean insertados en un registro deberá usarse la siguiente codificación WKT con las coordenadas de los vértices de cada línea agrupados entre paréntesis:

MULTILINESTRING ((10 40, 20 15, 40 10) , (5 7, 12 5) , (45 30, 30 25, 27 39, 41 50 ))

POLYGON

Los polígonos son líneas cerradas en las que el primer y el último vértice coinciden en el espacio. Sirven para representar superficies como parcelas, embalses o usos del suelo.

Permiten hacer cálculos de superficie, selecciones espaciales y recortar capas entre otros geoprocesos.

Cuando sean insertados en un registro deberá usarse la siguiente codificación WKT con las coordenadas de los vértices de cada polígono, teniendo en cuenta que el primero y el último deben ser el mismo:

POLYGON(5 20, 20 10, 35 22, 15 35, 5 20)

Si se quiere dejar huecos dentro de un polígono como el de la segunda imagen, habrá que añadir un segundo polígono que delimite la línea del agujero:

POLYGON((5 20, 20 10, 35 22, 15 35, 5 20) , (16 19, 20 30, 25 29))

MULTIPOLYGON

En este caso un mismo registro amacena varias geometrías poligonales con los mismos atributos. Es útil por ejemplo para usos del suelo, en donde una única clase como la urbana es común para todas las áreas que se clasifiquen como tales (dependería siempre de con cuántas clases quieras trabajar)

POLYGON((5 20, 20 10, 35 22, 15 35, 5 20) , (36 32, 70 19, 50 45), (6 45, 5 50, 55 50, 60 45))

Dimensiones de las geometrías

Dado que las geometrías son objetos espaciales, contarán con una u otra dimensión según sus características medibles:

GeometríaDimensión
La nada-1No hay coordenadas, no hay nada que medir
Puntos0Una posición concreta en el espacio
Líneas1La unión de varias posiciones que da lugar a las líneas, permitiendo medir longitudes
Polígonos2Una línea cerrada da lugar a una superficie con valores medibles de ancho y largo (y a partir de ellas otras derivadas como la superficie)

De momento solo trataremos estas dimensiones pero existen más:

  • Tercera dimensión: una coordenada Z con valores de altura nos permitirá medir también volúmenes.
  • Cuarta dimensión: coordenadas temporales T que nos permiten medir velocidades, entre otras cosas.


Las relaciones espaciales: Matriz DE-9IM

Las relaciones espaciales se materializan en forma de predicados geográficos o espaciales: se comprueba usando como argumento dos objetos geométricos si estos cumplen alguna relación espacial, devolviendo un resultado booleano (TRUE si se cumple la relación o FALSE si no)

Los principales predicados geográficos son los equals, disjoint, intersects, touches, crosses, within, contains, overlaps y distances. Podemos sintetizarlos en un modelo topológico denominado matriz DE-9IM (Dimensionally Extended 9 intersection Matrix):

matriz DE-9IM

Fuente: postgis.nett

Cada relación espacial o predicado tendrá en cuenta unas u otras de las relaciones representadas en la matriz en función de si requieren analizar el interior, el exterior o los bordes de las geometrías así como la dimensión de las relaciones entre las geometrías:

  • T – existe intersección en cualquier dimensión
  • F – no existe intersección (dimensión -1)
  • 0 – la intersección se produce en puntos concretos
  • 1 – la intersección da lugar a una recta
  • 2 – la intersección origina un polígono
  • * – no importa si hay intersección

El resultado origina un patrón de la matriz DE-9IM específico para cada una de las relaciones espaciales o predicados como veremos más abajo.

matrizPatrón DE-9IM


Predicados espaciales

Los predicados espaciales son «operaciones de tipo lógico que nos indican si entre dos objetos geográficos existe o no un tipo de relación dada» (Análisis espacial – Relaciones topológicas; Libro SIG de Víctor Olaya)

Es decir, es la forma que tenemos para decirle al programa informático que compruebe si dos o más objetos geográficos (entidades) se tocan, se cruzan, a qué distancia están, qué espacio no comparten ninguno de los dos…

Estos predicados espaciales (también llamados geográficos o topológicos según el autor) varían su sintaxis según la herramienta que estemos usando. En este post resumo la sintaxis de estos predicados en PostGIS.

Con ellos obtendremos valores TRUE (booleanos) cuando se cumpla la relación espacial consultada entre las geometrías de los objetos incluidas en el predicado.

Los siguientes predicados espaciales y muchos otros más podéis encontrarlos en la documentación oficial de PostGIS

INTERSECT

Las geometrías tienen algún punto en común

ST_Intersects(geometry A, geometry B)

EQUALS

Las geometrías son idénticas

ST_Equal(geometry A, geometry B)

DISJOINT

Las geometrías no coinciden en ningún momento

ST_Disjoint(geometry A , geometry B)

CROSSES

Dos geometrías coinciden en un único punto

ST_Crosses(geometry A, geometry B)

OVERLAPS

Existe un solapamiento entre dos objetos, generando una nueva geometría

ST_Overlaps(geometry A, geometry B)

TOUCHES

Las geometrías se tocan sin coincidir sus interiores en ningún momento

ST_Touches(geometry A, geometry B)

WITHIN/CONTAINS

Comprueba si una geometría está dentro de otra (within) o si una geometría contiene a otra (contains)

A está dentro de B:

ST_Within(geometry A , geometry B)

A contiene B:

ST_Contains(geometry A, geometry B)

DISTANCE WITHIN

Si una geometría se encuentra a cierta distancia de otra

ST_DWithin(geometry A, geometry B, distance)

DISTANCE

En este caso, en vez de devolver TRUE o FALSE devuelve un valor decimal con la distancia entre las geometrías seleccionadas en las unidades del sistema de referencia de la capa:


Crear topología

La topología, referida al ámbito geográfico, son las relaciones espaciales que existen entre distintos objetos o fenómenos territoriales.

Cuando hacemos análisis espaciales como comentamos en este artículo se comprueba la existencia de estas relaciones, dónde se producen y en qué magnitud. Sin embargo, eso no quiere decir que los datos espaciales contengan topología, sino que dicha topología se calcula o extrae a posteriori a partir de objetos posicionados en un marco espacial.

Por ejemplo, si tenemos dos polígonos correspondientes a dos parcelas que se encuentran pegadas la una a la otra, un análisis espacial nos devolvería el dato que dice que se encuentran compartiendo frontera.

Cuando los objetos almacenan topología, lo que hacen es guardar esas relaciones existentes entre ellos, es decir, las relaciones son implícitas a ellos. De este modo, cuando los datos se editen, los objetos seguirán manteniendo esas relaciones.

Si ahora modificáramos el límite de una de esas parcelas, ampliando su tamaño sobre la otra, no se produciría un solape, sino que la otra parcela vería reducida su tamaño y seguirían compartiendo borde.

Añadir topología es muy útil porque reduce la geometría a sus primitivas, haciendo que ocupen menos espacio y permitiendo hacer análisis espaciales más rápidos y precisos. Sin embargo, resulta contraproducente usarla en muchas situaciones. Recomiendo leer el apartado de topología del capítulo sobre modelos de información geográfica del libro SIG de Víctor Olaya para ampliar más sobre este tema.

Añadir topología con PostGIS

PostGIS permite que añadamos topología a nuestras capas espaciales en unos pocos pasos:

1 – Activar la extensión PostGIS y la extensión PostGIS_topology

Antes de comenzar es necesario que la base de datos PostgreSQL cuente con las funciones espaciales (proporcionadas por la extensión PostGIS) y con las funciones topológicas (dadas en PostGIS_topology).

Bastará con ejecutar las siguientes sentencias:

CREATE EXTENSION postgis;
CREATE EXTENSION postgis_topology; 

2 – Crear esquema topológico

Lo primero es crear con el siguiente código un esquema en PostgreSQL en el que que se almacenará la topología:

SELECT topology.CreateTopology('nombre_topología', SRC, tolerancia);

Se le asigna un nombre a la topología, generalmente el nombre de la capa a la que se la añadiremos junto a un prefijo o un sufijo ‘topo’ (topo_carreteras por ejemplo)

El sistema de referencia de la topología (25830 por ejemplo)

La tolerancia clúster (por ejemplo 0.1) asigna la misma posición a los vértices que se encuentren dentro de esa tolerancia, desplazándolos un poco durante el proceso. Por tanto, dicha tolerancia debe ser pequeña para que así reasigne solo aquellos vértices que realmente pudieran verse repetidos.

El resultado es un nuevo esquema que contará con las siguientes tablas, cada una almacenando una parte de la topología: los nodos, los bordes, las caras y las relaciones existentes entre ellos.

3 – Añadir columna topogeométrica a la capa

Tendremos que usar la siguiente sentencia:

SELECT topology.AddTopoGeometryColumn('nombre_topo', 'esquema_capa', 'nombre_capa', 'nombre_topogeom', 'tipo_geometría');

Nombre_topo indica el esquema topológico creado en el punto 1 en el que se guardará la topología

Esquema_capa se refiere al esquema en el que se encuentra la capa a la que vamos a dar topología

Nombre_capa es el nombre de la tabla (o capa, es lo mismo) espacial

Nombre_topogeom es el nombre de la columna que contendrá la topogeometría. Habitualmente se la llama topogeom

Tipo_geometría es el tipo de geometría de la capa

Cuando se añade una capa al esquema topológico por un lado se añade una nueva columna llamada topogeom a la tabla de la capa que va a albergar topología.

Pero también por otro lado se añade automáticamente un registro en la tabla de metadatos layer del esquema topology con la capa que incorpora la topología. Este es un esquema que se crea automáticamente cuando activamos la extensión de topología en una base de datos de PostgreSQL.

4 – Rellenar la nueva columna topogeom

Se actualiza la capa rellenando la columna topogeom con los datos de la columna de geometría transformados a topología:

UPDATE nombre_capa 
SET topogeom = topology.toTopoGeom(
     columna_geom, 'esquema_topo', 
     layer_id, tolerancia
);

Columna_geom es la columna con geometría de la tabla

Esquema_topo es el esquema topológico que creamos al inicio

Layer_id es el identificador de la capa de topología asignado en la tabla layer del esquema topology

Tolerancia es el umbral para reasignar vértices explicado en el punto 1

Pueden generarse errores durante el proceso al emplear tolerancias muy grandes o al existir geometrías no válidas. En tal caso hay que revisar las geometrías para que sean válidas y reducir la tolerancia, es decir, exigir más precisión a la hora de crear la topología.

5 – Ya en QGIS

Una vez hecho todo esto, desde QGIS veríamos un esquema en el que se encuentran cuatro capas: la de los nodos, la de los bordes, la de las caras y la de las relaciones. Las podremos añadir al proyecto y operar con ellas aprovechando sus ventajas, pero teniendo también en cuenta sus inconvenientes, ya que por ejemplo no son adecuadas para fines estéticos o de diseño.

Por ejemplo, si simplificáramos las geometrías de las Comunidades Autónomas de España con ST_Simplify sobre la columna geom (la de geometrías simples) se originarían gaps entre las distintas regiones.

En cambio, si lo aplicáramos sobre la columna topogeom (la columna que hemos añadido con la topología) esto no se produciría puesto que mantendría la consistencia de los datos, es decir, mantendría los polígonos unidos:

6 – Borrar la topología de una capa

Si quisiéramos borrar la topología habría que usar el siguiente código:

SELECT topology.DropTopology('nombre_capa');


Crear topología de red

Topología de red

A la hora de hacer análisis espaciales es conveniente que nuestros geodatos cuenten con topología. En el post de topología en PostGIS explico brevemente qué es la topología y qué bondades ofrece, pero en resumidas cuentas la topología almacena las relaciones espaciales existentes en nuestros datos.

La topología aplicada a capas de carreteras, ríos o cualquier entidad lineal del territorio lo que hace es estructurar los datos para que cada arco comience siempre en un nodo y acabe en otro, identificando en sus atributos el nodo en el que empieza y en el que acaba. Dicho de otro modo, cada cruce es un punto y cada calle es un arco que debe conectar sí o sí con dos puntos (aunque sea una calle sin salida).

Gracias a esta información podremos hacer análisis de redes tales como:

  • Averiguar la ruta más corta entre dos nodos de la red
  • La ruta más corta para atravesar varios puntos y regresar al origen
  • Calcular los costes de desplazamiento entre varias rutas

Además, podríamos añadir a la coctelera variables como la velocidad o el sentido de la circulación para cada tramo, los giros prohibidos de cada nodo, el estado del tráfico, restricciones…

Básicamente, lo que se hace es calcular la longitud total del recorrido sumando las longitudes de los arcos por los que ha sido posible circular. A ese valor se le aplicarán los distintos factores que condicionen el viaje como por ejemplo:

  • El consumo de carburante en litros/km
  • Emisiones contaminantes en kg CO2/km
  • Tiempo de recorrido en función de la velocidad de la vía
  • Mayor o menor factor de consumo en función de la carga y la pendiente

El valor final puede ser comparado entre distintas alternativas para encontrar la ruta óptima en función del tiempo y los costes económicos y/o ecológicos que estemos dispuestos a asumir.

Añadir topología a una red en PostGIS

Activar las extensiones PostGIS y pgRouting

Lo primero que necesitamos es que la base de datos tenga activadas las extensiones PostGIS y pgRouting:

create extension postgis;
create extension pgrouting;

pgRouting es una extensión de PostGIS dedicada al análisis de redes con la que podremos añadir topología a capas multilínea o multilinestring como puede ser la red de carreteras, de metro o la red fluvial y empezar a usarlas para nuestros análisis de redes.

Importar y preparar los datos

Si obtenemos la capa de líneas desde OpenStreetMap y la importamos en pgRouting como cuento en esta otra entrada la topología se generará automáticamente, pero lo habitual es que tengamos que construirla.

Para importar un .shp se puede usar el PostGIS Shapefile Import/Export manager, que viene por defecto con PostGIS y permite añadir a la base de datos que seleccionemos (que tenga activadas las extensiones del punto anterior) los shapefiles que se encuentren en nuestro PC.

Para crear la topología de redes es necesario asegurarnos de que la capa de líneas cuenta con las siguientes columnas:

  1. Una columna ID que identifique cada de forma individual
  2. Una columna con la geometría de cada arco
  3. Otra columna de tipo double que vaya a almacenar los costes de desplazamiento para cada arco
  4. Una columna que vaya a almacenar el ID del nodo en el que empieza el arco (source) y otra columna que almacene el ID del nodo en el que termine (target)

El siguiente código viene a añadir las columnas de costes y de nodos source y target, que son las que no suelen estar presentes en los shapefiles:

ALTER TABLE nombre_capa ADD COLUMN coste double precision;
ALTER TABLE nombre_capa ADD COLUMN source int;
ALTER TABLE nombre_capa ADD COLUMN target int;

Crear la topología de red

Una vez cumplamos estos requisitos, ejecutaremos el siguiente código para crear la topología de redes:

SELECT pgr_createTopology('nombre_capa', 0.001, 'geom', 'gid', 'source', 'target');

pgr_createTopology necesita las siguientes variables:

  • El nombre de la capa/tabla a la que añadiremos la topología.
  • La tolerancia o umbral, habitualmente en metros, a partir del cual se asigna la misma posición en el espacio a los vértices que se encuentren dentro de él. Debe ser pequeña para que así reasigne solo aquellos vértices que realmente pudieran verse repetidos, por eso en el ejemplo se ha puesto 0.001.
  • Las columnas de geometría, de ID y source y target

Una vez hecho esto, se creará una nueva tabla con el nombre de la capa de líneas a la que le hemos añadido la topología + el sufijo »_vértices_pgr» que almacenará los nodos de la red, y las columnas source y target se actualizarán almacenando para cada vértice los ID de los nodos a los que está conectado.

La capa ya estará lista para llevar acabo análisis de redes.


Importar red viaria de OSM con pgRouting

¿Qué es pgRouting?

Pgrouting es un complemento que amplía las posibilidades de calcular proximidades con PostGIS añadiendo funcionalidades de red. En vez de calcular distancias en línea recta entre objetos geométricos (como puedan ser dos puntos) se calcularán rutas en base a una red de nodos y vértices.

Además se puede añadir a la coctelera variables como el coste máximo por unidad de espacio, tiempos de recorrido, emisiones contaminantes, estado del tráfico y demás condicionantes para hallar la ruta óptima, similar a lo que hace Google con Maps.

Incluso pueden crearse rutas para visitar varios puntos y volver al origen (conocido como el Problema del Viajante) optimizando los recursos disponibles.

Pero para hacer estos análisis necesitamos como base una red de vértices interconectados con nodos: capas vectoriales que almacenen callejeros de núcleos urbanos o redes de carreteras. OpenStreetMap (OSM) permite descargarnos geodatos de todos aquellos sitios cartografiados en su plataforma de forma totalmente gratuita, y además a la carta: podemos elegir el área concreta de la que descargaremos los datos.

En esta entrada veremos cómo descargar pgRouting e importar la red viaria de OSM a PostGIS y dejarla lista para hacer análisis de redes.

Descargar pgRouting

Si ya estáis usando PostGIS, pgRouting suele venir dentro del paquete de herramientas espaciales que incluye, por lo que no tendríais que hacer nada.

Si no es el caso, hay que usar la aplicación Stack Builder que trae PostgreSQL para descargar alguno de los paquetes de herramientas espaciales que se encuentran en la categoría de Spatial Extensions en los que se incluya tanto PostGIS como pgRouting.

Importar datos de OpenStreetMap

1- Obtener los datos

Como hemos dicho, OpenStreetMap es una plataforma flexible que permite descargarnos sus conjuntos de datos para zonas específicas que elijamos. En el post Fuentes de geodatos hay una sección dedicada a esta plataforma con enlaces a distintos servicios de descarga en función del conjunto de datos que necesitemos.

Servicios como Geofabrik permiten la descarga de archivos .shp en algunos conjuntos de datos, pero la mayoría, si no son todos, descargan los archivos de OpenStreetMap en formato .osm, propio de esta plataforma. Por ello este post va dirigido a la importación de archivos .osm.

Para seleccionar datos de OSM usando coordenadas tendremos que acceder a la API desde el navegador y hacer una llamada añadiendo al final del enlace de la barra de direcciones el comando map?bbox= seguido de las coordenadas de una bouncing box que delimite el área del que queremos datos:

La bouncing box puede obtenerse en la página bbox finder

Por ejemplo, la descarga de datos OSM para San Javier (Bolivia) podría ser el siguiente enlace:

http://overpass-api.de/api/xapi?map?bbox=-62.522936,-16.285458,-62.488689,-16.266425

El último paso de este punto sería renombrar el archivo descargado para que su extensión sea .osm. Este archivo contendrá la cartografía OSM disponible para las cordenadas indicadas, pero pgRouting importará solo la red de carreteras.

2- Crear base de datos con PostGIS y pgRouting

Para importar estos datos a PostgreSQL tendremos que crear una base de datos espaciales activando las extensiones PostGIS y pgRouting. Puede hacerse tanto a través de la interfaz de pgAdmin o del siguiente código:

CREATE DATABASE nombre
    WITH 
    OWNER = usuario
    ENCODING = 'UTF8'
    CONNECTION LIMIT = -1;
CREATE EXTENSION postgis;
CREATE EXTENSION pgrouting;

También puede simplemente activarse estas extensiones en una base de datos ya existente.

3- Importar los datos a la base de datos

La importación deberá hacerse con la Consola de Windows o Símbolo de Sistema.

Lo primero una vez abierto es cambiar la ruta de trabajo a la ruta en la que se encuentra el archivo osm2pgrouting.exe. La ruta puede variar en función de dónde tengáis instalado PostgreSQL:

cd C:\Program Files\PostgreSQL\12\bin\

Después se debe señalar en primer lugar el nombre del archivo .exe a abrir (osm2pgRouting) junto a los siguientes parámetros:

osm2pgRouting --file C:\Users\usuario\Downloads\archivo.osm --dbname nombre_database --username usuario --password constraseña --conf mapconfig.xml --clean

–osm2pgRouting = el archivo .exe que ejecuta osm2pgrouting

–file = el archivo .osm a importar

–dbname = el nombre de la base de datos con PostGIS y pgRouting activados a la que se importará el archivo

–username = usuario de la base de datos PostgrSQL

–password = contraseña de acceso del usuario

–conf = la ruta en la que se encuentra el archivo mapconfig.xml. Este archivo indica a osm2pgRouting las tipologías de las vías y carreteras que tiene que importar, por lo que puede modificarse para quedarnos solo las que nos interesen. Si estuviese en la misma carpeta que osm2pgRouting bastaría con poner su nombre.

–clean elimina las tablas que ya existiesen en la base de datos con el mismo nombre

Si todo ha salido bien, tras tiempo (en función de la cantidad de datos) la consola de Windows devolverá al final del todo lo siguiente:

4- Resultados

Dentro de la base de datos que hemos indicado en los parámetros veremos creadas nuevas tablas con los datos de OpenStreetMap:

Entre las ventajas de usar datos OSM para nuestros análisis de redes e importarlos con este sistema es que la topología se genera automáticamente. Las tablas que usaremos para hacer los análisis de redes serán ways (arcos) y ways_vertices (nodos).

Si lo visualizáramos en QGIS obtendríamos algo así:

Cada arco termina en un nodo que conecta con otros arcos. En la tabla de atributos podemos obtener información como su identificador, el nombre de la vía, su longitud, con qué nodos conecta…

Cada nodo almacena su identificador y sus coordenadas.

¡Ya tendríamos listo todo para hacer análisis de redes ya sea en PostGIS o en QGIS!


About

🐘🌏Apuntes de SQL para Postgre y PostGIS

https://roberer.github.io/