选择另一个表中具有多个条件的所有行

Pio*_*otr 3 mysql join

这是我的简化数据库方案

-- -----------------------------------------------------
-- 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');
Run Code Online (Sandbox Code Playgroud)

这是表格表示

+----+--------------------------+
| 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  |
+----+------------+----------+---------+
Run Code Online (Sandbox Code Playgroud)

我需要选择所有具有关键“格式”和价值“书籍”和“AudioCD”的产品。但我只需要满足所有这些条件的产品。所以我只想要 product_id 1,而不是 2(因为它没有 AudioCD)。另外我还想添加语言键。

有没有办法在没有多个 JOINS 的情况下做到这一点?多个 JOINS 的问题始于我假设键值属性的 20-30 个组合,因为它们将被自由添加。

感谢任何提示!

Tar*_*ryn 6

您可以通过使用的组合得到的结果WHEREGROUP BY以及HAVING返回products有两个BookAudio值。

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;
Run Code Online (Sandbox Code Playgroud)

请参阅SQL Fiddle with Demo。基本上,您将在WHERE子句中放置所需的值- 这将是Bookand Audio。然后,你GROUP BYproduct.idproduct.name这些都是每个产品特有的。最后,您将使用该HAVING子句计算properties.value返回的不同值。这应该等于您在WHERE条款中拥有的项目数。

这也可以使用 内部的条件聚合来编写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
Run Code Online (Sandbox Code Playgroud)

参见SQL Fiddle with Demo

如果要包含Language键,则可以在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
  and sum(case when t.key = 'Language' and t.value = 'English' then 1 else 0 end) > 0
Run Code Online (Sandbox Code Playgroud)

演示