Here is my simplified database scheme
-- -----------------------------------------------------
-- Table `products`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `products` (
`id` INT NULL AUTO_INCREMENT,
`name` VARCHAR(45) NULL,
PRIMARY KEY (`id`))
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `properties`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `properties` (
`id` INT NOT NULL AUTO_INCREMENT,
`product_id` INT NULL,
`key` VARCHAR(45) NULL,
`value` VARCHAR(45) NULL,
PRIMARY KEY (`id`))
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Data for table `products`
-- -----------------------------------------------------
INSERT INTO `products` (`id`, `name`) VALUES (1, 'English Book with AudioCD');
INSERT INTO `products` (`id`, `name`) VALUES (2, 'Polish Book');
-- -----------------------------------------------------
-- Data for table `properties`
-- -----------------------------------------------------
INSERT INTO `properties` (`id`, `product_id`, `key`, `value`) VALUES (1, 1, 'Format', 'Book');
INSERT INTO `properties` (`id`, `product_id`, `key`, `value`) VALUES (2, 1, 'Format', 'Audio');
INSERT INTO `properties` (`id`, `product_id`, `key`, `value`) VALUES (3, 2, 'Format', 'Book');
INSERT INTO `properties` (`id`, `product_id`, `key`, `value`) VALUES (4, 1, 'Language', 'English');
INSERT INTO `properties` (`id`, `product_id`, `key`, `value`) VALUES (5, 2, 'Language', 'Polish');
And here are tables representations
+----+--------------------------+
| id | name |
+----+--------------------------+
| 1 | English Book wit AudioCD |
| 2 | Polish Book |
+----+--------------------------+
+----+------------+----------+---------+
| id | product_id | key | value |
+----+------------+----------+---------+
| 1 | 1 | Format | Book |
| 2 | 1 | Format | AudioCD |
| 3 | 2 | Format | Book |
| 4 | 1 | Language | English |
| 5 | 2 | Language | Polish |
+----+------------+----------+---------+
I need to select all products that have key 'Format' and value 'Book' and 'AudioCD'. But I need only products that met all of these conditions. So I want only product_id 1, not 2 (beacuse it doesn't have AudioCD). Additionaly I want might to add also Language key to that.
Is there way to do it without multiple JOINS? The problem with multiple JOINS starts where I would have let's say 20-30 combinations of key-value attributes, because they will be added freely.
Appreciate any hints!
1 Answer 1
You can get the result by using a combination of a WHERE
, GROUP BY
, and HAVING
to return the products
that have both the Book
and Audio
values.
select p.id, p.name
from products p
inner join properties t
on p.id = t.product_id
where t.key = 'Format'
and t.value in ('Book', 'Audio')
group by p.id, p.name
having count(distinct t.value) = 2;
See SQL Fiddle with Demo. Basically, you'll place the values that you want in the WHERE
clause - this would be the Book
and Audio
. Then you'll GROUP BY
the product.id
and product.name
these are unique to each product. Finally you will use the HAVING
clause to count the distinct properties.value
that are returned. This should equal the number of items that you have in your WHERE
clause.
This could also be written using conditional aggregation inside of the HAVING
:
select p.id, p.name
from products p
inner join properties t
on p.id = t.product_id
group by p.id, p.name
having sum(case when t.key = 'Format' and t.value = 'Book' then 1 else 0 end) > 0
and sum(case when t.key = 'Format' and t.value = 'Audio' then 1 else 0 end) > 0
If you want to include the Language
key, then you can use conditional aggregation in the HAVING
clause:
select p.id, p.name
from products p
inner join properties t
on p.id = t.product_id
group by p.id, p.name
having
sum(case when t.key = 'Format' and t.value = 'Book' then 1 else 0 end) > 0
and sum(case when t.key = 'Format' and t.value = 'Audio' then 1 else 0 end) > 0
and sum(case when t.key = 'Language' and t.value = 'English' then 1 else 0 end) > 0
See Demo
-
I would not ever got that! Thanks a lot. I would apply that to my more complex database and check if that works there correctly. But still it's correct answer to above question.Piotr– Piotr2014年09月03日 14:50:09 +00:00Commented Sep 3, 2014 at 14:50
-
I didn't mention that (my bad), but what if I want to include language key into this?Piotr– Piotr2014年09月03日 15:01:54 +00:00Commented Sep 3, 2014 at 15:01
-
@Piotr What do you mean include the language key? Do you want to include that in a filter? If so, what are the requirements? WHERE language = English or what?Taryn– Taryn2014年09月03日 15:06:59 +00:00Commented Sep 3, 2014 at 15:06
-
@Piotr I've updated my answer with another example on how to include the
Language
key, I'm guessing since it's not clear the type of filtering you need with that.Taryn– Taryn2014年09月03日 15:36:44 +00:00Commented Sep 3, 2014 at 15:36