Relating MySQL-table content correctly - php

I have the following tables in a database:
products
assembly_steps
parts
warnings
I want to relate the content of these tables as follows:
A product consists of many assembly_steps. An assembly_step can have different part and warnings. So I build the tables
assembly_steps_has_parts
assembly_steps_has_warnings
products_has_assembly_steps
to relate the data. The ...has...-tables are connected with their related partners by foreign keys. I modeled that with the MySQL-Workbench.
I am confused about the mechanism to relate the info. How do I program that in PHP?
I think first you add the content on the lowest level, that would be parts and warnings. Then you add the assembly step and relate the data. But I don't know how to do this.
Here you find an overview: Database-Model

Relational databases relate entities/values by recording them together in a row in a table.
To relate assembly_steps to parts, just insert a row into assembly_steps_has_parts, e.g. if you have the assembly_step_id in $assembly_step_id, and the part_id in $part_id, then:
INSERT INTO assembly_steps_has_parts (assembly_steps_id, parts_id)
VALUES ($assembly_step_id, $part_id)

You wouldn't program this in PHP, you'd handle it fully with mysql. The way this would be structured in mysql would be something like this:
assembly_steps
assembly_id
assembly_description (or something like that
assembly_id is the primary key
parts
part_id
part_name
part_id is the primary key
assembly_steps_has_parts
assemply_id
part_id
In this table, you'd have a dual primary key. Both assembly and part id are foreign keys AND primary keys for their respective tables.
The way that dual primary keys work is that there are two keys to make up one primary key on one table. That means that instead of limiting to 1 key, it limits the table to one of any combination of these keys to make one.
For instance:
pk1 pk2
1 1
1 2
1 3
2 1
2 2
2 3
You could query them like this (this is a generic query, but the basic idea)
select a.assembly_description, p. part_name
from assembly_id a
join assembly_steps_has_parts ats
on a.assembly_id = ats.assembly_id
join parts p
on ats.part_id = p.part_id
You'd do the same thing for the other tables. From that point you'd just call the results of your query in php the way you would handle any other query.

Related

How to find a way that items with the same names wont collide with each other when left join tables

I have been looking around for answers for countless hrs and could not find much.I am making a food project and I was wondering if there is a way to prevent 2 items with the same name from colliding each other with their 2nd table.To make things clear I have 1 table which is
CREATE TABLE cuisine (id int,dish_name varchar(32));
//2nd table CREATE TABLE ingrediant (id int,dish_name varchar(32),ingrediant);
I was wondering if lets say 2 people posted dish_name = pizza with different ingrediants,when i left join how would the tables know which one to join because the only thing that are matching is the dish_name.
I know i could add the ingrediants to the first table but lets say i am adding up to 50 ingrediants and other items it is too much stuff.
An example i can use is Ebay.
If 2 people post items with the exact same name,when the item is clicked how does it know which info it is for.Hope it is clear
If you would model your relations correctly, you don't have that problem.
You would have to define a foreign key in the 2nd table, and the foreign key would have to refer to the primary key (or at least some other uniquely indexed field or combination of fields) in the 1st table. If you plan on using dish_name as a foreign key in the 2nd table, that would necessarily mean that dish_name would have to be unique in the 1st table.
This is probably a bad idea, so it's better to create a dish_id foreign key column in your 2nd table and get rid of the dish_name column.

DB Design; or Conditional Selects with json data

I have a DB with several tables that contain basic, static ID-to-name data. 2 Columns only in each of these reference tables.
I then have another table that will be receiving data input by users. Each instance of user input will have it's own row with a timestamp, but the important columns here will contain either one, or several of the ID's related to names in one of the other tables. For the ease of submitting and retrieving this information I opted to input it as text, in json format.
Everything was going great until I realized I'm going to need to Join the big table with the little tables to reference the names to the ID's. I need to return the IDs in the results as well.
An example of what a few rows in this table might look like:
Column 1 | Column 2 | Timestamp
["715835199","91158582","90516801"] | ["11987","11987","22474"] | 2012-08-28 21:18:48
["715835199"] | ["0"] | 2012-08-28 21:22:48
["91158582","90516801"] | ["11987"] | 2012-08-28 21:25:48
There WILL be repeats of the ID#'s input in this table, but not necessarily in the same groupings, hence why I put the ID to name pairings in a separate table.
Is it possible to do a WHERE name='any-of-these-json-values'? Am I best off doing a ghetto join in php after I query the main table to pull the IDs for the names I need to include? Or do I just need to redo the design of the data input table entirely?
First of all:
Never, ever put more than one information into one field, if you want to access them seperately. Never.
That said, I think you will need to create a full N:M relation, which includes a join table: One row in your example table will need to be replaced by 1-N rows in the join table.
A tricky join with string matching will perform acceptably only for a very small number of rows, and the WHERE name='any-of-these-json-values' is impossible in your construct: MySQL doesn't "understand", that this is a JSON array - it sees it as unstructured text. On a join table, this clause comes quite naturally as WHERE somecolumn IN (1234,5678,8012)
Edit
Assuming your Column 1 contains arrays of IDs in table1 and Column 2 carries arrays of IDs in table2 you would have to do something like
CREATE TABLE t1t2join (
t1id INT NOT NULL ,
t2id INT NOT NULL ,
`Timestamp` DATETIME NOT NULL ,
PRIMARY KEY (t1id,t2id,`Timestamp`) ,
KEY (t2id)
)
(you might want to sanity-check the keys)
And on an insert do the following (in pseudo-code)
Remember timestamp
Cycle all permutations of (Column1,Column2) given by user
Create row
So for your third example row, the SQL would be:
SELECT #now:=NOW();
INSERT INTO t1t2join VALUES
(91158582,11987,#now),
(90516801,11987,#now);

SQL return only not empty columns from row as new row

I'm in the situation where my client e-mails me an excel-file with 50 columns of data extremely un-normalized. I then export it to CSV and upload into MySQL -- single table. The columns are for different ingredients (10 columns of data for each ingredient -- title, category, etc) and then 40 different columns for characteristics on each ingredients. So each ingredient in the table has all of these 50 columns even though every column doesn't apply for that ingredient.
My question is if I can create a SQL that selects only filled in characteristics for one selected ingredient and leaves out all of the other columns?
(I know that another option is to build my own CSV-parser that created multiple tables and then write SQL for them instead, but I wanna investigate solving this as is first. If that's not possible then I just have to face that and build a parser ;P)
This is as far as I came but this doesn't completely exclude columns not filled in (or that contains "nei".
SELECT
IF(`Heving-vanlig-gjaerbakst` <> '' AND `Heving-vanlig-gjaerbakst` <> 'nei', `Heving-vanlig-gjaerbakst`, 'random') AS `test1`,
IF(`Frys-kort` <> '' AND `Frys-kort` <> 'nei', `Frys-kort`, 'random') AS `test2`
... and for the 38 other rows ...
FROM x
WHERE id = 123
And I'd rather not solve this in the PHP-code by skipping empty rows =P
Example row (column names first):
g1 gruppe ug1 undergruppe artnr artikkel beskrivelse status enhet ansvar prisliste Heving-vanlig-gjaerbakst Heving-soete-deiger Deig-stabilitet Smaksgiver Saftighet Krumme-poring Skorpe Volum Konservering Skjaerbarhet Frys-lang Frys-kort Kjoel Holdbarhet E-fri Azo-fri Mandler Aprikoskjerner Helmiks Halvmiks Base Konsentrat Utstrykning Bakefasthet Frukt-Baerinnhold Slippegenskaper Hindre-koksing Palmefri Fritering Smidighet Baking Kreming Roere Fylning Dekor Prefert Viskositet Cacaoinnhold Fet-innhold
100150 Bakehjelpemidler 100150200 Fiber/potetprodukter 10085 Potetflakes sekk 15 kg Egnet til lomper, lefser, brød og annet bakverk. B... Handel Sekk Trond Olsen JA xxx xxx xxx
As you can see most columns are empty here. X, XX and XXX is a form of grade-system, but for some columns the content is instead "yes" or "no".
And as I said, the first 10 columns are information about that product, the other 40 is different characteristics (and it's those I wanna work with for one given product).
It sounds a bit as if you'd like to convert the table you have into two tables:
CREATE TABLE Ingredients
(
g1 ...,
gruppe ...,
ug1 ...,
undergruppe ...,
artnr ... PRIMARY KEY,
artikkel ...,
beskrivelse ...,
status ...,
enhet ...,
ansvar ...,
prisliste ...
);
I've opted to guess that the artnr is the primary key, but adapt what follows to the actual primary key. This table contains the eleven (though your question said ten) columns that are common to all ingredients. You then have another table which contains:
CREATE TABLE IngredientProperties
(
artnr ... NOT NULL REFERENCES Ingredients,
property VARCHAR(32) NOT NULL,
value VARCHAR(3) NOT NULL,
PRIMARY KEY(artnr, property)
);
You can then load the populated columns from your original table into these two. At worst, there'd be 40 entries in IngredientProperties for one entry in Ingredient. You might make 'property' into a foreign key reference to a defining list of possible ingredient properties (a third table that defines the possible values for the properties - basically, a record of the column names from your original table). If you add the third table, it might logically be called IngredientProperties (too), in which case the table I called IngredientProperties needs to be renamed.
You can then join Ingredients and IngredientProperties to get the information you want.
I'm not sure that I recommend this solution; it is basically a use of the 'Entity Attribute Value' approach to database design. However, for extremely sparse information like you seem to have, and when used with the constraint of the third table.
What you can't sensibly do is handle all possible combinations of 40 columns as that number grows exponentially with the number of columns (and is pretty large with N = 40).

How to Structure a Table where *some* Columns can have Multiple Values? [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How to Store Multiple Options selected by User in a Table
I am very confused by this.
I want my users to be able to restrict who may contact them. So I need to create a new table of RESTRICTIONS for each user and their selected criteria, which will have a column for each user's restriction criteria. Those columns will include age, income, looking for, drugs, etc. Some of those columns (looking for, drugs, etc.) might contain multiple choices, and therein lies my problem. How do I structure the table in this way, considering some criteria (columns) can have multiple values while others do not?
I've been told about join tables and enum values (both of which I've never worked with before), but I am really confused as to how I would structure a table in which some columns (not all), can contain multiple choices.
How do I store those multiple choices in those specific columns of the table and how do I structure the overall table of RESTRICTIONS?
A DB column (at least theorethically) should NOT hold multiple values. Unfortunately, there are some programmers that store multiple values in a single column (values separated by comma for examples) - those programmers (in most cases) destroy the concept of DB and SQL.
I suggest you to read about Database Normalization to get a start in organizing your tables. And, do your best to achieve the Codd's Third Normal Form
This is the simplest way. Multiple attributes become rows in a second table.
CREATE TABLE restrictions (
user_id INTEGER PRIMARY KEY, -- references users (user_id), not shown
age_restriction VARCHAR(10) NULL,
income_restriction VARCHAR(20) NULL
);
CREATE TABLE looking_for (
user_id INTEGER NOT NULL REFERENCES restrictions (user_id),
looking_for VARCHAR(35) NOT NULL, -- could also be a foreign key.
PRIMARY KEY (user_id, looking_for)
);
INSERT INTO restrictions (user_id) VALUES (1);
INSERT INTO restrictions (user_id, age_restriction) VALUES (2, '> 25');
INSERT INTO looking_for VALUES (1, 'boat');
INSERT INTO looking_for VALUES (1, 'dunky fazoo');
If you wanted to accept multiple restrictions on age, such as '> 25' and '< 55', you could build another table for that, too.
To retrieve all the restrictions, use an OUTER JOIN.
SELECT r.user_id, r.age_restriction, r.income_restriction, lf.looking_for
FROM restrictions r
LEFT JOIN looking_for lf ON lf.user_id = r.user_id
You probably need more than one table.
You have a "users" table already, right? If one of your "restrictions" criteria can have just one value per user, then that column belongs in the "users" table. So you might have columns "min_age" and "max_age" in the users table, because presumably each user has only one, contiguous range of ages they are looking for.
On the other hand, for each restriction criterion that can have multiple values, you need a new table. So you might have a table "users_restrictions_drugs" in which the primary key is (user, drug). But you might also have a table "users_restrictions_lookingfor" in which the primary key is (user, lookingfor). Whatever "looking for" is.
For some of these tables it may make sense either
to define the second column (the one that isn't "user") as an enum
or (better) to have an additional table that sets out the possible values of that second column and is referenced by a foreign key.
table restrictions
user_id smoker_ok min_height max_height min_age max_age
-------------------------------------------------------
1 Y 150 200 24 34
2 N 100 180 32 57
table drug_restrictions
user_id drug_id drug_allowed
----------------------------------
1 H N
1 M Y
2 E Y
Would be an example. In the restrictions table, you can store explicit, singular values - smokers yes or no, or min and max requirements.
For each table where there are multiple choices, you can create a join table - I've given an example for drugs.
In the drug_restrictions table, user 1 says she doesn't want people using H, but does want people using M.
This solution allows you to use the "drug_id" as a foreign key to whatever table in your database populates the "drugs" field on the user interface. It allows you to use regular, standard SQL conventions for those foreign keys, and to enforce them at the database level by declaring them as foreign keys.
The drawback is, of course, that you have to query lots of tables to find matching records, and that's not much fun.
So, you could also follow Catcall's recommendation - this dramatically reduces the number of tables, but makes it impossible to use "standard" foreign key integrity constraints. This might be okay - it's certainly going to be faster.
I'd be reluctant to use enums - they tend to lead to complex queries, and are not "standard" SQL.
There's no problem to have tables where some columns have duplicate values. Consider a tbale with users; there's no problem if two users have the same birthday?
The only problem is a table where a primary key occurs more than once. For instance, a user table may very well have username as its primary key, and you wouldn't want two users with the same username.
So, make one table that lists users, one that lists restrictions, and one that joins the two. The latter will have one entry for every combination of user/permission.

orderby statements on multiple joins

I have three tables, each contain an auto-incrementing PK. I need to select the latest (ie, ORDERBY DESC) entries from the mix of these tables.
I'd like to do it in one query, with two joins.
My idea was is to somehow select a table, order it by id DESC, then somehow merge the results.
Does anyone have a way (or probably a better idea) of doing this?
(using mysql)
EDIT:
Sorry - here's a more detailed spec:
I have three tables, posts, stories, favs.
Each has (at the least) three columns id, uid and date. They are both PK's. id auto-increments (separately for each table). uid is FK, but that's irrelevant. I need the result set to contain the latest 20 or 30 entries.
UNION with no joins would be a possability.
see http://dev.mysql.com/doc/refman/5.0/en/union.html for details.
Since the tables have no relation, joining them doesn't make any sense, right?
As far as I understood, You would like to order records originating from 3 different tables in one result set by their creation date. There are two ways to achieve this:
Use a PK that is sortable and unique for all 3 tables. I know You can do this easily in PostgreSQL. In MySQL You need a workaround: Create a 4th table with an auto-increment PK and change the other 3 tables PK to a FK pointing at the 4th table PK. This is somewhat cumbersome and You need to be aware of the fact, that an auto-increment field is not a good candidate for a reliably sortable property (deleting records leads to gaps in the sequence, which might be filled later).
Add a column created to each of the 3 tables and store the creation date of each record. The UNION can then be sorted by this column. You already have a column date. Can't You use this column?

Categories