MySQL: JOIN и WHERE с несколькими совпадениями - PullRequest
1 голос
/ 27 марта 2019

Я бы хотел выбрать продукты из таблицы products с атрибутами с идентификаторами 2 и 5, используя следующий запрос:

SELECT `products`.`title`, `products`.`price` 
FROM `products` 
LEFT JOIN `products_attributes_mapping` 
    ON `products`.`id` = `products_attributes_mapping`.`product_id` 
WHERE 
    `products_attributes_mapping`.`attribute_value_id` IN (2) 
    AND `products_attributes_mapping`.`attribute_value_id` IN (5) 
GROUP BY `products`.`id`

Я ожидаю, что продукт 'Example product 1, blue, size 1' будет возвращен.Но я не получаю никакого результата, даже если продукт с идентификатором 1 имеет attribute_value_id 2 и 5, назначенные в таблице products_attributes_mapping.

Я использую IN, потому что я хотел бы иметь возможностьпредоставить несколько атрибутов, я упростил это только для примера.

SQL-скрипта: http://sqlfiddle.com/#!9/2fd94f2/1/0

Схема

CREATE TABLE `products` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `title` varchar(255) CHARACTER SET utf8 NOT NULL,
    `price` double NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4;

CREATE TABLE `products_attributes` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `name` varchar(255) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;

CREATE TABLE `products_attributes_mapping` (
    `product_id` int(11) NOT NULL,
    `attribute_value_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `products_attributes_values` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `attribute_id` int(11) NOT NULL,
    `name` varchar(255) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4;

Данные

INSERT INTO `products` VALUES 
    (1,'Example product 1, blue, size 1',10),
    (2,'Example product 2, yellow, size 1',10),
    (3,'Example product 3, black, size 2',15),
    (4,'Example product 4, red, size 2',15);

INSERT INTO `products_attributes` VALUES 
    (1,'Color'),
    (2,'Size');

INSERT INTO `products_attributes_mapping` VALUES 
    (1,2),
    (1,5),
    (2,4),
    (2,5),
    (3,3),
    (3,6),
    (4,1),
    (4,6);

INSERT INTO `products_attributes_values` VALUES 
    (1,1,'red'),
    (2,1,'blue'),
    (3,1,'black'),
    (4,1,'yellow'),
    (5,2,'1'),
    (6,2,'2'),
    (7,2,'3'),
    (8,2,'4');

1 Ответ

2 голосов
/ 27 марта 2019

Использование агрегации действительно может быть решением.Вы можете использовать предложение HAVING, чтобы убедиться, что продукты имеют определенные значения атрибута:

SELECT p.title, p.price
FROM products p
INNER JOIN products_attributes_mapping pm ON p.id = pm.product_id 
GROUP BY p.id, p.title, p.price
HAVING 
    MAX(pm.attribute_value_id = 2) = 1
    AND MAX(pm.attribute_value_id = 5) = 1

В ваша база данных БД , этот запрос возвращает:

title                            | price
---------------------------------|-------
Example product 1, blue, size 1  | 10

Вы можете легко расширить выражение, добавив дополнительные условия AND MAX(...) = 1.

Другой вариант - использовать последовательность условий WHERE EXISTS с коррелированными подзапросами для поиска в таблицах атрибутов.Это так же хорошо, но будет расширяться как более длинный запрос, если вам нужно добавить много условий.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...