简体   繁体   中英

Query for a one to many relationship SQL

I have these tables

Table FoodName - Table FoodIngredients - Table FoodDescrip     
  ID | Name    -    ID | Ingredient    - ID | Descrip | IDFoodN | IDIngred
  01 |  A      -   001 |    AA         - 01 |   abcb  |   01    |  001
  02 |  B      -   002 |    BB         - 02 |   abcb  |   01    |  002
                   003 |    CC           03 |   abcb  |   01    |  003
                                         04 |   abcb  |   02    |  001
                                         05 |   abcb  |   02    |  003

I tried

Select 
  f.Name 
From 
  f.FoodName 
INNER JOIN 
  FoodDescrip D ON f.ID = D.ID 
INNER JOIN 
  FoodIngredients I ON I.ID = D.ID 
WHERE 
  Ingredient LIKE NOT ='BB'"

I need to know that food that doesn't have the ingredient BB, but I need to do the search by the name of the ingredient not by the id. I tried to use Ingredient NOT LIKE but it doesn't work. (I'm using Inner JOIN)

One approach uses conditional aggregation to check for the ingredient:

SELECT
    f1.ID, f1.Name
FROM FoodName f1
INNER JOIN FoodDescrip f2
    ON f1.ID = f2.IDFoodN
INNER JOIN FoodIngredients f3
    ON f2.IDIngred = f3.ID
GROUP BY
    f1.ID, f1.Name
HAVING
    SUM(CASE WHEN f3.Ingredient = 'BB' THEN 1 ELSE 0 END) = 0

I would use NOT EXISTS for this one. (Or NOT IN like AB_87's answer.) Your FoodDescrip table is a cross reference between food and ingredients. You want every food that doesn't have a cross reference to the BB ingredient.

SELECT f.Name 
FROM FoodName f 
WHERE NOT EXISTS 
(
    SELECT * 
    FROM FoodDescrip d 
    JOIN FoodIngredients i ON i.ID = d.IDIngred 
    AND i.Ingredient = ‘BB’
    AND d.IDFoodN = f.ID
)

“Select all food we're not exists a cross reference between that food and the BB ingredient.”

The outer query selects from the food table so you can return the food name. The subquery in the NOT EXISTS joins out from the cross reference table to the ingredient table so you can find the ones with the BB name, and is also filtered down to only the food in the outer query. So, if any BBs are found to exist in the subquery linked to the food, the NOT EXISTS will be false, and the food will be removed from the result by the WHERE clause.

This can be tweaked slightly to use NOT IN instead of NOT EXISTS. It's basically the exact same approach. Here's that:

SELECT f.Name 
FROM FoodName f 
WHERE f.ID NOT IN 
(
    SELECT d.IDFoodN
    FROM FoodDescrip d 
    JOIN FoodIngredients i ON i.ID = d.IDIngred 
    AND i.Ingredient = ‘BB’
)

“Select all foods where the ID is not in the list of food IDs linked to the BB ingredient in the cross reference table.”

I prefer these over Tim's approach because I think they read nicer. Also, they may perform better because they can throw out a food from the results as soon as they find one reference to the BB ingredient. Tim's method of conditional aggregation requires counting all the matches and seeing if the result is equal to vs. greater than zero.

You can try this. Get FoodId which has ingredient 'BB' and then retrieve all food which do not have FoodId retrieved earlier.

SELECT *
FROM   FoodName
WHERE  ID NOT IN (   SELECT d.IDFoodN
                     FROM   FoodIngredients i
                            JOIN FoodDescrip d ON i.ID = d.IDIngred
                     WHERE  i.Ingredient = 'BB' );

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM