I have run into a problem when I have tried to select data from more than 1 table and I was wondering if anyone could help me. The tables are linked and I'm trying to select only things with a certain id. I have had success with queries with 1 table but with 2 tables it kicked an error my way:
Query to get data from Recipe failed: Column Recipe_ID in where clause is ambiguous.
Here is my query:
$query="SELECT * FROM Recipe, Ingredients_Needed WHERE Recipe_ID ='$chosen_id'";
Does Recipe_ID appear in both Recipe and Ingredients_Needed?
IF it does then you need to do something like this:
SELECT * FROM Recipe, Ingredients_Needed WHERE Recipe.Recipe_ID = ....
As a side note, use correct join syntax not an implicit join ie:
SELECT r.*, n.*
FROM Recipe r
INNER JOIN Ingredients_Needed n ON n.Recipe_ID = r.Recipe_ID
WHERE r.Recipe_ID = ...
This will make it clearer especially if you are joining on multiple tables with different conditions etc as you can see the aliased tables quickly just by looking at the query instead of guessing.
Specify the table:
WHERE table.Recipe_ID=
specify which table receipe_id refere to: Recipe and/or Ingredients_Needed
SELECT * FROM Recipe r, Ingredients_Needed i WHERE r.Recipe_ID = x and r.Recipe_ID = x
Just name your tables with aliases and specify relation. You probably have same column name in both tables or in table Ingredients_Needed column Recipe_ID have multiple same entries. So your SQL will be:
SELECT * FROM Recipe R
LEFT JOIN Ingredients_Needed I on R.ID=I.Recipe_ID
WHERE R.ID = '$choden_id'
If you in table Recipe haven't ID but have Recipe_ID, just change R.ID for R.Recipe_ID
Related
I have this query I need to optimize further since it requires too much cpu time and I can't seem to find any other way to write it more efficiently. Is there another way to write this without altering the tables?
SELECT category, b.fruit_name, u.name
, r.count_vote, r.text_c
FROM Fruits b, Customers u
, Categories c
, (SELECT * FROM
(SELECT *
FROM Reviews
ORDER BY fruit_id, count_vote DESC, r_id
) a
GROUP BY fruit_id
) r
WHERE b.fruit_id = r.fruit_id
AND u.customer_id = r.customer_id
AND category = "Fruits";
This is your query re-written with explicit joins:
SELECT
category, b.fruit_name, u.name, r.count_vote, r.text_c
FROM Fruits b
JOIN
(
SELECT * FROM
(
SELECT *
FROM Reviews
ORDER BY fruit_id, count_vote DESC, r_id
) a
GROUP BY fruit_id
) r on r.fruit_id = b.fruit_id
JOIN Customers u ON u.customer_id = r.customer_id
CROSS JOIN Categories c
WHERE c.category = 'Fruits';
(I am guessing here that the category column belongs to the categories table.)
There are some parts that look suspicious:
Why do you cross join the Categories table, when you don't even display a column of the table?
What is ORDER BY fruit_id, count_vote DESC, r_id supposed to do? Sub query results are considered unordered sets, so an ORDER BY is superfluous and can be ignored by the DBMS. What do you want to achieve here?
SELECT * FROM [ revues ] GROUP BY fruit_id is invalid. If you group by fruit_id, what count_vote and what r.text_c do you expect to get for the ID? You don't tell the DBMS (which would be something like MAX(count_vote) and MIN(r.text_c)for instance. MySQL should through an error, but silently replacescount_vote, r.text_cbyANY_VALUE(count_vote), ANY_VALUE(r.text_c)` instead. This means you get arbitrarily picked values for a fruit.
The answer hence to your question is: Don't try to speed it up, but fix it instead. (Maybe you want to place a new request showing the query and explaining what it is supposed to do, so people can help you with that.)
Your Categories table seems not joined/related to the others this produce a catesia product between all the rows
If you want distinct resut don't use group by but distint so you can avoid an unnecessary subquery
and you dont' need an order by on a subquery
SELECT category
, b.fruit_name
, u.name
, r.count_vote
, r.text_c
FROM Fruits b
INNER JOIN Customers u ON u.customer_id = r.customer_id
INNER JOIN Categories c ON ?????? /Your Categories table seems not joined/related to the others /
INNER JOIN (
SELECT distinct fruit_id, count_vote, text_c, customer_id
FROM Reviews
) r ON b.fruit_id = r.fruit_id
WHERE category = "Fruits";
for better reading you should use explicit join syntax and avoid old join syntax based on comma separated tables name and where condition
The next time you want help optimizing a query, please include the table/index structure, an indication of the cardinality of the indexes and the EXPLAIN plan for the query.
There appears to be absolutely no reason for a single sub-query here, let alone 2. Using sub-queries mostly prevents the DBMS optimizer from doing its job. So your biggest win will come from eliminating these sub-queries.
The CROSS JOIN creates a deliberate cartesian join - its also unclear if any attributes from this table are actually required for the result, if it is there to produce multiples of the same row in the output, or just an error.
The attribute category in the last line of your query is not attributed to any of the tables (but I suspect it comes from the categories table).
Further, your code uses a GROUP BY clause with no aggregation function. This will produce non-deterministic results and is a bug. Assuming that you are not exploiting a side-effect of that, the query can be re-written as:
SELECT
category, b.fruit_name, u.name, r.count_vote, r.text_c
FROM Fruits b
JOIN Reviews r
ON r.fruit_id = b.fruit_id
JOIN Customers u ON u.customer_id = r.customer_id
ORDER BY r.fruit_id, count_vote DESC, r_id;
Since there are no predicates other than joins in your query, there is no scope for further optimization beyond ensuring there are indexes on the join predicates.
As all too frequently, the biggest benefit may come from simply asking the question of why you need to retrieve every single row in the tables in a single query.
Im trying to make a select to make cooking recipe select within the items you have.
i have a table named ingredientsOwn with the following structure:
idType (int) amount (int)
Another table named recipes with this structure:
idRecipe (int) name (varchar)
And another table named recipeIngredients
idRecipe (int) idType (int) amount (int)
I would like to show the recipes you can do with the elements you have, how could i perform this?
Im trying to implement it in only one query cause i really dont know how to go throw and array on node js.
Thanks
The way I would go around this is, try to compute for each recipe, the number of ingredients you need, and join that with the number of ingredients own, and if the two numbers match, you have a candidate recipe.
So, to get the number of ingredients a recipe needs you'll have to do something like (this is more like a sql server syntax, so please try to focus in the concepts, and not the syntax):
select idRecipe, count(*) as neededIngredientsCount
from recipeIngredients
group by idRecipe
To get the number of available ingredients for each receipe, you have to join your ingredientsOwn with recipeIngredients, to be able to tell how many matching ingredients you have for each recipe.
select ingredientsOwn.idRecipe, count(*) as matchingIngredientsCount
from ingredientsOwn inner join recipeIngredients
on ingredientsOwn.idType = recipeIngredients.idType
where ingredientsOwn.amount >= recipeIngredients.amount
group by ingredientsOwn.idRecipe
Now you join the previous 2 queries to get the idRecieps that you have enough ingredients for, and join them with the recipes table to get the recipe name.
select r.idRecipe, r.name from
((select idRecipe, count(*) as neededIngredientsCount
from recipeIngredients
group by idRecipe) as in
inner join
(select ingredientsOwn.idRecipe, count(*) as matchingIngredientsCount
from ingredientsOwn inner join recipeIngredients
on ingredientsOwn.idType = recipeIngredients.idType
where ingredientsOwn.amount >= recipeIngredients.amount
group by ingredientsOwn.idRecipe) as io
on in.idRecipe = io.idRecipe
and in.neededIngredientsCount = io.matchingIngredientsCount
inner join
(select * from recipes) as r
on r.idRecipe = in.idRecipe)
Hope this helps, and sorry for not being able to provide valid mysql syntax.
SELECT * FROM recipes INNER JOIN (
select idRecipe from recipeIngredients
WHERE recipeIngredients.idType IN (
SELECT ingredientsOwn.idType from ingredientsOwn
)
) as a ON a.idRecipe = recipes.idRecipe
I have multiple tables, related by multiple foreign keys as in the following example:
Recipes(id_recipe,name,calories,category) - id_recipe as PK.
Ingredients(id_ingredient,name,type) - id_ingredient as PK.
Contains(id_ingredient,id_recipe,quantity,unit) - (id_ingredient,id_recipe) as PK, and as Foreign Keys for Recipes(id_recipe) and Ingredients(id_ingredient).
You can see this relations represented in this image.
So basically Contains is a bridge between Recipes and Ingredients.
The query I try to write it's supposed to give as result the names of the recipes whose ingredients type are "bovine" but not "lactic".
My attempt:
SELECT DISTINCT Recipes.name
FROM Ingredients JOIN Contains USING(id_ingredient) JOIN Recipes USING (id_recipe)
WHERE Ingredients.type = "bovin"
AND Ingredients.type <> "lactic";
The problem is it still shows me recipes that have at least one lactic ingredient.
I would appreciate any help!
This is the general form of the kind of query you need:
SELECT *
FROM tableA
WHERE tableA.ID NOT IN (
SELECT table_ID
FROM ...
)
;
-- EXAMPLE BELOW --
The subquery gives the id values of all recipes that the "lactic" ingredient is used in, the outer query says "give me all the recipes not in that list".
SELECT DISTINCT Recipes.name
FROM Recipes
WHERE id_recipe IN (
SELECT DISTINCT id_recipe
FROM `Ingredients` AS `i`
INNER JOIN `Contains` AS `c` USING (id_ingredient)
WHERE `i`.`type` = "lactic"
)
;
Alternatively, using your original query:
You could've changed the second join to a LEFT JOIN, changed it's USING to an ON & included AND type = "lactic" there instead, and ended the query with HAVING Ingredients.type IS NULL (or WHERE, I just prefer HAVING for "final result" filtering). This would tell you which items could not be joined to the "lactic" ingredient.
A common solution of this type of question (checking conditions over a set of rows) utilizes aggregate + CASE.
SELECT R.Name
FROM Recipes R
INNER JOIN Contains C
on R.ID_Recipe = C.ID_Recipe
INNER JOIN Ingredients I
on C.ID_Ingredient = I.ID_Ingredient
GROUP BY R.name
having -- at least one 'lactic' ingredient
sum(case when type = 'lactic' then 1 else 0 end) = 0
and -- no 'bovin' ingredient
sum(case when type = 'bovin' then 1 else 0 end) > 0
It's easy to extend to any number of ingredients and any kind of question.
Hijacked the fiddle of xQbert
SELECT R.NAME
FROM CONTAINS C
INNER JOIN INGREDIENTS I
ON I.ID_INGREDIENTS = C.ID_INGREDIENTS AND I.TYPE = 'bovine' AND I.TYPE <> "lactic"
INNER JOIN RECIPES R
ON R.ID_RECIPE = C.ID_RECIPE
GROUP BY R.NAME
That should work, maybe you need to escape 'contains'. It could be recognized as a SQL function.
SQL Fiddle
In my example burgers and pasta have 'Bovin' and thus show up. So do cookies but cookies also have 'lactic' which is why they get excluded.
SELECT R.Name
FROM Recipes R
INNER JOIN Contains C
on R.ID_Recipe = C.ID_Recipe
INNER JOIN Ingredients I
on C.ID_Ingredient = I.ID_Ingredient
LEFT JOIN (SELECT R2.ID_Recipe
FROM Ingredients I2
INNER JOIN Contains C2
on C2.ID_Ingredient = I2.ID_Ingredient
INNER JOIN Recipes R2
on R2.ID_Recipe = C2.ID_Recipe
WHERE Type = 'lactic'
GROUP BY R2.ID_Recipe) T3
on T3.ID_Recipe = R.ID_Recipe
WHERE T3.ID_Recipe is null
and I.Type = 'Bovin'
GROUP BY R.name
There likely is a more elegant way of doing this. I really wanted to CTE this and join it to itself.. but no CTE in mySQL. Likely a way to do this using exists too.... I'm not a big fan of using IN clauses as the performance generally suffers. Exists fastest, Joins 2nd fastest, in slowest (generally speaking)
The inline view (sub query) returns the ID_recipe of those you don't want to include.
The outer query returns the Name of the recipes with ingredients you want.
By joining these two together using an outer join we return all recipes and only those with the undesired ingredient. We then limit the results to only those where the recipe ID doesn't exist for the undesired ingredient. (undesired ingredient not found) you'll get only those recipes having all desired ingredients.
You can use NOT EXISTS for this.
Try this:
SELECT DISTINCT Recipes.`name`
FROM Recipes JOIN Contains AS C1 USING (id_recipe) JOIN Ingredients USING(id_ingredient)
WHERE Ingredients.type = "bovin"
AND NOT EXISTS (
SELECT 1
FROM Contains AS C2 JOIN Ingredients USING(id_ingredient)
WHERE C1.id_recipe = C2.id_recipe
AND Ingredients.type = "lactic"
)
I have a many-to-many relationship between two tables, and I am using a pivot table to manage it.
The structure is as follows:
Table 1: cars (car_id, ...)
Table 2: brands (brand_id, ...)
Pivot table: cars_brands (car_id, brand_id, units, discount)
Using only one query, I am trying to select every field of the cars of a certain brand PLUS the number of units and discount (both fields of the pivot table...)
My try:
SELECT c.*, cb.units, cb.discount
FROM (cars c, cars_brands cb)
INNER JOIN cb ON c.car_id = cb.car_id
WHERE cb.brand_id = 1
ORDER BY c.car_id asc
I am getting a
#1066 - Not unique table/alias: 'cb'
MySQL error.
Any help is much appreciated.
Thanks in advance.
I think what you want is:
SELECT c.*, cb.units. cb.discount
FROM cars AS c
JOIN car_brands as cb ON cb.car_id = c.car_id
JOIN brands AS b ON b.brand_id = cb.brand_id
WHERE b.brand_name = 'Audi'
ORDER BY c.car_id ASC
you can try this also:-
select c.*,cb.units,cb.discount from car c,brands b,cars_brands cb where c.car_id=cb.car_id and b.brand_id=cb.brand_id and cb.brand_id=8888 order by cb.car_id asc;
So basically I'm joining 3 tables together. The main table is recipe, then it goes to ingredients list then ingredient.
So I need to have a query which has only recipes which contain NO chicken. The problem I am having is that because recipes have many ingredients when I use where != that just removes the ingredients with that meat but leaves the others.....how can i account for the multiple ingredients.
select Recipe.name as "No chicken" from Recipe inner join IngredientList on Recipe.recipeId=IngredientList.recipeId inner join Ingredients on IngredientList.IngredientId=Ingredients.ingredientId where type!="chcicken" group by Recipe.name;
Your original statement has a GROUP BY with no aggregate function. That doesn't make sense. It should be an ORDER BY if you're trying to sort.
Try something like this:
SELECT `Recipe`.`name` AS "No chicken"
FROM `Recipe`
WHERE `Recipe`.`RecipeId` NOT IN (
SELECT DISTINCT `IngredientList`.`RecipeId` AS `RecipeID`
FROM `IngredientList`
INNER JOIN `Ingredients` ON `IngredientList`.`IngredientId` = `Ingredients`.`IngredientId`
WHERE `Ingredients`.`Type` = 'chicken'
)
ORDER BY `Recipe`.`name`
Depending on your schema, you may need to use SELECT DISTINCT in the main select statement if you're getting duplicate recipe names.
The above have some typos, but Amirshk has a logically correct answer.
However, I recommend one avoid the IN() and NOT IN() clauses in MySQL as they are very, very slow on a set of tables as big as a large recipe database would get. IN and NOT IN can be re-written as joins to cut the runtime to 1/100th the time in MySQL 5.0. Even with MySQL 5.5's great improvements, the equivalent JOIN query benchmarks 1/5th the time on large tables.
Here is the revised query:
SELECT
Recipe.name AS "No Chicken"
FROM Recipe LEFT JOIN
(
SELECT IngredientList.recipeId, Ingredients.ingredientId
FROM IngredientList JOIN Ingredients USING (IngredientId)
WHERE Ingredients.type = 'chicken'
) WithChicken
ON Recipe.recipeId = WithChicken.recipeId
WHERE WithChicken.recipeId IS NULL;
This is pretty obtuse, so here is simplified SQL that provides the key concept of the NOT IN(...) equivalent exclusion join:
SELECT whatever FROM x
WHERE x.id NOT IN (
SELECT id FROM y
};
becomes
SELECT whatever FROM x
LEFT JOIN y ON x.id = y.id
WHERE y.id IS NULL;
Use an inner query to filter recipes with chicken, then select all the recipes without them.
As so:
select
Recipe.name as "No chicken"
from Recipe
inner join IngredientList on Recipe.recipeId=IngredientList.recipeId
inner join Ingredients on IngredientList.IngredientId=Ingredients.ingredientId
where Recipe.recipeId NOT IN (
select
Recipe.recipeId
from Recipe
inner join IngredientList on Recipe.recipeId=IngredientList.recipeId
inner join Ingredients on IngredientList.IngredientId=Ingredients.ingredientId
type ="chcicken" group by Recipe.recipeId)