Запрос по двум условиям MySQL

83
12 мая 2021, 07:50

Имею 3 таблицы:

Values

id - products_id - properties_id - value

Properties

id - name

  1. Размер
  2. Цвет
  3. Новинка

Products

id - name - category - price

Необходимо вывести все товары зелёного цвета и являющиеся новинкой.

Смог сделать запрос к одному пункту

 - R::getAll('SELECT products.* FROM products  JOIN `values` as `v` ON
   v.products_id = products.id JOIN `properties` as `pr` ON pr.id =
   v.properties_id WHERE pr.name = "Новинка" AND v.value = "1"');

по 2м не понимаю как сделать запрос, пробовал через GROUP BY - выдаёт ошибку, INTERSECT вроде как тоже оказался не уместен
Проблема в том, что нужно вывести не дублируя, тоесть чтобы осталось например

-1 велосипед

//он является и новинкой и зелёный, а не

-1 велосипед

//новинка)

-2 велосипед

(зелёный)

Answer 1

Тут стоит подумать на тему "А действительно ли нужно присоединять таблицу properties?" Насколько я понял, таблица properties является справочником, а результирующий запрос должен выдать товары по выставленному фильтру, используя параметры вызова из справочника. Т.е. SQL-запрос выполняется как результат HTTP-запроса со страницы. Значит, параметр id для названия свойства из таблицы properties можно получить прямо со страницы и передать в виде параметров HTTP-запроса - [2 => "Зеленый", 3 => "1"], чтобы не заморачиваться с поиском по названию свойства. Тогда задача существенно упрощается: нужно отобрать товары из products, используя всего 2 таблицы - собственно products и описание его свойств values.

Запрос для такого действия может выглядеть либо как его сделал @АлександрНиколаев (с "разворачиванием" имеющихся свойств в выборку через LEFT JOIN):

SELECT prod.* FROM products AS prod
LEFT JOIN 'values' AS val_color ON val_color.products_id = prod.id
LEFT JOIN 'values' AS val_new ON val_new.products_id = prod.id
WHERE 
   val_new.properties_id=3 AND val_new.value="1" AND
   val_color.properties_id=2 AND val_color.value="Зеленый"

либо с выбором ID подходящих строк из products с помощью вложенного запроса (я специально привожу более сложный пример запроса, использующий GROUP BY и "разворачивание" свойств properties через GROUP_CONCAT() как пример возможной альтернативы):

SELECT * 
FROM `products` 
WHERE `id` IN 
  (SELECT DISTINCT `products_id`
   FROM `values` 
   WHERE 
     (`properties_id`=2 AND `value`="Зеленый") OR
     (`properties_id`=3 AND `value`="1")
   GROUP BY `products_id` 
   HAVING 
     GROUP_CONCAT(DISTINCT `properties_id` ORDER BY `properties_id` SEPARATOR ",")="2,3");

Если же значение критерия нужно искать именно по названию критерия (т.е. ["Цвет" => "Зеленый", "Новинка" => "1"]), то вложенный запрос существенно "потяжелеет".

SELECT * 
FROM `products` 
WHERE `id` IN 
  (SELECT DISTINCT v.`products_id`
   FROM `values` AS v LEFT JOIN `properties` AS p ON v.`properties_id`=p.`id`
   WHERE 
     (p.`name`="Цвет" AND v.`value`="Зеленый") OR
     (p.`name`="Новинка" AND v.`value`="1")
   GROUP BY v.`products_id` 
   HAVING 
     GROUP_CONCAT(DISTINCT p.`name` ORDER BY p.`name` SEPARATOR ",")="Новинка,Цвет");

Тестовый пример:

MariaDB [homestead]> select * from properties;
+----+----------------+
| id | name           |
+----+----------------+
|  1 | Размер         |
|  2 | Цвет           |
|  3 | Новинка        |
+----+----------------+
3 rows in set (0.003 sec)
MariaDB [homestead]> select * from products;
+----+--------------------+----------+-------+
| id | name               | category | price |
+----+--------------------+----------+-------+
|  1 | Велосипед          | 5        |   100 |
|  2 | Велосипед          | 5        |   200 |
|  3 | Самокат            | 7        |   150 |
|  4 | Велосипед          | 5        |   100 |
+----+--------------------+----------+-------+
4 rows in set (0.000 sec)
MariaDB [homestead]> select * from `values`;
+----+------------+---------------+----------------+
| id | product_id | properties_id | value          |
+----+------------+---------------+----------------+
|  1 |          1 |             2 | Синий          |
|  2 |          1 |             3 | 1              |
|  3 |          2 |             3 | 1              |
|  4 |          2 |             2 | Зеленый        |
|  5 |          2 |             1 | King Size      |
|  6 |          3 |             2 | Зеленый        |
|  7 |          4 |             2 | Синий          |
|  8 |          4 |             3 | 1              |
+----+------------+---------------+----------------+
8 rows in set (0.000 sec)
MariaDB [homestead]> SELECT prod.* FROM products AS prod LEFT JOIN `values` AS val_color ON val_color.products_id=prod.id LEFT JOIN `values` AS val_new ON val_new.products_id=prod.id WHERE val_new.properties_id=3 AND val_new.value="1" AND val_color.properties_id=2 AND val_color.value="Зеленый";
+----+--------------------+----------+-------+
| id | name               | category | price |
+----+--------------------+----------+-------+
|  2 | Велосипед          | 5        |   200 |
+----+--------------------+----------+-------+
1 row in set (0.003 sec)
MariaDB [homestead]> SELECT * FROM `products`  WHERE `id` IN (SELECT DISTINCT `products_id` FROM `values` WHERE (`properties_id`=2 AND `value`="Зеленый") OR      (`properties_id`=3 AND `value`="1") GROUP BY `products_id` HAVING       GROUP_CONCAT(DISTINCT `properties_id` ORDER BY `properties_id` SEPARATOR ",")="2,3");
+----+--------------------+----------+-------+
| id | name               | category | price |
+----+--------------------+----------+-------+
|  2 | Велосипед          | 5        |   200 |
+----+--------------------+----------+-------+
1 row in set (0.001 sec)
MariaDB [homestead]> SELECT * FROM `products`  WHERE `id` IN (SELECT DISTINCT v.`products_id` FROM `values` AS v LEFT JOIN `properties` AS p ON v.`properties_id`=p.`id` WHERE (p.`name`="Цвет" AND v.`value`="Зеленый") OR      (p.`name`="Новинка" AND v.`value`="1") GROUP BY v.`products_id` HAVING GROUP_CONCAT(DISTINCT p.`name` ORDER BY p.`name` SEPARATOR ",")="Новинка,Цвет");
+----+--------------------+----------+-------+
| id | name               | category | price |
+----+--------------------+----------+-------+
|  2 | Велосипед          | 5        |   200 |
+----+--------------------+----------+-------+
1 row in set (0.001 sec)
Answer 2
select prod.* from products as prod
left join values as val_color on val_color.products_id = prod.id
left join values as val_new on val_new.products_id = prod.id
left join properties as prop on prop.id = val_color.properties_id and prop.id = val_new.properties_id
where val_new.properties_id = 3 and val_color.properties_id = 2 and val_color.value = "Зеленый"
READ ALSO
Nginx + Node.js + phpmyadmin Настройка

Nginx + Node.js + phpmyadmin Настройка

Доброго времени суток!

100
Простейшая админ панель для работы с БД

Простейшая админ панель для работы с БД

Появилась необходимость создать админ панель для администрирования базы пользователей 2мя-3мя людьмиТак как я новичок в этом деле, прошу...

119
Как настроит php на cors

Как настроит php на cors

Впервые работаю с react, пытаюсь отправит запрос на свой сервер из react приложении но сервер не отвечает:

88
Битая кодировка в формах ОС

Битая кодировка в формах ОС

При отправке форм с сайта в теле письма все кирилические и спец символы приходят в виде %2525D0%2525A2%2525D0%2525B5%2525D1%252581%2525D1%252582 - текст в форме был...

105