1. ProcedimientosAlmacenados(stored procedures) Un procedimiento almacenado se puede definir como un programa, procedimiento ó función, el cual está almacenado en la base de datos y listo para ser usado. Utilizar parámetros de salida : Si se ejecuta una consulta de SQL ad hoc que devuelve una sola fila, debes utilizar un conjunto de resultados. Un procedimiento almacenado ofrece la posibilidad de utilizar parámetros de salida, lo que resulta ser mucho más rápido. Si ejecutas sólo una consulta, no notarás la diferencia, pero si ejecutas la consulta siguiente 2000 veces, es probable que ganes mucho devolviendo @key como parámetro de salida en lugar de un conjunto de resultados. Manejar las dependencias : En un sistema complejo que incluye unos centenares de tablas, muchas veces quieres saber dónde se usa una tabla o columna determinada, por ejemplo si quieres cambiar una columna de alguna manera. Si todo el código está en procedimientos almacenados, sólo se tiene que buscar en el texto de los procedimientos para encontrar las referencias. Si envías instrucciones SQL directamente desde la aplicación, se debe buscar en una cantidad mayor de código, y si el nombre de la columna tiene un nombre común la tarea puede ser casi imposible. Establecer bloques de lógico: permite esconder los detalles de las base de datos dentro de los procedimientos, así que se convierten en una capa de abstracción.
2. ProcedimientosAlmacenados Permisos:Un procedimiento almacenado es la solución clásica para manejar el acceso de los usuarios a los datos. Un usuario no debería tener los permisos para ejecutar SELECT, INSERT, UPDATE y DELETE directamente Almacenar los planes de consulta en la caché: Otra ventaja importante de los procedimientos almacenados es el rendimiento. Cuando un procedimiento almacenado se ejecuta por la primera vez, los motores de DB crea un plan de consulta y lo almacena en la caché para poder reutilizarlo en el futuro. Reducir el tráfico en la red: El tráfico en la red también afecta el rendimiento de las aplicaciones. Poner esta consulta en un procedimiento reduce la cantidad de bytes que se transmite por la red, lo que puede mejorar el rendimiento si hay mucho tráfico. La diferencia es aún más evidente cuando tienes una serie de instrucciones SELECT/INSERT/UPDATE interrelacionadas. Un procedimiento almacenado te permite utilizar tablas temporales o variables de tabla para procesar todos los datos en el servidor. En caso de utilizar instrucciones de SQL ad hoc(Cliente), “se tiene que enviar todos los datos entre el servidor y el cliente o la capa intermedia” esto se puede resolver creando tablas temporales sin un procedimiento almacenado, - pero se puedes tener problemas con el connectionpooling y los conjunto de registro -.
3. ProcedimientosAlmacenadosPostgres Un procedimiento almacenado en PostgreSQL se puede escribir en multiples lenguajes de programación. En una instalación por defecto de PostgreSQL podremos tener disponibles los siguientes lenguajes: PL/pgSQL, PL/Perl, PL/Tcl y PL/Python. El único lenguaje que está disponible automáticamente es PL/pgSQL. Para utilizar PL/Perl, PL/Tcl o PL/Python tendremos que haber configurado/compilado PostgreSQL con estos parámetros --with-perl --with-tcl --with-python. Tambien existen muchos otros lenguajes disponibles como módulos adicionales, entre ellos, PL/Java, PL/PHP, PL/R, PL/Ruby, PL/Sheme y PL/sh, pero estos tienen que descargarse e instalarse por separado . PL/pgSQLes muy parecido al lenguaje PL/SQL utilizado por Oracle, Los objetivos de PL/pgSQL cuando se creo fueron: Poder ser usado para crear funciones y disparadores (triggers) Añadir estructuras de control al lenguaje SQL Poder realizar cálculos complejos Heredar todos los tipos, funciones y operadores definidos por el usuario Poder ser definido como un lenguaje "de confianza" Fácil de usar
4. ProcedimientosAlmacenadosPostgres PL/pgSQLes un lenguaje estructurado en bloques. Como mínimo tendremos un bloque principal en nuestro procedimiento almacenado y dentro de este podremos tener subbloques. CREATE OR REPLACE FUNCTION ejemplo_txt(integer, integer) RETURNStext AS $$ DECLARE numero1 ALIAS FOR $1; numero2 ALIAS FOR $2; constante CONSTANTinteger := 100; resultado INTEGER; resultado_txtTEXT DEFAULT 'El resultado es 104'; BEGIN resultado := (numero1 * numero2) + constante; IF resultado <> 104 THENresultado_txt := 'El resultado NO es 104'; END IF; RETURNresultado_txt; END; $$ LANGUAGEplpgsql;
5. Tablasparticionadas Normalmente las tablas particionadas se crean por que las tablas tiene un gran tamaño ademas del gran número de inserciones diarias. Permitiendo un esquema de organización de los datos en la tabla. Cuando hay grandes conjuntos de datos, las operaciones frecuentes de mantenimiento (estas se realizan en los datos estáticos) puede tener efectos costosos, tales como problemas de rendimiento, problemas de bloqueo, las copias de seguridad (espacio, tiempo y costos operativos), así como un impacto negativo en la escalabilidad global del servidor. Las busquedasmediante índices globales tiene un pobre desempeño ya que se revisan toda la tabla. Las tablas particionadas permite dividir los datos dentro de múltiples objetos de almacenamiento llamados particiones de datos o rangos de acuerdo con los valores en una o mas columnas de la tabla. Las tablas particionadas mejorar la escalabilidad y manejabilidad de las tablas grandes y las tablas que tienen diferentes patrones de acceso como las fechas. Cada partición de datos se puede almacena por separado. .
6. Tablasparticionadas proceso de consultas también pueden tomar ventaja de la separación de los datos para evitar la exploración de datos irrelevantes. Con el uso de índices que incluyen las columnas de partición se reduce el ámbito de busqueda y con ello se mejora la rapidez. Las particiones de tablas dan la posibilidad de ser truncadas por separado y por ello son usadas muy a menudo en tablas donde la validez de sus datos están sujetos a fechas donde el truncate de una partición no afecta al total de la tabla. las particiones se dividen en una partición activa y las de archivado. si una tabla grande existe en un sistema con varias CPU, la partición de la tabla puede llevar a un mejor desempeño a través de operaciones paralelas. El rendimiento de las operaciones a gran escala a través de conjuntos de datos extremadamente grandes (por ejemplo, millones de filas) se pueden beneficiar mediante la realización de múltiples operaciones en paralelo. Cada N días se trunca mediante un job la partición que será la particionactiva para realizar inserts, de esta manera las otras particiones se mantendrán intactas, manteniendo así un histórico de datos para consultas limitadas por fechas y provocando menor movimiento de bloques de memoria y la desfragmentación causada por inserción o borrado de datos. .
7. TablasparticionadasPostgresql En postgresql el tipo de partitioning soportado se denomina particionado mediante herencia de tablas. Cada partición puede ser creada como una tabla hija de una unica tabla padre. La tabla padre normalmente debe de ser una tabla vacia, que representara a todo el conjunto de datos. Los dos tipos de particionado mas comunes en postgresql son: 1. Particionar por rangos. La tabla es particionada mediante rangos definidos en base a la columna de clave primaria o cualquier columna que no se solape entre los rangos de valores asignados a diferentes tablas hijas. 2. Particionar por lista. La tabla es particionada listando los valores de cada una de las claves en cada particion. .
8. TablasparticionadasPostgresql En postgresql para poder utilizar dicha técnica es necesario seguir algunos pasos: 1. Crear la tabla "maestra", de la cual todas las tablas hijas heredaran. Esta tabla no contendra datos, no debe de tener ningun tipo de restricción (check), no debe de tener indice ni nada por el estilo. 2. Crear todas las tablas hijas heredando de la tabla maestra. Por lo regular estas tablas heredaran todos los campos de la tabla padre y no es necesario crearlos nosotros mismos. 3. Agregar a todas las tablas hijas las restricciones correspondientes sobre los datos que albergaran. Algunos ejemplos de esto serian: CHECK ( pais IN ('México', 'Argentina')), CHECK ( id_cliente BETWEEN 100 AND 200 ) ... CHECK ( id_cliente BETWEEN 5000 AND 5200), estos dos ultimos ejemplos son para evitar traslapes entre los ids de clientes. 4. Para cada particion, crear un indice para la columna(s) Clave, tambien se pueden poner los indices que a cada quien le convengan. El indiceclave primaria no es estrictamente necesario, pero en la mayoria de los casos ayuda mucho. Si se nesecita una clave unica o clave primaria, esta se necesitaras crear para cada tabla hija. 5. Definir una regla (RULE) o trigger para redirigir las modificaciones de la tabla padre (master) a la apropiada particion. 6. Verificar que este habilitado a on el parametroconstraint_exlusion (SET constraint_exclusion = on;) en el archivo de configuracionpostgresql.conf para que los querys sean optimizados para el partitioning. .
9. TablasparticionadasPostgresql CREATE TABLE produccion(id int not null, no_serieint, id_modeloint, id_personalint, fecha_fabricacion date, fecha_salida date); particionaremos en base al modelo del producto fabricado. SELECT * from modelos order by id; id | modelo | caracteristicas | descripcion | imagen -+ 1 | ODIN | 5 litros por minuto | calentador de paso | 2 | DELTA 01 | 7 litros por minuto | calentador de paso | 3 | DELTA 01-PLUS | 9 litros por minuto | calentador de paso | 4 | DELTA 02 | 13 LITROS POR MINUTO | CALENTADOR DE PASO | Crear las tablas hijas heredadas de la tabla "produccion", aqui es donde se crean las restricciones (CHECK) para los datos que albergaran cada tabla hija. CREATE TABLE produccion_odin ( CHECK (id_modelo = 1) ) INHERITS (produccion); CREATE TABLE produccion_delta01 ( CHECK (id_modelo = 2) ) INHERITS (produccion); CREATE TABLE produccion_delta01plus ( CHECK (id_modelo = 3) ) INHERITS (produccion); CREATE TABLE produccion_delta02 ( CHECK (id_modelo = 4) ) INHERITS (produccion); Creacion clave primariapara agilizar la busqueda de registros o garantizar la unicidad de los mismos. ALTER TABLE produccion_odin ADD PRIMARY KEY (id); Creacion de indices los cualespordefecto son B-TREE CREATE INDEX produccion_odin_id ON produccion_odin (id); CREATE INDEX produccion_delta01_id ON produccion_delta01 (id); CREATE INDEX produccion_delta01plus_id ON produccion_delta01plus (id); CREATE INDEX produccion_delta02_id ON produccion_delta02 (id); .
10. TablasparticionadasPostgresql Bien, ahora procederemos a crear el juego de reglas para garantizar el llenado en cascada de los datos y que cuando estos caigan en la tabla maestra sean redirigidos a sus respectivas tablas. . -- Regla para produccion_odin CREATE RULE produccion_odin_insert_rule AS ON INSERT TO produccion WHERE ( id_modelo = 1 ) DO INSTEAD INSERT INTO produccion_odin VALUES ( NEW.id, NEW.no_serie, NEW.id_modelo, NEW.id_personal, NEW.fecha_fabricacion, NEW.fecha_salida); -- Regla para produccion_delta01 CREATE RULE produccion_delta01_insert_rule AS ON INSERT TO produccion WHERE ( id_modelo = 2 ) DO INSTEAD INSERT INTO produccion_delta01 VALUES ( NEW.id, NEW.no_serie, NEW.id_modelo, NEW.id_personal, NEW.fecha_fabricacion, NEW.fecha_salida); -- Regla para produccion_delta01plus CREATE RULE produccion_delta01plus_insert_rule AS ON INSERT TO produccion WHERE ( id_modelo = 3 ) DO INSTEAD INSERT INTO produccion_delta01plus VALUES ( NEW.id, NEW.no_serie, NEW.id_modelo, NEW.id_personal, NEW.fecha_fabricacion, NEW.fecha_salida); -- Regla para produccion_delta02 CREATE RULE produccion_delta02_insert_rule AS ON INSERT TO produccion WHERE ( id_modelo = 4 ) DO INSTEAD INSERT INTO produccion_delta02 VALUES ( NEW.id, NEW.no_serie, NEW.id_modelo, NEW.id_personal, NEW.fecha_fabricacion, NEW.fecha_salida);
11. TablasparticionadasPostgresql Se procede a crearuna vista quecontendratodos los valores, talcomosi se tratase de unatablaenorme: CREATE VIEW produccion_total AS SELECT * FROM produccion_odin UNION ALL SELECT * FROM produccion_delta01 UNION ALL SELECT * FROM produccion_delta01plus UNION ALL SELECT * FROM produccion_delta02; Con esto se tiene un esquema de tablas particionadas, para terminar se habilita el obtimizador de consultas para evitar la busqueda de algun dato en todas las particiones Para configurar desde psql: SET constraint_exclusion = on; o modificando el postgresql.conf de la siguiente manera, anexando la siguiente linea: constraint_exclusion = on .
12.
13. Logs automáticos de cambios a las tablas. Una aplicación puede guardar un registro corriente de cambios, creando un trigger que se dispare siempre que una tabla se modifique.
16. Transaccionesautónomas En ocasiones es necesario que los datos escritos por parte de una transacción sean persistentes a pesar de que la transacción se deshaga con un ROLLBACK. Es muy común que, por ejemplo, en caso de que se produzca algún tipo de error queramos insertar un registro en una tabla de log con el error que se ha produccido y hacer ROLLBACK de la transacción. Pero si hacemos ROLLBACK de la transacción tambien se realiza una INSERT a un LOG. Quedando el error en la tabla de log, pero las acciones de la transacción principal retornarían a su estado anterior. Utilizacion Mecanismo de Logging (log de seguimiento ) Contador de Intentos Medidor de uso del software (registrar la llamada a la aplicacion independientemente que la transacción realice commit o rollback) Componentes reusables(la posibilidad de ofrecer unidades independientes de trabajo (también conocidas como cartridges) que realicen su tarea sin efecto sobre el ambiente que lo llama) No, pero puedes emularlo usando dblink o algún otro mecanismo dentro de una función para establecer una segunda conexión a la BD, y hacer cosas en esa otra conexión.
17. EjemploTransaccionesautónomas CREATE OR REPLACE FUNCTION fn_log_error(p_functionvarchar, p_locationint, p_errorvarchar) RETURNSvoid AS $$ DECLARE v_sqlvarchar; v_returnvarchar; v_errorvarchar; BEGIN PERFORMdblink_connect(‘NombreConexion', 'dbname=...'); v_sql := 'INSERT INTO error_log (function_name, location, error_message, error_time) ‘ || 'VALUES (''' || p_function_name || ''', ' || p_location || ', ''' || p_error || ''', clock_timestamp())'; SELECT INTO v_return * FROMdblink_exec('NombreConexion', v_sql, false); --Obtiene el mensage de error SELECT INTO v_error * FROMdblink_error_message('NombreConexion'); IF position('ERROR' in v_error) > 0 OR position('WARNING' in v_error) > 0 THEN RAISE EXCEPTION '%', v_error; END IF; PERFORMdblink_disconnect('NombreConexion'); EXCEPTION WHENothersTHEN PERFORMdblink_disconnect('NombreConexion'); RAISE EXCEPTION '(%)', SQLERRM; END; $$ LANGUAGEplpgsqlSECURITYDEFINER;
18. vistas materializadas o materializedviews Almacenan la definición de la vista propiamente dicha, Almacenan los registros que resultan de la ejecución de la sentencia SELECT que define la vista. los resultados se almacenan físicamente constituyendo una tabla real que ocupa espacio en el disco duro. Si una vista (view) utiliza muchas tablas base enlazadas de forma compleja, y dicha vista va a ser utilizada frecuentemente, será muy conveniente definirla como una vista materializada. Las materializedviewmejorar el rendimiento de la base de datos, ya que la sentencia SQL base de la vista sólo se ejecutará una vez. Si se reutilizarse en el futuro, se necesita un mecanismo para actualizar o refrescar dicha vista materializada, ya que las tablas base de la vista pueden haber sufrido modificaciones desde la creación de la misma.
19. vistas materializadas o materializedviews Se comporta como un punto de vista. Es decir, los datos de la tabla cambia cuando los datos en las tablas subyacentes tienen cambios. tipos de vistas materializadas: Instantánea(Snapshot) son las más simples de implementar. Ellos sólo se actualizan cuando se actualiza de forma manual. Ansioso(Eager) se actualizan tan pronto como cualquier cambio se realiza en la base de datos que lo afectan. Perezoso(Lazy) se actualizan cuando se confirma la transacción. Muy perezoso(VeryLazy) son funcionalmente equivalentes a los Snapshot. La única diferencia es que los cambios se registran de forma incremental y son aplicado cuando la tabla se actualiza de forma manual.
20. vistas materializadas o materializedviews algunas consultas son muy lentas. Es posible que se haya gotado todas las técnicas estándar acelerar lasconsultas. Se nesecitan la reestructuración completa de los datos. Lo que terminan haciendo es almacenar bits de pre-consulta de información para no tener que ejecutar la consulta cuando se necesita los datos. Normalmente, esto se le llama "caché" fuera del mundo de bases de datos. Desarrollo de una vista materializada CREATE TABLEmatviews ( mv_nameNAME NOT NULL PRIMARY KEY , v_nameNAME NOT NULL , last_refreshTIMESTAMP WITH TIME ZONE ); Las columnas son : mv_name: el nombre de la vista materializada .v_name: el nombre de la vista en la queestabasada la vista materializada. last_refresh: es la ultimavez(tiempo) en que la vista materilizadafueactulizada.
21. vistas materializadas o materializedviews Funcioncreate_matview esta función revisa si hay una vista materializada con ese nombre . Si es así, se produce una excepción. De lo contrario, crea una nueva tabla de la vista, e inserta una fila en la tabla matviews .. CREATE OR REPLACE FUNCTION create_matview(NAME, NAME) RETURNS VOID SECURITY DEFINER LANGUAGE plpgsql AS ' DECLARE matviewALIAS FOR $1; view_nameALIAS FOR $2; entrymatviews%ROWTYPE; BEGIN SELECT * INTO entry FROM matviews WHERE mv_name = matview; IF FOUND THEN RAISE EXCEPTION ‘Vista Materializada ''''%'''' ya exite.'', matview; END IF; EXECUTE ''REVOKE ALL ON '' || view_name || '' FROM PUBLIC''; EXECUTE ''GRANT SELECT ON '' || view_name || '' TO PUBLIC''; EXECUTE ''CREATE TABLE '' || matview || '' AS SELECT * FROM '' || view_name; EXECUTE ''REVOKE ALL ON '' || matview || '' FROM PUBLIC''; EXECUTE ''GRANT SELECT ON '' || matview || '' TO PUBLIC''; INSERT INTO matviews (mv_name, v_name, last_refresh) VALUES (matview, view_name, CURRENT_TIMESTAMP); RETURN; END ';
22. vistas materializadas o materializedviews Funciondrop_matvie esta función elimina la vista materilizada y la remueve de la tabla matviews .. CREATE OR REPLACE FUNCTION drop_matview(NAME) RETURNS VOID SECURITY DEFINER LANGUAGE plpgsql AS ‘ DECLARE matview ALIAS FOR $1; entry matviews%ROWTYPE; BEGIN SELECT * INTO entry FROM matviews WHERE mv_name = matview; IF NOT FOUND THEN RAISE EXCEPTION '‘Vista materilizada% no existe.'', matview; END IF; EXECUTE ''DROP TABLE '' || matview; DELETE FROM matviews WHERE mv_name=matview; RETURN; END '; ';
23. vistas materializadas o materializedviews Funcionrefresh_matview Esta funcion actualiza o carga las vistas materializadas para que los datos no se convierta en obsoletos. Utiliza un algoritmo de fuerza bruta que eliminarán todas lasfilas y vuelva y las recarga con los datos de la vista ejecutada.Tenga en cuenta que es posible que desee eliminar los índices en la vistamaterializada antes de la ejecución de esta funcion, y los vuelve a recrear después de que termine de ejecutar esta funcion. CREATE OR REPLACE FUNCTION refresh_matview(name) RETURNS VOID SECURITY DEFINER LANGUAGE plpgsql AS ' DECLARE matview ALIAS FOR $1; entrymatviews%ROWTYPE; BEGIN SELECT * INTO entry FROM matviews WHERE mv_name = matview; IF NOT FOUND THEN RAISE EXCEPTION ‘Vista materializada % no existe.'', matview; END IF; EXECUTE ''DELETE FROM '' || matview; EXECUTE ''INSERT INTO '' || matview || '' SELECT * FROM '' || entry.v_name; UPDATE matviews SET last_refresh=CURRENT_TIMESTAMP WHERE mv_name=matview; RETURN; END ';
24. vistas materializadas o materializedviews Ejemplo de una vista materializadatipo Snapshot CREATE TABLE jugadores( pname VARCHAR(255) PRIMARY KEY ); CREATE TABLE juego_resultado( pname VARCHAR(255) NOT NULL, score INTEGER NOT NULL ); CREATE VIEW jugadores_total_puntaje_vAS SELECT pname, sum(score) AS total_score FROM game_score GROUP BY pname; En estecaso los juagdoresjuegancadadia y correruna vista esdemasidocostoso en rendimiento y desempeño, porconsiguente se decide imprementaruna vista materializadapara el puntaje total de cadajugador (jugadores_total_puntaje_v). Se ejecutan los siguinetescomandos: SELECT create_matview('jugadores_total_puntaje_mv', 'jugadores_total_puntaje_v'); CREATE INDEX pname_idx ON player_total_score_mv(pname); Cadanoche (o cadahoradepende de como los jugadoresmirar los resultados) , se deberefrescar la vista materializada con el siguinetecomando. DROP INDEX pname_idx ON jugadores_total_puntaje_mv; SELECT refresh_matview('jugadores_total_puntaje_mv'); CREATE INDEX pname_idx ON jugadores_total_puntaje_mv(pname); Even though the scores in player_total_score_mv isn't going to change to reflect the most current scores until the refresh is run, the players will learn to accept that. ';
25. Recursividad La recursividad permite la capacidad de poder seleccionar un conjunto de datos de forma recursiva de manera que podemos obtener una serie de datos en estructura de árbol. La devolución de datos jerárquicos es un uso frecuente de las consultas recursivas; por ejemplo, mostrar los empleados en un organigrama o los datos en un escenario de lista de materiales en donde un producto primario tiene uno o varios componentes que, a su vez, tienen subcomponentes o son componentes de otros elementos primarios. Una solución es una expresión de tabla común (CTE) , esta ofrece la gran ventaja de poder hacer referencia a sí misma, creando así una CTE recursiva. Una CTE recursiva es aquélla en la que una CTE inicial se ejecuta varias veces para devolver subconjuntos de datos hasta que se obtenga el conjunto de resultados completo. Se considera que una consulta es recursiva cuando hace referencia a un CTE recursiva. Una CTE recursiva puede simplificar en gran medida el código necesario para ejecutar una consulta recursiva en una instrucción SELECT, INSERT, UPDATE, DELETE o CREATE VIEW. Anteriormente la solución utilizaba tablas temporales, cursores y lógica para controlar el flujo de los pasos recursivos.
26. EjemploRecursividad CREATE TABLE empleado( id_Empleado INTEGER PRIMARY KEY, -- ID empleado Nombre TEXT, Apellido TEXT, Cargo TEXT, parent_EmpleadoINTEGER REFERENCES empleado, -- upper department ID DeptIDINTEGER,-- department name ); FirstNamenvarchar(30) NOT NULL, LastNamenvarchar(40) NOT NULL, Title nvarchar(50) NOT NULL, DeptIDsmallint NOT NULL, ManagerIDint NULL,.
27. EjemploRecursividad La jerarquía en colombia La jerarquía es algo así: El mundo contiene Colombia, que contienen... departamentos, que contienen... Municipios, que contienen... Barrios CREATE TABLE lugares ( id INTEGER PRIMARY KEY, parent_lugares INTEGER REFERENCES lugares, nombre TEXT );
28. EjemploRecursividad INSERT INTO lugares VALUES (0, NULL, ‘COLOMBIA'); INSERT INTO lugares VALUES (1, 0, 'ANTIOQUIA'); INSERT INTO lugares VALUES (2, 1, ‘AMAGA'); INSERT INTO lugares VALUES (3, 1, ‘MEDELLIN'); INSERT INTO lugares VALUES (4, 1, ‘ENVIGADO'); INSERT INTO lugares VALUES (5, 0, ‘BOGOTA'); INSERT INTO lugares VALUES (6, 5, ‘SOACHA'); INSERT INTO lugares VALUES (7, 3, ‘LAURELES'); Extraer todos los municipios de antioquia CREATE VIEW vMunicipiosAntioquia AS WITH RECURSIVE MunicipiosAntioquia AS ( SELECT * FROM lugares WHERE nombre = ‘ANTIOQUIA' UNION ALL SELECT d.* FROM lugares AS d, MunicipiosAntioquia AS sd WHERE d.parent_lugares = sd.id ) SELECT * FROM MunicipiosAntioquia; SELECT * FROM vlugaresORDER BY nombre;