Mostrando las entradas con la etiqueta postgresql. Mostrar todas las entradas
Mostrando las entradas con la etiqueta postgresql. Mostrar todas las entradas
viernes, agosto 03, 2012
Actualizar el valor de un campo, a partir del campo de otra tabla
UPDATE tabla1
SET tabla1.campo = tabla2.campo
FROM tabla2 WHERE tabla1.campo_id = tabla2.campo_id
La documentación oficial, siempre tan amable ;-)
http://www.postgresql.org/docs/8.3/static/sql-update.html
lunes, noviembre 01, 2010
Actualizar desde postgresql-8.1 a postgresql-8.3
Cosas de cosas... en la oficina tenemos un servidor que estaba en etch y bueno, luego de revisar que tanto tenía funcionando, tomar la decisión... lo más importante es el servidor Postgresql así que a actualizarlo!
De dónde saqué cómo hacerlo? pues, el aptitude muy completo me lanzó:

Con lo que le pregunté a Google y me devolvió, entre otras cosas[0]:
Primeramente debe instalar las últimas versiones de los paquetes (postgresql-8.3 y postgresql-client-8.3)
# aptitude install postgresql-8.3
Tenga en cuenta que la instalación de postgresql-8.3 creará automáticamente un clúster por omisión 8.3/main. Tiene que borrar el clúster 8.3 existente
# pg_dropcluster --stop 8.3 main
Luego, necesita actualizar el clúster 8.1/main a 8.3
# pg_upgradecluster 8.1 main
Posteriormente, elimina el clúster 8.1/main después de actualizar los clústers que tenga.
# pg_dropcluster 8.1 main
Por último, elimina los paquetes postgresql-8.1 y postgresql-client-8.1
# aptitude purge postgresql-8.1 postgresql-client-8.1
Gracias gente!
[0]http://glosario-x.blogspot.com/2009/11/actualizar-desde-postgresql-81.html
De dónde saqué cómo hacerlo? pues, el aptitude muy completo me lanzó:

Con lo que le pregunté a Google y me devolvió, entre otras cosas[0]:
Primeramente debe instalar las últimas versiones de los paquetes (postgresql-8.3 y postgresql-client-8.3)
# aptitude install postgresql-8.3
Tenga en cuenta que la instalación de postgresql-8.3 creará automáticamente un clúster por omisión 8.3/main. Tiene que borrar el clúster 8.3 existente
# pg_dropcluster --stop 8.3 main
Luego, necesita actualizar el clúster 8.1/main a 8.3
# pg_upgradecluster 8.1 main
Posteriormente, elimina el clúster 8.1/main después de actualizar los clústers que tenga.
# pg_dropcluster 8.1 main
Por último, elimina los paquetes postgresql-8.1 y postgresql-client-8.1
# aptitude purge postgresql-8.1 postgresql-client-8.1
Gracias gente!
[0]http://glosario-x.blogspot.com/2009/11/actualizar-desde-postgresql-81.html
viernes, julio 02, 2010
crosstab para consultas de tablas cruzadas
Esta no le he necesitado aún, pero me puse a revisarla para ver qué tal funciona y ver sí luego se le puede sacar provecho.
La documentación oficial[0] habla por sí sola, sobre lo que se quiere. hacerla funcionar no es complicado, pero tiene su engaño.
Lo que se tiene:
Lo que se quiere:
SELECT est_002::text, agri02_002::text, count(*)::numeric
FROM consulta_agri04 GROUP BY agri02_002, est_002 ORDER BY est_002, agri02_002;
est_002 | agri02_002 | count
----------+------------------------+-------
Mérida | CACAO | 83
Mérida | CAFE | 169
Mérida | CAÑA | 68
Mérida | CEREALES Y LEGUMINOSAS | 421
Mérida | FRUTALES | 746
Mérida | HORTALIZAS | 1277
Mérida | ORNAMENTALES | 41
Mérida | RAICES Y TUBERCULOS | 484
Táchira | CACAO | 12
Táchira | CAFE | 29
Táchira | CAÑA | 1
Táchira | CEREALES Y LEGUMINOSAS | 310
Táchira | FRUTALES | 209
Táchira | HORTALIZAS | 451
Táchira | RAICES Y TUBERCULOS | 222
Trujillo | CAFE | 26
Trujillo | CAÑA | 20
Trujillo | CEREALES Y LEGUMINOSAS | 97
Trujillo | FRUTALES | 174
Trujillo | HORTALIZAS | 361
Trujillo | RAICES Y TUBERCULOS | 127
con ésta consulta verifico cuántas columnas tendré:
est_002 |CACAO |CAFE |CAÑA
Mérida |83 |169 |68
Táchira |12 |29 |1
Trujillo | |26 |20
SELECT DISTINCT agri02_002 FROM consulta_agri04 ORDER BY agri02_002, ya que luego voy a necesitar esa información para definir la salida.
SELECT est_002, agri02_002, count(*)::numeric
Acá defindo, como primer campo, la columna pivote, el segunda campo, serán las columnas, y el tercer campo, los valores a rellenar en la tabla.
Acá defino as columnas de salida en la tabla:
AS lista(estado text, renglon1 text, renglon2 text, renglon3 text, renglon4 text, renglon5 text, renglon6 text, renglon7 text, renglon8 text)
primer campo, el pivot, los demás serán tantos como me devuelva la consulta con el DISTINCT.
De esta amnera, la consulta completa queda:
SELECT * FROM public.crosstab
(
'SELECT est_002, agri02_002, count(*)::numeric FROM consulta_agri04 GROUP BY agri02_002, est_002 ORDER BY est_002, agri02_002',
'SELECT DISTINCT agri02_002 FROM consulta_agri04 ORDER BY agri02_002'
) AS lista(estado text, renglon1 text, renglon2 text, renglon3 text, renglon4 text, renglon5 text, renglon6 text, renglon7 text, renglon8 text);
y devuelve:
estado | renglon1 | renglon2 | renglon3 | renglon4 | renglon5 | renglon6 | renglon7 | renglon8
----------+----------+----------+----------+----------+----------+----------+----------+----------
Mérida | 83 | 169 | 68 | 421 | 746 | 1277 | 41 | 484
Táchira | 12 | 29 | 1 | 310 | 209 | 451 | | 222
Trujillo | | 26 | 20 | 97 | 174 | 361 | | 127
(3 filas)
[0]http://www.postgresql.org/docs/8.3/interactive/tablefunc.html
viernes, octubre 16, 2009
Insertar varios registros sí no existe
Bueno... es posible que esta idea este extraña, pero así es como lo tenemos.
Nuestro código político está más o menos así:
\d region
Tabla «public.region»
Columna | Tipo | Modificadores
---------+-----------------------+---------------
reg_001 | character varying(2) | not null
reg_002 | character varying(35) | not null
Índices:
«region_pkey» PRIMARY KEY, btree (reg_001)
\d estado
Tabla «public.estado»
Columna | Tipo | Modificadores
---------+-----------------------+---------------
reg_001 | character varying(2) | not null
est_001 | character varying(2) | not null
est_002 | character varying(35) | not null
Índices:
«estado_pkey» PRIMARY KEY, btree (est_001)
«estado_ukey» UNIQUE, btree (reg_001, est_001)
Restricciones de llave foránea:
«estado_fkey» FOREIGN KEY (reg_001) REFERENCES region(reg_001) ON UPDATE CASCADE ON DELETE RESTRICT
\d municipio
Tabla «public.municipio»
Columna | Tipo | Modificadores
---------+-----------------------+---------------
reg_001 | character varying(2) | not null
est_001 | character varying(2) | not null
mun_001 | character varying(2) | not null
mun_002 | character varying(35) | not null
Índices:
«municipio_ukey» UNIQUE, btree (reg_001, est_001, mun_001)
Restricciones de llave foránea:
«municipio_fkey» FOREIGN KEY (est_001) REFERENCES estado(est_001) ON UPDATE CASCADE ON DELETE RESTRICT
\d parroquia
Tabla «public.parroquia»
Columna | Tipo | Modificadores
---------+-----------------------+---------------
reg_001 | character varying(2) | not null
est_001 | character varying(2) | not null
mun_001 | character varying(2) | not null
par_001 | character varying(2) | not null
par_002 | character varying(35) | not null
Índices:
«parroquia_ukey» UNIQUE, btree (reg_001, est_001, mun_001, par_001)
\d centro_poblado
Tabla «public.centro_poblado»
Columna | Tipo | Modificadores
---------+-----------------------+-------------------------------------------------------------
reg_001 | character varying(2) | not null
est_001 | character varying(2) | not null
mun_001 | character varying(2) | not null
par_001 | character varying(2) | not null
cpo_001 | character varying(3) | not null
cpo_002 | character varying(50) | not null
id | integer | not null default nextval('centro_poblado_id_seq'::regclass)
Y para el registro de datos, existe la posibilidad de que el detalle no llegue sino hasta la parroquia, por ejemplo, por lo que habría que seleccionar un Centro Poblado del tipo 'No Aplica', pero, un 'No Aplica' por cada parroquia existente, son como... muchos! al menos para insertarlos manualmente y uno por uno, así que decidí estudiar un poquito y ver cómo lo hacía vía plpgsql, he aquí el resultado:
CREATE OR REPLACE FUNCTION leer(out a varchar) RETURNS SETOF varchar LANGUAGE plpgsql AS $$
DECLARE
r record;
p varchar := '01';
BEGIN
-- recorrer la tabla tantas veces como parroquias existan
FOR r IN SELECT par_001 FROM centro_poblado GROUP BY par_001 LOOP
-- recorrer la tabla tantas veces como parroquias existan y centros poblados con código igual a '000' existan
FOR r IN SELECT COUNT(*) AS cuenta FROM centro_poblado WHERE par_001 = p AND cpo_001 = '000' LOOP
-- inicializo 'a'
a = r.cuenta;
-- sí a leido como entero, es igual a cero, es porque no existe un centro poblado con código igual a '000'
IF (a::int = 0) THEN
-- insertar el registro
INSERT INTO centro_poblado (reg_001, est_001, mun_001, par_001, cpo_001, cpo_002) VALUES ('06', '14', '01', p, '000', 'No Aplica');
END IF;
-- incremento para pasar a la siguiente parroquia
p := '0'|| p::int + 1;
-- paso a la siguiente parroquia
RETURN next;
END LOOP;
END LOOP;
END;
$$;
Seguro, alguien consigue otra forma de hacerlo mejor, pero al menos sirve... También es posible ir lanzando algunas notificaciones para saber por dónde y cómo va, tal como RAISE NOTICE ' Parroquia tiene %',p; pero es a gusto de cada quien...
Listo! yo mismo lo hice de otra forma:
--
-- funcion para insertar no aplica para parroquias
--
CREATE OR REPLACE FUNCTION insertar_no_aplica_centro_poblado(e varchar, out a varchar) RETURNS SETOF varchar LANGUAGE plpgsql AS $$
DECLARE
l record;
r record;
p record;
BEGIN
create temp table audit_tmp (usuario integer, ip inet, ocurrencia timestamp default now());
-- listar todas las parroquias y municipios agrupadas
FOR l IN SELECT mun_001 FROM centro_poblado WHERE est_001 = e GROUP BY mun_001 ORDER BY mun_001 LOOP
FOR r IN SELECT par_001 FROM centro_poblado WHERE est_001 = e AND mun_001 = l.mun_001 GROUP BY par_001 ORDER BY par_001 LOOP
FOR p IN SELECT cpo_001 FROM centro_poblado WHERE est_001 = e AND mun_001 = l.mun_001 AND par_001 = r.par_001 GROUP BY cpo_001 ORDER BY cpo_001 LOOP
IF (p.cpo_001 = '000') THEN
RAISE NOTICE 'No Aplica para : %', r.par_001;
EXIT;
ELSE
insert into audit_tmp values (1, '127.0.0.1');
RAISE NOTICE 'Insertando para la parroquia : %', r.par_001;
INSERT INTO centro_poblado (reg_001, est_001, mun_001, par_001, cpo_001, cpo_002) VALUES ('06', e, l.mun_001, r.par_001, '000', 'No Aplica');
EXIT;
END IF;
END LOOP;
END LOOP;
END LOOP;
END;
$$;
Ya ven, es otra forma...
Nuestro código político está más o menos así:
\d region
Tabla «public.region»
Columna | Tipo | Modificadores
---------+-----------------------+---------------
reg_001 | character varying(2) | not null
reg_002 | character varying(35) | not null
Índices:
«region_pkey» PRIMARY KEY, btree (reg_001)
\d estado
Tabla «public.estado»
Columna | Tipo | Modificadores
---------+-----------------------+---------------
reg_001 | character varying(2) | not null
est_001 | character varying(2) | not null
est_002 | character varying(35) | not null
Índices:
«estado_pkey» PRIMARY KEY, btree (est_001)
«estado_ukey» UNIQUE, btree (reg_001, est_001)
Restricciones de llave foránea:
«estado_fkey» FOREIGN KEY (reg_001) REFERENCES region(reg_001) ON UPDATE CASCADE ON DELETE RESTRICT
\d municipio
Tabla «public.municipio»
Columna | Tipo | Modificadores
---------+-----------------------+---------------
reg_001 | character varying(2) | not null
est_001 | character varying(2) | not null
mun_001 | character varying(2) | not null
mun_002 | character varying(35) | not null
Índices:
«municipio_ukey» UNIQUE, btree (reg_001, est_001, mun_001)
Restricciones de llave foránea:
«municipio_fkey» FOREIGN KEY (est_001) REFERENCES estado(est_001) ON UPDATE CASCADE ON DELETE RESTRICT
\d parroquia
Tabla «public.parroquia»
Columna | Tipo | Modificadores
---------+-----------------------+---------------
reg_001 | character varying(2) | not null
est_001 | character varying(2) | not null
mun_001 | character varying(2) | not null
par_001 | character varying(2) | not null
par_002 | character varying(35) | not null
Índices:
«parroquia_ukey» UNIQUE, btree (reg_001, est_001, mun_001, par_001)
\d centro_poblado
Tabla «public.centro_poblado»
Columna | Tipo | Modificadores
---------+-----------------------+-------------------------------------------------------------
reg_001 | character varying(2) | not null
est_001 | character varying(2) | not null
mun_001 | character varying(2) | not null
par_001 | character varying(2) | not null
cpo_001 | character varying(3) | not null
cpo_002 | character varying(50) | not null
id | integer | not null default nextval('centro_poblado_id_seq'::regclass)
Y para el registro de datos, existe la posibilidad de que el detalle no llegue sino hasta la parroquia, por ejemplo, por lo que habría que seleccionar un Centro Poblado del tipo 'No Aplica', pero, un 'No Aplica' por cada parroquia existente, son como... muchos! al menos para insertarlos manualmente y uno por uno, así que decidí estudiar un poquito y ver cómo lo hacía vía plpgsql, he aquí el resultado:
CREATE OR REPLACE FUNCTION leer(out a varchar) RETURNS SETOF varchar LANGUAGE plpgsql AS $$
DECLARE
r record;
p varchar := '01';
BEGIN
-- recorrer la tabla tantas veces como parroquias existan
FOR r IN SELECT par_001 FROM centro_poblado GROUP BY par_001 LOOP
-- recorrer la tabla tantas veces como parroquias existan y centros poblados con código igual a '000' existan
FOR r IN SELECT COUNT(*) AS cuenta FROM centro_poblado WHERE par_001 = p AND cpo_001 = '000' LOOP
-- inicializo 'a'
a = r.cuenta;
-- sí a leido como entero, es igual a cero, es porque no existe un centro poblado con código igual a '000'
IF (a::int = 0) THEN
-- insertar el registro
INSERT INTO centro_poblado (reg_001, est_001, mun_001, par_001, cpo_001, cpo_002) VALUES ('06', '14', '01', p, '000', 'No Aplica');
END IF;
-- incremento para pasar a la siguiente parroquia
p := '0'|| p::int + 1;
-- paso a la siguiente parroquia
RETURN next;
END LOOP;
END LOOP;
END;
$$;
Seguro, alguien consigue otra forma de hacerlo mejor, pero al menos sirve... También es posible ir lanzando algunas notificaciones para saber por dónde y cómo va, tal como RAISE NOTICE ' Parroquia tiene %',p; pero es a gusto de cada quien...
Listo! yo mismo lo hice de otra forma:
--
-- funcion para insertar no aplica para parroquias
--
CREATE OR REPLACE FUNCTION insertar_no_aplica_centro_poblado(e varchar, out a varchar) RETURNS SETOF varchar LANGUAGE plpgsql AS $$
DECLARE
l record;
r record;
p record;
BEGIN
create temp table audit_tmp (usuario integer, ip inet, ocurrencia timestamp default now());
-- listar todas las parroquias y municipios agrupadas
FOR l IN SELECT mun_001 FROM centro_poblado WHERE est_001 = e GROUP BY mun_001 ORDER BY mun_001 LOOP
FOR r IN SELECT par_001 FROM centro_poblado WHERE est_001 = e AND mun_001 = l.mun_001 GROUP BY par_001 ORDER BY par_001 LOOP
FOR p IN SELECT cpo_001 FROM centro_poblado WHERE est_001 = e AND mun_001 = l.mun_001 AND par_001 = r.par_001 GROUP BY cpo_001 ORDER BY cpo_001 LOOP
IF (p.cpo_001 = '000') THEN
RAISE NOTICE 'No Aplica para : %', r.par_001;
EXIT;
ELSE
insert into audit_tmp values (1, '127.0.0.1');
RAISE NOTICE 'Insertando para la parroquia : %', r.par_001;
INSERT INTO centro_poblado (reg_001, est_001, mun_001, par_001, cpo_001, cpo_002) VALUES ('06', e, l.mun_001, r.par_001, '000', 'No Aplica');
EXIT;
END IF;
END LOOP;
END LOOP;
END LOOP;
END;
$$;
Ya ven, es otra forma...
miércoles, octubre 14, 2009
Eliminando registros duplicados, desde SQL
Zas! Por un error de programación, de repente aparecieron un montón de registros duplicados, y cuando digo un montón, son un montón!!!
y ahora? pues, a pensar con cabeza fría... cuáles son las opciones?
La tabla:
\d centro_poblado
Tabla «public.centro_poblado»
Columna | Tipo | Modificadores
---------+-----------------------+-------------------------------------------------------------
reg_001 | character varying(2) | not null
est_001 | character varying(2) | not null
mun_001 | character varying(2) | not null
par_001 | character varying(2) | not null
cpo_001 | character varying(3) | not null
cpo_002 | character varying(50) | not null
id | integer | not null default nextval('centro_poblado_id_seq'::regclass)
Como la tabla tiene un id de tipo serial, eso ayuda.
Verficiar cuáles son los registros repetidos:
SELECT COUNT(*), reg_001, est_001, mun_001, par_001 FROM centro_poblado GROUP BY cpo_002, reg_001, est_001, mun_001, par_001 ORDER BY reg_001, est_001, mun_001, par_001;
Verificar los registros repetidos:
SELECT * FROM centro_poblado WHERE reg_001 = '06' AND est_001 = '14' AND mun_001 = '01' AND par_001 = '05';
Contar los registros repetidos, en detalle:
SELECT count(*) FROM centro_poblado WHERE reg_001 = '06' AND est_001 = '14' AND mun_001 = '01' AND par_001 = '05' AND cpo_001 = '018';
Verificar el id min y máx del detalle de los registros repetidos:
SELECT min(id), max(id) FROM centro_poblado WHERE reg_001 = '06' AND est_001 = '14' AND mun_001 = '01' AND par_001 = '05' AND cpo_001 = '018';
La verdad es que luego descubrí que se podía hacer así:
SELECT * FROM centro_poblado AS a
WHERE EXISTS(
SELECT cpo_001, COUNT(cpo_001)
FROM centro_poblado
WHERE a.cpo_001= cpo_001
GROUP BY cpo_001
HAVING COUNT(cpo_001)>1)
ORDER BY cpo_001
Eliminar los registros repetidos, menos 1:
DELETE FROM centro_poblado WHERE reg_001 = '06' AND est_001 = '14' AND mun_001 = '01' AND par_001 = '05' AND cpo_001 = '018' AND id > 1;
Esto también se puede resolver con una tabla temporal, y metiendo la consulta en una función y todo eso, pero aun no lo he probado...
Finalmente, y gracias a la lista de usuarios de postgres y su histórico, me encontré [1]:
DELETE FROM centro_poblado WHERE ctid NOT IN (SELECT min(ctid) FROM centro_poblado GROUP BY cpo_002, reg_001, est_001, mun_001, par_001);
[1]http://archives.postgresql.org/pgsql-es-ayuda/2007-01/msg00353.php
y ahora? pues, a pensar con cabeza fría... cuáles son las opciones?
- borrar todos los duplicados a través del mismo sistema
- pensar en un script en cualquier lenguaje que haga todo de un sólo halón
La tabla:
\d centro_poblado
Tabla «public.centro_poblado»
Columna | Tipo | Modificadores
---------+-----------------------+-------------------------------------------------------------
reg_001 | character varying(2) | not null
est_001 | character varying(2) | not null
mun_001 | character varying(2) | not null
par_001 | character varying(2) | not null
cpo_001 | character varying(3) | not null
cpo_002 | character varying(50) | not null
id | integer | not null default nextval('centro_poblado_id_seq'::regclass)
Como la tabla tiene un id de tipo serial, eso ayuda.
Verficiar cuáles son los registros repetidos:
SELECT COUNT(*), reg_001, est_001, mun_001, par_001 FROM centro_poblado GROUP BY cpo_002, reg_001, est_001, mun_001, par_001 ORDER BY reg_001, est_001, mun_001, par_001;
Verificar los registros repetidos:
SELECT * FROM centro_poblado WHERE reg_001 = '06' AND est_001 = '14' AND mun_001 = '01' AND par_001 = '05';
Contar los registros repetidos, en detalle:
SELECT count(*) FROM centro_poblado WHERE reg_001 = '06' AND est_001 = '14' AND mun_001 = '01' AND par_001 = '05' AND cpo_001 = '018';
Verificar el id min y máx del detalle de los registros repetidos:
SELECT min(id), max(id) FROM centro_poblado WHERE reg_001 = '06' AND est_001 = '14' AND mun_001 = '01' AND par_001 = '05' AND cpo_001 = '018';
La verdad es que luego descubrí que se podía hacer así:
SELECT * FROM centro_poblado AS a
WHERE EXISTS(
SELECT cpo_001, COUNT(cpo_001)
FROM centro_poblado
WHERE a.cpo_001= cpo_001
GROUP BY cpo_001
HAVING COUNT(cpo_001)>1)
ORDER BY cpo_001
Eliminar los registros repetidos, menos 1:
DELETE FROM centro_poblado WHERE reg_001 = '06' AND est_001 = '14' AND mun_001 = '01' AND par_001 = '05' AND cpo_001 = '018' AND id > 1;
Esto también se puede resolver con una tabla temporal, y metiendo la consulta en una función y todo eso, pero aun no lo he probado...
Finalmente, y gracias a la lista de usuarios de postgres y su histórico, me encontré [1]:
DELETE FROM centro_poblado WHERE ctid NOT IN (SELECT min(ctid) FROM centro_poblado GROUP BY cpo_002, reg_001, est_001, mun_001, par_001);
[1]http://archives.postgresql.org/pgsql-es-ayuda/2007-01/msg00353.php
jueves, septiembre 10, 2009
Jugando con los privilegios de accesos a Bases de Datos PostgreSQL
El escenario varias BDs con
CREATE USER user1 IN ROLE dba LOGIN ENCRYPTED PASSWORD '123456';
CREATE USER user2 LOGIN ENCRYPTED PASSWORD '123456';
CREATE USER admin1 IN ROLE dba LOGIN ENCRYPTED PASSWORD '123456';
ALTER GROUP dba ADD USER user2;
CREATE DATABASE db1 OWNER TO user1;
CREATE TABLE tabla1 (id serial, nombre character varying(10));
quitar todos los privilegios sobre la tabla: tabla1 para los usuarios user1 y user2
REVOKE ALL ON tabla1 FROM user1;
REVOKE ALL ON tabla1 FROM user2;
otorgar privilegios de consulta sobre la tabla talba1 para el usuario user1
GRANT SELECT ON tabla1 TO user1;
otorgar privilegios para insertar datos sobre la tabla tabla1 para el usuario user2 (requiere privilegios sobre la secuencia de la tabla)
GRANT INSERT ON tabla1 TO user2;
GTANT UPDATE ON tabla1_id_seq TO user2;
Para poder actualizar/eliminar un registro, se requiere tener privilegios de selección (requiere privilegios sobre la secuencias de la tabla)
GRANT UPDATE ON tabla1 TO user2;
GRANT DELETE ON tabla1 TO user2;
- Un usuario diferente para cada BD con privilegios para insertar/modificar
- Un usuario capaz de leer todas las BDs
- Un usuario admin de todas las BDs
- Un Rol, que agrupe todos los usuarios y no tenga ningún privilegio
CREATE USER user1 IN ROLE dba LOGIN ENCRYPTED PASSWORD '123456';
CREATE USER user2 LOGIN ENCRYPTED PASSWORD '123456';
CREATE USER admin1 IN ROLE dba LOGIN ENCRYPTED PASSWORD '123456';
ALTER GROUP dba ADD USER user2;
CREATE DATABASE db1 OWNER TO user1;
CREATE TABLE tabla1 (id serial, nombre character varying(10));
quitar todos los privilegios sobre la tabla: tabla1 para los usuarios user1 y user2
REVOKE ALL ON tabla1 FROM user1;
REVOKE ALL ON tabla1 FROM user2;
otorgar privilegios de consulta sobre la tabla talba1 para el usuario user1
GRANT SELECT ON tabla1 TO user1;
otorgar privilegios para insertar datos sobre la tabla tabla1 para el usuario user2 (requiere privilegios sobre la secuencia de la tabla)
GRANT INSERT ON tabla1 TO user2;
GTANT UPDATE ON tabla1_id_seq TO user2;
Para poder actualizar/eliminar un registro, se requiere tener privilegios de selección (requiere privilegios sobre la secuencias de la tabla)
GRANT UPDATE ON tabla1 TO user2;
GRANT DELETE ON tabla1 TO user2;
lunes, septiembre 07, 2009
Migrar BD de Mysql a PostGreSQL (parte 3)
Conversión de tipos
Resulta, que cuando vemos lo que nos dice el espejo que hemos creado de mysql sobre postgresql con dbi-link, los campos son creados como tipo text, con un comentario en la vista que dice qué tipo de dato aproximado debería haber ahí...
Esto es así, porque los tipos de datos entre diferentes gestores normalmente no son iguales, es decir, todos aplican los estándares, pero además, cada uno le da su toque, de manera que es posible que tenga más o menos, y que de paso, se llamen diferentes... o más bien, se le aplique aliases...
El tema es que cuando trato de leer para insertar en otra tabla, en un proceso de migración, debo tener muy presente los tipos de datos, ocasionalmente, podría recibir un mensaje del tipo:
Error de SQL:
ERROR: la columna «id» es de tipo integer pero la expresión es de tipo text
LINE 1: INSERT INTO cultu03 (id, cultu02_001, cultu03_002) (SELECT c...
^
HINT: Necesitará reescribir la expresión o aplicarle una conversión de tipo.
En la declaración:
INSERT INTO cultu03 (id, cultu02_001, cultu03_002) (SELECT codcat, codedi, catedi FROM sir_cultura_catedi)
Con lo cual, cualquiera se asusta a la primera... y no hace falta... con leer el mensaje y fijarme en la frase "aplicarle una conversión de tipos" será más que suficiente para tener algo de calma nuevamente...
¿qué es una conversión de tipos? digamos que tenemos un campo de un tipo de dato (p.e. text), pero queremos "interpretar" su contenido como sí fuera otro tipo de dato (p.e. int)... a eso se le conoce como conversión de tipos
INSERT INTO cultu03 (id, cultu02_001, cultu03_002) (SELECT codcat::int, codedi::int, catedi FROM sir_cultura_catedi)
o
INSERT INTO cultu03 (id, cultu02_001, cultu03_002) (SELECT cast(codcat as int), cast (codedi as int), catedi FROM sir_cultura_catedi)
y listo!!!
Una consultica que tiene algo más:
INSERT INTO cultu04 (reg_001, est_001, mun_001, par_001, cpo_001, cultu04_001, cultu04_002, cultu04_003, cultu04_004, cultu04_005, cultu04_006, cultu04_007, cultu02_001, cultu03_001)
(SELECT codreg, codest, codmun, codpar, codcen, anno, nombre, direccion, telefono, correo, web,
CASE WHEN area <> '' THEN
CAST(area AS numeric)
ELSE
0
END,
codedi::int, codcat::int FROM sir_cultura_edif_det)
Creo que no hace falta explicar la estructura de las tablas, sólo entender que leo en una tabla e inserto lo que estoy leyendo, con algunas particularidades sencillas....
Una más
INSERT INTO acu03 (reg_001, est_001, mun_001, par_001, cpo_001, acu03_001, acu01_001, acu03_003, acu03_004, acu03_005, acu02_001, acu03_006, acu03_007, acu03_008)
(SELECT codreg, codest, codmun, CASE WHEN codpar = 'NA' THEN '00' ELSE codpar END, CASE WHEN codcen = 'NA' THEN '000' ELSE codcen END, EXTRACT(YEAR FROM agno)::int, tipo::int, nb_acueducto, fuentes, caudal, tratamiento::int, suscriptores, poblacion, produccion FROM servicios_acueducto)
Resulta, que cuando vemos lo que nos dice el espejo que hemos creado de mysql sobre postgresql con dbi-link, los campos son creados como tipo text, con un comentario en la vista que dice qué tipo de dato aproximado debería haber ahí...
Esto es así, porque los tipos de datos entre diferentes gestores normalmente no son iguales, es decir, todos aplican los estándares, pero además, cada uno le da su toque, de manera que es posible que tenga más o menos, y que de paso, se llamen diferentes... o más bien, se le aplique aliases...
El tema es que cuando trato de leer para insertar en otra tabla, en un proceso de migración, debo tener muy presente los tipos de datos, ocasionalmente, podría recibir un mensaje del tipo:
Error de SQL:
ERROR: la columna «id» es de tipo integer pero la expresión es de tipo text
LINE 1: INSERT INTO cultu03 (id, cultu02_001, cultu03_002) (SELECT c...
^
HINT: Necesitará reescribir la expresión o aplicarle una conversión de tipo.
En la declaración:
INSERT INTO cultu03 (id, cultu02_001, cultu03_002) (SELECT codcat, codedi, catedi FROM sir_cultura_catedi)
Con lo cual, cualquiera se asusta a la primera... y no hace falta... con leer el mensaje y fijarme en la frase "aplicarle una conversión de tipos" será más que suficiente para tener algo de calma nuevamente...
¿qué es una conversión de tipos? digamos que tenemos un campo de un tipo de dato (p.e. text), pero queremos "interpretar" su contenido como sí fuera otro tipo de dato (p.e. int)... a eso se le conoce como conversión de tipos
INSERT INTO cultu03 (id, cultu02_001, cultu03_002) (SELECT codcat::int, codedi::int, catedi FROM sir_cultura_catedi)
o
INSERT INTO cultu03 (id, cultu02_001, cultu03_002) (SELECT cast(codcat as int), cast (codedi as int), catedi FROM sir_cultura_catedi)
y listo!!!
Una consultica que tiene algo más:
INSERT INTO cultu04 (reg_001, est_001, mun_001, par_001, cpo_001, cultu04_001, cultu04_002, cultu04_003, cultu04_004, cultu04_005, cultu04_006, cultu04_007, cultu02_001, cultu03_001)
(SELECT codreg, codest, codmun, codpar, codcen, anno, nombre, direccion, telefono, correo, web,
CASE WHEN area <> '' THEN
CAST(area AS numeric)
ELSE
0
END,
codedi::int, codcat::int FROM sir_cultura_edif_det)
Creo que no hace falta explicar la estructura de las tablas, sólo entender que leo en una tabla e inserto lo que estoy leyendo, con algunas particularidades sencillas....
Una más
INSERT INTO acu03 (reg_001, est_001, mun_001, par_001, cpo_001, acu03_001, acu01_001, acu03_003, acu03_004, acu03_005, acu02_001, acu03_006, acu03_007, acu03_008)
(SELECT codreg, codest, codmun, CASE WHEN codpar = 'NA' THEN '00' ELSE codpar END, CASE WHEN codcen = 'NA' THEN '000' ELSE codcen END, EXTRACT(YEAR FROM agno)::int, tipo::int, nb_acueducto, fuentes, caudal, tratamiento::int, suscriptores, poblacion, produccion FROM servicios_acueducto)
jueves, septiembre 03, 2009
Migrar BD de Mysql a PostGreSQL (parte 1 Versión 2)
Diciendo que soy usuario Debian, no digo mucho, pero les cuento que habiendo reinstalado mi equipo de la oficina, en el momento en que estoy devolviendo las cosas a la normalidad, se me ocurrió hacer un aptitude search dbi_link y vaya sorpresa, existe!
Pues, se me ocurrió instalarlo, y probar luego, cómo sería el procedimiento para que funcione... de manera que está sería la versión 2 de Migrar BD de Mysql a PostGreSQL (parte 1)
instalarlo:
Pues, se me ocurrió instalarlo, y probar luego, cómo sería el procedimiento para que funcione... de manera que está sería la versión 2 de Migrar BD de Mysql a PostGreSQL (parte 1)
instalarlo:
- aptitude install dbi_link
- find /usr/ -iname dbi_link
- createdb migracion
- createlang plperlu -d migracion
- psql -d migracion -f /path/completo/dbi_link.sql
- psql -d migracion -f /path/completo/mysql.sql
jueves, junio 18, 2009
Campos calculados desde PostGreSQL (I)
Vale... He aquí la necesidad: Necesito una consulta que entre sus resultados, hay una columna que es calculada de la forma:
r = ((a / b) * 1000)
donde b debe ser mayor que cero, obviamente
En SQL (para postgresql) va algo así:
SELECT
CASE WHEN SUM(b) > 0 THEN
round(((SUM(a) / SUM(b)) * 1000),2)
ELSE 0
END
AS rendimiento
FROM miTabla
r = ((a / b) * 1000)
donde b debe ser mayor que cero, obviamente
En SQL (para postgresql) va algo así:
SELECT
CASE WHEN SUM(b) > 0 THEN
round(((SUM(a) / SUM(b)) * 1000),2)
ELSE 0
END
AS rendimiento
FROM miTabla
jueves, mayo 28, 2009
Migrar Datos de una tabla a otra, con ligeras diferencias
Eu!
Siguiendo el tema de las migraciones, recientemente me tocó que pasar unos datos, de una tabla a otra, que contenían ligeras diferencias, par de campos de tipo real y double precsion pasaban a ahora a numeric, más algunos campos menos que tenía la tabla destino.
Sencillo, me hice una query que insertara según lo que leyera:
INSERT INTO agri04 (id, reg_001, est_001, mun_001, par_001, cpo_001, agri04_001, agri03_001, agri04_003, agri04_004, agri04_005, agri04_006, agri04_007, agri04_008) (SELECT id, codreg, codest, codmun, codpar, codcen, EXTRACT(YEAR FROM fecha), id_det, fund, reno, ssem, scos, prod, valor FROM agricolav);
El tema de los tipos de datos, en vista de la fortuna de que ambos eran números con decimales, sólo tuve que seleccionar el tamaño de los numerics, que al final quedaron de (12,2) algunos, y otros de (10,2).
Otro caso:
INSERT INTO municipios
(gid, id, municipio, estado, the_geom)
(SELECT gid, id, municipio,
CASE
WHEN estado = 'Mrida' THEN 14
WHEN estado = 'Tchira' THEN 20
WHEN estado = 'Trujillo' THEN 21
END,
the_geom FROM municipios_region);
Siguiendo el tema de las migraciones, recientemente me tocó que pasar unos datos, de una tabla a otra, que contenían ligeras diferencias, par de campos de tipo real y double precsion pasaban a ahora a numeric, más algunos campos menos que tenía la tabla destino.
Sencillo, me hice una query que insertara según lo que leyera:
INSERT INTO agri04 (id, reg_001, est_001, mun_001, par_001, cpo_001, agri04_001, agri03_001, agri04_003, agri04_004, agri04_005, agri04_006, agri04_007, agri04_008) (SELECT id, codreg, codest, codmun, codpar, codcen, EXTRACT(YEAR FROM fecha), id_det, fund, reno, ssem, scos, prod, valor FROM agricolav);
El tema de los tipos de datos, en vista de la fortuna de que ambos eran números con decimales, sólo tuve que seleccionar el tamaño de los numerics, que al final quedaron de (12,2) algunos, y otros de (10,2).
Otro caso:
INSERT INTO municipios
(gid, id, municipio, estado, the_geom)
(SELECT gid, id, municipio,
CASE
WHEN estado = 'Mrida' THEN 14
WHEN estado = 'Tchira' THEN 20
WHEN estado = 'Trujillo' THEN 21
END,
the_geom FROM municipios_region);
viernes, abril 17, 2009
Conectar Postgis a Postgresql
Hola... como siempre, esto es para yo tener dónde buscar cuando se me olviden las cosas... sí de paso le sirve a alguien, pues... quizás publique mi número de cuenta por sí quieren donar algo, jajajaja, es broma...
Para que una BD (en Postgresql), pueda servir a efectos de un SIG, debemos trabajar con Postgis, de manera que, creamos la BD:
CREATE DATABASE mapas;
CREATE LANGUAGE plpgsql;
Obviamente debemos tener instalado postgis, que en Debian es suficiente con aptitude install postgresql-tuversion-postgis (por si dudas, aptitude search postgis), con lo que en /usr/share/postgresql-miversion-postgis/ tengo los .sql necesarios.
psql -d mapas -f lwpostgis.sql
psql -d mapas -U carbonara -f spatial_ref_sys.sql
y listo! ya la BD mapas se puede utilizar como almacen de un SIG
Para que una BD (en Postgresql), pueda servir a efectos de un SIG, debemos trabajar con Postgis, de manera que, creamos la BD:
CREATE DATABASE mapas;
CREATE LANGUAGE plpgsql;
Obviamente debemos tener instalado postgis, que en Debian es suficiente con aptitude install postgresql-tuversion-postgis (por si dudas, aptitude search postgis), con lo que en /usr/share/postgresql-miversion-postgis/ tengo los .sql necesarios.
psql -d mapas -f lwpostgis.sql
psql -d mapas -U carbonara -f spatial_ref_sys.sql
y listo! ya la BD mapas se puede utilizar como almacen de un SIG
jueves, abril 02, 2009
Migrar BD de Mysql a PostGreSQL (parte 2)
Después de no sé cuánto tiempo me he decidido a migrar la BD que tengo en mysql a Postgresql. Las razones no son importantes, o más bien es justamente eso, son datos tan importantes que es un crímen tenerlos en mysql...
Para esta demostración voy a utilizar unas tablas básicas que son similares a esto:
CREATE TABLE `Region` (
`codreg` char(2) NOT NULL default '',
`nomreg` varchar(35) NOT NULL default '',
PRIMARY KEY (`codreg`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='Tabla de Region';
CREATE TABLE `Estado` (
`codreg` char(2) NOT NULL default '',
`codest` char(2) NOT NULL default '',
`nomest` varchar(35) NOT NULL default '',
PRIMARY KEY (`codreg`,`codest`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
Con estas dos tablas se pueden observar ya varios detalles, la utilización de mayúsculas y minúsculas para el nombre de las tablas y la falta de integridad referencial, por otro lado la nueva BD está en utf8 (no es de mi agrado, pero es el acuerdo en que se llegó en la oficina).
Una vez hecho lo escrito en la primera parte de este post he creado un esquema dentro de la misma BD en Postgresql, de manera de leer y copiar en la misma BD, pero en diferentres esquemas. El esquema lo he llamado migracion, donde debo crear las tablas tal y como las necesito, de manera que:
CREATE TABLE region (
codreg character varying(2) NOT NULL,
nomreg character varying(50) NOT NULL
);
ALTER TABLE migracion.region OWNER TO carbonara;
ALTER TABLE ONLY region
ADD CONSTRAINT region_pkey PRIMARY KEY (codreg);
CREATE TABLE estado (
codreg character varying(2) NOT NULL,
codest character varying(2) NOT NULL,
nomest character varying(50) NOT NULL
);
ALTER TABLE ONLY estado
ADD CONSTRAINT estado_pkey PRIMARY KEY (codreg, codest);
ALTER TABLE ONLY estado
ADD CONSTRAINT estado_fkey FOREIGN KEY (codreg) REFERENCES region(codreg) ON UPDATE CASCADE ON DELETE RESTRICT;
Luego, el sql que lee de un lado y escribe del otro:
INSERT INTO migracion.region (codreg, nomreg) SELECT codreg, nomreg FROM "Region";
INSERT INTO migracion.estado (codreg, codest, nomest) SELECT codreg, codest, nomest FROM "Estado"
y listo! Todavía no entiendo por qué me tardé tanto en escribir este post, pero bue...
Para esta demostración voy a utilizar unas tablas básicas que son similares a esto:
CREATE TABLE `Region` (
`codreg` char(2) NOT NULL default '',
`nomreg` varchar(35) NOT NULL default '',
PRIMARY KEY (`codreg`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='Tabla de Region';
CREATE TABLE `Estado` (
`codreg` char(2) NOT NULL default '',
`codest` char(2) NOT NULL default '',
`nomest` varchar(35) NOT NULL default '',
PRIMARY KEY (`codreg`,`codest`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
Con estas dos tablas se pueden observar ya varios detalles, la utilización de mayúsculas y minúsculas para el nombre de las tablas y la falta de integridad referencial, por otro lado la nueva BD está en utf8 (no es de mi agrado, pero es el acuerdo en que se llegó en la oficina).
Una vez hecho lo escrito en la primera parte de este post he creado un esquema dentro de la misma BD en Postgresql, de manera de leer y copiar en la misma BD, pero en diferentres esquemas. El esquema lo he llamado migracion, donde debo crear las tablas tal y como las necesito, de manera que:
CREATE TABLE region (
codreg character varying(2) NOT NULL,
nomreg character varying(50) NOT NULL
);
ALTER TABLE migracion.region OWNER TO carbonara;
ALTER TABLE ONLY region
ADD CONSTRAINT region_pkey PRIMARY KEY (codreg);
CREATE TABLE estado (
codreg character varying(2) NOT NULL,
codest character varying(2) NOT NULL,
nomest character varying(50) NOT NULL
);
ALTER TABLE ONLY estado
ADD CONSTRAINT estado_pkey PRIMARY KEY (codreg, codest);
ALTER TABLE ONLY estado
ADD CONSTRAINT estado_fkey FOREIGN KEY (codreg) REFERENCES region(codreg) ON UPDATE CASCADE ON DELETE RESTRICT;
Luego, el sql que lee de un lado y escribe del otro:
INSERT INTO migracion.region (codreg, nomreg) SELECT codreg, nomreg FROM "Region";
INSERT INTO migracion.estado (codreg, codest, nomest) SELECT codreg, codest, nomest FROM "Estado"
y listo! Todavía no entiendo por qué me tardé tanto en escribir este post, pero bue...
martes, enero 08, 2008
Migrar BD de Mysql a PostGreSQL (parte 1)
El escenario: una BD en mysql que hay que migrar a postgresql, tomando en cuenta que hay sistemas que están utilizando ambos motores al mismo tiempo.
Temporalmente, se estableció una conexión con dbi-link. La documentación es más o menos sencilla, sólo hay que retocar el escript que corresponda al motor a conectar, en mi caso, mysql. En la carpeta examples, consigimos para conectar entre otros, oracle, mssql, mysql.
Los pasos, son más o menos así:
Los pasos, son más o menos así:
- crear el lenguaje plperl para la BD: create language plperlu;
- lanzas dbi_link.sql ( te crea el esquema y las funciones necesarias), en mi caso, lo hice con psql <>
- Conectar con la BD, igual, psql <>
Esto lo logré con algo de ayuda, además de la documentación, de parte del amigo seyko, que se puede ver en http://www.forosdelweb.com/f21/reporte-almacen-con-datos-postgresql-mysql-536140/#post2201060.
En los próximos minutos/horas/días/semanas/meses espero publicar la segunda parte, donde copiaremos datos de mysql a postgresql.
Para los usuarios de Debian y derivados, que prefieren aptitude, acá una forma muy parecida de hacerlo
lunes, enero 07, 2008
Empezando a darle uso útil al blog... con PostGreSQL
Normalmente tengo que pasar varios minutos o hasta horas buscando y leyendo en internet cómo hacer tal cual cosa, y que por lo general, ya lo he hecho antes... ¿olvidadizo? bueno, sólo a veces y con algunas cosas... Así que, es probable que a partir de ahora este blog empiece a verse más movido y con cositas más interesantes...
De entrada, será cosas de PostGreSQL, por sí alguien lee esto y quiere, de vez en cuando, darle alguna mirada.
Primer caso
Tengo dos (2) tablas, la primera me registra una serie de actividades, y la segunda, registra los diferentes estatus por los que va a pasar en un momento determinado.
actividad (id serial, nombre, fecha_inicio, fecha_culmina);
actividad_estatus (id int CONSTRAINT fk_est FOREIGN KEY (id) REFERENCES actividad(id), estatus varchar(1));
Obviamente, sólo coloca la información descriptiva para el problema.
Al registrar una actividad, normalemente tiene estatus no iniciado, por lo que podría ser interesante dejar al motor que cuandos e inserte la actividad, se inserte el estatus con ciertos valores por iniciales.
Creamos una función en lenguaje plpgsql, que devuelva un trigger, así:
Ahora no se me ocurre qué más escribir, así que... hasta otro momento!
Primer caso
Crear una función en plpgsql, que guarde el valor de un campo de una tabla, en otra tabla, con un disparador
Tengo dos (2) tablas, la primera me registra una serie de actividades, y la segunda, registra los diferentes estatus por los que va a pasar en un momento determinado.
actividad (id serial, nombre, fecha_inicio, fecha_culmina);
actividad_estatus (id int CONSTRAINT fk_est FOREIGN KEY (id) REFERENCES actividad(id), estatus varchar(1));
Obviamente, sólo coloca la información descriptiva para el problema.
Al registrar una actividad, normalemente tiene estatus no iniciado, por lo que podría ser interesante dejar al motor que cuandos e inserte la actividad, se inserte el estatus con ciertos valores por iniciales.
Creamos una función en lenguaje plpgsql, que devuelva un trigger, así:
begin
insert into ssc.actividad_estatus (id) values (NEW.id);
return NEW;
end;
Ahora no se me ocurre qué más escribir, así que... hasta otro momento!
Suscribirse a:
Entradas (Atom)