查找一个表中位于另外两个表中的所有行

时间:2013-08-15 21:18:43

标签: mysql sql relational-division

我正在努力解决这个问题。我有三张桌子......配料,食谱和商店。我正在尝试建立一个查询,告诉我我可以从商店的原料制作什么食谱。我的表是:


    mysql> SELECT * FROM ingredients;
    +---------+
    | id      |
    +---------+
    | apple   |
    | beef    |
    | cheese  |
    | chicken |
    | eggs    |
    | flour   |
    | milk    |
    | pasta   |
    | sugar   |
    | tomato  |
    +---------+
    10 rows in set (0.00 sec)

    mysql> SELECT * FROM stores;
    +----------+------------+
    | name     | ingredient |
    +----------+------------+
    | target   | apple      |
    | target   | chicken    |
    | target   | flour      |
    | target   | milk       |
    | target   | sugar      |
    | wal-mart | beef       |
    | wal-mart | cheese     |
    | wal-mart | flour      |
    | wal-mart | milk       |
    | wal-mart | pasta      |
    | wal-mart | tomato     |
    +----------+------------+
    11 rows in set (0.00 sec)

    mysql> SELECT * FROM recipes;
    +---------------+------------+
    | name          | ingredient |
    +---------------+------------+
    | apple pie     | apple      |
    | apple pie     | flour      |
    | apple pie     | milk       |
    | apple pie     | sugar      |
    | cheeseburger  | beef       |
    | cheeseburger  | cheese     |
    | cheeseburger  | flour      |
    | cheeseburger  | milk       |
    | fried chicken | chicken    |
    | fried chicken | flour      |
    | spaghetti     | beef       |
    | spaghetti     | pasta      |
    | spaghetti     | tomato     |
    +---------------+------------+
    13 rows in set (0.00 sec)

    mysql>

鉴于上述情况,我想构建一个查询,在其中我给它商店名称(比如这个例子中的沃尔玛),它会生成一份我可以用沃尔玛(cheeseburger& amp)提供的配料制作的食谱清单;意大利面条。)

以下是创建这些表的SQL:


    CREATE TABLE IF NOT EXISTS ingredients (
      id varchar(32) NOT NULL,
      PRIMARY KEY (id)
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;

    INSERT INTO ingredients (id) VALUES
    ('apple'),
    ('beef'),
    ('cheese'),
    ('chicken'),
    ('eggs'),
    ('flour'),
    ('milk'),
    ('pasta'),
    ('sugar'),
    ('tomato');

    CREATE TABLE IF NOT EXISTS recipes (
      `name` varchar(32) NOT NULL,
      ingredient varchar(32) NOT NULL,
      PRIMARY KEY (`name`,ingredient)
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;

    INSERT INTO recipes (`name`, ingredient) VALUES
    ('apple pie', 'apple'),
    ('apple pie', 'flour'),
    ('apple pie', 'milk'),
    ('apple pie', 'sugar'),
    ('cheeseburger', 'beef'),
    ('cheeseburger', 'cheese'),
    ('cheeseburger', 'flour'),
    ('cheeseburger', 'milk'),
    ('fried chicken', 'chicken'),
    ('fried chicken', 'flour'),
    ('spaghetti', 'beef'),
    ('spaghetti', 'pasta'),
    ('spaghetti', 'tomato');

    CREATE TABLE IF NOT EXISTS stores (
      `name` varchar(32) NOT NULL,
      ingredient varchar(32) NOT NULL,
      UNIQUE KEY NAME_INGREDIENT (`name`,ingredient)
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;

    INSERT INTO stores (`name`, ingredient) VALUES
    ('target', 'apple'),
    ('target', 'chicken'),
    ('target', 'flour'),
    ('target', 'milk'),
    ('target', 'sugar'),
    ('wal-mart', 'beef'),
    ('wal-mart', 'cheese'),
    ('wal-mart', 'flour'),
    ('wal-mart', 'milk'),
    ('wal-mart', 'pasta'),
    ('wal-mart', 'tomato');

4 个答案:

答案 0 :(得分:3)

试试这个:

SELECT r.name FROM recipes r          
GROUP BY r.name
HAVING COUNT(*) = (SELECT COUNT(*) 
     FROM recipes r2 INNER JOIN stores s 
        ON r2.ingredient = s.ingredient AND s.name = 'wal-mart'
     WHERE r.name = r2.name)

fiddle demo

答案 1 :(得分:1)

这是获得指定结果的一种方法。

如果您只想检查一个或两个食谱,那么在内联视图查询中包含一个WHERE子句(在下面的查询中注释掉,它将返回所有食谱。)如果您只想检查一个或两个商店,在外部查询中包含一个谓词(在下面的查询中注释掉。)

 SELECT s.name AS store_name
      , r.name AS recipe_name
      , i.ri_count
  FROM  ( SELECT ri.name
               , COUNT(DISTINCT ri.ingredient) AS ri_count
            FROM recipes ri
        -- WHERE ri.name IN ('fried chicken','spaghetti')
           GROUP
              BY ri.name
         ) i
   JOIN recipes r
     ON r.name = i.name
   LEFT
   JOIN stores s
     ON s.ingredient = r.ingredient
 -- AND s.name IN ('target','wal-mart')
  GROUP
     BY s.name
      , r.name
 HAVING COUNT(DISTINCT s.ingredient) = i.ri_count

答案 2 :(得分:0)

首先,我想告诉你,改善你的模型会更好。尝试将其标准化。另一方面,如果我没有错,你需要做这样的事情:

SELECT GROUP_CONCAT(r.name SEPARATOR " & ") recipients
FROM stores s
JOIN ingredients i ON s.ingredient=i.id
JOIN recipes r ON r.ingredient=i.id
WHERE s.name="wal-mart"
GROUP BY r.ingredient

玩这个 http://sqlfiddle.com/#!2/3c763/27

答案 3 :(得分:0)

(稍微改造数据以仅使用数字键)

CREATE TABLE IF NOT EXISTS ingredients (
  ing_id INTEGER NOT NULL PRIMARY KEY
  , ing_name varchar(32) NOT NULL
) ;

INSERT INTO ingredients (ing_id, ing_name) VALUES
(1, 'apple' ),
(2, 'beef' ),
(3, 'cheese' ),
(4, 'chicken' ),
(5, 'eggs' ),
(6, 'flour' ),
(7, 'milk' ),
(8, 'pasta' ),
(9, 'sugar' ),
(10, 'tomato' );

CREATE TABLE IF NOT EXISTS recipes (
  rec_id INTEGER NOT NULL PRIMARY KEY
  , rec_name varchar(32) NOT NULL
 );

INSERT INTO recipes (rec_id, rec_name) VALUES
(1, 'apple pie' ) ,
(2, 'cheeseburger' ) ,
(3, 'fried chicken' ) ,
(4, 'spaghetti' ) ;

CREATE TABLE IF NOT EXISTS recipe_ingredient (
  rec_id INTEGER NOT NULL REFERENCES recipes(rec_id)
  , ing_id INTEGER NOT NULL REFERENCES ingredients (ing_id)
  , PRIMARY KEY (rec_id, ing_id)
 );

INSERT INTO recipe_ingredient (rec_id, ing_id) VALUES
(1, 1), (1, 6), (1, 7), (1, 9),
(2, 2), (2, 3), (2, 6), (2, 7),
(3, 4), (3, 6), (4, 2),
(4, 8), (4, 10);
8), (4, 10);

CREATE TABLE IF NOT EXISTS stores (
  sto_id INTEGER NOT NULL PRIMARY KEY
  , sto_name varchar(32) NOT NULL
 );

INSERT INTO stores (sto_id, sto_name) VALUES
(1, 'target' ),
(2, 'wal-mart' );

CREATE TABLE IF NOT EXISTS store_ingredient (
  sto_id INTEGER NOT NULL REFERENCES stores(sto_id)
  , ing_id INTEGER NOT NULL REFERENCES ingredients(ing_id)
  , PRIMARY KEY (sto_id, ing_id)
 );

INSERT INTO store_ingredient (sto_id, ing_id ) VALUES
(1, 1), (1, 4), (1, 6), (1, 7), (1, 9),
(2, 2), (2, 3), (2, 6), (2, 7), (2, 8), (2, 10)
        ;

查询以查找沃尔玛可以提供所有成分的所有食谱。

SELECT r.rec_name
        -- All the recipes
FROM recipes r
WHERE NOT EXISTS (
        SELECT *
        -- ... that do not contain any ingredient
        FROM recipe_ingredient ri
        WHERE ri.rec_id = r.rec_id
        AND NOT EXISTS (
                SELECT *
                -- ... that cannot be provided by wal-mart.
                FROM store_ingredient si
                JOIN stores s ON s.sto_id = si.sto_id
                WHERE si.ing_id = ri.ing_id
                 AND s.sto_name = 'wal-mart'
                )
        );