Amigo, usted es un depresivo coder que se ve horrorizado por complejas y hermosas consultas SQL en sistemas legados que desconocen IX, FK y demás fancy features? Tiene el terrible problema de hacer transformaciones de datos tan complicadas y cargadas que los discos de su HD saldran girando fuera de su eje? Si esta en esa situación, lo comprendo, sufro de los mismos males. Por eso hoy le recomiendo, hacer una idiotes. Si señor, montar MySQL en un RAM disk.
WTF? Pues bien la idea no es novedosa, más no es aplicada a diario. Y les puedo decir que no puedo hacer comparativa alguna, mis consultas y ejecucciones son tan rápidas que ya tengo tiempo libre para mi familia xD. Para realizar esta amable tarea, basta que consigamos un buen driver de RAM Disk, recomiendo este (Descargar), el cual es muy fácil de entender y de usar. Creamos nuestro RAM disk, de unos 500 MB. Que tanto puede ser no?
Una vez creado procedemos a parar MySQL, editar el my.ini para apuntar nuestro datadir a nuestra recien creada unidad RAM. Entonces procedemos a copiar el contenido de todo datadir original en nuestro RAM disk, prendemos MySQL y apreciad el poder de no usar el disco duro!!!
Espero que lo pongan a prueban, y vean la velocidad. Solo hay que tener cuidado de no apagar la maquina, sino recuperaste tu info. Para recuperarla basta revertir los cambios en my.ini y copiar los archivos del RAM disk al datadir original.
Google Web
12 dic 2008
23 oct 2008
GData
A petición del señor babuino, hoy les explicare como es que pelee contra GData (la API mundial del mundo de Google y sus servicios, excepto Maps y Gmail entre otros jajaja). Pues bien esta madre funciona por pseudoREST, para los ignorantes Wikipedia es amiga.
Les explicare brevemente como usarlo con PHP, para que no lloren. Lo primero es hacer login para obtener un token, el cual será válido para el servicio que usaremos en este caso es 'wise' que es spreadsheets. Para la ocasión hice una pequeña función que usaremos asi:
function doPost($url, $data)
{
$params = array('http' => array(
'method' => 'POST',
'content' => $data,
'header' =>
'Content-Length: ' . strlen($data) . '
Content-Type: application/x-www-form-urlencoded'
));
$ctx = stream_context_create($params);
$response = file_get_contents($url, false, $ctx);
return $response;
}
$data = array();
$data['accountType'] = 'GOOGLE';
$data['Email'] = 'simio@gmail.com';
$data['Passwd'] = 'simio';
$data['service'] = 'wise';
$data['source'] = 'test';
$query = http_build_query($data);
$result = doPost("https://www.google.com/accounts/ClientLogin", $query);
Esto nos debe de regresar algunos token u otros errores dependiendo el caso. Si nos regresa algunos token, usaremos el ultimo, Auth, ya tengo la función para sacarlo.
function getToken($data)
{
$tokens = split("\n", $data);
foreach ($tokens AS $current)
{
$artemp = split("=", $current);
if ($artemp[0] == 'Auth')
return $artemp[1];
}
return '';
}
$auth = getToken($result);
Ya tenemos nuestro token, ahora a hacer caos! En lo personal no logre conectarme con file_get_contents por GET. Esto porque, necesitas modificar el HTTP Header para agregar el token, pero al parecer PHP destroza este header cuando Google le regresa un Location Header, asi que en su lugar usare CURL. Teniendo una clase asi:
function doCurlGet($url, $auth)
{
$curl_handle = curl_init();
curl_setopt($curl_handle, CURLOPT_URL, $url);
curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, 1);
curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($curl_handle, CURLOPT_HTTPHEADER, array('Authorization: GoogleLogin auth=' . $auth));
$output = curl_exec($curl_handle);
if ($output === false)
return curl_error($curl_handle);
curl_close($curl_handle);
return trim($output);
}
Ahora ya podemos usar los servicios, por ejemplo obtener un listado de todas mis hojas de calculo sería algo asi:
$url = "http://spreadsheets.google.com/feeds/spreadsheets/private/full";
$data = doCurlGet($url, $auth);
Si queremos en lugar de recibir XML, un poco de JSON para ahorrar nuestro tiempo de coding, solo agregamos un '?alt=json' a los URL de GData para obtener content type en JSON.
Voila!!!!
Les explicare brevemente como usarlo con PHP, para que no lloren. Lo primero es hacer login para obtener un token, el cual será válido para el servicio que usaremos en este caso es 'wise' que es spreadsheets. Para la ocasión hice una pequeña función que usaremos asi:
function doPost($url, $data)
{
$params = array('http' => array(
'method' => 'POST',
'content' => $data,
'header' =>
'Content-Length: ' . strlen($data) . '
Content-Type: application/x-www-form-urlencoded'
));
$ctx = stream_context_create($params);
$response = file_get_contents($url, false, $ctx);
return $response;
}
$data = array();
$data['accountType'] = 'GOOGLE';
$data['Email'] = 'simio@gmail.com';
$data['Passwd'] = 'simio';
$data['service'] = 'wise';
$data['source'] = 'test';
$query = http_build_query($data);
$result = doPost("https://www.google.com/accounts/ClientLogin", $query);
Esto nos debe de regresar algunos token u otros errores dependiendo el caso. Si nos regresa algunos token, usaremos el ultimo, Auth, ya tengo la función para sacarlo.
function getToken($data)
{
$tokens = split("\n", $data);
foreach ($tokens AS $current)
{
$artemp = split("=", $current);
if ($artemp[0] == 'Auth')
return $artemp[1];
}
return '';
}
$auth = getToken($result);
Ya tenemos nuestro token, ahora a hacer caos! En lo personal no logre conectarme con file_get_contents por GET. Esto porque, necesitas modificar el HTTP Header para agregar el token, pero al parecer PHP destroza este header cuando Google le regresa un Location Header, asi que en su lugar usare CURL. Teniendo una clase asi:
function doCurlGet($url, $auth)
{
$curl_handle = curl_init();
curl_setopt($curl_handle, CURLOPT_URL, $url);
curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, 1);
curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($curl_handle, CURLOPT_HTTPHEADER, array('Authorization: GoogleLogin auth=' . $auth));
$output = curl_exec($curl_handle);
if ($output === false)
return curl_error($curl_handle);
curl_close($curl_handle);
return trim($output);
}
Ahora ya podemos usar los servicios, por ejemplo obtener un listado de todas mis hojas de calculo sería algo asi:
$url = "http://spreadsheets.google.com/feeds/spreadsheets/private/full";
$data = doCurlGet($url, $auth);
Si queremos en lugar de recibir XML, un poco de JSON para ahorrar nuestro tiempo de coding, solo agregamos un '?alt=json' a los URL de GData para obtener content type en JSON.
Voila!!!!
14 oct 2008
Linksys Lohan y sus amigas
Esta es una historia real, que aunque no quería decir, lo dire, fue mi novia. Todo comenzo porque desde hace unos días tenemos una Wifi sin seguridad, llamada Linksys, lo cual es bueno dado que tengo inet en ocasiones en el depa. Mi novia sin embargo, le causo caos el nombre de Linksys, y en su afán de encontrar el origen de la red en cuestión, hablaba con la vecina, que hizo un magnifico WTF.
La vecina ajeraba a mi novia por el pago de la reparación de una bomba de agua (que no existe, porque siempre he tenido agua), la cual llevan un mes cobrandome que porque no han podido repararla por culpa mía y del vecino de arriba. Lo curioso es que la explicación transcendental de mi vecina a porque hay agua y no bomba, es porque esta la tubería "directa" en lo que se repara la bomba.
WTF? Directa? Esto me lleva muchas preguntas, como haces llegar agua a un tercer piso con la presión de la calle? Si el agua llega "directa" para que pago por la bomba sino la necesito? Es un gran misterio, pero lo cierto es que no pienso caer en la trampa de esa bomba inexistente.
Al finalizar la platica, la vecina le hablo a su hija, gritandole "Linksys" (al menos eso jura mi novia). Que cuando me llamo para avisarme de la amenaza de la bomba, me dijo orgullosa, ya se donde esta la Wifi, la tiene la vecina, asi se llama su hija. Y yo asi todo amable, como?. Entonces me ajero que si que su hija se llama Linksys, como Linksys Lohan (esto ya es cosecha adicional xD). Le explique que es una marca, pero mi novia insiste que Linksys y Cisco son nuestros vecinos. Y dicen que yo soy el geek.
La vecina ajeraba a mi novia por el pago de la reparación de una bomba de agua (que no existe, porque siempre he tenido agua), la cual llevan un mes cobrandome que porque no han podido repararla por culpa mía y del vecino de arriba. Lo curioso es que la explicación transcendental de mi vecina a porque hay agua y no bomba, es porque esta la tubería "directa" en lo que se repara la bomba.
WTF? Directa? Esto me lleva muchas preguntas, como haces llegar agua a un tercer piso con la presión de la calle? Si el agua llega "directa" para que pago por la bomba sino la necesito? Es un gran misterio, pero lo cierto es que no pienso caer en la trampa de esa bomba inexistente.
Al finalizar la platica, la vecina le hablo a su hija, gritandole "Linksys" (al menos eso jura mi novia). Que cuando me llamo para avisarme de la amenaza de la bomba, me dijo orgullosa, ya se donde esta la Wifi, la tiene la vecina, asi se llama su hija. Y yo asi todo amable, como?. Entonces me ajero que si que su hija se llama Linksys, como Linksys Lohan (esto ya es cosecha adicional xD). Le explique que es una marca, pero mi novia insiste que Linksys y Cisco son nuestros vecinos. Y dicen que yo soy el geek.
7 oct 2008
Cuando ignoras detalles triviales..
Cuando uno toma un lenguaje con algo de años en el mercado, piensa que no debe lidiar con detalles triviales como los arrays. PHP, en este punto, es la excepción. Con un inimaginable control de arreglos horrible. Hoy perdía mi tiempo en una librería en PHP para el cliente, al más puro MVC. Pero el Controller no me ejecutaba las acciones, a simple vista el único cambio en este modelo era que la acción estaba corriendo de un control aislado, fuera de la forma.
El Controller trabaja de forma sencilla y rápida, navegando el modelo y encontrando cual control tiene activada la bandera de ejecución, siempre y cuando el control no este en una lista de arreglos. Asi que tenemos un codigo algo asi:
$ignoreList=array(...);
while (list($key, $value) = each($definedVars)) {
$ignoreItem = in_array($key, $ignoreList);
if ($ignoreItem) continue;
...
if (is_array($value)){
estamismafuncion($value);
}
}
Como ven algo, que debería funcionar... si, siempre y cuando no se te ocurra buscar un Cero (0) con la función in_array(), porque siempre existe! Asi que cuando la recursión, buscaba en un arreglo (que es la mayoría de los casos, dado que es un árbol de controles) el primer elemento lo ignoraba, aun cuando la $ignoreList no tuviera un zero!.
Esto se debe que toda lista de PHP tiene un NULL de elemento final por cuestiones de punteros, y 0 == NULL en las funciones como in_array. Así que la única forma humana de corregirlo fue:
$ignoreItem = in_array("" . $key, $ignoreList);
Gracias PHP por hacer mi vida miserable!
PD. Este mismo bug del zero == null me paso con un Iterator que hice en PHP, luego les contare.
El Controller trabaja de forma sencilla y rápida, navegando el modelo y encontrando cual control tiene activada la bandera de ejecución, siempre y cuando el control no este en una lista de arreglos. Asi que tenemos un codigo algo asi:
$ignoreList=array(...);
while (list($key, $value) = each($definedVars)) {
$ignoreItem = in_array($key, $ignoreList);
if ($ignoreItem) continue;
...
if (is_array($value)){
estamismafuncion($value);
}
}
Como ven algo, que debería funcionar... si, siempre y cuando no se te ocurra buscar un Cero (0) con la función in_array(), porque siempre existe! Asi que cuando la recursión, buscaba en un arreglo (que es la mayoría de los casos, dado que es un árbol de controles) el primer elemento lo ignoraba, aun cuando la $ignoreList no tuviera un zero!.
Esto se debe que toda lista de PHP tiene un NULL de elemento final por cuestiones de punteros, y 0 == NULL en las funciones como in_array. Así que la única forma humana de corregirlo fue:
$ignoreItem = in_array("" . $key, $ignoreList);
Gracias PHP por hacer mi vida miserable!
PD. Este mismo bug del zero == null me paso con un Iterator que hice en PHP, luego les contare.
2 oct 2008
Cansado...
Pues bien, los multiproyectos ya me tienen cansado fisicamente, creo que es momento de que Simio se tome un break de la vida. Si señor, hare lo que siempre he querido. Voy a insultar a Babuchas. Mentiras, solo dire nonsense en lo que esta putada terminada de trabajar. En que estoy?
* Verificando un migrador que hara merge a dos bases de datos enormes en una sola.
* Soportando una aplicación VB6 misteriosa.
* Intentando hacer que un código legado de webservices horribles funcionen.
* Intentando conectarme a otros webservices más horribles.
* Adaptando mi framework a una versión ligera con YUI, y muriendo actualizando el código de YUI 5.2 a 6 (cabrones cambiaron muchas tonterías).
* Trabajando en mi framework.
* Insultando a Babuchas.
Lo ven! Mi vida no es fácil. Tengo mucho que hacer! Por eso estoy cansado hoy...
* Verificando un migrador que hara merge a dos bases de datos enormes en una sola.
* Soportando una aplicación VB6 misteriosa.
* Intentando hacer que un código legado de webservices horribles funcionen.
* Intentando conectarme a otros webservices más horribles.
* Adaptando mi framework a una versión ligera con YUI, y muriendo actualizando el código de YUI 5.2 a 6 (cabrones cambiaron muchas tonterías).
* Trabajando en mi framework.
* Insultando a Babuchas.
Lo ven! Mi vida no es fácil. Tengo mucho que hacer! Por eso estoy cansado hoy...
29 sept 2008
Pieza de arte (solo para conocedores)
A continuación dejare posteada una pieza de arte del código, para que los conocedores admiren y critiquen. Este código es original de un proyecto al que le estoy dando mantenimiento (y si ya borre esta clase del proyecto).
public class DbConn {
public string connString;
SqlConnection conn;
SqlTransaction trans;
SqlCommand command;
public bool modifyConn;
public int timeout;
public DbConn(bool _modifyConn) {
connString = ConfigurationManager.ConnectionStrings["Conn2"].ConnectionString;
conn = new SqlConnection(connString);
trans = null;
modifyConn = _modifyConn;
}
public DbConn() : this(false) { }
public SqlConnection fnObtenerConexion() {
return conn;
}
public DataTable fnObtenerDataTable(string consulta) {
fnObtenerComando(consulta);
DataTable dt = new DataTable();
SqlDataAdapter adap = new SqlDataAdapter(command);
adap.Fill(dt);
return dt;
}
public DataSet fnObtenerDataSet(string consulta) {
fnObtenerComando(consulta);
DataSet ds = new DataSet();
SqlDataAdapter adap = new SqlDataAdapter(command);
adap.Fill(ds);
return ds;
}
public string fnEjecutarConsulta(string consulta) {
return fnEjecutar(consulta, 2);
}
public string fnEjecutarEscalar(string consulta) {
return fnEjecutar(consulta, 1);
}
public string fnEjecutar(string consulta, int tipo) {
if (!modifyConn) conn.Open();
string resultado = "";
fnObtenerComando(consulta);
if (tipo == 1) {
resultado = command.ExecuteScalar().ToString();
} else if (tipo == 2) {
resultado = command.ExecuteNonQuery().ToString();
}
if (!modifyConn) conn.Close();
return resultado;
}
public void fnObtenerComando(string consulta) {
if (trans != null) {
command = new SqlCommand(consulta, conn, trans);
} else {
command = new SqlCommand(consulta, conn);
}
command.CommandTimeout = timeout;
}
}
public class DbConn {
public string connString;
SqlConnection conn;
SqlTransaction trans;
SqlCommand command;
public bool modifyConn;
public int timeout;
public DbConn(bool _modifyConn) {
connString = ConfigurationManager.ConnectionStrings["Conn2"].ConnectionString;
conn = new SqlConnection(connString);
trans = null;
modifyConn = _modifyConn;
}
public DbConn() : this(false) { }
public SqlConnection fnObtenerConexion() {
return conn;
}
public DataTable fnObtenerDataTable(string consulta) {
fnObtenerComando(consulta);
DataTable dt = new DataTable();
SqlDataAdapter adap = new SqlDataAdapter(command);
adap.Fill(dt);
return dt;
}
public DataSet fnObtenerDataSet(string consulta) {
fnObtenerComando(consulta);
DataSet ds = new DataSet();
SqlDataAdapter adap = new SqlDataAdapter(command);
adap.Fill(ds);
return ds;
}
public string fnEjecutarConsulta(string consulta) {
return fnEjecutar(consulta, 2);
}
public string fnEjecutarEscalar(string consulta) {
return fnEjecutar(consulta, 1);
}
public string fnEjecutar(string consulta, int tipo) {
if (!modifyConn) conn.Open();
string resultado = "";
fnObtenerComando(consulta);
if (tipo == 1) {
resultado = command.ExecuteScalar().ToString();
} else if (tipo == 2) {
resultado = command.ExecuteNonQuery().ToString();
}
if (!modifyConn) conn.Close();
return resultado;
}
public void fnObtenerComando(string consulta) {
if (trans != null) {
command = new SqlCommand(consulta, conn, trans);
} else {
command = new SqlCommand(consulta, conn);
}
command.CommandTimeout = timeout;
}
}
23 sept 2008
wizzdull peachepero
Continuando con las proezas de mejorar el mundo del dev. Hoy me enfrento contra la mayor tontería. Tener un SOAP Server en PHP, con un WSDL autogenerado. Ok lo hice, sin muchos problemas (después de modificar varias partes de la librería WSDLCreator). Pero el problema es con los tipo de datos, digo, no es algo normal tener algunos SOAP Actions con distintos complex types? Pues según yo esto es lo normal, la neta del planeta, lo unico bueno y chido. Pero para PHP, no creo...
En primera el WSDLCreator no tiene ni idea de como manejar una clase sin métodos, según ellos me dan una función para agregar la clase como un Namespace, pero nadaaaaaaaaa. El WSDL menciona en el Action que regresa esa clase, pero en la definición de tipos, nada de nada. Si le pongo una función dummy, aparece, pero momento me genera otro Port y Binding dandome en la madre. Esto porque? Porque al parecer el SoapServer, es muy chafa que solo puede tener una clase, y aunque la función dummy no se utilize, la busca y falla. Que optimo sistema.
En fin seguire peleando, si logro dar con el clavo les dire como logran tener webservices en PHP de forma linda.
En primera el WSDLCreator no tiene ni idea de como manejar una clase sin métodos, según ellos me dan una función para agregar la clase como un Namespace, pero nadaaaaaaaaa. El WSDL menciona en el Action que regresa esa clase, pero en la definición de tipos, nada de nada. Si le pongo una función dummy, aparece, pero momento me genera otro Port y Binding dandome en la madre. Esto porque? Porque al parecer el SoapServer, es muy chafa que solo puede tener una clase, y aunque la función dummy no se utilize, la busca y falla. Que optimo sistema.
En fin seguire peleando, si logro dar con el clavo les dire como logran tener webservices en PHP de forma linda.
19 sept 2008
Ahoy!!!!!
Hoy es el día Internacional de hablar como pirata (http://en.wikipedia.org/wiki/International_Talk_Like_a_Pirate_Day), así que espero que todos ustedes mozalbetes, digan ARGGHH!!!!!
PD. Ya tengo MAC de nuevo xD
PD. Ya tengo MAC de nuevo xD
17 sept 2008
16 sept 2008
No more UNION
El día de hoy quiero compartir con ustedes un hallazgo de esas funciones místicas (si Pimienta, es místico esto) de MySQL. Se trata del keyword ROLLUP de GROUP BY. Qué es esto y como funciona? Supongamos el siguiente ejemplo:
SELECT NombreCliente, SUM(MontoPago) FROM Clientes GROUP BY NombreCliente
Ahorrando los detalles de que era habia un JOIN en Clientes y Pagos. Tenemos un listado con la suma de los pagos por cliente, pues bien, si queriamos tener un gran total de esto la gente era tonta y en lenguaje de programación hacian una suma de las sumas de pagos (he visto mucho código así, lo juro). Otros menos torpes como yo, usamos el famoso UNION para agregar un NULL como NombreCliente y sumar todo. Ok nada novedoso para muchos, pero bueno MySQL trae un nivel bastante agradable para mejorar esto.
SELECT NombreCliente, SUM(MontoPago) FROM Clientes GROUP BY NombreCliente
UNION
SELECT NULL as NombreCliente, SUM(MontoPago) FROM Clientes
Usando ROLL UP tenemos:
SELECT NombreCliente, SUM(MontoPago) FROM Clientes GROUP BY NombreCliente WITH ROLLUP
Lo que nos da el mismo resultado, y es hermoso. Claro este SELECT no tiene complicaciones, pero cuando tienes un SELECT construido para generar un pivote dinámico, tienes complicaciones existenciales más importantes que reflejar los JOINS y clausulas del UNION.
Espero que le ayude a alguién este tip, que a mi la verdad me sirvió de mucho!
EDIT: A lo que preguntaron por messenger, si en SQL Server al parecer si existe la palabra clave ROLLUP, se maneja igual según MSDN, más no lo he checado.
SELECT NombreCliente, SUM(MontoPago) FROM Clientes GROUP BY NombreCliente
Ahorrando los detalles de que era habia un JOIN en Clientes y Pagos. Tenemos un listado con la suma de los pagos por cliente, pues bien, si queriamos tener un gran total de esto la gente era tonta y en lenguaje de programación hacian una suma de las sumas de pagos (he visto mucho código así, lo juro). Otros menos torpes como yo, usamos el famoso UNION para agregar un NULL como NombreCliente y sumar todo. Ok nada novedoso para muchos, pero bueno MySQL trae un nivel bastante agradable para mejorar esto.
SELECT NombreCliente, SUM(MontoPago) FROM Clientes GROUP BY NombreCliente
UNION
SELECT NULL as NombreCliente, SUM(MontoPago) FROM Clientes
Usando ROLL UP tenemos:
SELECT NombreCliente, SUM(MontoPago) FROM Clientes GROUP BY NombreCliente WITH ROLLUP
Lo que nos da el mismo resultado, y es hermoso. Claro este SELECT no tiene complicaciones, pero cuando tienes un SELECT construido para generar un pivote dinámico, tienes complicaciones existenciales más importantes que reflejar los JOINS y clausulas del UNION.
Espero que le ayude a alguién este tip, que a mi la verdad me sirvió de mucho!
EDIT: A lo que preguntaron por messenger, si en SQL Server al parecer si existe la palabra clave ROLLUP, se maneja igual según MSDN, más no lo he checado.
Suscribirse a:
Entradas (Atom)