Conoce SQL – Restricciones – UNIQUE

Las restricciones nos ayudan a mantenera la integridad de los datos. ¿Qué es esto? Pues evitar tener datos corruptos o inservibles o inválidos. Las bases de datos tienen dos grandes enemigos: La redundancia de los datos y la corrupción de los datos. Así como la primera aunque muy mala permitiría funcionar a la base de datos la segunda nos dejaría inutilizada la base de datos puesto que si no nos podemos fiar de los datos que hay pues apaga y vámonos.

1- A: Restricción UNIQUE

Ésta restricción impedirá que en una tabla se repita el valor de una (o varias) columnas en las filas de dicha tabla.
Tenemos por ejemplo la tabla Usuarios con el nombre y el correo electrónico.

Queremos simplemente que el correo electrónico no se repita. El método que he visto una y otra vez en muchos sitios los sitio es:

$sql=”SELECT email FROM usuarios WHERE email=$_POST[‘email’]”;
$respuesta=mysql_query($sql);
$fila=mysql_fetch_array($respuesta);
if($fila[‘email’]!=””) // o if(mysql_affected_rows()>0)

echo “Error, el email ya existe”;
else
{

$sql=”INSERT INTO usuarios (nombre,email) VALUES (‘”.$_POST[‘nombre’].”’,’”. $_POST[‘email’].”’)”;
mysql_query($sql);

echo “Usuario creado”;
}

Vaya rollo de código, además de no ser coherente y ser difícil de leer para alguien que no conoce php es poco eficiente ya que le mandas buscar en todos los email. Además si queremos acceder a la base de datos desde otro programa (phpmyadmin por ejemplo o hacemos otra página en jsp) podemos meter la gamba e insertar un usuario con email repetido. Esta validación puede hacerla mysql y da igual quien meta los datos, mysql lo valida y si está bien inserta el usuario y si no pues no. Así tenemos independencia del sistema que usemos para acceder a la base de datos y no tenemos que currarnos un algoritmo en cada lenguaje que usemos.

Lo ideal en estos casos es usar la restricción UNIQUE.

UNIQUE nombreRestriccion (columna)

Es importante también dar nombres a las restricciones para identificarlas rápidamente. La nomenclatura que yo sigo es “nombreTabla_camposAfectados_TipoRestriccion”.

Asi el código php solo sería:

$sql=”INSERT INTO usuarios (nombre,email) VALUES (‘”.$_POST[‘nombre’].”’,’”$_POST[‘email’].”’)”;
if(!mysql_query($sql))
echo “Error al insertar usuario: “.mysql_error();
/* Aqué se podría analizar el error sql. En este caso sería fácil de identificar puesto que solo tenemos una restricción pero podemos tener más */
else
echo “Usuario insertado”;

Al ejecutar la consulta mysql mira que ese email no se repita (por el unique que hemos puesto) y si se repite lanza un error. Es mas fácil de leer, más eficiente (hace trabajar mucho menos a mysql) y no hay posibilidad de duplicar los datos sea cual sea la herramienta que usemos para insertar usuarios.

También se pueden poner más columnas

UNIQUE nombreRestriccion (columna1,columna2)
Este unique valora las dos columnas como un solo elemento, me explico:
UNIQUE usuarios_usuario_email_uk (nombre,email)
Ésta lo que no nos permite es que se repita nombre y email a la vez:

Usuario Email
Alvaro [email protected]
Paco [email protected] [bien porque no se repite la pareja]
Alvaro [email protected] [error, ya existe una pareja igual]

o varios uniques
UNIQUE usuarios_usuario_uk (usuario)
UNIQUE usuarios_email_uk (email)

Usuario Email
Alvaro [email protected]
Paco [email protected] [error, ya existe ese correo]
Alvaro [email protected] [error, ya existe ese nombre]

Espero no haberte liado con este par de ejemplos, solo quería exponer las posibilidades que nos ofrece unique.

Las restricciones se pueden definir en la tabla:

CREATE TABLE usuarios (
usuario VARCHAR(25),
email VARCHAR(50),
UNIQUE usuarios_email_uk (email)
);

O si la tabla ya está creada se añaden:

ALTER TABLE usuarios ADD UNIQUE usuarios_email_uk (email);

Si se añade la restricción ojo con no tener ningún dato que viole dicha restricción, mysql dará error y no creará la restricción hasta que los datos sean válidos.

Y por último, para eliminar una restricción se hace con la siguiente consulta SQL:

ALTER TABLE tabla DROP INDEX restricción;
en nuestro caso
ALTER TABLE usuarios DROP INDEX usuarios_email_uk;

Como puedes observar en la consulta lo que se borra es un índice y precisamente por esto es por lo que es mucho mas eficiente la restricción unique, porque usa índices en vez de buscar cada vez el email en la tabla.

Y esto es todo lo que hay que decir sobre el uso de la restricción UNIQUE.

You may also like...

10 Responses

  1. marcos dice:

    Buenisimo! Muy clarom justo andaba buscando esto :) lo que no termino d entender es de que me sirve ponerle un nombre a la restricción :-/
    Nos vmos!!

  2. Maxpowel dice:

    Me alegra que te haya servido de ayuda!

    Lo de los nombres a las restricciones es para tener control sobre ellas. No es obligatorio pero sí recomendado. Si tienes pocas restricciones igual no te hacen falta pero cuando la base de datos se vaya haciendo más compleja los nombres ayudan mucho para identificar las restricciones.

    Y bueno, con el nombre puedes hacer referencia más fácilmente a una restricción. Te refieres a ella con el nombre ya sea para editarla o borrarla por ejemplo “ALTER TABLE usuarios DROP INDEX usuarios_email_uk;”
    Si no la hubiéramos llamado “usuarios_email_uk” pues a la hora de eliminarla nos sería más lioso por el tema de indicar a mysql qué queremos borrar.

    Espero haberme explicado 😉

  3. marcos dice:

    Hola!! Muchas gracias por la explicación, ahora sí me queda claro ^_^

    Muy buena página, sigan así!! Saludos desde Argentina! :)

    P/D: tengo una duda con campos autoincrementales pero nose donde plantearla. Gracias, hasta luego!

  4. Maxpowel dice:

    Me alegra poder serte de ayuda 😀

    Cuéntame qué problema tienes con los autoincrementales, cuando solucionemos tus dudas ya veré cómo lo organizo 😉

  5. marcos dice:

    Ok, muchas gracias! :) Te cuento:

    yo tengo 2 tablas:

    usuario(idUsuario,apellido,nombre,…,idDomicilio)
    domicilio(idDomicilio,calle,nro,etc)

    donde las claves primarias son: idUsuario en usuario, idDomicilio en domicilio
    la foránea: idDomicilio en usuario

    Los ids son de tipo entero y auto incrementales(para las primarias).

    El problema es el sgte.: cuando yo hago un insert en usuario, digamos: INSERT INTO usuario(apellido,nombre) VALUES(…)
    no paso el idUsuario ya que es autoincremental y me lo crea bien, pero al no pasar idDomicilio me lo deja en NULL (se que no puedo hacerlo autoinc porque ya hay un valor con esa cualidad)
    Si hago el insert de domicilio obviamente el idDomicilio está bien creado.

    Mi duda es como hacer para que el idDomicilio de usuario se corresponda con el idDomicilio de domicilio.

    Perdón x la extensión del comentario pero espero que se entienda, hasta luego!.

  6. Maxpowel dice:

    Bien, creo que te he comprendido así que te planteo mi solución.

    No puedes puedes insertar en dos tablas a la vez pero es muy común querer hacer varias consultas con una sola instrucción (como es tu caso).

    Para ello existen los procedimientos. En tu caso tendrías que crear un procedimiento que recibiera tanto los datos del usuario y los del domicilio. Dentro del procedimiento insertas primero el domicilio (haciendo la consulta de inserción que ya tienes) y a continuación pones la consulta de inserción de usuario poniendo como idDomicilio una funcion llamada LAST_INSERT_ID().
    LAST_INSERT_ID() lo que hace es devolver el valor del ultimo autoincrement, en este caso te devuelve el id del domicilio que acabas de insertar con lo que la relación usuario->domicilio es correcta.

    Una vez hecho el procedimiento, la consulta sería tan sencilla como:
    call insertar_usuario(‘Pepito’,’Calle falsa 123′)
    Inserta la calle falsa 123 y luego a Pepito usando el Id de esa calle recién insertada.
    Espero que la explicación sea comprensible 😛

    Aquí escribí cosillas sobre procedimientos:
    http://www.congdegnu.es/2009/05/11/conoce-sql-lenguaje-procedimental-3-procedimientos/

    Por cierto, veo que en tu base de datos (en como es ese trozo de esquema) en un domicilio pueden vivir varias personas. En este caso deberías comprobar (en el procedimiento) que si el domicilio ya existe, en vez de hacer un insert del domicilio insertes el Id del domicilio existente (obteniéndolo mediante un select) y luego en vez de usar LAST_INSERT_ID() usas el id recién buscado.

    Si te gusta la solución y decides hacer un procedimiento puedes preguntarme las dudas que tengas en el artículo de procedimientos.

    Un saludo!

  7. marcos dice:

    Hola! No he entendido mucho de procedimientos pero simplemente puse el LAST_INSERT_ID() en la tabla usuario y anduvo a la perfección!! ^_^

    Muchas gracias, adiós!! :)

  8. Jorge dice:

    Muy bueno me gusta la metodologia que tienes pero una pregunta dime como puedo ir en orden cronologico para ver todo lo referente a SQL transact sql.

  9. Maxpowel dice:

    Transact SQL es de Microsoft, pero para ir en orden cronológico (si te he entendido bien) añades una columna tipo timestamp con la fecha de creación y luego usas “ORDER BY”

  10. miguel dice:

    eres un máquina tio

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *