I have created a facet search for a product catalog like following screen image and wonder if I designed it correctly.
I found Joel Brown's post with an ERD that sketches an Entity-Attribute-Value (EAV) model. I tried to build this model in Sequel Pro and run following query:
select distinct P.*
from attribute_product_values APV
INNER JOIN products P
on P.ID = APV.product_id
left JOIN attribute_values AV
on AV.ID = APV.attribute_value_id
and AV.value_id = 6
left JOIN attribute_values AV2
on AV2.ID = APV.attribute_value_id
and AV2.value_id = 3
where AV.ID is not null
or AV2.ID is not null
The result of above query was as expected but the query looks very odd to me because of the following reasons:
- If I remove the distinct value I get the same product multiple times
I have to use a left join instead of an inner join
I have to add a 'where not null' statement for each value I am searching for
As of above reasons I feel that I have wrongly designed the relationships between the tables.
I was wondering if the below data model can be designed better in order to make the query simpler and faster ?
Here is the SQL dump of the tables:
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
# Dump of table attribute_categories
# ------------------------------------------------------------
CREATE TABLE `attribute_categories` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`attribute_id` int(11) DEFAULT NULL,
`category_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fk_attribute_categories_cat` (`category_id`),
KEY `fk_attribute_categories_att` (`attribute_id`),
CONSTRAINT `fk_attribute_categories_att` FOREIGN KEY (`attribute_id`) REFERENCES `attributes` (`id`),
CONSTRAINT `fk_attribute_categories_cat` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
LOCK TABLES `attribute_categories` WRITE;
/*!40000 ALTER TABLE `attribute_categories` DISABLE KEYS */;
INSERT INTO `attribute_categories` (`id`, `attribute_id`, `category_id`)
VALUES
(1,1,1),
(2,2,1);
/*!40000 ALTER TABLE `attribute_categories` ENABLE KEYS */;
UNLOCK TABLES;
# Dump of table attribute_product_values
# ------------------------------------------------------------
CREATE TABLE `attribute_product_values` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`attribute_value_id` int(11) DEFAULT NULL,
`attribute_category_id` int(11) DEFAULT NULL,
`product_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fk_attribute_product_values_att` (`attribute_value_id`),
KEY `fk_attribute_product_values_cat` (`attribute_category_id`),
KEY `fk_attribute_product_values_pro` (`product_id`),
CONSTRAINT `fk_attribute_product_values_att` FOREIGN KEY (`attribute_value_id`) REFERENCES `attribute_values` (`id`),
CONSTRAINT `fk_attribute_product_values_cat` FOREIGN KEY (`attribute_category_id`) REFERENCES `attribute_categories` (`id`),
CONSTRAINT `fk_attribute_product_values_pro` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
LOCK TABLES `attribute_product_values` WRITE;
/*!40000 ALTER TABLE `attribute_product_values` DISABLE KEYS */;
INSERT INTO `attribute_product_values` (`id`, `attribute_value_id`, `attribute_category_id`, `product_id`)
VALUES
(1,2,1,1),
(2,5,2,1),
(3,3,1,2),
(4,6,2,2),
(6,3,1,3),
(7,6,2,3);
/*!40000 ALTER TABLE `attribute_product_values` ENABLE KEYS */;
UNLOCK TABLES;
# Dump of table attribute_values
# ------------------------------------------------------------
CREATE TABLE `attribute_values` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`attribute_id` int(11) NOT NULL,
`value_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_attribute` (`attribute_id`),
KEY `fk_value` (`value_id`),
CONSTRAINT `fk_attribute` FOREIGN KEY (`attribute_id`) REFERENCES `attributes` (`id`),
CONSTRAINT `fk_value` FOREIGN KEY (`value_id`) REFERENCES `values` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
LOCK TABLES `attribute_values` WRITE;
/*!40000 ALTER TABLE `attribute_values` DISABLE KEYS */;
INSERT INTO `attribute_values` (`id`, `attribute_id`, `value_id`)
VALUES
(1,1,1),
(2,1,2),
(3,1,3),
(4,1,4),
(5,2,5),
(6,2,6);
/*!40000 ALTER TABLE `attribute_values` ENABLE KEYS */;
UNLOCK TABLES;
# Dump of table attributes
# ------------------------------------------------------------
CREATE TABLE `attributes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) COLLATE latin1_general_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
LOCK TABLES `attributes` WRITE;
/*!40000 ALTER TABLE `attributes` DISABLE KEYS */;
INSERT INTO `attributes` (`id`, `name`)
VALUES
(1,'Television Resolution'),
(2,'Television Feature');
/*!40000 ALTER TABLE `attributes` ENABLE KEYS */;
UNLOCK TABLES;
# Dump of table categories
# ------------------------------------------------------------
CREATE TABLE `categories` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) COLLATE latin1_general_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
LOCK TABLES `categories` WRITE;
/*!40000 ALTER TABLE `categories` DISABLE KEYS */;
INSERT INTO `categories` (`id`, `name`)
VALUES
(1,'Television');
/*!40000 ALTER TABLE `categories` ENABLE KEYS */;
UNLOCK TABLES;
# Dump of table products
# ------------------------------------------------------------
CREATE TABLE `products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
` name` varchar(50) COLLATE latin1_general_ci DEFAULT NULL,
`price` float DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
LOCK TABLES `products` WRITE;
/*!40000 ALTER TABLE `products` DISABLE KEYS */;
INSERT INTO `products` (`id`, ` name`, `price`)
VALUES
(1,'Sony ',298),
(2,'Samsung',252),
(3,'LG',45);
/*!40000 ALTER TABLE `products` ENABLE KEYS */;
UNLOCK TABLES;
# Dump of table values
# ------------------------------------------------------------
CREATE TABLE `values` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) COLLATE latin1_general_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
LOCK TABLES `values` WRITE;
/*!40000 ALTER TABLE `values` DISABLE KEYS */;
INSERT INTO `values` (`id`, `name`)
VALUES
(1,'4K Ultra HD'),
(2,'1080p'),
(3,'1080i'),
(4,'760p'),
(5,'Smart TV'),
(6,'3D');
/*!40000 ALTER TABLE `values` ENABLE KEYS */;
UNLOCK TABLES;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;