Intentando arrojar un poco de luz a problemas que puedes encontrarte. Especialmente con PostgreSql
viernes, 30 de noviembre de 2012
Uno de mis vicios. Los juegos
En los siguientes lugares encontrarás miles de horas de diversión.
Humble Bundle
Elige lo que quieres pagar por los juegos, cada cierto tiempo ofrecen packs muy interesantes. Puedes suscribirte para que te avisen.
GOG
Juegos que nunca mueren muy muy buenos. Podrás jugar en los modernos ordenadores juegos clasicos.
Steam
El monopolio de las descargas de juego. Todo tipo de juegos antiguos, nuevos, indies. Suele haber ofertas por temporada y algunos días.
Humble Bundle
Elige lo que quieres pagar por los juegos, cada cierto tiempo ofrecen packs muy interesantes. Puedes suscribirte para que te avisen.
GOG
Juegos que nunca mueren muy muy buenos. Podrás jugar en los modernos ordenadores juegos clasicos.
Steam
El monopolio de las descargas de juego. Todo tipo de juegos antiguos, nuevos, indies. Suele haber ofertas por temporada y algunos días.
lunes, 26 de noviembre de 2012
Deserializacion de JSON con la librería de Java Jackson
Después de probar varias librerías para Java que me ayudasen a serializar/deserializar JSON, me decanté por Jackson por ser la más completa y que además me permitía el acceso a bajo nivel.
El motivo de esta entrada es enseñar la deserialización usando el acceso a bajo nivel (Streaming) y combinarlo con el acceso de más alto nivel (Data Object binding)
Nuestro objeto JSON se compone de un Objeto con varias propiedades, una de las cuales es un array de otro tipo de objecto:
{"usuarioId":1,"color":4,
"lista":[{"tipo":1,"id":1005,"cantidad":3},
{"tipo":1,"id":1006,"cantidad":4},
{"tipo":1,"id":1007,"cantidad":2}]}
En el siguiente trozo de código vamos a parsear el JSON recibido en un parámetro "data" de la llamada de un webservice:
public void webservice( HttpServletRequest request, HttpServletResponse response ) {
int error = 0;
JsonParser jp = null;
try {
String datos = request.getParameter("data");
JsonFactory f = new MappingJsonFactory();
jp = f.createJsonParser(datos);
JsonToken current;
current = jp.nextToken();
if (current != JsonToken.START_OBJECT) {
System.out.println("Error: root should be object: quiting.");
return;
}
while (jp.nextToken() != JsonToken.END_OBJECT) {
String fieldName = jp.getCurrentName();
// move from field name to field value
current = jp.nextToken();
if ( fieldName=="usuarioId" ) {
System.out.println("Procesando: " + fieldName + " valor: " + jp.getValueAsString());
jp.skipChildren();
} else if (fieldName=="color") {
System.out.println("Procesando: " + fieldName + " valor: " + jp.getValueAsString());
jp.skipChildren();
} else if (fieldName.equals("lista")) {
if (current == JsonToken.START_ARRAY) {
// For each of the records in the array
while (jp.nextToken() != JsonToken.END_ARRAY) {
// read the record into a tree model,
// this moves the parsing position to the end of it
//JsonNode node = jp.readValueAsTree();
// And now we have random access to everything in the object
//System.out.println("tipo: " + node.get("tipo").asText());
//System.out.println("id: " + node.get("id").asText());
// prueba sobre un objeto
Item item=jp.readValueAs(Item.class);
log.error(" probando " + item.toString() );
}
} else {
System.out.println("Error: records should be an array: skipping.");
jp.skipChildren();
}
} else {
System.out.println("Unprocessed property: " + fieldName);
jp.skipChildren();
}
}
jp.close(); // ensure resources get cleaned up timely and properly
....
La clase Item sería:
public class Item implements Serializable {
private Long id;
private Long tipo;
private Long cantidad;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getTipo() {
return tipo;
}
public void setTipo(Long valor) {
this.tipo = valor;
}
public Long getCantidad() {
return cantidad;
}
public void setCantidad(Long valor) {
this.cantidad = valor;
}
@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
hash += (tipo != null ? tipo.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Item)) {
return false;
}
Item other = (Item) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "com.jpa.Item[ id=" + id + " ]";
}
}
Referencias:
http://outerthought.org/blog/415-ot.html
El motivo de esta entrada es enseñar la deserialización usando el acceso a bajo nivel (Streaming) y combinarlo con el acceso de más alto nivel (Data Object binding)
Nuestro objeto JSON se compone de un Objeto con varias propiedades, una de las cuales es un array de otro tipo de objecto:
{"usuarioId":1,"color":4,
"lista":[{"tipo":1,"id":1005,"cantidad":3},
{"tipo":1,"id":1006,"cantidad":4},
{"tipo":1,"id":1007,"cantidad":2}]}
En el siguiente trozo de código vamos a parsear el JSON recibido en un parámetro "data" de la llamada de un webservice:
public void webservice( HttpServletRequest request, HttpServletResponse response ) {
int error = 0;
JsonParser jp = null;
try {
String datos = request.getParameter("data");
JsonFactory f = new MappingJsonFactory();
jp = f.createJsonParser(datos);
JsonToken current;
current = jp.nextToken();
if (current != JsonToken.START_OBJECT) {
System.out.println("Error: root should be object: quiting.");
return;
}
while (jp.nextToken() != JsonToken.END_OBJECT) {
String fieldName = jp.getCurrentName();
// move from field name to field value
current = jp.nextToken();
if ( fieldName=="usuarioId" ) {
System.out.println("Procesando: " + fieldName + " valor: " + jp.getValueAsString());
jp.skipChildren();
} else if (fieldName=="color") {
System.out.println("Procesando: " + fieldName + " valor: " + jp.getValueAsString());
jp.skipChildren();
} else if (fieldName.equals("lista")) {
if (current == JsonToken.START_ARRAY) {
// For each of the records in the array
while (jp.nextToken() != JsonToken.END_ARRAY) {
// read the record into a tree model,
// this moves the parsing position to the end of it
//JsonNode node = jp.readValueAsTree();
// And now we have random access to everything in the object
//System.out.println("tipo: " + node.get("tipo").asText());
//System.out.println("id: " + node.get("id").asText());
// prueba sobre un objeto
Item item=jp.readValueAs(Item.class);
log.error(" probando " + item.toString() );
}
} else {
System.out.println("Error: records should be an array: skipping.");
jp.skipChildren();
}
} else {
System.out.println("Unprocessed property: " + fieldName);
jp.skipChildren();
}
}
jp.close(); // ensure resources get cleaned up timely and properly
....
La clase Item sería:
public class Item implements Serializable {
private Long id;
private Long tipo;
private Long cantidad;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getTipo() {
return tipo;
}
public void setTipo(Long valor) {
this.tipo = valor;
}
public Long getCantidad() {
return cantidad;
}
public void setCantidad(Long valor) {
this.cantidad = valor;
}
@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
hash += (tipo != null ? tipo.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Item)) {
return false;
}
Item other = (Item) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "com.jpa.Item[ id=" + id + " ]";
}
}
Referencias:
http://outerthought.org/blog/415-ot.html
viernes, 28 de septiembre de 2012
curl script
Si necesitas hacer repetidas llamadas curl via script Linux:
curl -S -w "%{url_effective}\n" -L "http://dominio.com:7080/api/pepe.php?apikey=56de¶metro1=390¶metro2=elquesea"
curl -S -w "%{url_effective}\n" -L "http://dominio.com:7080/api/pepe.php?apikey=56de¶metro1=390¶metro2=elquesea"
miércoles, 19 de septiembre de 2012
Componer fechas
Como componer en postgres una fecha con partes de otras fechas:
select date (
date_part('year' ,current_timestamp) || '-' ||
date_part('month',current_timestamp) || '-' ||
date_part('day' ,current_timestamp));
select date (
date_part('year' ,current_timestamp) || '-' ||
date_part('month',current_timestamp) || '-' ||
date_part('day' ,current_timestamp));
jueves, 6 de septiembre de 2012
Redes Servidores y UDP confiable
Pregunta sobre Reliable UDP con interesantes comentarios:
http://stackoverflow.com/questions/2940106/simple-reliable-udp-c-libraries
Boost.Asio
http://www.boost.org/doc/libs/1_51_0/doc/html/boost_asio.htmlLibrería muy utilizada encuadrada dentro del framework Boost con múltiples utilidades. Tiene sockets asincronos, ejemplo de un servidor de chat,
UDP - UDP reliable protocol
http://udt.sourceforge.net/index.htmlSe parece mucho a las funciones normales de sockets, minima barrera de entrada. Usado por Raknet y otros frameworks. El mejor protocolo para envío de mucha información en redes de amplio ancho de banda.
NOTA: en documentos de paco hay un ejemplo de implementación de EPOLL.
lunes, 3 de septiembre de 2012
Estadísticas de acierto de la cache
Es muy interesante saber en nuestra base de datos cuantas veces encontramos en la caché el resultado de las consultas. Si tenemos valores altos, seguro que no accedemos al disco a buscar los resultados y por lo tanto nuestra base de datos está funcionando bien.
Vamos a ver el porcentaje de acierto a nivel de base de datos:
SELECT d.datname, pg_database_size(d.datname), SUM(pg_stat_get_db_blocks_hit(d.oid)) / SUM(pg_stat_get_db_blocks_fetched(d.oid)) AS hit_rate
FROM pg_database d
GROUP BY d.datname
HAVING SUM(pg_stat_get_db_blocks_fetched(d.oid)) > 0
Pero aún más interesante es ver el resultado a nivel de tabla:
select t.schemaname, t.relname, heap_blks_hit*100/(heap_blks_hit+heap_blks_read) as BCHR
from pg_statio_user_tables t
where t.heap_blks_read > 0
Si tus tablas más accedidas tienen valores superiores a 95% todo va bien.
Sino revisa tus valores de "shared_buffers" y "effective_cache_size", si son correctos quizá tengas que revisar tus sentencias o aumentar tu memoria.
Referencias:
http://blog.kimiensoftware.com/2011/05/postgresql-vs-oracle-differences-4-shared-memory-usage-257
Vamos a ver el porcentaje de acierto a nivel de base de datos:
SELECT d.datname, pg_database_size(d.datname), SUM(pg_stat_get_db_blocks_hit(d.oid)) / SUM(pg_stat_get_db_blocks_fetched(d.oid)) AS hit_rate
FROM pg_database d
GROUP BY d.datname
HAVING SUM(pg_stat_get_db_blocks_fetched(d.oid)) > 0
Pero aún más interesante es ver el resultado a nivel de tabla:
select t.schemaname, t.relname, heap_blks_hit*100/(heap_blks_hit+heap_blks_read) as BCHR
from pg_statio_user_tables t
where t.heap_blks_read > 0
Si tus tablas más accedidas tienen valores superiores a 95% todo va bien.
Sino revisa tus valores de "shared_buffers" y "effective_cache_size", si son correctos quizá tengas que revisar tus sentencias o aumentar tu memoria.
Referencias:
http://blog.kimiensoftware.com/2011/05/postgresql-vs-oracle-differences-4-shared-memory-usage-257
jueves, 30 de agosto de 2012
Indices OnlyScan
A partir de Postgres 9.2 tenemos índices que si contienen los campos necesarios de una consulta, no acuden a la tabla a recuperar la información, sino que la recuperan directamente del índice. Para disfrutar de esta mejora tenemos que activar la consulta de este modo:
SET enable_indexonlyscan TO true;
Podeis ver ejemplos de uso en:
http://michael.otacoo.com/postgresql-2/postgresql-9-2-highlight-index-only-scans/
Tened cuidado al utilizarlo para no crear índices sobre campos que se actualicen muy a menudo, ya que perderemos rendimiento durante las actualizaciones e inserciones y no compensará la ganancia sobre las consultas.
Enlaces útiles de Postgres
Analizador de logs
http://michael.otacoo.com/postgresql-2/postgres-pgbadger-sneaking-in-log-files-for-you/#comment-10881
http://michael.otacoo.com/postgresql-2/postgres-pgbadger-sneaking-in-log-files-for-you/#comment-10881
miércoles, 29 de agosto de 2012
Estadisticas Postgres
Es interesante revisar las estadísticas de acceso a cada tabla de tu base de datos:
Guarda esta información, antes de hacer optimizaciones, y luego reseteala y posteriormente compara los resultados:
Para ver cuantas conexionest tienes establecidas y que están ejecutando:
Y los bloqueos con:
Si haces uso intensivo de procedimientos almacenados, también puedes consultar su rendimiento:
Si no tienes estadísticas calculadas, será porque no tienes activada la recopilación de las mismas,
Referencias:
http://www.postgresql.org/docs/9.1/static/monitoring-stats.html#MONITORING-STATS-SETUP
select * from pg_stat_database;
select * from pg_stat_user_tables;
select * from pg_stat_user_indexes;
Guarda esta información, antes de hacer optimizaciones, y luego reseteala y posteriormente compara los resultados:
select * from pg_stat_reset();
Para ver cuantas conexionest tienes establecidas y que están ejecutando:
select * from pg_stat_activity;
Y los bloqueos con:
select * from pg_locks;
Si haces uso intensivo de procedimientos almacenados, también puedes consultar su rendimiento:
select * from pg_stat_user_functions
Si no tienes estadísticas calculadas, será porque no tienes activada la recopilación de las mismas,
Referencias:
http://www.postgresql.org/docs/9.1/static/monitoring-stats.html#MONITORING-STATS-SETUP
martes, 28 de agosto de 2012
Reemplazar cadena en ficheros recursivamente
Busca a partir del directorio, recursivamente en todos los ficheros y cambia "lo_que_busco" por "lo_cambio_por_esto":
for i in `grep -l -R "lo_que_busco" ./directorio`; do sed 's/lo_que_busco/lo_cambio_por_esto/g' -i $i; done;
for i in `grep -l -R "lo_que_busco" ./directorio`; do sed 's/lo_que_busco/lo_cambio_por_esto/g' -i $i; done;
Trucos ssh
Conexión sin contraseña a un servidor ssh:
En el servidor origen de la conexión:
$ ssh-keygen -t rsa
El contenido del fichero resultante ~/.ssh/id_rsa.pub, hay que añadirlo al final del fichero ~/.ssh/authorized_keys, del usuario con el que quiero conectarme en el servidor destino donde quiero conectarme.
Copiar la llave id_rsa.pub al servidor remoto a traves de: ssh_copy_id id_rsa.pub usuario_remoto@servidor_remoto
Nota: en el servidor destino tiene que estar activados los siguientes parámetros del /etc/ssh/sshd_config
RSAAuthentication yes
PubkeyAuthentication yes
Copiar directorios
Copiar un directorio remoto vía ssh, con compresión y recursividad
scp -rC usuario@servidor.com:/origen ./destino
En el servidor origen de la conexión:
$ ssh-keygen -t rsa
Copiar la llave id_rsa.pub al servidor remoto a traves de: ssh_copy_id id_rsa.pub usuario_remoto@servidor_remoto
Nota: en el servidor destino tiene que estar activados los siguientes parámetros del /etc/ssh/sshd_config
RSAAuthentication yes
PubkeyAuthentication yes
Copiar directorios
Copiar un directorio remoto vía ssh, con compresión y recursividad
scp -rC usuario@servidor.com:/origen ./destino
miércoles, 22 de agosto de 2012
Creación de índices
Cuando en producción nos encontramos que tenemos que optimizar las consultas, nos encontraremos a veces que no podemos parar los servidores.
La optimización muchas veces pasa por crear un índice para alguna tabla, pero en Postgres esta operación bloquea la tabla.
Esto puede hacer caer el servidor de aplicaciones en el caso de aplicaciones web dinámicas, si el índice es muy grande.
Por fortuna Postgres nos facilita hacer esta operación de forma concurrente sin necesidad de bloquear la tabla, la operación será más lenta pero podremos hacerla en caliente:
CREATE INDEX CONCURRENTLY idx_salary ON employees(last_name, salary);
La optimización muchas veces pasa por crear un índice para alguna tabla, pero en Postgres esta operación bloquea la tabla.
Esto puede hacer caer el servidor de aplicaciones en el caso de aplicaciones web dinámicas, si el índice es muy grande.
Por fortuna Postgres nos facilita hacer esta operación de forma concurrente sin necesidad de bloquear la tabla, la operación será más lenta pero podremos hacerla en caliente:
CREATE INDEX CONCURRENTLY idx_salary ON employees(last_name, salary);
martes, 26 de junio de 2012
Ranking compuesto
Tenemos una tabla con resultados parciales:
Enlaces de Interés:
http://www.postgresql.org.es/node/376 (Funciones de ventana)
Tabla Parcial
id
campo1
campo2
...
campo N
Tenemos una tabla con el ranking actual:
Tabla Ranking
idposicionposicion_anterior
Vamos a actualizar el ranking con la suma de los resultados parciales, guardando además la posición anterior a la actualización para saber cuanto se ha avanzado o retrocedido desde el último recalculo del ranking.
begin;
-- Insertamos los nuevos registros en el ranking
insert into ranking (id,posicion,ant_posicion) select id,999999,999999
from parcial where id not in (select id from ranking);-- Actualizamos la posición actual y guardamos la anterior
update ranking set posicion_anterior=ranking.posicion,posicion=temporal.posicion
from (
select rank() over ( order by sum (campo1+campo2...campoN) desc) as posicion,
sum (campo1+campo2) as puntos,id
from parcial
group by id) as temporal
where ranking.id = temporal.id;
insert into ranking (id,posicion,ant_posicion) select id,999999,999999
from parcial where id not in (select id from ranking);-- Actualizamos la posición actual y guardamos la anterior
update ranking set posicion_anterior=ranking.posicion,posicion=temporal.posicion
from (
select rank() over ( order by sum (campo1+campo2...campoN) desc) as posicion,
sum (campo1+campo2) as puntos,id
from parcial
group by id) as temporal
where ranking.id = temporal.id;
-- Borramos del ranking quien no tenga puntos parciales
delete from ranking where id not in (select id from parcial);
delete from ranking where id in
(select id from rankings.acumulado where campo1+campo2...campoN=0 );
end;
Tenemos una tabla con los puntos acumulados por equipos de diferentes regiones y divisiones.
region_id
division
equipo
puntos
Como hacer un ranking de un plumazo:
select region_id,division,rank() over ( partition by region_id,division order by puntos desc) as posicion,id from
(select region_id,division,id,sum(puntos) as puntos
from rankings.diario group by region_id,division,id) as temporal;
delete from ranking where id not in (select id from parcial);
delete from ranking where id in
(select id from rankings.acumulado where campo1+campo2...campoN=0 );
end;
Tenemos una tabla con los puntos acumulados por equipos de diferentes regiones y divisiones.
region_id
division
equipo
puntos
Como hacer un ranking de un plumazo:
select region_id,division,rank() over ( partition by region_id,division order by puntos desc) as posicion,id from
(select region_id,division,id,sum(puntos) as puntos
from rankings.diario group by region_id,division,id) as temporal;
Enlaces de Interés:
http://www.postgresql.org.es/node/376 (Funciones de ventana)
martes, 12 de junio de 2012
Corregir nicks de usuarios con caracteres no válidos
Aquí podeis observar como arreglar la típica tabla de usuarios en la que se se han introducidos nicks con caracteres extraños que luego dan problemas al hacer login.
La tabla principal que almacena los usuarios es USUARIO
La tabla aux_usuario almacena los usuarios con caracteres no admitidos
La tabla aux_usuario_bueno almacena los usuarios que realizada la traducción
de un caracter incorrecto a otro, no supone nick duplicado en la tabla principal
-- Borramos la tabla de apoyo si existe
drop table if exists aux_usuario;
-- Rellenamos la tabla con los usuarios problemáticos
create table aux_usuario as select * from usuario where nick <> translate(nick, 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_áéíóúÁÉÍÓÚçÇñÑüÜ;,.-{}[]¡!ªº"@#$%&/()=¿?^*+€´:àèìòùÀÈÌÒÙâêîôûÂÊÎÔÛ¨¬', 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_aeiouAEIOUcCnNuU____________________________________________________');
-- Actualizamos el nick substituyendo los caracteres problematicos
update aux_usuario set nick=translate(nick,'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_áéíóúÁÉÍÓÚçÇñÑüÜ;,.-{}[]¡!ªº"@#$%&/()=¿?^*+€´:àèìòùÀÈÌÒÙâêîôûÂÊÎÔÛ¨¬', 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_aeiouAEIOUcCnNuU____________________________________________________') where nick <> translate(nick, 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_áéíóúÁÉÍÓÚçÇñÑüÜ;,.-{}[]¡!ªº"@#$%&/()=¿?^*+€´:àèìòùÀÈÌÒÙâêîôûÂÊÎÔÛ¨¬', 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_aeiouAEIOUcCnNuU____________________________________________________') ;
-- Creamos una tabla excluyendo los duplicados
drop table if exists aux_usuario_bueno;
create table aux_usuario_bueno as select * from aux_usuario aux where aux.nick not in ( select nick from usuario) and aux.nick not in ( select nick from (
-- Actualizamos la tabla principal
update usuario set nick=aux_usuario_bueno.nick from aux_usuario_bueno where aux_usuario_bueno.id=usuario.id ; ===================================================================== --- PARA LOS DUPLICADOS
--- Los usuarios que no al ser traducido el nick colisiona con otro
--- existente le añadimos el identificador para hacerlo único.
--- En el caso de nombres mayores de 10 caracteres añadimos
--- el identificador por segunda vez. ===================================================================== -- Borramos la tabla de apoyo si existe
drop table if exists aux_usuario2;
-- Rellenamos la tabla con los usuarios problemáticos create table aux_usuario2 as select * from usuario where nick <> translate(nick, 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_áéíóúÁÉÍÓÚçÇñÑüÜ;,.-{}[]¡!ªº"@#$%&/()=¿?^*+€´:àèìòùÀÈÌÒÙâêîôûÂÊÎÔÛ¨¬\\''', 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_aeiouAEIOUcCnNuU_______________________________________________________');
update aux_usuario2 set nick=translate(nick, 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_áéíóúÁÉÍÓÚçÇñÑüÜ;,.-{}[]¡!ªº"@#$%&/()=¿?^*+€´:àèìòùÀÈÌÒÙâêîôûÂÊÎÔÛ¨¬\\''', 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_aeiouAEIOUcCnNuU_______________________________________________________') where nick <> translate(nick, 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_áéíóúÁÉÍÓÚçÇñÑüÜ;,.-{}[]¡!ªº"@#$%&/()=¿?^*+€´:àèìòùÀÈÌÒÙâêîôûÂÊÎÔÛ¨¬\\''', 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_aeiouAEIOUcCnNuU_______________________________________________________');
update aux_usuario2 set nick=id::varchar where length(nick) >=10; update aux_usuario2 set nick=nick||id::varchar where length(nick) <10;
update usuario set nick=aux_usuario2.nick from aux_usuario2 where aux_usuario2.id=usuario.id ;
-- Borramos la tabla de apoyo si existe
drop table if exists aux_usuario;
-- Rellenamos la tabla con los usuarios problemáticos
create table aux_usuario as select * from usuario where nick <> translate(nick, 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_áéíóúÁÉÍÓÚçÇñÑüÜ;,.-{}[]¡!ªº"@#$%&/()=¿?^*+€´:àèìòùÀÈÌÒÙâêîôûÂÊÎÔÛ¨¬', 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_aeiouAEIOUcCnNuU____________________________________________________');
-- Actualizamos el nick substituyendo los caracteres problematicos
update aux_usuario set nick=translate(nick,'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_áéíóúÁÉÍÓÚçÇñÑüÜ;,.-{}[]¡!ªº"@#$%&/()=¿?^*+€´:àèìòùÀÈÌÒÙâêîôûÂÊÎÔÛ¨¬', 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_aeiouAEIOUcCnNuU____________________________________________________') where nick <> translate(nick, 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_áéíóúÁÉÍÓÚçÇñÑüÜ;,.-{}[]¡!ªº"@#$%&/()=¿?^*+€´:àèìòùÀÈÌÒÙâêîôûÂÊÎÔÛ¨¬', 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_aeiouAEIOUcCnNuU____________________________________________________') ;
-- Creamos una tabla excluyendo los duplicados
drop table if exists aux_usuario_bueno;
create table aux_usuario_bueno as select * from aux_usuario aux where aux.nick not in ( select nick from usuario) and aux.nick not in ( select nick from (
-- Actualizamos la tabla principal
update usuario set nick=aux_usuario_bueno.nick from aux_usuario_bueno where aux_usuario_bueno.id=usuario.id ; ===================================================================== --- PARA LOS DUPLICADOS
--- Los usuarios que no al ser traducido el nick colisiona con otro
--- existente le añadimos el identificador para hacerlo único.
--- En el caso de nombres mayores de 10 caracteres añadimos
--- el identificador por segunda vez. ===================================================================== -- Borramos la tabla de apoyo si existe
drop table if exists aux_usuario2;
-- Rellenamos la tabla con los usuarios problemáticos create table aux_usuario2 as select * from usuario where nick <> translate(nick, 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_áéíóúÁÉÍÓÚçÇñÑüÜ;,.-{}[]¡!ªº"@#$%&/()=¿?^*+€´:àèìòùÀÈÌÒÙâêîôûÂÊÎÔÛ¨¬\\''', 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_aeiouAEIOUcCnNuU_______________________________________________________');
update aux_usuario2 set nick=translate(nick, 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_áéíóúÁÉÍÓÚçÇñÑüÜ;,.-{}[]¡!ªº"@#$%&/()=¿?^*+€´:àèìòùÀÈÌÒÙâêîôûÂÊÎÔÛ¨¬\\''', 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_aeiouAEIOUcCnNuU_______________________________________________________') where nick <> translate(nick, 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_áéíóúÁÉÍÓÚçÇñÑüÜ;,.-{}[]¡!ªº"@#$%&/()=¿?^*+€´:àèìòùÀÈÌÒÙâêîôûÂÊÎÔÛ¨¬\\''', 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_aeiouAEIOUcCnNuU_______________________________________________________');
update aux_usuario2 set nick=id::varchar where length(nick) >=10; update aux_usuario2 set nick=nick||id::varchar where length(nick) <10;
update usuario set nick=aux_usuario2.nick from aux_usuario2 where aux_usuario2.id=usuario.id ;
miércoles, 2 de mayo de 2012
selects favoritas con variables en psql
Puedes tener tus scripts de ayuda preparados, con esas consultas que sueles usar continuamente, y que te pregunten los parametros para después ejecutarlos.
Por ejemplo me voy a crear un script en /home/postgres que se llama nick.sql, que me va a servir para buscar a un usuario por su nick:
\set nick 'pepe'
\prompt NICK nick \set nick_comillas '\'' :nick '\'' \echo select * from usuario where nick= :nick_comillas;
\prompt seguro seguro
select * from usuario where nick= :nick_comillas;
Para ejecutarlo dentro de psql \i nick.sql y nos pedirá el parametro del nick y ejecutará la sentencia. Aquí se muestra como poner una variable de tipo cadena ya que las comillas son complicadas de formatear.
Por ejemplo me voy a crear un script en /home/postgres que se llama nick.sql, que me va a servir para buscar a un usuario por su nick:
\set nick 'pepe'
\prompt NICK nick \set nick_comillas '\'' :nick '\'' \echo select * from usuario where nick= :nick_comillas;
\prompt seguro seguro
select * from usuario where nick= :nick_comillas;
Para ejecutarlo dentro de psql \i nick.sql y nos pedirá el parametro del nick y ejecutará la sentencia. Aquí se muestra como poner una variable de tipo cadena ya que las comillas son complicadas de formatear.
jueves, 9 de febrero de 2012
Crear una instancia de prueba de Amazon
Tutorial muy completo para crear una instancia gratuita de prueba en Amazon. Muy bien explicado:
http://alanta.info/como-crear-un-servidor-gratuito-en-amazon.html
miércoles, 8 de febrero de 2012
Monitorización Linux
Quien está conectado y que está haciendo
Otras herramientas de monitorización de actividad, memoria, uso de disco, cpu... http://www.cyberciti.biz/tips/top-linux-monitoring-tools.html
w [user_name]
17:24:24 up 88 days, 17:12, 5 users, load average: 0,09, 0,07, 0,08
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
gme pts/0 paco.bit.mad 09:35 0.00s 0.61s 0.00s w
wdev pts/1 fe.bit.mad Fri10 5days 0.01s 0.01s -bash
gme pts/2 fe.bit.mad Fri11 27:06m 6.31s 6.28s ./gmeser
wdev pts/5 paco.bit.mad 09:35 7:48m 0.01s 0.01s -bash
gme pts/7 fe.bit.mad 12:23 4:58m 0.04s 0.04s -bash
Otras herramientas de monitorización de actividad, memoria, uso de disco, cpu... http://www.cyberciti.biz/tips/top-linux-monitoring-tools.html
Llamadas repetitivas a Web Services con wget
Necesitaba hacer multiples llamadas a webservices, y luego verificar los resultados devueltos.
En lugar de hacerlo enlace a enlace, preparé un fichero llamadas.url donde almacene las llamadas a realizar en forma de proceso por lotes:
wget -S -i llamadas.url -O resultados_http.txt -a info_llamadas.txtLos resultados de las llamadas se guardarán en resultados_http.txt La información de errores y tiempos se guardará en: info_llamadas.txt
martes, 7 de febrero de 2012
Vacuum de tablas con muchas actualizaciones
Si revisando los logs de tu Base de datos postgres, te encuentras a menudo con la siguiente entrada:
2012-02-06 18:44:20 CETLOG: vacuum automático de la tabla «esquema.tabla»: recorridos de índice: 1Esto se debe a que esa tabla tiene muchas inserciones o updates y constantemente tiene que aprovisionar nuevas páginas, el proceso de vacuum autoático se lanza para liberar las páginas inservibles, etc. Para solucionar este problema lo mejor es indicar un Factor de relleno (Fillfactor) para la tabla. Este factor representa cuanto espacio se deja en una página para el alojamiento de nuevas inserciones o actualizaciones de tuplas de esa página. Por defecto este valor es 100, por lo que todas la actualizaciones/inserciones irán a páginas nuevas y no se modificará la página actual. Para empezar un buen valor es 80, pero si la tabla tiene un ínidice muy alto de actualización tendrás que probar con valores por debajo de 50. Despues de hacer la modificación, no tendrá efecto hasta que hagas un Vacuum full (Ojo esta operación bloqueará la tabla, por lo que es mejor que no haya conexiones a la base de datos):
ALTER TABLE rankings.ranking SET ( fillfactor = 80 ); VACUUM FULL rankings.ranking;Este factor también afecta a los índices que sean modificados con mucha frecuencia, la sintaxis para modificarlos es:
ALTER INDEX "tabla_idx" ON "esquema"."tabla" USING btree ("campo_a_indexar"); DROP INDEX.tabla_idx; CREATE INDEX "tabla_idx" ON "esquema"."tabla" USING btree ("campo_a_indexar") WITH (fillfactor = 80);Referencias: http://www.postgresql.org/docs/9.0/static/sql-createtable.html
lunes, 6 de febrero de 2012
Update basado en select
Si un día necesitas guardar algunos campos de una tabla antes de que la tabla sea machacada por un proceso, puedes guardar los datos en una tabla auxiliar:
drop table if exists aux_coleccion; create table aux_coleccion as select * from coleccion;Despues de que hayamos guardado una copia de la tabla podemos machacar su contenido. Ahora podemos recuperar el contenido original de los campos que queramos:
update coleccion set precio=a.precio,nivel=a.nivel,puntos=a.puntos,estado=a.estado, nombre=a.nombre,descripcion=a.descripcion, from aux_coleccion a where a.id=coleccion.id;
Extraer permisos de un usuario
Si tienes una base de datos con un usuario y quieres copiar sus permisos en otro lugar, puedes utilizar la siguiente consulta, el resultado puedes ejecutarlo en otro lugar:
select 'grant ' || privilege_type || ' on table ' || table_schema || '.' || table_name || ' to ' || grantee || ';' from information_schema.role_table_grants where grantee='';
Suscribirse a:
Entradas (Atom)