MySQL table relations - join if field has value - mysql

There could be X different tables in my system and one table that stores all relation data of those tables. For example the database could look like this
-------------------------- -----------------------------------
| Table fruits | | Table drinks |
-------------------------- -----------------------------------
| id | name | | id | name |
-------------------------- -----------------------------------
| 1 | banana | | 1 | strawberry apple cocktail|
| 2 | apple | | 2 | banana juice |
| 3 | strawberry | | 3 | cola |
-------------------------- -----------------------------------
--------------------------
| table food |
--------------------------
| id | name |
--------------------------
| 1 | apple pie |
--------------------------
And the table for the relations contains the id and the table name from both entries.
--------------------------------------------------
| table relations |
--------------------------------------------------
| id | table_1 | id_1 | table_2 | id_2 |
--------------------------------------------------
| 1 | fruits | 1 | drinks | 2 | banana + banana juice
| 2 | fruits | 2 | drinks | 1 | apple + s.a. cocktail
| 3 | fruits | 3 | drinks | 1 | strawberry + s.a cocktail
| 4 | food | 1 | fruits | 2 | apple pie + apple
| 5 | drinks | 3 | food | 1 | if someone wants to mix it
--------------------------------------------------
If I have a drink (strawberry apple cocktail) I want to get all fruit ids that belong to it (strawberry and apple) and the other way around when I have a fruit (strawberry) I would like to query for all related drink ids.
So I need one query structure to find both sides and since it is all dynamic I can't know if the fruit is in table_1 or table_2 (otherwise it would be simple)
I would like to have something like this (attention pseudocode) to query all ids from drinks that contain bananas (table: fruits, id: 1)
SELECT id
FROM drinks
LEFT JOIN relations AS rel ON drinks.id = (rel.table_1 == drinks)? rel.id_1 : rel.id_2
WHERE
((rel.id_1 = '1' AND rel.table_1 = 'fruits') OR ( rel.id_2 = '1' AND rel.table_2 = 'fruits' ))
My issues
When I join my relation table only one time, I would have to query two times since I don't know if I have to join it on id_1 or id_2
When I do 2 Joins like this
Select * from drinks
LEFT JOIN relations AS relation1 ON drinks.id = relation1.id_1
LEFT JOIN relations AS relation2 ON drinks.id = relation2.id_2
WHERE
( (relation1.id_1 = '1'
AND relation1.table_1 = 'fruits')
OR ( relation2.id_2 = '1'
AND relation2.table_2 = 'fruits' )
OR ( relation1.id_2 = '1'
AND relation1.table_2 = 'fruits')
OR ( relation2.id_1 = '1'
AND relation2.table_1 = 'fruits'))
I can search for everything from both sides (have drinks -> receive fruits / have fruit -> receive drinks) but I get also the drink with the same Id like the fruit
I would like to avoid to select * from relations since I build the query with php with other conditions like fruit name, dateCreated and so on and I would have to restructure the entire query if it contains relations
You can download the mysql dump here to test it. I hope someone could be so kind to help me.
Thank you and sry for my bad english

Your problem is in your design, the drink table has many-to-many relation with fruit table, so you should separate fruit_drink_relation's table.
The same many-to-many relation exist for fruit_drink_food_relation's table.
To look for food, query the second relation's table, otherwise, the first relation table is enough.

Utterly bad design there.
See, the Relational Database Management Systems possess the powerful mechanism of keeping and enforcing consistent (!) relationships between entities (represented as records in the tables).
Why not use it as it is?
You got n..n (many-to-many) relationships, not more than that, as seen from examples.
There's a more-than-common pattern for it, the relation table that essentially consists of two columns both of which are foreign keys to the entities you'd need to connect together:
CREATE TABLE fruits_juices (
fruit_id INT,
juice_id INT
FOREIGN KEY fruit_id
REFERENCES fruits(id),
FOREIGN KEY juice_id
REFERENCES juices(id)
)
Do that for each relation you need - and this it.
And that is nothing bad in creating a couple of relationships - that's the way RDBMS supposed to work (hence what they're best at).
Don't try to be unnecessary versatile: your use cases don't demand that whatsoever.
Too one-for-all solutions are usually not good in all the cases they suppose to cover and have an additional maintenance cost to keep them from falling apart (data consistency, in your case)

I think you want a snowflake schema.
Looks like currently you are -in the ugly- half way of that.

Related

WHERE statement with dynamic input

I have two tables. The first one (item) is listing apartments. The second (feature) is a list of features that an apartment could have. Currently we list about 25 different features.
As every apartment can have a different set of features, I think it makes sense to have a 1:1 relationship between items and features table.
If in feature table for one the features the value is '1', this means that the linked apartment has this feature.
+-------------+------------+--------------+-------------+------------+
| table: item | | | | |
+-------------+------------+--------------+-------------+------------+
| id | created_by | titel | description | address |
+-------------+------------+--------------+-------------+------------+
| 10 | user.id | Nice Flat | text | address.id |
+-------------+------------+--------------+-------------+------------+
| 20 | user.id | Another Flat | text | address.id |
+-------------+------------+--------------+-------------+------------+
| 30 | user.id | Bungalow | text | address.id |
+-------------+------------+--------------+-------------+------------+
| 40 | user.id | Apartment | text | address.id |
+-------------+------------+--------------+-------------+------------+
+----------------+---------+--------------+----------------+--------------+------+
| table: feature | | | | | |
+----------------+---------+--------------+----------------+--------------+------+
| id | item_id | key_provided | security_alarm | water_supply | lift |
+----------------+---------+--------------+----------------+--------------+------+
| 1 | 10 | 1 | 0 | 0 | 1 |
+----------------+---------+--------------+----------------+--------------+------+
| 2 | 20 | 0 | 1 | 1 | 0 |
+----------------+---------+--------------+----------------+--------------+------+
| 3 | 30 | 1 | 1 | 0 | 1 |
+----------------+---------+--------------+----------------+--------------+------+
| 4 | 40 | 1 | 1 | 1 | 1 |
+----------------+---------+--------------+----------------+--------------+------+
I want to build a filter functionality so user can select to show only apartments with certain features.
e.g.:
$key_provided = 1;
$security_alarm = 1;
$water_supply = 0;
Does this database approach sounds reasonable for you?
What’s the best way to build a MySQL query to retrieve only apartments where the filter criteria match, keeping in mind that the number of features can be grow in future?
A better approach is to have a features table. In your case, they all seem to be binary -- yes or no -- so you can get away with:
create table item_features (
item_feature_id int auto_increment primary key,
item_id int not null,
feature varchar(255)
foreign key item_id references items(item_id)
);
The data would then have the positive features, so the first item would be:
insert into item_features (item_id, feature)
values (1, 'key_provided'), (1, 'lift');
This makes it easy to manage the features, particularly adding new ones. You might want to use a trigger, check constraint, or reference table to validate the feature names themselves, but I don't want to stray too far from your question.
Then checking for features is a little more complicated, but not that much more so. One method is explicitly using exists and not exists for each desired/undesired one:
select i.*
from items i
where exists (select 1
from item_features itf
where itf.item_id = i.item_id and
itf.feature = 'key_provided'
) and
exists (select 1
from item_features itf
where itf.item_id = i.item_id and
itf.feature = 'security_alarm'
) and
not exists (select 1
from item_features itf
where itf.item_id = i.item_id and
itf.feature = 'water supply'
);
For your existing data structure, you can filter as follows:
select i.*
from item i
inner join feature f
on f.item_id = i.id
and f.key_provided = 1
and f.security_alarm = 1
and f.water_supply = 0
This will give you all the apartments that satisfy the given criteria. For more criterias, you can just add more conditions to the on part of the join.
As a general comment about your design:
since you are creating a 1-1 relationship between apartments and features, you might as well consider having a single table to store them (spreading the information over two tables does not have any obvious advantages)
your design is OK as long as features do not change too often, since, basically, everytime a new feature is created, you need to add more columns to your table. If features are added (or removed) frequently, this can become heavy to manage; in that case, you could consider having a separated table where each (item, feature) tuple is stored in a different row, which will make this of things easier to do (with the downside that queries will get more complicated to write)

Storing similar products with different attributes

I need to store similar products with different attributes (like length or color), should I add this products with different ids or as one product with many attributes?
Main problem that I need possibility to see quantities of products by attribute (4 red boots, 3 blue etc) and need to implement attribute selector on product page.
If create one id how to store attributes in cart table with selected attributes and how to manage quantities?
If create many ids it is easy to manage quantities but how to implement attributes selection?
I think about some SKU for linking product id with attributes and attribute values. But how to link all together?
If you're building an ecommerce site, this domain concept is generally known as "variant"; see here.
Typically, you have a "products" table containing the major attributes of the item, and a "variants" table, linked back to the product by product ID.
If you're only ever dealing with similar products, you can have the variant table contain columns for all attributes (e.g. size, price, colour); if you have lots of different types of variant (e.g. clothes, shoes, sports equipment), you are at the "inheritance modeling using relational databases"; see this answer.
Tables are there to store any data which you have. If you need any particular information about those entries you have to use different queries. For example assume the following product table:
+----+-------------+-------------+
| ID | ProductNAme | ProductType |
+----+-------------+-------------+
| 1 | Product A | Type A |
+----+-------------+-------------+
| 2 | Product B | Type A |
+----+-------------+-------------+
| 3 | Product C | Type B |
+----+-------------+-------------+
You can get the quantity of Type A Products by writing the following query (view This Link for a better understanding):
Select count(*) as CountOfProduct from Product where ProductType="Type A";
EDIT
You mentioned in your comment that one product could possibly have different types and different quantities. You can do that in one table but that looks messy. If you want this to happen you need two build a 1-Many **relationship** between two tables called product and type.
The type table could be something like this :
| TYPE_ID | TYPE |
|---------|--------|
| 1 | Type A |
| 2 | Type B |
| 3 | Type C |
and your product table could be something like this:
| PRODUCT_ID | PRODUCTNAME | QUANTITY | TYPE_ID |
|------------|-------------|----------|---------|
| 1 | Product A | 3 | 1 |
| 2 | Product B | 2 | 1 |
| 3 | Product C | 1 | 2 |
| 4 | Product C | 5 | 3 |
**take note that type_id is the foreign key which builds the relation between these two tables. And since you can have multiple products with the same type (like product C in this example) this table will be your many table and type table will be your one table. Hence, with putting the foreign key in the many table you will establish the one to many relationship.
Now, in order to combine (or in other words to join) these two tables you will need to write a join query as following:
select ProductName,quantity,Type from Product p
inner join type t on t.type_id=p.type_id
and the result will be what you want:
| PRODUCTNAME | QUANTITY | TYPE |
|-------------|----------|--------|
| Product A | 3 | Type A |
| Product B | 2 | Type A |
| Product C | 1 | Type B |
| Product C | 5 | Type C |
Check this link out for the fiddle

One-to-one relation through pivot table

Okay so I have a soccer website im building when a user signs up they get a team and and 6 different stadium to chose from. so I have a teams table:
----------------------------------------
| user_id | team_name | stadium_id |
----------------------------------------
| 1 | barca | 2 |
----------------------------------------
Then I decided to make the stadiums its own table
----------------------------------------------
| id | name | capacity | price |
----------------------------------------------
| 1 | Maracana | 50000 | 90000 |
------------------------------------------------
| 2 | Morombi | 80000 | 150000 |
------------------------------------------------
to get the teams arena name I would have to get the arena_id from the teams table and then fetch the arena name with the id. Now I don't think this is very efficient so I gave it some thought and I think the best solution is adding a pivot table like so:
| id | arena_id | team_id |
---------------------- ----------------
| 1 | 2 | 1
--------------------------------------|
| 2 | 1 | 2
--------------------------------------|
I usually think of pivot tables as tables for many to many relationships not one to one relationships. Is using a pivot table in this instance the best solution or should I just leave the implementation I'm currently using?
You don't need to use a pivot-table for this. It can either be a One-To-One or a One-To-Many relationship. It's a One-To-One if every user/team does only relate to one stadium (no stadium can be used by two teams). In a One-To-Many relationship multiple teams/users could use the same stadium, which might become necessary if you have thousands of users and start running out of stadiums.
A JOIN statement would be efficient and sufficient here.
SELECT s.name, t.team_name -- Get the team's and stadium's name
FROM team t -- From the team table
JOIN stadium s -- Join it with the stadium table
ON (t.stadium_id = s.id) -- Join ON the stadium_id
This will return the team name and stadium name of every registered team.
You might need to adjust the query, but you should be able to catch the grasp of it after reading the MySQL reference I linked above.

How to design table schema for storing values from <select multiple>

I have a select-multiple list html control, which enables end user to select multiple items. Currently, I stored user selected values as "truck, heavy truck, others" into mysql varchar field. But both truck and heavy truck are searched out if user select truck on select multiple control. SQL statement for this query is like "select truckType from table where trackType like '%truck%'".
Seems the only way for the solution is to put selected values into another table.
Is there any other way to handle this issue in one table?
add , to the end and start also
so value becomes , truck, heavy truck, others,
use this query
select truckType from table where trackType like '%, truck,%
NOTE: , in like '%, truck,%'
you should make a separate table:
------------------
typeId | type |
-----------------
1 | truck |
2 | heavy truck |
....
Also table for entity:
------------------
entityid | |
-----------------
1 | entity1 |
2 | entity2 |
and other table will have multiple to one relationship with this table:
-------------------------------
id | typeId | entityId |
-------------------------------
1 | 1 | 2 |
2 | 2 | 2 |
2 | 2 | 1 |
And your select would be a join of this two tables.

SQL commands for two tables, which need primary and foreign key

I have table structures as follows:
Table Person :
+-------+--------+
| sno | sname |
+-------+--------+
| 1 | Bhanu |
| 2 | Raghu |
| 3 | Bunny |
| 4 | Shyam |
+-------+--------+
Table Friend :
+------+---------+
| sno | Friend |
+------+---------+
| 1 | 2 |
| 2 | 1 |
| 3 | 4 |
| 4 | 3 |
+------+---------+
Bhanu is friend of Raghu, vice versa.
Bunny is friend of Shyam, vice versa.
If I give the 'Bhanu' name from person table, I should be able to get his friend name as well. Which is mentioned in Friend table with respect to his sno values from person table.
Please do help me in performing this read operation with this kind of database.
You'll need to access the person table twice, and so will need aliases. I suggest this table structure first:
person (id, name)
friend (from_person_id, to_person_id)
This is much the same as your table structure, but the names are a bit clearer.
Now you could have something like:
SELECT
p2.name
FROM
friend
INNER JOIN person p1 ON (p1.id = friend.from_person_id)
INNER JOIN person p2 ON (p2.id = friend.to_person_id)
WHERE
p1.name = 'Bhanu'
;
That lists all the friendships going from "from" to "to". You might additionally want to list all the friendships going the other way, in which case you could add OR p2.name = 'Bhanu'.