Сложный MySQL Query 1-N Отношения - PullRequest
2 голосов
/ 03 января 2012

В настоящее время я разрабатываю базу данных (MySQL) для рецептов напитков и хочу, чтобы пользователи могли вводить, какие ингредиенты у них есть, и я дам им все рецепты, которые включают только этих ингредиентов. Мне нужен запрос, в котором я могу выбрать все напитки из таблицы drinks, где единственные ингредиенты похожи (). Причина в том, что кто-то может ввести водку или водку smirnoff, но я угощу их как то же самое ) ингредиенты в таблице user_ingredients

Вот три таблицы, которые будут задействованы в этом запросе:

напитки

CREATE TABLE `drinks` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `drink` varchar(64) NOT NULL DEFAULT '',
  `glass` int(11) unsigned NOT NULL,
  `instructions` text,
  PRIMARY KEY (`id`),
  UNIQUE KEY `un_drink` (`drink`),
  KEY `in_id` (`id`),
  KEY `fk_glass` (`glass`),
  CONSTRAINT `fk_glass` FOREIGN KEY (`glass`) REFERENCES `glasses` (`id`) ON DELETE NO         ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=11946 DEFAULT CHARSET=utf8;

recipe_ingredients

CREATE TABLE `drink_ingredients` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `drink` int(11) unsigned NOT NULL,
  `ingredient` int(11) unsigned NOT NULL,
  `amount` int(11) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `in_drink` (`drink`),
  KEY `in_ingredient` (`ingredient`),
  KEY `fk_amount` (`amount`),
  CONSTRAINT `fk_amount` FOREIGN KEY (`amount`) REFERENCES `amounts` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `fk_drink` FOREIGN KEY (`drink`) REFERENCES `drinks` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `fk_ingredient` FOREIGN KEY (`ingredient`) REFERENCES `ingredients` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=46026 DEFAULT CHARSET=utf8;

user_ingredients

CREATE TABLE `user_ingredients` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `user` int(11) unsigned DEFAULT NULL,
  `ingredient` int(11) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fk_user_ingredient` (`ingredient`),
  CONSTRAINT `fk_user_ingredient` FOREIGN KEY (`ingredient`) REFERENCES `ingredients` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

Спасибо, я застрял на этом некоторое время. -Stefan

Ответы [ 2 ]

2 голосов
/ 03 января 2012

Вы можете найти рецепты, для которых у пользователя есть все ингредиенты, используя внешнее соединение.Если данный ингредиент не найден в запасе пользователя, ui.* будет НЕДЕЙСТВИТЕЛЕН.

Затем подсчитайте ингредиенты для напитка, сравните с количеством совпадающих ингредиентов в поставке пользователя (NULL не считаются), и если количество равно, то все ингредиенты соответствуют поставке пользователя.

SELECT d.*
FROM drinks d
INNER JOIN drink_ingredient di ON d.id = di.drink
LEFT OUTER JOIN user_ingredients ui 
    ON di.ingredient = ui.ingredient AND ui.user = :user
GROUP BY d.id
HAVING COUNT(di.ingredient) = COUNT(ui.ingredient);

Даже если у пользователя есть дополнительные ингредиенты, которые не входят в рецепт этого напитка, я предполагаю, что все в порядке, эти ингредиенты просто останутся в шкафу в следующий раз.: -)

Сопоставление входных данных пользователя с канонической строкой в ​​таблице ingredients является другим вопросом.То есть, заполняя таблицу user_ingredients, чтобы числовые внешние ключи ссылались на правильную строку.Вы можете сделать это во время ввода данных пользователя, где они перечисляют ингредиенты, которые у него есть.

INSERT INTO user_ingredients (user, ingredient)
SELECT :user, i.id
FROM ingredients i
WHERE :ingredient REGEXP CONCAT('[[:<:]]', i.name, '[[:>:]]');

Таким образом, ввод пользователя 'smirnoff vodka' соответствует регулярному выражению '[[:<:]]vodka[[:>:]]'

Обратите внимание, что подстрокаПодобный матч сложно оптимизировать.Придется запустить полное сканирование таблицы.Но я предполагаю, что у вас есть только несколько сотен строк в таблице ингредиентов, так что это будет не так уж плохо.В противном случае вам придется использовать решение для полнотекстовой индексации.

0 голосов
/ 03 января 2012

Я думаю, что ваши два критерия:

  1. рецепты с только введенными пользователем ингредиентами и
  2. сопоставление неоднозначных ингредиентов с «лайком»

несовместимы. Представьте, что у вас есть (отвратительный) коктейль с красным и белым вином (вряд ли я знаю, но только в качестве примера), и ваш пользователь вводит «вино». Теперь вы не знаете, есть ли у них подходящие ингредиенты для вашего напитка

Я бы сказал, если это возможно, имеет смысл составить окончательный список разрешенных ингредиентов (например, отдельный список всех ингредиентов, доступных в вашей базе данных) и предоставить пользователям очень простой способ предложить дополнения.

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