whether to combine or to split table - php

I can't figure out what is the better solution.
A table : 35 of fields and 2 of them are longtext type. (Already built-in in the CMS that I'm about to use)
B table : similar amounts of fields and 3 of them are text type.(Newly required data for my project)
At first, I decided to use already existing 'A' table and add new 'B' table. It's far easy way 'cause the CMS already provides the classes and methods (etc - search, crud...) that is needed to my project.
But, my concern is the performance. Since table 'B' is sort of add-on to 'A', most of the DB actions, like showing list, searching, getting contents..., must refer at least those two tables at once each time.
JOIN and multiple queries are absolutely needed. And this site needs to show many lists and contents to online users at same time.
What would be the best solution? Is combining two tables more efficient, or re-using the already-existing codes as much as I can and preserving compatibility to the CMS?
ps. If I combine two tables, I can reduce some of the unnecessary varchar fields in table 'A'.

You only duplicate columns that you will use to relate tableB with tableA. Reasons why you shouldn't duplicate non-primary key columns:
a. table size grows and it's a waste of space if there are duplicate fields between tables.
b. regardless of the column size to be duplicated, it's very laborious maintaining tables just to make sure all duplicate columns are updated correctly. Think for example there's a duplicate column named detail. If you update this column in tableA for a particular id, then you need to make another effort to update the corresponding record in tableB.
c. Will consume lots of effort, resources, time, and money to maintain duplicate non primary key columns between tables.

Combining 2 tables is too expensive and not wise. If those two tables contains duplicate fields, you will waste lots of database space which will impact the CRUD operations. Best approach is to create table B but add only the primary key[s] fields from table A that you can use to link the two tables. In this way it will be easy to retrieve to both tables for data you specifically need. You also don't need to retrieve all data from both tables.
Example:
Table A:
id
fname
lname
.
.
rest of the 35 fields
Table B:
tableb_id
id (from table A)
.
.
rest of table B fields
Your Query:
Select column[s]
From TableA, TableB
Where TableA.id = TableB.id
And (all other conditions like TableA.lname = "Santos");
or use Inner Join
SELECT column_name(s)
FROM tableA INNER JOIN tableB ON tableA.id = tableB.ID
WHERE (all other conditions like TableA.lname = "Santos");

Related

Relating MySQL-table content correctly

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.

Moving data from 1 pair of table to another similar pair in Cakephp

I have 2 similar pairs of tables (Table1 and Table2, TableA and TableB).
Table1 has many Table2. Table2 belongs to Table1. In other words, there is a one-to-many relationship between Table1 and Table2. TableA and TableB has the same one-to-many relationship as Table1 and Table2.
I would like to move a row in Table1 plus all the associated rows in Table2 to the matching table pair TableA and TableB.
What is the best way to transfer the table row and the associated rows? Do I save each row to the new table one by one using save() or is there a way to save all the rows at one go using saveall()?
Is it a problem to use saveall() to do the data transfer if the table fields are similar but not exactly the same. Between the table pair, some rows are the same and some rows are different.
I am using Cakephp 2.4.5. Thank you for your help.
You'll need to use saveAssociated to solve this, but if your tables are not exactly the same then you will first need to transform your data into something manageable. Here is an example:
$table_1 = $this->Table_1->find('first', array('conditions => array('Table_1.whatever' => 'something')));
// here you can manually build the data you need to save.
$transfer_data['Table_A'] = $table_1['whatever you needed to change'];
You might need a for loop or something to convert your data to the slightly different version that you need for table a and b, but without knowing what the difference is there I cannot write the code that does the transformation. Once the transformation is complete you can just save your save as explained in the manual using saveAssociated.

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.

Managing Foreign Keys

So I have a database with a few tables.
The first table contains the user ID, first name and last name.
The second table contains the user ID, interest ID, and interest rating.
There is another table that has all of the interest ID's.
For every interest ID (even when new ones are added), I need to make sure that each user has an entry for that interest ID (even if its blank, or has defaults).
Will foreign keys help with this scenario? or will I need to use PHP to update each and every record when I add a new key?
Foreign keys are a kind of constraint, so they can only fail when you attempt to add records.
You can accomplish what you are describing with a trigger. I don't know the MySql syntax, but in SQL Server it would look something like this:
CREATE TRIGGER TR_ensure_user_interest ON interest FOR INSERT, UPDATE AS
BEGIN
INSERT user_interest (user_id, interest_id)
SELECT user_id, interest_id
FROM inserted
,user
EXCEPT (SELECT user_id, interest_id)
END
Note that this is a rather inefficient approach, but it should cover many of the cases you're concerned about.
UPDATE: I agree with the others who have observed the design "smell" here. If you can accomplish the required result using JOIN queries, that would be a much more efficient solution. However, I was trying to answer the question actually asked. (Plus, I have been in this situation, where physical records are helpful to other database users who are not adept at compound queries.)
For every interest ID (even when new
ones are added), I need to make sure
that each user has an entry for that
interest ID (even if its blank, or has
defaults).
It sounds like you need an OUTER JOIN (either LEFT or RIGHT) in one of your queries instead.
For example, if you wanted to get the level of interest a particular person has for each interest:
Assuming your tables look like this:
users:
user_id PK
user
user_interests:
user_id PK FK
interest_id PK FK
interest_level
interests:
interest_id PK
interest
SELECT i.interest, ui.interest_level
FROM interests i
INNER JOIN user_interests ui USING (interest_id)
LEFT JOIN users u USING (user_id)
WHERE user_id = ?
? is a placeholder.
Note that ui.interest_level will be null for interests with no data.
It sounds like you are forcing your physical design to mirror your logical design too tightly.
Maybe it would be a good idea to rethink exactly why you need to insert a row for every user in the physical table. Couldn't you just write your queries to assume the default value for an interestID if there isn't an associated interestID for a given user?
"Will foreign keys help with this scenario?"
No.
Your constraint is a sort of "completeness" constraint. It implies that for each new Interest added, there must be as many rows added to the USER_INTEREST table as there are users.
No SQL system is able to enforce that for you. It's up to you to enforce it through code.

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