Inner join with multiple matching records - mysql

I have 3 tables in my database: sProduct, sProductDetail and sProductDetailWarehouse. This is basically a webshop with having multiple EANs possible for a single product. For instance a t-shirt with multiple colors available, each color being it's own EAN.
The important bits about tables:
sProduct has ID which is primary key and title (varchar).
sProductDetail has ID (primary key), ID_sProduct (the correlation to the sProduct table), EAN and title
sProductDetailWarehouse has ID (primary key), ID_sProductDetail (correlation to the detail table) and stock (int).
What I would want is to use something similar to this:
select pd.ID,pd.title,pdw.stock from sProduct p
inner join sProductDetail pd on pd.ID_sProduct=p.ID
left join sProductDetailWarehouse pdw on pdw.ID_sProductDetail=pd.ID
and only have it return 1 record on join with the highest stock. The problem is I can't use order by since I have multiple products in a query needing to be ordered by their release date.
So basically out of every one sProduct.ID I would need only one sProductDetail.ID returned even though there might be many. Can anyone help with this?
Thanks.

Related

SQL Inner Join With Multiple Columns

I've got 2 tables - dishes and ingredients:
in Dishes, I've got a list of pizza dishes, ordered as such:
In Ingredients, I've got a list of all the different ingredients for all the dishes, ordered as such:
I want to be able to list all the names of all the ingredients of each dish alongside each dish's name.
I've written this query that does not replace the ingredient ids with names as it should, instead opting to return an empty set - please explain what it that I'm doing wrong:
SELECT dishes.name, ingredients.name, ingredients.id
FROM dishes
INNER JOIN ingredients
ON dishes.ingredient_1=ingredients.id,dishes.ingredient_2=ingredients.id,dishes.ingredient_3=ingredients.id,dishes.ingredient_4=ingredients.id,dishes.ingredient_5=ingredients.id,dishes.ingredient_6=ingredients.id, dishes.ingredient_7=ingredients.id,dishes.ingredient_8=ingredients.id;
It would be great if you could refer to:
The logic of the DB structuring - am I doing it correctly?
The logic behind the SQL query - if the DB is built in the right fashion, then why upon executing the query I get the empty set?
If you've encountered such a problem before - one that requires a single-to-many relationship - how did you solved it in a way different than this, using PHP & MySQL?
Disregard The Text In Hebrew - Treat It As Your Own Language.
It seems to me that a better Database Structure would have a Dishes_Ingredients_Rel table, rather than having a bunch of columns for Ingredients.
DISHES_INGREDIENTS_REL
DishesID
IngredientID
Then, you could just do a much simpler JOIN.
SELECT Ingredients.Name
FROM Dishes_Ingredients_Rel
INNER JOIN Ingredients
ON Dishes_Ingredients.IngredientID = Ingredients.IngredientID
WHERE Dishes_Ingredients_Rel.DishesID = #DishesID
1. The logic of the DB structuring - am I doing it correctly?
This is denormalized data. To normalize it, you would restructure your database into three tables:
Pizza
PizzaIngredients
Ingredients
Pizza would have ID, name, and type where ID is the primary key.
PizzaIngredients would have PizzaId and IngredientId (this is a many-many table where the primary key is a composite key of PizzaId and IngredientID)
Ingredients has ID and name where ID is the primary key.
2. List all the names of all the ingredients of each dish alongside each dish's name. Something like this in MySQL (untested):
SELECT p.ID, p.name, GROUP_CONCAT(i.name) AS ingredients
FROM pizza p
INNER JOIN pizzaingredients pi ON p.ID = pi.PizzaID
INNER JOIN ingredients i ON pi.IngredientID = i.ID
GROUP BY p.id
3. If you've encountered such a problem before - one that requires a single-to-many relationship - how did you solved it in a way different than this, using PHP & MySQL?
Using a many-many relationship, since that what your example truly is. You have many pizzas which can have many ingredients. And many ingredients belong to many different pizzas.
The reason you are getting an empty result is because you are setting a join condition that never gets satisfied. During the INNER join execution the database engine compares each record of the first table with each record of the second one trying to find a match where the id of the ingredient table record being evaluated is equal to ingredient1 AND ingredient2 AND so on. It would return some result if you create a record in the first table with the same ingredient in all 8 columns (testing purposes only).
Regarding the database structure, you choose a denormalized one creating 8 columns for each ingredient. There are a lot of considerations possible on this data structure (performance, maintainability, or just think if you are asked to insert a dish with 9 ingredients for example) and I would personally go for a normalized data structure instead.
But if you want to keep this, you should write something like:
SELECT dishes.name, ingredients1.name, ingredients1.id, ingredients2.name, ingredients2.id, ...
FROM dishes
LEFT JOIN ingredients AS ingredients1 ON dishes.ingredient_1=ingredients1.id
LEFT JOIN ingredients AS ingredients2 ON dishes.ingredient_2=ingredients2.id
LEFT JOIN ingredients AS ingredients3 ON dishes.ingredient_3=ingredients3.id
...
The LEFT join is required to get a result for unmatched ingredients (0 value when no ingredient is set reading your example)

How to get row only if column value equals "joined_table1.id" or "joined_table2.id"?

I have 2 different tables that hold different types of products, e.g. Phones or TVs. Each of those two tables have a PK.
I also provide a catalog functionality, where a single catalog may contain multiple TVs and multiple Phones. In the catalog_items.idItem column I am storing the PK of the item being added to the catalog (but not its type).
My question is, how do I retrieve all catalog_items rows where the item is present in at least one of the two product tables, by just knowing the ID that is joining the three tables?
To visualize:
Table tvs:
id
Name
Table phones:
id
Name
Table catalog_items:
id
idItem (may contain a ID from either tvs.id or phones.id, but also may be a different product entirely; I want to exclude rows with IDs of other products besides TVs and Phones)
First, are you sure you don't have duplicate key values in the two tables? IOW, table tv.id can not exist in phones.tv.
SELECT
a.id as cat_id,
b.id as tv_id,
b.name as tv_name,
c.id as phone_id,
c.name as phone_name
FROM catalog_items a
LEFT JOIN tvs b
ON a.iditem = b.id
LEFT JOIN
ON a.iditem = c.id
WHERE NOT(b.id IS NULL) AND NOT(c.id IS NULL)
Not the best way to store the data, or the most efficient way to have to retrieve it. It would be much better to have all of the products in one table.
One alternative would be to create a view that unions the product tables, and then use the view in a simple two table inner join. This would allow you to order the results by any of the fields in the product tables.

MYSQL one to many: acessing the primary key from the fitst table, not the Fk from the second

A mysql one to many question: various products and where I sell them. I have a variety of products that I can sell, and several locations to sell them. I have a products table with a Primary Key: product_id. I have a locations table with PK location_id and a field product_id to link to the products table.
So, SELECT * FROM products LEFT JOIN locations on product_id.
I got an ambiguous column error. That doesn't make sense, they should line up.
So: SELECT * FROM products LEFT JOIN locations on products.product_id = locations.product_id.
That worked.
Here's the problem: there are many products I can get but don't currently sell. Some products have no locations. To create a link to add a location, I need the product_id. If I print out $row['product_id'] I am getting the product ids from the locations table, which is blank if they are unsold. I need the product ids from the first table.
This can't be an unusual situation.
Ideas?
You need to specify in your SELECT statement that you want to use the product_id from the products table. Here is a sample of what I am talking about (SQL Fiddle):
SELECT p.product_id, p.description, p.unit_of_measure, l.location
FROM products p
LEFT JOIN locations l on l.product_id = p.product_id
As you can see for product_id 3 there is no location but yet I am still displaying the product_id.
MySQL supports the using clause, so you can write the first version as:
SELECT *
FROM products LEFT JOIN
locations
USING (product_id);
Without the using clause, MySQL needs to know the table where a column comes from. Because there are two tables with the same column name, MySQL is confused.
This also has the advantage that the * only includes the product_id column once, not twice.

A MySQL query addressing three tables: How many from A are not in B or C?

I have a problem formulating a MySQL query to do the following task, although I have seen similar queries discussed here, they are sufficiently different from this one to snooker my attempts to transpose them. The problem is (fairly) simple to state. I have three tables, 'members', 'dog_shareoffered' and 'dog_sharewanted'. Members may have zero, one or more adverts for things they want to sell or want to buy, and the details are stored in the corresponding offered or wanted table, together with the id of the member who placed the ad. The column 'id' is unique to the member, and common to all three tables. The query I want is to ask how many members have NOT placed an ad in either table.
I have tried several ways of asking this. The closest I can get is a query that doesn't crash! (I am not a MySQL expert by any means). The following I have put together from what I gleaned from other examples, but it returns zero rows, where I know the result should be greater than zero.
SELECT id
FROM members
WHERE id IN (SELECT id
FROM dog_sharewanted
WHERE id IS NULL)
AND id IN (SELECT id
FROM dog_shareoffered
WHERE id IS NULL)
THis query looks pleasingly simple to understand, unlike the 'JOIN's' I've seen but I am guessing that maybe I need some sort of Join, but how would that look in this case?
If you want no ads in either table, then the sort of query you are after is:
SELECT id
FROM members
WHERE id NOT IN ( any id from any other table )
To select ids from other tables:
SELECT id
FROM <othertable>
Hence:
SELECT id
FROM members
WHERE id NOT IN (SELECT id FROM dog_shareoffered)
AND id NOT IN (SELECT id FROM dog_sharewanted)
I added the 'SELECT DISTINCT' because one member may put in many ads, but there's only one id. I used to have a SELECT DISTINCT in the subqueries above but as comments below mention, this is not necessary.
If you wanted to avoid a sub-query (a possible performance increase, depending..) you could use some LEFT JOINs:
SELECT members.id
FROM members
LEFT JOIN dog_shareoffered
ON dog_shareoffered.id = members.id
LEFT JOIN dog_sharewanted
ON dog_sharewanted.id = members.id
WHERE dog_shareoffered.id IS NULL
AND dog_sharewanted.id IS NULL
Why this works:
It takes the table members and joins it to the other two tables on the id column.
The LEFT JOIN means that if a member exists in the members table but not the table we're joining to (e.g. dog_shareoffered), then the corresponding dog_shareoffered columns will have NULL in them.
So, the WHERE condition picks out rows where there's a NULL id in both dog_shareoffered and dog_sharewanted, meaning we've found ids in members with no corresponding id in the other two tables.

Joins on MySQL many-to-many tables

This has been driving me mad.
I have three tables:
items
ID
name
type
cats
ID
name
items_to_cats
FK_ITEM_ID
FK_CAT_ID
This is a simple many-to-many relationship. I have items and categories. Each item can be linked to one or more categories. This is done via a simple joining table where each row maintains a relationship between one item and one category using foreign key constraints.
You will notice that my "items" table has a field called "type". This is an indexed column that defines the type of content stored there. Example values here are "report", "interview", "opinion", etc.
Here's the question. I want to retrieve a list of categories that have at least one item of type "report".
Ideally I want to get the result in a single query using joins. Help!
select distinct cats.id, cats.name
from cats
join items_to_cats on items_to_cats.fk_cat_id=cats.id
join items on items.id=items_to_cats.fk_item_id
where items.type='report'
Just as a point of database design, if you have a small set of legal values for items.type, i.e. "report", "interview", "opinion", maybe a couple more, then you really should create a separate table for that with, say, an id and a name, then just put the type id in the items table. That way you don't get into trouble because somewhere it's mis-spelled "raport", or even more likely, someone puts "reports" instead of "report".
or how about this :
SELECT c.id, c.name
FROM cats c
WHERE c.id IN
(SELECT ic.fk_cat_id
FROM items_to_cats ic
JOIN items i on i.id=ic.fk_item_id
WHERE items.type='report'
)