【问题标题】:Complex MySQL Query 1-N Relationship复杂的 MySQL 查询 1-N 关系
【发布时间】:2012-01-03 01:23:25
【问题描述】:

我目前正在为饮料食谱设计一个数据库 (MySQL),并希望用户能够输入他们拥有的成分,我会给他们所有包含这些成分的食谱。我需要的是一个查询,我可以从表 drinks 中选择所有饮料,其中唯一的成分是 like这样做的原因是有人可能会输入伏特加或 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;

谢谢你,我在这个问题上纠结了一段时间。 -斯蒂芬

【问题讨论】:

    标签: mysql sql


    【解决方案1】:

    您可以使用外连接找到用户拥有所有成分的食谱。如果在用户的供应中找不到给定的成分,ui.* 将为 NULL。

    然后计算饮料的成分,比较用户供应中匹配成分的计数(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'匹配正则表达式'[[:&lt;:]]vodka[[:&gt;:]]'

    请注意,这样的子字符串匹配很难优化。它必须运行全表扫描。但我假设您在成分表中只有几百行,所以它不会太糟糕。否则,您必须使用全文索引解决方案。

    【讨论】:

    • 感谢您的快速回复,但如果用户有:伏特加、朗姆酒和蔓越莓以及您的回答,只会退回包含所有 3 种的饮料,但伏特加蔓越莓应该是有效的。
    • 不,我相信如果用户有给定食谱不需要的额外成分,它们将被忽略。
    • 你的权利这个查询完美无缺,非常感谢你的帮助
    【解决方案2】:

    我认为你的两个标准:

    1. 配方用户输入的成分,并且
    2. 用“like”匹配模棱两可的成分

    不兼容。想象一下,你有一个(可怕的)鸡尾酒配红白葡萄酒(我不太可能知道,这只是一个例子),你的用户输入了“wine”。现在你不知道他们是否有适合你饮料的成分

    我想说,如果可能的话,建立一个明确的允许成分列表(例如,数据库中所有可用成分的不同列表)并为用户提供一种非常简单的方法来建议添加会更有意义。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-03
      • 1970-01-01
      • 2015-08-04
      • 2020-02-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多