Cargando la página...

Integridad referencial

Publicado por Gastón el 24/04/2009 en MySQL - Nivel Intermedio

Tabla de contenidos

Avatar de
  • Integridad Referencial para una Base de Datos Consistente
  • Relación uno a muchos
    • Inconsistencias
    • Integridad Referencial
  • Relación muchos a muchos
    • Inconsistencias
    • Integridad Referencial

La tabla de contenidos pertenece al artículo completo y no al contenido parcial de cada página del mismo si fuese el caso.

Leer los comentarios.

Resúmen

Artículo destinado a introducir los principios de la integridad referencial para bases de datos y en particular su uso en MySQL, más precisamente en tablas INNOdb.
El artículo se basa en dos ejemplos detallados (realmente detallados) para su fácil entendimiento; ejemplos basados en casos reales (en dos módulos de este mismo sitio).
Si bien el artículo es plenamente práctico, intento incluir algunos conceptos teóricos para alivianar un poco su lectura y llegar mejor a buen puerto.

Sobre el autor

Avatar de Gastón

Gastón tiene 31 años, vive en Argentina / Santa Fe / Santa Fe y su ocupación es Desarrollador de aplicaciones web.

Forma parte del club desde 19/10/2009 a las 20:52 habiendo estado en linea ayer a las 18:27 por última vez.

Ha publicado 50 artículos en clubdesarrolladores con un promedio de valoración de 7.96 puntos. Puedes visitar su sitio web en http://www.devstudios.com.ar

Estadísticas

  • Leido 13162 veces
  • Valorado 9.42 puntos

Integridad Referencial para una Base de Datos Consistente



La situaciones que vamos a estudiar son muy sencillas y comunes.
Se tienen entidades que ofrecen algún tipo de relación lógica entre sus cardinalidades, y se desea automatizar la actualización de las mismas manteniendo la base de datos consistente.

Relación uno a muchos



En este caso estudiaremos el típico caso de una ocurrencia de entidad A pertenece a una ocurrencia de la entidad B. Se trata de una relación de uno a muchos o N a N, o maestro/detalle.
Por ejemplo, en este mismo módulo de artículos se da ésta situación: este artículo pertenece a la categoría MySQL y la categoría MySQL puede tener muchos artículos en su haber.

En este tipo de relaciones, luego de realizar el módelo conceptual, obtenemos el modelo lógico el cual resulta en la siguiente definición de tablas (solo se mostrarán las claves primarias y foraneas, obviando cualquier otro tipo de columnas):

CREATE TABLE categorias
(
id_categoria integer not null primary key,
categoria varchar(30) not null
);

CREATE TABLE articulos
(
id_articulo integer not null primary key,
id_categoria integer null,
articulo varchar(60) not null,
texto text not null
);


En la definición de la tabla articulos decimos que un artículo no debe necesariamente pertenecer a una categoría (puede ser null su referencia a categoria).

Veamos un poco de teoría. Una clave primaria se define como una o más columnas que identifican unívocamente una fila en nuestra misma tabla. En nuestro ejemplo tenemos id_categoría en la tabla categorias e id_articulo en la tabla articulos. Como se puede ver en el código éstas se definen con las palabras claves primary key.
Una clave foranea se define como una o más columnas que identifican unívocamente una fila en otra tabla. Es decir, una clave foranea referencia a una clave primaria de una tabla foranea. En el ejemplo anterior sería id_categoria de la tabla articulos, que haría referencia a id_categoria de la tabla categorias.
Como se puede ver, el motor de base de datos no sabe aún de esta referencia (ni de cómo tratarla), a eso lo veremos a continuación. Pero antes notemos un detalle muy importante, en la definición de tablas anterior no especificamos que tipo de tabla son, y MySQL soporta integridad referencial en tablas INNOdb, no así en tablas MyISAM. Con el siguiente código podremos reparar tal situación y convertir nuestras tablas a INNOdb:

ALTER TABLE categorias TYPE=INNODB
ALTER TABLE articulos TYPE=INNODB


Inconsistencias



Una inconsistencia es una situación que se presenta cuando una clave foranea referencia a una clave primaria inexistente en la tabla referenciada. Lo explicaré con un ejemplo.

Supongamos que tenemos dos categorías y tres artículos:

INSERT INTO categorias (id_categoria, categoria) VALUES
(1, 'MySQL'),
(2, 'Microsoft SQL Server');

INSERT INTO articulos (id_articulo, id_categoria, articulo, texto) VALUES
(1, 1, 'Tipos de tablas en MySQL', 'Contenido del artículo...'),
(2, 1, 'Procedimientos almacenados', 'Contenido del artículo...'),
(3, 2, 'Tablas autoreferenciadas', 'Contenido del artículo...');


Como se puede ver tenemos estas referencias:

(1) (1) 'Tipos de tablas en MySQL'   -------> (1) 'MySQL'
(2) (1) 'Procedimientos almacenados' -------> (1) 'MySQL'
(3) (2) 'Tablas autoreferenciadas' -------> (2) 'Microsoft SQL Server'


Nota: Los número encerrados entre paréntesis indican las claves primarias (y foraneas en el caso de la primer columna) de las tablas en cuestión.

Y ahora por algún motivo eliminamos de la tabla categorias la categoría de MySQL (1).
¿Qué sucede con los artículos que correspondían a esta categoría (1) y (2)?...
¡Quedan huérfanos! apuntarían a claves incorrectas y nuestra base de datos se volvería inconsistente.
Cuando digo que apuntarían a claves incorrectas se pueden ver dos posibles situaciones:

1 - Se elimina (1) MySQL de la tabla categorias, los articulos que apuntaban a (1) MySQL seguirían apuntando a este, pero ¡éste ya no existe!

(1) (1) 'Tipos de tablas en MySQL'   -------> (???)
(2) (1) 'Procedimientos almacenados' -------> (???)
(3) (2) 'Tablas autoreferenciadas' -------> (2) 'Microsoft SQL Server'


2 - Se elimina (1) MySQL de la tabla categorias, y se agrega una nueva categoría a esta tabla (1) 'PostgreSQL' (esto es totalmente válido, se cumple la restricción de clave primaria en categorias). Ahora tenemos que:

(1) (1) 'Tipos de tablas en MySQL'   -------> (1) 'PostgreSQL'
(2) (1) 'Procedimientos almacenados' -------> (1) 'PostgreSQL'
(3) (2) 'Tablas autoreferenciadas' -------> (2) 'Microsoft SQL Server'


Noten la primera ocurrencia de relación: (1) (1) 'Tipos de tablas en MySQL' -------> (1) 'PostgreSQL'... creo que habla por si misma.

Integridad Referencial



Estas situaciones son muy comunes y, aunque pueden resolverse mediante el lenguaje de programación cliente, es mejor dejar a la propia base de datos mantener la consistencia. Esto se debe a que mantener la consistencia desde un lenguaje como PHP o C++ por ejemplo representaría tener que realizar una consulta extra luego de cada UPDATE o DELETE en la tabla referenciada, lo que implicaría una perdida de legibilidad y de performance.

Por ejemplo si la intención es eliminar la categoría de MySQL, deberemos también actualizar la tabla articulos para que la consistencia se mantenga entre ambas tablas, por lo que deberemos actualizar aquellas filas cuya columna id_categoria sea igual a 1.

DELETE FROM categorias WHERE id_categoria = 1;
UPDATE articulos SET id_categoria = NULL WHERE id_categoria = 1;


Y si la intención es modificar el id_categoria de 'MySQL' a 3 también deberíamos hacer lo propio en aquellas filas de tabla articulos que referencian a MySQL.

UPDATE categorias SET id_categoria = 3 WHERE id_categoria = 1;
UPDATE articulos SET id_categoria = 3 WHERE id_categoria = 1;


Si por algún motivo olvidamos esto nuestra base de datos quedará inconsistente, o lo que es lo mismo, incoherente tal como se explicó anteriormente.

Ahora por fin, relacionaremos las tablas. He aquí la sintaxis de la sentencia ALTER TABLE que creará la restricción de clave ajena (o foránea) y por ende la integridad referencial para estas dos tablas:

ALTER TABLE articulos 
ADD FOREIGN KEY(id_categoria) REFERENCES categorias(id_categoria)
ON DELETE SET NULL
ON UPDATE CASCADE


Esto se puede leer asi: Alterar la tabla articulos agregando una restricción a la clave foranea id_categoria (de la tabla articulos) que referencia a id_categoria (de la tabla categorias), cuando se elimine alguna categoría (y por consiguiente su id_categoria en la tabla categorias) setear NULL a id_categoria (de la tabla articulos) y cuando se modifique alguna categoria (se modifique id_categoria de la tabla categorias) aplicar esa modificación a todos los articulos (a todos los id_categoria de la tabla articulos).

Lealo nuevamente si tiene dudas, igualmente aqui hay un ejemplo esclarecedor.

Por ejemplo si hacemos:

DELETE FROM categorias WHERE id_categoria = 1;


automáticamente se le dará el valor NULL a todas las tuplas de articulos cuyo id_categoria sea igual a 1.

Quedaria asi:

(1) (NULL) 'Tipos de tablas en MySQL'   -------> NULL
(2) (NULL) 'Procedimientos almacenados' -------> NULL
(3) (2) 'Tablas autoreferenciadas' -------> (2) 'Microsoft SQL Server'


Y si en lugar de la eliminación anterior hacemos una actualización como ésta:

UPDATE categorias SET id_categoria = 3 WHERE id_categoria = 1;


automáticamente se le dará el valor 3 a todas las tuplas de articulos cuyo id_categoria sea igual a 1.

Nos quedaria asi:

(1) (3) 'Tipos de tablas en MySQL'   -------> (3) 'MySQL'
(2) (3) 'Procedimientos almacenados' -------> (3) 'MySQL'
(3) (2) 'Tablas autoreferenciadas' -------> (2) 'Microsoft SQL Server'


¿Fácil no? una solución elegante y eficiente.

Y que sucede si hubiera definido la restricción de la siguiente manera:

ALTER TABLE articulos 
ADD FOREIGN KEY(id_categoria) REFERENCES categorias(id_categoria)
ON DELETE CASCADE
ON UPDATE CASCADE


¡CASCADE tanto en DELETE como en UPDATE! A eso lo veremos a continuación cuando expliquemos integridad referencial en una relación de muchos a muchos (dé vuelta la página!).

Si tiene alguna pregunta sobre éste artículo por favor deje un comentario y será respondido.

Descargas

No existen descargas asociadas

Comentarios

Sus comentarios son importantes.

Listado de comentarios

1
13
mayo
2009
Avatar de Guille SSS
Guille SSS aportó:
Muy bueno el artículo, me fue muy útil.

Cómo implementarías unas tablas Productos, Categorias y Subcategorias?

Donde a un Producto le podés indicar a que categoría y subcategoría pertenece.

saludos
Guillermo
2
13
mayo
2009
Avatar de Gastón
Gastón [Autor] aportó:
Me alegro que te haya sido útil.

En cuanto a tu pregunta, y asumiendo que no existe una relación directa entre las ocurrencias de Categorías y Subcategorías lo plantearía de la siguiente manera.

Te hago un bosquejo que simularía un diagrama DER de lo que voy a aplicar:

Categorias 1 -------- N Productos N -------- 1 Subcategorias

CREATE TABLE
categorias
(
id_categoria integer not null primary key,
categoria varchar(30) not null
);

CREATE TABLE subcategorias
(
id_subcategoria integer not null primary key,
subcategoria varchar(30) not null,
);

CREATE TABLE productos
(
id_producto integer not null primary key,
id_categoria integer null,
id_subcategoria integer null,
producto varchar(60) not null
);

ALTER TABLE productos
ADD FOREIGN KEY(id_categoria) REFERENCES categorias(id_categoria)
ON DELETE CASCADE ON UPDATE CASCADE;

ALTER TABLE productos
ADD FOREIGN KEY(id_subcategoria) REFERENCES subcategorias(id_subcategoria)
ON DELETE CASCADE ON UPDATE CASCADE;


Como podrás apreciar, no he colocado relación alguna entre categorías y subcategorías; si existiera tal relación se simplificaría aún más la integridad referencial al relacionar productos únicamente con subcategorias.
Por otro lado asumo que al eliminar o modificar una categoría o subcategoría se deben propagar los mismos en cascada en la tabla productos. Lo que no siempre es deseable.

Para completar la idea, todo depende de las reglas de negocios que debas respetar. La que he planteado es una alternativa, pero yo desconozco las reglas de tu sistema.
Sería posible implementar una relación 1 a N entre Categorías y Subcategorías, y luego aplicar otra relación 1 a N entre Subcategorias y Productos resultando un diagrama DER similar a este:

Categorias 1 -------- N Subcategorias 1 -------- N Productos

Existen infinidad (bueno, no tanto) de posibilidades para diseñar. Todo parte de las relaciones obtenidas en el diagrama DER.

Saludos.
3
14
mayo
2009
Avatar de Guille SSS
Guille SSS aportó:
Gracias por tu respuesta,
en realidad sí hay una relación entre categorias y subcategorias, cada categoria tiene sus subcategorias, asi que como vos decis, voy a conectar la tabla productos directamente con subcategorias, esto lo habia pensado pero pensé que estaría mal conectar directamente productos y subcategorias salteando categorias.



CREATE TABLE IF NOT EXISTS `categorias` (
`idcategoria` int(11) unsigned NOT NULL auto_increment,
`categoria` varchar(30) NOT NULL,
PRIMARY KEY (`idcategoria`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;

CREATE TABLE IF NOT EXISTS `subcategorias` (
`idsubcategoria` int(11) unsigned NOT NULL auto_increment,
`idcategoria` int(11) unsigned NOT NULL,
`subcategoria` varchar(30) NOT NULL,
PRIMARY KEY (`idsubcategoria`),
KEY `idcategoria` (`idcategoria`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;

CREATE TABLE IF NOT EXISTS `productos` (
`idproducto` int(11) unsigned NOT NULL auto_increment,
`producto` varchar(30) NOT NULL,
`idsubcategoria` int(11) unsigned NOT NULL,
PRIMARY KEY (`idproducto`),
KEY `idsubcategoria` (`idsubcategoria`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;

ALTER TABLE `subcategorias`
ADD CONSTRAINT `subcategorias_ibfk_1` FOREIGN KEY (`idcategoria`) REFERENCES `categorias` (`idcategoria`) ON UPDATE CASCADE;

ALTER TABLE `productos`
ADD CONSTRAINT `productos_ibfk_3` FOREIGN KEY (`idsubcategoria`) REFERENCES `subcategorias` (`idsubcategoria`) ON UPDATE CASCADE,




En las claves foráneas no pongo delete cascade, porque todavia no se si quiero borrar en cascada...

saludos
guillermo

4
05
julio
2009
Avatar de Jim Unikornian
Jim Unikornian aportó:
Excelente!!! nunca mejor explicado un lujo de artículo te felicito!!!!!
5
24
noviembre
2009
Avatar de Jibraham Cervantes
Excelente el artículo que escribiste, y vaya la forma en que lo explicas :)

Como verás soy nuevo usuario, pero me llama la atención mucho el diseño de BD además que lo veo en la uni y te agradezco, ya que me será muy útil en mi proyecto final para checar las inconsistencias que pudieran existir en el sistema que se tiene que crear :)

Bueno me despido!!!

Gracias por la informació!!!

Saludos!!!
6
15
abril
2010
Avatar de Daniel Santilli
Daniel Santilli aportó:
Hola. Muy bueno tu artículo!!! pero tengo una duda que no me la pude sacar, y espero me puedas ayudar. Gracias de antemano!
Resulta que tengo una base armada en la cual tengo 4 tablas. Hay una tabla en la cual tengo su propio código como clave y los códigos de las otras tres tablas como atributos. Las tres tablas que quiero relacionar tienen cada una sus claves correspondientes pero ninguna tiene el código de la que te mencionaba al principio. Mi duda es como hacer para relacionar la Tabla Principal con las otras tres ya que cuando relaciono a ésta con la primer tabla no tengo inconvenientes pero cuando la quiero relacionar con la segunda me tira este error: Error No 1061 Duplicate Key Name 'FK_Clave' y porsupuesto que con la terecera me dice lo mismo.

Espero me puedas dar una solución! Muchas gracias.
7
16
mayo
2010
Avatar de COMO Yo
COMO Yo aportó:
Saludos, tengo una inquietud:
resulta que tengo dos tablas relacionadas articulo y fotoarticulo en fotoarticulo hay una fk apuntando a el idarticulo pero cuando deseo insertar un registro nuevo no lo relaciona , agradezco si me pueden ayudar con el asunto o me remitan a donde puedo encontrar documentación del tema
8
23
noviembre
2010
Avatar de Gastón
Gastón [Autor] aportó:
@Daniel, parece ser un problema de definición de indices. Verifica que los estes aplicando correctamente (y en el lugar indicado).

@Como, en una relación maestro/detalle como la que indicas, lo primero es dar de alta al maestro (articulo), y luego dar de alta detalles (foto).
Al dar de alta articulo, seguramente tendras disponible la clave primaria de esta, la cual será clave foranea de foto.

La base de datos no relaciona automaticamente, es tu responsabilidad cargar los datos en el orden indicado y asociar las entidades de la manera que corresponden.

Saludos.
9
23
noviembre
2010
Avatar de BrunoX
BrunoX aportó:
hola gaston, aca estoy; Te planteo mi duda... como guillermo, yo tambien tengo que relacionar CATEGORIAS, SUBCATEGORIAS Y PRODUCTOS.
1° Quisiera saber que estructura utilizar.
2° Lo que tengo que hacer es lo siguiente:
llenar con "categorias" un array php, luego a cada categoria, asignarle su correspondiente "subcategoria" y a cada subcategoria" su correspondiente producto. deberia quedarme algo como lo que esta en esta web: http://www.yaguar.com/frontendSP/asp/index.asp
en la parte que dice GONDOLAS esta plasmado lo que quiero hacer.
Corregíme si me equivoco.

salu2 y muchas gracias de antemano!

Bruno.
10
24
noviembre
2010
Avatar de Gastón
Gastón [Autor] aportó:
Hola Bruno.

La siguiente estructura parece viable para lo que te propones hacer.

CREATE TABLE
categorias
(
id_categoria integer not null primary key,
categoria varchar(30) not null,
);

CREATE TABLE subcategorias
(
id_subcategoria integer not null primary key,
id_categoria integer not null,
subcategoria varchar(30) not null,
index(id_categoria)
);

CREATE TABLE productos
(
id_producto integer not null primary key,
id_subcategoria integer null,
producto varchar(60) not null,
index(id_subcategoria)
);

ALTER TABLE subcategorias
ADD FOREIGN KEY(id_categoria) REFERENCES
categorias(id_categoria)
ON DELETE CASCADE ON UPDATE CASCADE;

ALTER TABLE productos
ADD FOREIGN KEY(id_subcategoria) REFERENCES
subcategorias(id_subcategoria)
ON DELETE CASCADE ON UPDATE CASCADE;


Lo primero que deberias tener es un listado de las categorias

SELECT id_categoria,
categoria 
FROM categorias
ORDER BY categoria ASC


Luego para obtener las subcategorías de una categoría dada:

SELECT id_subcategoria,
subcategoria 
FROM subcategorias
WHERE id_categoria = :id_categoria
ORDER BY subcategoria ASC


siendo :id_categoria el id de la categoria de primera elección.

Luego para mostrar un listado con los productos de una subcategoría en especial, el proceso es similar:

SELECT id_producto,
producto
FROM productos
WHERE id_subcategoria = :id_subcategoria
ORDER BY producto ASC


Una forma de implementar esto con PHP sería la siguiente:

1 - Traer todas las categorías
2 - Por cada categoría
2.a - Traer sus subcategorías
3 - Mostrarlo de manera similar a tu pagina de referencia, usando CSS y Javascript para darle el comportamiento deseado (mostrar todas las categorias, y al hacer click en una de estas, mostrar sus subcategorias).
4 - Al hacer click en una subcategoría, redirigir a una página que muestre un listado de los productos asociados a esa subcategoría.

Tenes otras formas, por supuesto, de implementar esto. Ajax es una opción. Pero la expuesta me pareció la manera más simple.

Espero que te oriente. Saludos.
11
24
noviembre
2010
Avatar de BrunoX
BrunoX aportó:
Gaston, antes que nada te agradezco la ayuda infinitamente, me ayudaste a saber que mi planteamiento (de PHP) no era erroneo, asi como vos dijiste me lo habia planteado.
El problema que me surge ahora en la consulta de productos es el siguiente:

1° el ":id_subcategoria" me tira un error, no me lo toma con los dos puntos ":".

2° me devuelve lo mismo que esta consulta con "JOIN".

SELECT p.producto FROM
productos p JOIN subcategorias s ON p.id_subcategoria =
s.id_subcategoria ORDER BY p.id_subcategoria


Entonces utilizando esa consulta o la tuya sin los ":"
al presionar el link de la SUBCATEGORIA me devuelve solamente la primer ocurrencia de varios PRODUCTOS correspondientes a esa SUBCATEGORIA.

Aca va el codigo en PHP de mi consulta:



$id_subcat=$_GET["var"];

$h=0;

$consulta_prod = $db->consulta("SELECT p.producto FROM productos p JOIN subcategorias s ON p.id_subcategoria = s.id_subcategoria ORDER BY p.id_subcategoria");


if ($row = mysql_fetch_array($consulta_prod)){

do
{
$producto[$h]=$row["producto"];
$h++;
}
while($row=mysql_fetch_array($consulta_prod));
$h=0;
}

echo $producto[$id_subcat]."<br><br>";


Que es lo que me esta faltando agruegar para que me liste todos los productos correspondientes a esa categoria?

te agradezco mucho tu ayuda, me ayudo mucho a orientarme!

saludos!

Bruno.
12
24
noviembre
2010
Avatar de BrunoX
BrunoX aportó:
SOLUCIONADO!!! GRACIAS POR TODO GASTON!!!!

aca te dejo el codigo, a alguien le puede llegar a servir!

saludos!!! :D

$consulta_prod =
$db->consulta("SELECT id_subcategoria,producto FROM
productos WHERE id_subcategoria = id_subcategoria ORDER BY
id_subcategoria ASC");

if ($row = mysql_fetch_array($consulta_prod)){
do
{

if($row["id_subcategoria"]==$id_subcat+1)
{
$producto[$h]=$row["producto"];
$h++;
}
}
while($row=mysql_fetch_array($consulta_prod));
$h=0;
}

echo $subcat.'<br><br>';
while($h<$consulta_prod)
{
echo $producto[$h]."<br><br>";
$h++;
}
13
30
noviembre
2010
Avatar de Gastón
Gastón [Autor] aportó:
Hola Bruno.
Los ":" son solo una notación que suelo usar para hacer entender que es una variable que debe ser reemplazada por un valor. Es por eso que no te funcionaba. Recuerdo que esa notación se utilizaba en Delphi en componentes de acceso a consultas, pero eso ya es otra historia. Espero no haberte confundido con eso.

Con respecto al codigo que nos mostras, se puede ver que estas solicitando todos los productos, independientemente del id_categoría que tengan, pues:

WHERE id_subcategoria =
id_subcategoria


siempre es válido para cualquier producto. Estas comparando la columna consigo misma, es como si hicieras 1 = 1, lo cual siempre se cumple. Podrías obviar ese "WHERE" y obtendrías el mismo conjunto de datos.

Luego se ve un poco complejo el código, pero calculo que lo que hace es completar el array $producto con el nombre del producto siempre y cuando la subcategoría a la que pertenece sea la que se busca ($id_subcat + 1).

Ese código podría simplificarse considerablemente de la siguente forma:

echo
$subcat.'<br><br>';
$consulta_prod = $db->consulta("SELECT id_subcategoria,producto FROM
productos WHERE id_subcategoria = {$id_subcat} ORDER BY
id_subcategoria ASC");
while($row = mysql_fetch_array($consulta_prod)) {
echo $row['producto'] . "<br><br>";
}


Desconozco la utilidad de $h, pero podrías acumularla dentro del WHILE en caso que lo necesites.
Por otra parte, la consulta necesita que se le brinde un $id_subcat válido.


Espero que te haya servido.

Un abrazo.
14
17
febrero
2011
Avatar de BrunoX
BrunoX aportó:
Hola Gaston, aqui de nuevo... tengo un problema y la verdad nose a que se debe.. paso a comentarlo:

aca debajo esta el codigo y el problema es el siguiente:

este codigo lo que hace es ejecutar una consulta y me lista los productos segun las subcategorias pero al darle click en el submit del form, me tira el siguiente mensaje de error mysql:

"You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1"

y la verdad ya busque por todo el codigo y no encuento algo que este mal en la sintaxis, lo que si pude darme cuenta es que no me envia algunos valores por la url y cambiandole el ultimo "while" con lo siguiente
while($h<$consulta_prod)
me lista los productos pero no todos y me deja ingresarlo pero erroneamente.

no se que es lo que esta pasando y si hay alguna posibilidad de no renovar todo mi codigo te lo agradeceria por q ya probe bastante.. y no lo consigo.

gracias de antemano!


$consulta_prod = $db->consulta("SELECT p.id_producto,p.id_subcategoria,p.producto, p.precio_venta, p.foto FROM productos p JOIN subcategorias s ON p.id_subcategoria = s.id_subcategoria ORDER BY p.id_producto ASC");

$consulta_prod_count = $db->consulta("SELECT * FROM productos");

$filas = mysql_num_rows($consulta_prod_count);

if ($row = mysql_fetch_array($consulta_prod))
{
do
{

if($row["id_subcategoria"]==$id_subcat)
{
$id_prod[]=$row["id_producto"];
$producto[]=$row["producto"];
$precio[]=$row["precio_venta"];
$ruta_foto[]=$row["foto"];
//$h++;
}
}
while($row=mysql_fetch_array($consulta_prod));
$h=1;
}


//generando tabla para mostrar listado:
if($_COOKIE['auth']=="SI")
{

echo '<table id="hor-minimalist-b" border=0>

<thead>

<th scope="col">LISTADO</th>

</thead>
<tbody>

<tr><FORM method="get" action="Acarrito.php">';

do
{
echo '

<tr>
<td> <a href="pepe.php">'.$producto[$h].' </a></td>
<td style="color: ff4800;"> <b>'.$precio[$h].' +iva</b></td>
<td>
cant:<INPUT type="hidden" name="id_prod" value="'.$id_prod[$h].'">
<INPUT type="hidden" name="precio_venta" value="'.$precio[$h].'">
<INPUT class="campos_lista" name="cant" type="text" maxlength="3" size="1" value="1"><br>un<br>
</td>
<td>
<a href="#" class="tip">
<img src="img/camara.jpg" border=0>
<span><img src="'.$ruta_foto[$h].'"></span>
</a>
</td>
<td>
<INPUT type="image" name="submit" src="img/carrito.jpg">

</td>
</tr>
';
$h++;
}
while($h < $filas-1);


salu2 y espero puedas ayudarme!
15
18
febrero
2011
Avatar de Gastón
Gastón [Autor] aportó:
Bruno:

Si ejecutas esta consulta con algun cliente como phpmyadmin:

SELECT
p.id_producto,
p.id_subcategoria,
p.producto,
p.precio_venta,
p.foto
FROM
productos p
JOIN subcategorias s
ON p.id_subcategoria = s.id_subcategoria
ORDER BY
p.id_producto ASC


te tira error?
16
18
febrero
2011
Avatar de BrunoX
BrunoX aportó:
hola, mira hasta ayer lo probe desde la consola mysql y no me tiraba error, me tira el listado ordenado... pero esta tarde me fijo de nuevo y te aviso..

salu2
17
18
febrero
2011
Avatar de BrunoX
BrunoX aportó:
hola gaston, ahi probe y... no, no me tira error se ejecuta perfectamente la consulta... nose que le pasa.
18
18
febrero
2011
Avatar de BrunoX
BrunoX aportó:
Bueno... paso a comentarte gaston que por un lado solucione el error, y por otro quiero prender fuego la computadora con todo lo que hay alrededor... es increible...
te lo muestro:

ERROR:

</td>
</tr>
';
$h++;
}
while($h < $filas-1);


SOLUCION:

</td>
</tr>
</FORM>
';
$h++;
}
while($h < $filas-1);


igualmente, demas esta decir que esa linea donde se encuentra AHORA el <FORM> estaba despues del while asi que ni siquiera puedo decir que estaba faltando. Ahroa... por que lo hacia? solo dios sabe...

salu2 y gracias por prestarme algo de tu tiempo.. que siempre me es de gran ayuda..

xD!
19
12
julio
2011
Avatar de javier
javier aportó:
gaston: muy buen articulo, hace rato estaba buscando algo asi por suerte googleando lo encontre. Tengo una consulta, estoy realizando un trabajo y tengo unas dudas en cuanto a las tablas, trabajo con cuatro tablas categorias, subcategorias, marcas y productos. La tabla categoria puede o no tener subcategorias y se ingresan en productos, a su vez cada producto puede o no tener una marca asociada, las tablas las arme de la siguiente manera:
CREATE TABLE IF NOT EXISTS `categorias` (
`id_cat` int(10) unsigned NOT NULL auto_increment,
`nom_cat` varchar(25) NOT NULL,
PRIMARY KEY (`id_cat`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3
;

CREATE TABLE IF NOT EXISTS `subcategorias` (
`id_subcat` int(10) unsigned NOT NULL auto_increment,
`id_cat` int(10) unsigned NOT NULL,
`nom_subcat` varchar(30) NOT NULL,
PRIMARY KEY (`id_subcat`),
KEY `id_cat` (`id_cat`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6
;

CREATE TABLE IF NOT EXISTS `marcas` (
`id_marca` int(10) unsigned NOT NULL auto_increment,
`marca` varchar(25) NOT NULL,
`imagen` varchar(15) NOT NULL,
PRIMARY KEY (`id_marca`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3
;

CREATE TABLE IF NOT EXISTS `productos` (
`id_prod` int(10) unsigned NOT NULL auto_increment,
`nom_prod` varchar(20) NOT NULL,
`descrip` varchar(100) NOT NULL,
`imagen` varchar(15) NOT NULL,
`directorio` varchar(20) NOT NULL,
`id_subcat` int(10) unsigned NOT NULL,
`id_cat` int(10) unsigned NOT NULL,
`id_marca` int(10) unsigned NOT NULL,
PRIMARY KEY (`id_prod`),
KEY `id_subcat` (`id_subcat`),
KEY `id_cat` (`id_cat`),
KEY `id_marca` (`id_marca`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2
;

ALTER TABLE subcategorias
ADD FOREIGN KEY(id_cat) REFERENCES
categorias(id_cat)
ON DELETE CASCADE ON UPDATE CASCADE;

ALTER TABLE productos
ADD FOREIGN KEY(id_subcat) REFERENCES
subcategorias(id_subcat)
ON DELETE CASCADE ON UPDATE CASCADE;

ALTER TABLE productos
ADD FOREIGN KEY(id_cat) REFERENCES
categorias(id_cat)
ON DELETE CASCADE ON UPDATE CASCADE;

ALTER TABLE productos
ADD FOREIGN KEY(id_marca) REFERENCES
marcas(id_marca)
ON DELETE CASCADE ON UPDATE CASCADE;

Saludos y espero tu opinion. Gracias.
20
22
julio
2011
Avatar de Gastón
Gastón [Autor] aportó:
Hola Javier.

Un producto puede estar asociado a una categoría o a una subcategoría. Un producto no puede no estar asociado a nada.
En tu tabla productos se ve que exigis que tenga tanto categoría como subcategoría, eso podria no ser lo deseado y es facilmente solucionable permitiendo nulos para esas claves (Incluso podrías crear un trigger o utilizar checks para que al menos tenga un valor categoría o subcategoría, pero no los dos).
Lo mismo ocurre con la marca, si la marca es opcional, deberías aceptar null en esa columna.

La integridad es discutible, veo que colocaste CASCADE en todas.
Si eliminas una categoría eliminas sus subcategorías las cuales van a eliminar sus productos relacionados. Es un comportamiento que parece peligroso, siendo el producto tu entidad estrella en el modelo. Deberías protegerlo con un NO ACTION, forzando al usuario a eliminar el producto CONSCIENTEMENTE, y luego si, eliminar las categorías vacías.

Todo tiene que ver con las reglas de negocios que te impongan. Sin embargo nota esta cuestion técnica. Si eliminas una marca eliminarás sus productos asociados, todo esto de manera transaccional (ACID), como eliminarás luego las imágenes del producto cuando estas tuplas ya hayan sido eliminadas? Esto no es del todo independiente de la integridad referencial, tu programación cliente deberá primero seleccionar los productos de la marca, luego eliminar la marca y si es exitosa esta eliminación, (y mantenes la eliminación en cascada) deberás ir eliminando sus imágenes haciendo uso de la selección anterior, porque en este momento ya no existen dichos productos.

Espero haberte aclarado algunas dudas.

Saludos.
21
25
julio
2011
Avatar de javier
javier aportó:
gaston: gracias por responder, estoy algo confundido realize los cambios en base a lo sugerido. Lo que quiero hace seria algo asi: la tabla productos contiene a las categorias y subcategorias (una categ puede o no tener una subcateg y tener o no una marca asociada). Por ej: categoria Herramientas no tiene subcateg y tiene los sig productos mechas (sin marca y con imagen), tarugos (marca py con imagen del prod y marca), machos (marca uranga con imagen del prod y marca), otra categoria herramientas electricas que tendria subcategorias taladros, amoladoras, con marcas como bosch, metabo, etc. con imagenes del prod y la marca. Arme las tablas asi:
CREATE TABLE IF NOT EXISTS `categorias` (
`id_cat` int(10) unsigned NOT NULL auto_increment,
`nom_cat` varchar(25) NOT NULL,
PRIMARY KEY (`id_cat`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3
;

CREATE TABLE IF NOT EXISTS `subcategorias` (
`id_subcat` int(10) unsigned NOT NULL auto_increment,
`id_cat` int(10) unsigned NOT NULL,
`nom_subcat` varchar(30) NOT NULL,
PRIMARY KEY (`id_subcat`),
KEY `id_cat` (`id_cat`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6
;

CREATE TABLE IF NOT EXISTS `marcas` (
`id_marca` int(10) unsigned NOT NULL auto_increment,
`marca` varchar(25) NOT NULL,
`imagen` varchar(15) NOT NULL,
PRIMARY KEY (`id_marca`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3
;

CREATE TABLE IF NOT EXISTS `productos` (
`id_prod` int(10) unsigned NOT NULL auto_increment,
`nom_prod` varchar(20) NOT NULL,
`descrip` varchar(100) NOT NULL,
`imagen` varchar(15) NOT NULL,
`directorio` varchar(20) NOT NULL,
`id_subcat` int(10) unsigned NULL,
`id_cat` int(10) unsigned NULL,
`id_marca` int(10) unsigned NULL,
PRIMARY KEY (`id_prod`),
KEY `id_subcat` (`id_subcat`),
KEY `id_cat` (`id_cat`),
KEY `id_marca` (`id_marca`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2
;

ALTER TABLE categorias
ADD FOREIGN KEY(id_cat) REFERENCES
categorias(id_cat)
ON DELETE NO ACTION ON UPDATE CASCADE;

ALTER TABLE subcategorias
ADD FOREIGN KEY(id_cat) REFERENCES
categorias(id_cat)
ON DELETE NO ACTION ON UPDATE CASCADE;

ALTER TABLE productos
ADD FOREIGN KEY(id_subcat) REFERENCES
subcategorias(id_subcat)
ON DELETE NO ACTION ON UPDATE CASCADE;

ALTER TABLE productos
ADD FOREIGN KEY(id_cat) REFERENCES
categorias(id_cat)
ON DELETE NO ACTION ON UPDATE CASCADE;

ALTER TABLE productos
ADD FOREIGN KEY(id_marca) REFERENCES
marcas(id_marca)
ON DELETE NO ACTION ON UPDATE CASCADE;

ALTER TABLE marcas
ADD FOREIGN KEY(id_marca) REFERENCES
categorias(id_cat)
ON DELETE CASCADE ON UPDATE CASCADE;

Espero tu respuesta y gracias nuevamente
22
27
julio
2011
Avatar de javier
javier aportó:
gaston: realize nuevos cambios

CREATE TABLE IF NOT EXISTS `categorias` (
`id_cat` int(10) unsigned NOT NULL auto_increment,
`nom_cat` varchar(25) NOT NULL,
PRIMARY KEY (`id_cat`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3
;

CREATE TABLE IF NOT EXISTS `subcategorias` (
`id_subcat` int(10) unsigned NOT NULL auto_increment,
`nom_subcat` varchar(30) NOT NULL,
PRIMARY KEY (`id_subcat`),
KEY `id_cat` (`id_cat`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6
;

CREATE TABLE IF NOT EXISTS `marcas` (
`id_marca` int(10) unsigned NOT NULL auto_increment,
`marca` varchar(25) NOT NULL,
`imagen` varchar(15) NOT NULL,
PRIMARY KEY (`id_marca`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3
;

CREATE TABLE IF NOT EXISTS `productos` (
`id_prod` int(10) unsigned NOT NULL auto_increment,
`nom_prod` varchar(20) NOT NULL,
`descrip` varchar(100) NOT NULL,
`imagen` varchar(15) NOT NULL,
`directorio` varchar(20) NOT NULL,
`id_subcat` int(10) unsigned NULL,
`id_cat` int(10) unsigned NULL,
`id_marca` int(10) unsigned NULL,
PRIMARY KEY (`id_prod`),
KEY `id_subcat` (`id_subcat`),
KEY `id_cat` (`id_cat`),
KEY `id_marca` (`id_marca`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2
;

ALTER TABLE productos
ADD FOREIGN KEY(id_subcat) REFERENCES
subcategorias(id_subcat)
ON DELETE NO ACTION ON UPDATE CASCADE;

ALTER TABLE productos
ADD FOREIGN KEY(id_cat) REFERENCES
categorias(id_cat)
ON DELETE NO ACTION ON UPDATE CASCADE;

ALTER TABLE productos
ADD FOREIGN KEY(id_marca) REFERENCES
marcas(id_marca)
ON DELETE NO ACTION ON UPDATE CASCADE;

23
03
agosto
2011
Avatar de Gastón
Gastón [Autor] aportó:
Lo veo bien Javier.
Revisa unicamente la definición de la tabla subcategorias donde definis una clave sobre una columna inexistente (id_cat).

Saludos.

PD: Un consejo: Usa UTF8 como codificación de tus tablas.
24
04
agosto
2011
Avatar de javier
javier aportó:
gaston: tenes razon sobre la clave me olvide de colocarla, si alguien quiere utilizar estas definiciones espero les sea de utilidad. Una vez mas gracias.
25
03
marzo
2012
Avatar de Rafael Marchessi
Rafael Marchessi aportó:
Hola Gastón, sé que es un poco tarde para retomar los comentarios del post pero quisiera pregunarte por las dudas como tendría que crear las tablas para el sgte. menú (lo haría con una lista <ul><li>... con divs css):

Categoria 1
Subcategoria 1 -> link a productos
Subcategoria 1 -> link a productos
Subcategoria 1 -> link a productos

Categoria 2
Subcategoria 2 -> link a productos
Subcategoria 2 -> link a productos

Categoria 3 -> link a productos

...........

Debo crear tres tablas como mencionas? cat, subcat y prods?, ó solo 2?, cat y prods agregando un nuevo campo en cat?.

Muchas gracias de antemano por si me puedes guiar.
26
12
marzo
2012
Avatar de Gastón
Gastón [Autor] aportó:
@Rafael

De cualquiera de las dos maneras está bien, si cumple con los requerimientos. Tomá en cuenta que una relación jerarquica (arbol, con category_id) implica mayor trabajo de mantenimiento que dos tablas (categorias y subcategorías).
A veces pensar a futuro ahorra tiempo el día de mañana, a veces no.
Si estas seguro que necesitas solo dos niveles de jerarquía, quizá tener dos tablas es lo mejor.

Por mi parte prefiero "perder" tiempo realizando una relación jerarquica que soporte agregar más niveles el día de mañana sin mayores problemas.

Si te volcas por esto último, te recomiendo investigar sobre adjacency lists y nested sets. Son dos formas de implementar arboles en bases de datos relacionales.

Saludos.
27
13
marzo
2012
Avatar de Eduardo Ballesteros
Cordial saludo Gaston muy bueno tu articulo me ha sido de gran ayuda.

Mi pregunta es la siguiente:
Las reglas de integridad referencial me sirven para mantener la consistencia de los datos, por eso se implementan esas opciones para llaves foraneas de "On Update y OnDelete" hasta hay todo claro, pero que pasa con la parte de los Insert? te pongo este ejemplo relacionado con tu ejemplo numero 1, si yo inserto un articulo cuya categoria es 1000 que de antemano no existe, no habria una inconsistencia de nuevo en los datos?. Existe alguna forma para que no se dejen insertar articulos cuya categoria no exista? o este tipo de restricciones se deben hacer en la parte de aplicacion y no en la propia base de datos.


De antemano muchas gracias
28
14
marzo
2012
Avatar de Gastón
Gastón [Autor] aportó:
@Eduardo

Buena pregunta.
En la definición de la tabla colocamos

CREATE TABLE
articulos
(
id_articulo integer not null primary key,
id_categoria integer null,
articulo varchar(60) not null,
texto text not null
);


id_categoria es entero y acepta nulo. Esto es así porque los requerimientos dicen que un artículo puede no estar asociado a una categoría. Tal cual como lo definimos, la integridad relacional permitiría cargar un artículo sin categoría.

Sin embargo, sea definido nulo o no id_categoria, si se intenta relacionar el artículo con la categoría 1000 no existente, el propio motor no permitiría la inserción (o actualización), ignorando la sentencia y retornando un error.
Es por ello que la integridad referencial asegura la integridad y consistencia de los datos.

Con respecto a donde realizar la comprobación, por cuestiones de usabilidad recomiendo realizar una validación en el cliente (la aplicación) a fin de no esperar a que el usuario complete todo el formulario que al ser enviado retorne con error por no cumplir con las reglas definidas.

De todas maneras, imagina que la base de datos el día de mañana puede tener otro cliente (otra aplicación realizada quizá en otro lenguaje) en la cual por el motivo que fuera no se aplica la validación en el cliente. La base de datos igualmente impediría una inserción o actualización que comprometiera la integridad de datos.

Definir integridad referencial en base de datos nunca está de más.

Saludos.
29
23
marzo
2012
Avatar de Roldan Badillo
Roldan Badillo aportó:
Hola un saludo Gastón. Este articulo es muy bueno.
Tengo un pequeño problema: Estoy desarrollando una aplicación y no tengo mucha experiencia en bases de datos.
Esta aplicación incluye un formulario para solicitar una membresía(Membresía familiar), en este formulario deben de ingresar:

1.- nombre del solicitante
2.- edad del solicitante
3.- Nombre y edad de los integrantes de la familia

Me interesa saber, ¿como crear la tabla para esta membresía? ¿debo hacer 2 tablas?... una para el nombre y datos del solicitante y otra para los miembros de la familia (solo nombre y edad para ellos).

Perdon si es algo muy simple pero la verdad soy muy novato. Por tu atención gracias!
30
23
marzo
2012
Avatar de Gastón
Gastón [Autor] aportó:
@Roldan:

Una tabla para el solicitante, y otra tabla relacionada (1 a n) con los integrantes de la familia.

Saludos.
31
25
abril
2012
Avatar de Eduardo Ballesteros
Cordial saludo Gaston

Muchas gracias por tus respuestas, me han servido de mucho, pero me surge una nueva duda, ¿que pasa si digamos tengo una tabla donde la llave primaria es una llave compuesta, es decir que la llave primaria se compone de mas de un atributo?

¿Como se hace referencia a una llave foránea compuesta, y en particular en MySQL como se interpreta y usa esto?

De antemano muchas gracias

Agregar un comentario

Debe estar identificado para agregar un comentario