creating a join table problem! - php

I have 3 tables
customer, menu, and order.
The order table is suppose to join the customer and menu tables, and contains the primary keys of both. Here's how I tried to create the order table on phpmyadmin.
create table order(
customerID int not null,
itemID int not null,
primary key (customerID, itemID),
foreign key(customerID) reference customer(ID),
foreign key(itemID) reference menu(itemID)
)
This doesn't work. What am I doing wrong?!!

order is a reserved word, try another name, or quote it, like
create table `order`(
customerID int not null,
itemID int not null,
primary key (customerID, itemID),
foreign key(customerID) reference customer(ID),
foreign key(itemID) reference menu(itemID) )

It is complaining as order is a reserved keyword. Wrapping it with backticks as #TokenMacGuy tells you to solves your problem. Here is a list of them
Furthermore, as a general rule, you can transform your entities like so to avoid problems, especially with reserved keywords:-
a) The Entity is always modeled (on paper) as singular as it represents a concept/asset/person in the real world or problem domain. eg. ORDER, CHECK, STUDENT, CAR
b) the corresponding DB Table it is transformed into can always be named using plural. The logic is that the table will contain lots of instances of that Entity. Therefore ORDERS, CHECKS, STUDENTS, CARS

Related

How to properly index attributes in wordpress database

I am extending a product sales plugin and am trying to understand how wordpress handles database relations. I am building tables on activation using dbDelta. An example of a table schema would be:
$table_schema = [
"CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}plugin_orders` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`people_id` bigint(20) DEFAULT NULL,
`order_id` bigint(20) DEFAULT NULL,
`order_status` varchar(11) DEFAULT NULL,
`order_date` datetime DEFAULT NULL,
`order_total` decimal(13,2) DEFAULT NULL,
`accounting` tinyint(4) DEFAULT '0',
PRIMARY KEY (`id`),
KEY `people_id` (`people_id`),
KEY `order_id` (`order_id`)
) $collate;",
"CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}plugin_order_product` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`order_id` bigint(20) DEFAULT NULL,
`product_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `order_id` (`order_id`),
KEY `product_id` (`product_id`)
) $collate;"
];
I see that id in each table is the PRIMARY KEY but what does declaring the other KEYs actually do? I have read that wordpress uses MyISAM which doesn't actually build foreign key connections. While these tables may point to other tables already existing, in this example does declaring KEY order_id (order_id) create a variable of sorts called order_id that any other table can use to reference? Is this code specifically connecting one tables attributes to another tables attributes (it doesn't appear to be)? After these tables are built, I can inspect them in phpMyAdmin and see that there are indexes assigned but no foreign key constraints. How does this code create tables that point one table at another to build relations?
KEY `foo_bar` (`order_id`)
"KEY" is the same as "INDEX". It specifies that a separate data structure is maintained for the efficient access of the table via the column order_id.
foo_bar is the name of the index. It has no special meaning, and has very few uses. For example, DROP KEY foo_bar; is the way to get rid of the index.
In MyISAM, a "FOREIGN KEY" allowed, but ignored. In InnoDB, it does two things:
Create an index if one is not already provided
Provide a constraint. The default effectively "complain if the other table does not already have the value referenced".
Having an index is important for performance. The index above make this
SELECT ... WHERE order_id = 1234 ...
run in milliseconds, even if there are billions of rows in the table. Without the index, the query would take minutes or hours.
A PRIMARY KEY is a UNIQUE key, which is an INDEX.
UNIQUE(widget) says that only one row can have a particular value of `widget in the table.
PRIMARY KEY(id) says that each row is uniquely identified by the column id. InnoDB really wants each table to have a PK.
"id" is a convention (not a requirement) for the name of the PK. It is also INT AUTO_INCREMENT by convention. You may or may not actually ever touch id.
Tables can be related to each other in 3 main ways:
1:1 -- They share the same unique key. This is rarely useful; you may as well have a single table.
1:many -- An "order" has several "items" in it (one-order : many-items). This is usually handled by order_id being a column in the items table.
many:many -- students_classes -- each student is in many classes; each class has many students. This is implemented via a mapping table that has (usually) only two columns: student_id and class_id (no id is needed) and PRIMARY KEY(student_id, class_id) and INDEX(class_id, student_id). Those two indexes make it efficient to go from a known student to their classes, and vice versa.
Another convention for the PK of a table is to include the table name. (It is clutter to do that for other columns, such as order_status.) I was assuming this convention for student_id and class_id.
But now I am confused by your plugin_orders -- it has both id and order_id. If that table describes "orders", then I would expect order_id to be the PK instead of id.
And, if order_product is a list of all the "products" in each "order", then I would expect you to have the 1:many pattern.
What indexes to have?
PRIMARY KEY to uniquely identify each row -- either id or some column (or combination of columns) that are unique.
Other columns, as needed, for the SELECTs, UPDATEs, and DELETEs that you have. Do not blindly add indexes before having some clues of the queries that might need them.
Indexes sometimes help in sorting:
SELECT ... ORDER BY last_name, first_name;
together with
INDEX(last_name, first_name)
Indexes provide performance; FKs provide integrity checks. Neither is "required"; both are "desirable".
MyISAM is ancient; you should change to InnoDB.
Then do something like
SELECT ...
FROM plugin_orders AS o
JOIN plugin_order_product AS op
ON o.order_id = op.order_id
WHERE ...
In this example, the Optimizer will perform the query something like this:
Look at the WHERE to see which table is best filtered by the conditions there. Declare that to be the first table work with.
Scan through the first table, using an index if practical.
For each row in the first table, reach into the second table.
Reaching into the second table would probably be done via INDEX(order_id) on the second table. This would make the JOIN fast and efficient.
Both tables have INDEX(order_id), but that is not relevant.
Next example:
SELECT ...
FROM plugin_orders AS o
JOIN plugin_order_product AS op
ON o.order_id = op.order_id
WHERE o.people_id = 123 -- note
Pick o as the first table due to filtering on people_id
use op INDEX(people_id) to rapidly find the o rows that are relevant.
etc (op is the second table)
Next example:
SELECT ...
FROM plugin_orders AS o
JOIN plugin_order_product AS op
ON o.order_id = op.order_id
WHERE op.product_id = 9887 -- changed again
Pick op as the first table due to filtering on product_id
use o INDEX(people_id) to rapidly find the op rows that are relevant.
etc (o is the second table this time)

MySQL relationship database design

I have seen a question on this forum that I can relate with, but I can't apply the answers to my question.
Here it goes:
I have a memberlist table (id, name, number) I'll just make the columns short.
Next, I have an events table (id, eventName, description)
Now,
1. each member in the memberlist can join events as many as he wants.
2. each events in the events table can have members without limits (okay, say 1k members, like that or whatever).
What I have now is an event table that has a column named: "joiners" which will contain the id of a certain joiner/member. But I believe I'm wrong because how can a certain event handles many joiner's id?
I would rename memberlist into members to make your table naming more consistent. Or events into eventlist. Which ever you like more.
Then you want to define a many to many relation between members and events. This is done through an intermediate table which will reference both:
create table eventmembers (
id int unsigned not null primary_key auto_increment,
member_id int unsigned not null references members(id),
event_id int unsigned not null references events(id)
)
I'm assuming that on your memebers and events you already have id fields which are set to be primary keys.
If you want to get all events attended by a specific user you can then do
select events.*
from events
left join eventmembers
on events.id = eventmembers.event_id
where
member_id = ?
and get all the members in an event:
select members.*
from members
left join eventmembers
on members.id = eventmembers.member_id
where
event_id = ?
You'd want a third table called events_memberlist :
events_memberlist
- memberlistId
- eventId
This would allow you to maintain a many-to-many relationship between the two tables.
you need a third table for fun we will call it EventMemberTable, col's:
event id | member
it links the appropiat member to the appropiat event. Keeping your other tables clear of redundant data.
You can achieve this by having a middle table (between members, and events). Middle tables are necessary in situations where a 'many-to-many' relationship between two tables is required.
In the middle table, you will include the primary keys of both tables as foreign keys, on the same row, when a member has joined an event. The foreign keys effectively create the relationship between one member, and one event. The table however, can have thousands of these entries.
I hope that helps.
P.S. Maybe post some syntax next time.
Cheers
When you use a middle table as already mentioned like:
CREATE TABLE event_members (
member_id INT UNSIGNED NOT NULL REFERENCES members(id),
event_id INT UNSIGNED NOT NULL REFERENCES events(id)
)
you should also set up a unique index to prevent multiple entries for the same member/event combination like
ALTER TABLE event_members ADD UNIQUE INDEX uniq_event_members_idx (member_id, event_id);
Otherwise you might end up with loads of duplicates.

New table or field with array in field (php/mysql)

I need to store multiple id's in either a field in the table or add another table to store the id's in.
Each member will basically have favourite articles. Each article has an id which is stored when the user clicks on a Add to favourites button.
My question is:
Do I create a field and in this field add the multiple id's or do I create a table to add those id's?
What is the best way to do this?
This is a many-to-many relationship, you need an additional table storing pairs of user_id and article_id (primary keys of user and article tables, respectively).
You should create a new table instead of having comma seperated values in a single column.
Keep your database normalized.
You create a separate table, this is how things work in a relational database. The other solution (comma separated list of ids in one column) will lead to an unmaintainable database. For example, what if you want to know how many times an article was favorited? You cannot write queries on a column like this.
Your table will need to store the user's id and the article's id - these refer to the primary keys of the corresponding tables. For querying, you can either use JOINs or nested SELECT queries.
As lafor already pointed out this is a many-to-many relationship and you'll end up with three tables: user, article, and favorite:
CREATE TABLE user(
id INT NOT NULL,
...
PRIMARY KEY (id)
) ENGINE=INNODB;
CREATE TABLE article (
id INT NOT NULL,
...
PRIMARY KEY (id)
) ENGINE=INNODB;
CREATE TABLE favorite (
userID INT NOT NULL,
articleID INT NOT NULL,
FOREIGN KEY (userID) REFERENCES user(id) ON DELETE CASCADE,
FOREIGN KEY (articleID) REFERENCES article(id) ON DELETE CASCADE,
PRIMARY KEY (userID, articleID)
) ENGINE=INNODB;
If you then want to select all user's favorite articles you use a JOIN:
SELECT * FROM favorite f JOIN article a ON f.articleID = a.id WHERE f.userID = ?
If you want to know why you should use this schema, I recommend reading about database normilization. With multiple IDs in a single field you would even violate the first normal form and thus land in a world of pain...

MySQL auto-increment between tables

In MySQL, is it possible to have a column in two different tables that auto-increment? Example: table1 has a column of 'secondaryid' and table2 also has a column of 'secondaryid'. Is it possible to have table1.secondaryid and table2.secondaryid hold the same information? Like table1.secondaryid could hold values 1, 2, 4, 6, 7, 8, etc and table2.secondaryid could hold values 3, 5, 9, 10? The reason for this is twofold: 1) the two tables will be referenced in a separate table of 'likes' (similar to users liking a page on facebook) and 2) the data in table2 is a subset of table1 using a primary key. So the information housed in table2 is dependent on table1 as they are the topics of different categories. (categories being table1 and topics being table2). Is it possible to do something described above or is there some other structural work around that im not aware of?
It seems you want to differentiate categories and topics in two separate tables, but have the ids of both of them be referenced in another table likes to facilitate users liking either a category or a topic.
What you can do is create a super-entity table with subtypes categories and topics. The auto-incremented key would be generated in the super-entity table and inserted into only one of the two subtype tables (based on whether it's a category or a topic).
The subtype tables reference this super-entity via the auto-incremented field in a 1:1 relationship.
This way, you can simply link the super-entity table to the likes table just based on one column (which can represent either a category or a topic), and no id in the subtype tables will be present in both.
Here is a simplified example of how you can model this out:
This model would allow you to maintain the relationship between categories and topics, but having both entities generalized in the superentity table.
Another advantage to this model is you can abstract out common fields in the subtype tables into the superentity table. Say for example that categories and topics both contained the fields title and url: you could put these fields in the superentity table because they are common attributes of its subtypes. Only put fields which are specific to the subtype tables IN the subtype tables.
If you just want the ID's in the two tables to be different you can initially set table2's AUTO_INCREMENT to some big number.
ALTER TABLE `table2` AUTO_INCREMENT=1000000000;
You can't have an auto_increment value shared between tables, but you can make it appear that it is:
set ##auto_increment_increment=2; // change autoinrement to increase by 2
create table evens (
id int auto_increment primary key
);
alter table evens auto_increment = 0;
create table odds (
id int auto_increment primary key
);
alter table odds auto_increment = 1;
The downside to this is that you're changing a global setting, so ALL auto_inc fields will now be growing by 2 instead of 1.
It sounds like you want a MySQL equivalent of sequences, which can be found in DBMS's like PosgreSQL. There are a few known recipes for this, most of which involve creating table(s) that track the name of the sequence and an integer field that keeps the current value. This approach allows you to query the table that contains the sequence and use that on one or more tables, if necessary.
There's a post here that has an interesting approach on this problem. I have also seen this approach used in the DB PEAR module that's now obsolete.
You need to set the other table's increment value manually either by the client or inside mysql via an sql function:
ALTER TABLE users AUTO_INCREMENT = 3
So after inserting into table1 you get back the last auto increment then modify the other table's auto increment field by that.
I'm confused by your question. If table 2 is a subset of table 3, why would you have it share the primary key values. Do you mean that the categories are split between table 2 and table 3?
If so, I would question the design choice of putting them into separate tables. It sounds like you have one of two different situations. The first is that you have a "category" entity that comes in two flavors. In this case, you should have a single category table, perhaps with a type column that specifies the type of category.
The second is that your users can "like" things that are different. In this case, the "user likes" table should have a separate foreign key for each object. You could pull off a trick using a composite foreign key, where you have the type of object and a regular numeric id afterwards. So, the like table would have "type" and "id". The person table would have a column filled with "PERSON" and another with the numeric id. And the join would say "on a.type = b.type and a.id = b.id". (Or the part on the "type" could be implicit, in the choice of the table).
You could do it with triggers:
-- see http://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_last-insert-id
CREATE TABLE sequence (id INT NOT NULL);
INSERT INTO sequence VALUES (0);
CREATE TABLE table1 (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
secondardid INT UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (id)
);
CREATE TABLE table2 (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
secondardid INT UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (id)
);
DROP TRIGGER IF EXISTS table1_before_insert;
DROP TRIGGER IF EXISTS table2_before_insert;
DELIMITER //
CREATE
TRIGGER table1_before_insert
BEFORE INSERT ON
table1
FOR EACH ROW
BEGIN
UPDATE sequence SET id=LAST_INSERT_ID(id+1);
NEW.secondardid = LAST_INSERT_ID();
END;
//
CREATE
TRIGGER table2_before_insert
BEFORE INSERT ON
table2
FOR EACH ROW
BEGIN
UPDATE sequence SET id=LAST_INSERT_ID(id+1);
NEW.secondardid = LAST_INSERT_ID();
END;
//

Select Primary Key Value Without Knowing Field Name

I have 3 tables, images, icons, and banners, each with a unique primary key that is also auto_incremented named image_id, icon_id, and banner_id, respectively.
I'm looping through the above tables and I'm wondering if there's a way I can select the id column without specifying it's specific name.
Something like
SELECT PRIMARY_KEY
FROM {$table}
Where I don't have to change my table structure or use * as there would be much data to return and would slow down my application.
Just name the id columns id in each table. Reserve the whatever_id naming for foreign keys.
I'm not a LAMP guy, but it looks to me like you want the INFORMATION_SCHEMA tables.
A query something like :
SELECT pk.table_name, column_name as 'primary_key'
FROM information_schema.table_constraints pk
INNER JOIN information_schema.key_column_usage C
on c.table_name = pk.table_name and
c.constraint_name = pk.constraint_name
where constraint_type = 'primary key'
-- and pk.table_name LIKE '%whatever%'
This above query (filtered to whatever relevant set of tables you need) will give you bit a list of table names and associated Primary Keys. What that information on hand you could query something like :
SELECT {$PK_ColumnName}
FROM {$table}
Note, you might needs a more complicated syntax and string builder if you have composite primary keys (i.e. more than one field per key). Also, the information schema can be relatively expensive to query, so you'll either want to cache the result set up, or query it infrequently.
The PRIMARY key is different than the column that has the primary key on it. The primary key is both an index and a constraint that is placed on one or more columns, not a column itself. Your pseudocode query:
SELECT PRIMARY_KEY
FROM tablename
is equivalent to this:
SELECT keyname
FROM tablename
Which is invalid. What you really need to select is a column, not a key.
Unfortunately, there is no column alias or simple function that you can use to specify the columns that have the primary key constraint. It's most likely not available because the primary key can apply to more than one column.
To see which columns have the PRIMARY key constraint, you could use some reflection by querying the schema tables, using SHOW COLUMNS, etc.. Simply doing SELECT * FROM tablename LIMIT 1 would get you all the column names in the result, if you wanted to assume the first column had the primary key constraint.
Of course, you could just do SELECT * anyway, when you don't know the column name.
If you don't want to make an extra query to fetch the column name to construct the query, using built-in meta data, or your own, I'd heed Marc B's answer if you can.
Or you can use the standard SQL command
show columns from tablename
It will show the PRI column
Check the online documentation for more info

Categories