Archivo de la etiqueta: MySQL

Solucionar error session_start – header already sent – PHP:MySQL Tips

The PHP logo displaying the Handel Gothic font.

Os suena este error:

warning: session_start()[function.session-start]:cannot send session cache limiter – header already sent (output started at ….

De primeras tenemos que el session_start hay que ponerlo al principio de todo, fuera de la sección html, a veces el error viene por otro lado, la otra posibilidad

suele suceder cuando escribimos el session_start() en un fichero en el cual ya se ha iniciado la sesión en otro punto, como por ejemplo tenemos un fichero con funciones

el cual incluimos en otro fichero en el cual hemos puesto en la cabecera el session_start() y para proteger el fichero de funciones ponemos también el session_start(), tenemos

que ponerlo en los dos sitios si o si. Pues bien para solucionar esto me he creado una pequeña función, la cual pongo en el principio de mis ficheros php que chequea si se ha iniciado la sesión, si es asi regenera el id y si no pues la inicia.

function checkSession() {
if ( session_id() != null ){
session_regenerate_id();
} else {
session_start();
}

Llamo a la función en las cabeceras, si el session_id no es nulo se regenera, es decir la sesión esta iniciada, si no se inicia.

Anuncio publicitario

Saneando inputs – PHP:MySQL Tips

Como bien dice el mantra filter input – escape output
Una de las maneras que tenemos para filtrar los valores que nos llegan antes de usarlos, por ejemplo en una consulta mysql es por medio de la función mysql_real_escape_string($string, $link).
He desarrollado una pequeña función la cual si le pasamos un array o un string nos lo filtra. Esta función me es muy útil cuando envió a una pagina o bien vía $_GET o bien $_POST.

function sanitize( &$vars ) {
    global $con; // identificador de la conexion mysql
    if ( is_array( $vars ) ) {
        foreach ( $vars as &$var ) {
            mysql_real_escape_string( $var, $con );
        }
    } elseif ( is_string( $vars ) ) {
        mysql_real_escape_string( $vars, $con );
    }
}
// Aquí llamamos a la función - ejemplo
if( isset( $_GET ) ) {
    sanitize( $_GET );
}
if ( isset( $_POST ) ) {
    sanitize( $_POST );
}

De esta manera si tenemos un fichero con funciones ponemos esta y llamamos a la función en las cabeceras de los ficheros donde recibamos datos y cuando los tratemos ya los tendremos saneados para trabajar con ellos.
+Info: http://es2.php.net/manual/en/function.mysql-real-escape-string.php

Solución al error Asigment in condition – PHP:MySQL Tips

Los que desarrollamos con Zend Studio o con Eclipse y nos da por abrir proyectos antiguos, vemos que se nos empieza a quejar de errores en nuestro codigo antiguo. Uno de estos errores es el de ‘Asigment in condition’que viene a decir que estamos asignando un valor en una condicional.
Veamos el ejemplo.
Los que somos de la vieja escuela de PHP realizabamos las consultas a MySQL como nos las habian explicado, por lo menos en mi caso era de esta manera:

$sql = "Select * from `tabla`";
$consulta = mysql_db_query($dbname, $sql, $con);
while ( $resultado = mysql_fetch_array($consulta)) {
echo $resultado[0],$resultado[1];// presentábamos los datos 
}

Bien esta sintaxis la cual nos aprendimos de memoria ahora nos tirara dos errores. El primero lo explique en una entrada anterior y es el mysql_db_query, el cual ha pasado a deprecated y hay que usar el mysql_query($sql, $con), y la base de datos pasa a establecerse con la funcion mysql_select_db($dbname)
El segundo error que nos dará, es en el while el cual es debido a que estamos asignando valor a $resultado dentro de una estructura condicional. Para solucionarlo rápidamente añadiremos true == delante o detrás de la estructura.
La estructura corregida quedara de la siguiente manera

mysql_select_db($dbname); // Seleccionamos la base de datos para trabajar
$sql = "Select * from `tabla`";
$consulta = mysql_query($sql, $con);
while ( true == ($resultado = mysql_fetch_array($consulta))) {
echo $resultado[0],$resultado[1];// presentábamos los datos 
}

Si analizamos la sentencia de la condicional ahora dice mientras la asignacion de valor a resultado sea verdadero hara el echo.
+Info: Aquí

UTF-8 Por defecto en nuestras conexiones PHP:MySQL Tips

Conexión a MySQL clasica

$con = mysql_connect ("localhost","user","password") or die (mysql_error()); // establecemos la conexion
mysql_set_charset('utf8', $con); // establecemos el Charset
$dbname = "midb"; 
mysql_select_db($dbname, $con); // establecemos la base de datos por defecto
// Consulta de ejemplo
$sql = "Select * from mitabla";
// como mysql_db_query esta deprecated tenemos que acostumbrarnos a usar el mysql_query
$consulta = mysql_query($sql,$con); // asi ya no es necesario usar el $dbname en todas las consultas

Conexion a MySQL con Zend_Dd

require_once 'Zend/Loader/Autoloader.php'; // Para que me haga la autocarga de Zend
Zend_Loader_Autoloader::getInstance(); // Establece la autocarga
$pdoParams = array( PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES UTF8;');
$this->db = Zend_Db::factory('Pdo_Mysql',array(
    'host'  =>  'localhost',
    'username'  =>  'user',
    'password'  =>  'password',
    'dbname'    =>  'midb',
    'driver_options' => $pdoParams
));

Subquery Vs. Join – MySQL Tips

Tengo una aplicación web en la cual muestro los datos de las facturas de clientes, junto con el nombre de cliente. Desde un principio observe que la consulta un poco mas de lo normal, asi que me puse a investigar.

Propiedades de las tablas:

La tabla clientes tiene los tipicos campos, los que nos interesan son Id y Nombre, y luego el Id es PrimaryKey, NotNull, AutoIncrement, y tiene 732 registros

La otra tabla es regfacturas, en la cual se registran los datos importantes de las facturas, los campos que nos interesan son el id y el idCliente, el id es PrimaryKey, NotNull, AutoIncrement y el idCliente no esta definido como ForeingKey, luego probaremos a definirlo a ver que resultados nos salen. Esta tabla tiene 4950 registros.

Las dos tablas son InnoDB y como collation utf8, la version del servidor es 5.1.58 y voy a efectuar las consultas en la consola de MySQL.

Nota: Para que los resultados sean efectivos es recomendable efectuar un vaciado de la cache de consultas:


flush query cache;

La consulta original es:


Select r.id as id,r.codigo as codigo,r.fecha as fecha,r.importe as importe ,r.obs_alt as obs_alt,
 c.Nombre as nombre from regfacturas as r join clientes as c on r.id_cliente like c.id order by r.fecha desc

Los resultados de esta consulta fueron: 4945 rows in set (1.71 sec)

Visto estos tiempos, me plantee modificar la consulta y hacerla con subquery

La consulta con subquery es:


Select id, codigo, id_cliente as idCliente,
date_format(fecha,'%d-%m-%Y') as fecha,
importe, obs_alt as observaciones,
(Select Nombre from clientes where id = id_cliente) as cliente
FROM regfacturas order by fecha desc

Los resultados de esta consulta fueron: 4950 rows in set (0.16 sec)

Vistos estos resultados uno dice menuda diferencia entre la subquery y el join, entonces para que usar join, a partir de ahora todo a subquerys. Antes de lanzarnos como locos vamos a afinar un poco mas.

Vamos a probar ahora la consulta con join pero sin la ordenacion de fechas a ver si es eso lo que hace que funcione lenta


Select r.id as id,r.codigo as codigo,r.fecha as fecha,r.importe as importe ,r.obs_alt as obs_alt,
 c.Nombre as nombre from regfacturas as r join clientes as c on r.id_cliente like c.id

Los resultados de esta consulta fueron: 4945 rows in set (1.68 sec)

Parece que sin la ordenacion de fecha ganamos algo de velocidad, aunque el tiempo de respuesta sigue siendo inaceptable

Probemos ahora con la subquery


Select id, codigo, id_cliente as idCliente,
date_format(fecha,'%d-%m-%Y') as fecha,
importe, obs_alt as observaciones,
(Select Nombre from clientes where id = id_cliente) as cliente
FROM regfacturas

Los resultados de esta consulta fueron:  4950 rows in set (0.12 sec)

Con estos resultados vemos que la ordenación tanto en una como en otra tabla tiene un coste de entre 0.03 – 0.04 , y que con esta estructura de tabla es muchisimo mas rapido el uso de subquery que de inner joins

Agregando una ForeingKey

Al ir a agregar una ForeingKey al id_cliente me dio el siguiente error

ERROR 1452: Cannot add or update a child row: a foreign key constraint fails

Googleando descubri que no se podia agregar por que habia registros en la tabla en la cual queria crear la clave foranea que no estaban en la otra tabla.

Para descubir cuales eran estos registro hize la siguiente consulta:


SELECT COUNT( * ) AS cuenta
FROM regfacturas t1
LEFT JOIN clientes t2 ON t1.id_cliente = t2.id
WHERE t2.id IS NULL

El resultado de esta consulta nos dio 5, lo que nos quiere indicar que tenemos 5 campos que estan mal
A continuacion haciendo la siguiente consulta

SELECT * FROM regfacturas t1 LEFT JOIN clientes t2 ON t1.id_cliente = t2.id WHERE t2.id IS NULL

Salieron los campos que nos daban problemas, los borramos, y ya se pudo establecer la FK.
Ahora que ya la tenemos definida vamos a repetir las consultas a ver que sale
La consulta con inner Join dio el siguiente resultado: 4945 rows in set (1.80 sec)
Y la consulta con subquery: 4945 rows in set (0.13 sec)


Con estos resultados podemos decir que en determinados casos es mas rapido usar subquery que inner join, y que establecer foreing keys hace que la consulta se ralentize sobre todo en el caso del uso de inner join

Borrado múltiple – MySQL Tips

Muchas veces nos pondemos encontrar con el caso de tener que borrar un registro de dos tablas. Por ejemplo: Tenemos una tabla pedidos a la cual llamaremos ‘pedidos’ y otra tabla a cual la llamaremos ‘lineasPedido’ pues bien, queremos que si borramos el pedido se borren tambien las lineas de pedido, y todo esto con una sola consulta.

Como campos importantes en este ejemplo vamos a tomar que pedidos tiene un campo id , y lineasPedido tiene aparte del id un campo idPedido que es el que tiene la relacion con la tabla pedidos.

Imaginemos que queremos borrar el pedido 1000, pues vamos ha hacerlo con una sola consulta:


DELETE FROM c, d  USING `pedidos` p, `lineasPedido` l
 WHERE p.id = l.idPedido AND p.id = 1000

Y con esto en una sola consulta borrariamos los datos relacionados de las dos tablas

+ Info http://dev.mysql.com/doc/refman/5.1/en/delete.html