Alternatives for a many-to-many link table - php

I am working on a project where I want to allow the end user to basically add an unlimited amount of resources when creating a hardware device listing.
In this scenario, they can store both the quantity and types of hard-drives. The hard-drive types are already stored in a MySQL Database table with all of the potential options, so they have the options to set quantity, choose the drive type (from dropdown box), and add more entries as needed.
As I don't want to create a DB with "drive1amount", "drive1typeid", "drive2amount", "drive2typeid", and so on, what would be the best way to do this?
I've seen similar questions answered with a many-to-many link table, but can't think of how I could pull this off with that.

Something like this?
CREATE TABLE `hardware` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(256) NOT NULL,
`quantity` int(11) NOT NULL,
`hardware_type_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `type_id` (`hardware_type_id`),
CONSTRAINT `hardware_ibfk_1` FOREIGN KEY (`hardware_type_id`) REFERENCES `hardware_type` (`id`)
) ENGINE=InnoDB
hardware_type_id is a foreign key to your existing table
This way the table doesnt care what kind of hardware it is

Your answer relies a bit on your long term goals with this project. If you want to posses a data repository which has profiles all different types of hardware devices with their specifications i suggest you maintain a hardware table of each different types of hardware. For example you will have a harddisk table which consist of all different models and types of hardisks out there. Then you can assign a record from this specific table to the host configuration table. You can build the dataset as you go from the input from user.
If this is not clear to you let me know i will create a diagram and upload for you.

Related

How do I display the name of a table on SQL

First of all, I'm from Spain so I'm sorry if I made some mistakes writing. So, I have two problems. It will be better if I give context before. I am not even junior, still learning code, and I thought that it will be a good proyect to create a web page where you can add ingredients, foods with that ingredients, etc. So I decided to start learning PHP and SQL. Now I'm trying to create a database, starting with some ingredients and two kinds of rices. My 1st problem is that I don't know if I need to create a data base for that. The second and main one is that I don't have any idea about how to get this working as I want.
See, First of all I created the table for ingredients´
CREATE TABLE ingredientes(
id int(255) auto_increment not null,
ingrediente varchar(255) not null,
CONSTRAINT pk_ingredientes PRIMARY KEY(id) )ENGINE=InnoDb;
Sorry 'cause it's on spanish :/, but nothing to hard to understand.
So I add some ingredients.
Here the pic showing them
After that I created two tables, and add ingredients to them.
CREATE TABLE arroz_con_pollo(
id int(255) auto_increment not null,
ingrediente int(255) not null,
CONSTRAINT pk_arroz_con_pollo PRIMARY KEY(id),
CONSTRAINT fk_pollo_ingredientes FOREIGN KEY(ingrediente) REFERENCES ingredientes(id) )ENGINE=InnoDb;
CREATE TABLE arroz_cubana(
id int(255) auto_increment not null,
ingrediente int(255) not null,
CONSTRAINT pk_arroz_cubana PRIMARY KEY(id),
CONSTRAINT fk_cubana_ingredientes FOREIGN KEY(ingrediente) REFERENCES ingredientes(id))ENGINE=InnoDb;
Here the picture showing the ID's.
Here
So now I spend a lot of time researching and find out that I can show the names by using this command
SELECT a.id,i.ingrediente
FROM ingredientes i, arroz_cubana a
WHERE i.id = a.id;
And have something like this
At this point, everything is, more or less, working. My issue came when I want to create a data base that keep all the names (arroz con pollo, arroz cubana...) in an only table named as 'rices' to be able to choose a name, and automatically have the ingredients there, without any complication for the user. But, I literally have no idea. I've been coding for hours without any victory on that. And I haven't see anything similar on the web so, if someone tell me how to fix that issue or how to make that idea of a web to keep ingredients and foods, I'll be very greatful.
Your data structure is messed up. SQL is not designed to have a separate table for each ingredient. Instead, you want two other tables.
The first is for dishes:
CREATE TABLE dishes (
dish_id int auto_increment not null,
name varchar(255)
);
You would then insert appropriate rows into this:
INSERT INTO dishes (name)
VALUES ('arroz_on_pollo');
Then you have another table for the ingredients:
CREATE TABLE dishes_ingredients (
dish_ingredient_id int auto_increment primary key,
dish_id int not null
ingredient_id int not null,
CONSTRAINT fk_dish_ingredientes_dish FOREIGN KEY(dish_id) REFERENCES dishes(dish_id)
CONSTRAINT fk_dish_ingredientes_dish FOREIGN KEY(ingredient_id) REFERENCES ingredientes(ingredient_id)
);
Voila! New dishes are just rows in a table, so you can get the names using a SELECT.
Notes on structure:
int(255) really makes no sense. Just use int. The number in parentheses is a width for the value when printing it and 255 is a ridiculous width.
I am a fan of naming primary keys with the table name. That way, the primary key and foreign key typically have the same name.
You should not have a table per dish. Create one table "dish", that includes a column "name". Each row represents a dish. Then create a supporting table where you list the (multiple) ingredients for each dish. Look around for a tutorial on databases, this topic is too large to explain in a stackoverflow question (or several).
And so you do not need to be able to list the table names, the way you were considering. (Which is not something SQL supports directly; different databases provide non-standard ways to do it, but as explained you do not actually need such a feature.)

Save database storage or save queries accesing to multiple tables?

I am considering the structural database´s design of the following specific problem:
I have 2 different tables belonging to the same database. In the first table the detailed data of different objects is stored, where the column id refers to the the specific object.
On the other hand, the second table stores every single change that the objects in the first table have perceived. Every single row in our second table stores as well the id referencing to the object as the version_id which defines the different state versions of the objects, that is every single change effectuated.
Now let´s say the 'eliminated' parameter is set to "true" in a row of objects table for declaring an object as not visible in the object´s manager site. In our display site the table version is accesed for showing a linked object´s version, nevertheless the system shouldn´t display it if the object refered by id is marked as eliminated.
For solving this problem, I have two possible solutions: either increment the database storage, adding an eliminated column to theversion table, or I add a query in php for processing the parameter eliminated from the objects table after receiving the object id from the version table.
I want to know which disadvantage and advantage are presented in both different solutions, if saving storage cost would be prefarable than processing more queries and accesing multiple queries for receiving the data, or if contrary sacrificing storage cost and spreading the eliminated column into the version table leads to a better response time performance of the site by sparing multiple queries for accesing data from other tables.
CREATE TABLE `objects` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`eliminated` tinyint(1) DEFAULT NULL,
...
PRIMARY KEY (`id`),
KEY `id` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
CREATE TABLE `version` (
`version_id` int(11) NOT NULL AUTO_INCREMENT,
`object_id` int(11) NOT NULL,
`eliminated` tinyint(1) DEFAULT NULL, //optional
...
PRIMARY KEY (`version_id`),
KEY `id` (`version_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
The advantage of adding an eliminated column to the version table is that it provides you with details of the object elimination. It allows you to store details of the elimination for the object.
The drawback is that you are saving an extra row and also adding an extra column, which can create an overhead if there are a lot of rows in the table.
Which solution you use depends on how much data is being stored in your tables and also what data needs to be displayed to the user

Single mysql table for private messaging

I'm trying to create a single table for private messaging on a website. I created the following table which I think is efficient but I would really appreciate some feedback.
CREATE TABLE IF NOT EXISTS `pm` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`to` int(11) NOT NULL,
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`subject` varchar(255) DEFAULT NULL,
`message` text NOT NULL,
`read` tinyint(1) NOT NULL DEFAULT '0',
`deleted` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
FOREIGN KEY (user_id) REFERENCES User(user_id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
I have 2 columns that determine the status of the message: read and deleted
If read = 1, the message has been read by the receiver. If deleted = 1, either the sender or the receiver deleted the message from the sent or received inbox. If deleted = 2 both users deleted the message, therefor delete the row from the database table.
I see that you don't have have any indexes explicitly stated. Having the appropriate indexes on your table could improve your performance significantly. I also believe that for your message column you may want to consider making i a varchar with a max size explicitly stated. Other than those two items which you may already taken care of your table looks pretty good to me.
MySQL Table Performance Guidelines:
Add appropriate indexes to tables. Indexes aren't just for primary/unique keys add them to frequently referenced columns.
Explicitly state maximum lengths. Fixed length tables are faster than their counterpart
Always have an id column.
Add NOT NULL where ever you can. The nulls still take up space
Know your data types. Knowledge is power and can save on performance and space
Interesting Articles:
VarChar/TEXT Benchmarks
Similar Question
Some Best Practices
Data Type Storage Requirements
The articles and some of the items I have listed may not be 100% correct or reliable so make sure you do a bit of your own research if you are interested in further tuning your performance.
A few comments:
Charset=latin1 is going to piss some people of I'd suggest charset=utf8.
I'd suggest putting a foreign key check in not only on user_id, but on to as well.
Also I'd put an index on date, as you will be doing a lot of sorting on that field.
You need to split deleted in two fields, otherwise you will not know which user has deleted the message. (deleted_by_user, deleted_by_recipient)
Note that date is a reserved word and you'll need to change it into message_date or `backtick` it in your queries.
some comments:
not bad.
i would name the table something that other people might guess out of context. so maybe private_message instead of pm.
i would be explicit on the user column names, so maybe from_user_id, and to_user_id instead of 'user_id' and 'to'
i would consider pulling out the status into a new table with status, user_id, and date - this should give you a lot more flexibility in who is doing what to the message over time.
For displaying both the receiver's inbox and the senders outbox (and being able to delete messages respectively), you will probably need more information that what you currently have encoded. I would suggest a "deleted" field for each party. (As long as this is limited to only 1 user on each end and no broadcast messages, this works. This does not scale to broadcast messages, however, which would require more than 1 table to do efficiently)
You may also want to enforce key relationships with ON DELETE and ON UPDATE:
FOREIGN KEY (user_id) REFERENCES User(user_id) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (to) REFERENCES User(user_id) ON DELETE CASCADE ON UPDATE CASCADE
The removal or modification of a user will propagate changes or deletions to the messages table.
I think you may need to add an column called Parent_Message_ID which will have the parent mail ID. So that replies can also included.
If you think in future to add replies to your private messages.

Help with php/mysql mailer

I'm working on a real estate site and need to make notification mailer: when new property is inserted on a site, people who subscribed for notification in that particular country and/or area and/or city and/or particular property operation (rental, selling) will receive a notification on email. One person could subscribe for different areas, cities, etc, not only one. One person will receive only one notification a week let say if there are new properties for him, though. And I'm thinking on how better to create a mysql table for subscribers in order to easy retrieve them. Table like:
create table subscribers(
user_email varchar(255),
area_id int(4));
is a bad idea, because if there will be let say 100,000 (looking to the future) subscribers and each will subscribe for 10 areas there will be 1,000,000 rows in a table. So, I'm looking for efficient solution to do such task.
If you have additional recommendations, I will like to hear them.
Thanks in advance!
You should use a cross-reference (many-to-many) table. This will make data more normalized:
CREATE TABLE `areas` (
`id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(255) NOT NULL
PRIMARY KEY (`id`)
)
CREATE TABLE `subscribers` (
`id` int(10) unsigned NOT NULL auto_increment,
`email` varchar(255) NOT NULL
PRIMARY KEY (`id`)
)
-- cross ref table
CREATE TABLE `areas_subscribers` (
`area_id` int(10) unsigned NOT NULL,
`subscriber_id` int(10) unsigned NOT NULL,
UNIQUE KEY (`area_id`,`subscriber_id`)
)
And a million rows is not a problem. Especially with a cross ref table.
there will be 1,000,000 rows in a table
So what? mySQL can handle it.
As far as I can see, the way you are doing it is perfectly fine. It's nicely normalized, I can't think of a better method.
Your table looks correct, assuming that user_email is the primary key identifying your users. If so, add to your subscribers table a PRIMARY KEY (user_email, area_id) to indicate that both fields together make up your primary key.
Your concern about duplicating e-mails has little to do with the schema design and more to do with the query you intend to run. That, of course, will depend largely on how your other data are stored, but might look something like:
SELECT DISTINCT user_email WHERE area_id IN (...)
(For a list of area_id values that have seen listings in the past week.)
That's a simple query that could be optimized and improved given the rest of your schema, but it illustrates how easy it is to avoid generating multiple e-mails despite the same person being listed multiple times.
You can make an extra table of the email addresses.
So you only store an ID in the subscriber table and not the same email address over and over again (whereas there might be some optimizations in the database anyway).

Is there a normalized form for representing several objects from different tables?

For a system I am currently building, the following two scenarios occur:
My permissions system is the perennial favorite of attaching permissions to roles, and roles to users, but with a twist: permissions may be applied to any asset at any tier, and there are 4 "Tiers", numbered 0 through 3. As such, the role assignment table is 5 fields long: asset tier, asset ID, role ID, user ID, and a flag for "disabled." This "Tier/ID" system is also used in assigning users to a "Home Asset".
The unfortunate side effect of this system is that it is generally far simpler to resolve my these asset references in the application itself, instead of in the SQL (I know it would be possible to do, but it would result in some seriously ugly queries). Is there a better way to represent this sort of relationship? Is this an acceptable way of representing this relationship?
Side note: the tables for the assets themselves (they are hierarchal) are normalized, with foreign key references pointing from child to parent (e.g. Tier 3 contains info about Tier 2, etc), so once a single asset is pinned down it is very simple to locate its children and ancestors.
Similar problem, but different data context: At the lowest tier, devices can be "attached." Devices are also allowed to be placed onto a graphical representation (like a map), and their arrangement information is stored in the database. Graphical references (also called "points") should point at a real device, but a real device may not point at a graphical reference.
At present, there is a single reference table to all "points" (which represent devices) with information for their display layout, location, size, etc. I can identify three possible things to do from here:
Create a series of normalized tables to represent individual devices, and use a similar "device type / device ID" system to look them up, and store this information in the "points" table; this will still require the application to do intermediary work in resolving those references to actual devices in their respective tables
Create a series of normalized tables, and point them at the "points" table; this solution probably won't require the application to dereference the points data, but will require that all device tables be scanned for any Tier 3 object.
Using the points table as a guideline, add all of the relevant display data to each respective normalized table. While this would prevent having to resolve any lookups at all, it will still require that I scan every device table, and is also extremely intolerant to any changes in how the points data must be stored compared to the other solutions.
Is this problem already solved and I just didn't get the memo? Is there a standard design addressing this problem, or should I just assume that I will have to resolve these references myself?
Note: With respect to problem two, I have deemed the following two solutions to be horrible and I will not consider them (barring some crazy, amazing evidence that it is the best way, which I'm fairly sure it isn't):
Store all the information about each device (in a serialized array or some such mechanism) in the points table itself. This has the obvious problem of being completely unsearchable and defying normal form
Store all devices in a single, generic table. Unfortunately, these devices are all extremely different in the type of data they provide, as well as how they must be accessed, and doing so in one table would require, last time I tried working it out on paper, at least 30 (and probably more) columns, more than half of which would be null for every row.
Any solutions are requested and appreciated, though I am specifically looking for existing patterns and/or normalized schemas which address this issue, if any exist. If you know for a fact that it is the correct answer, "No" will suffice and I will continue to use the application layer to resolve these references. It has not been a problem yet, I just prefer to know I'm not forging a path in the wrong direction when someone has already solved this/these problem(s). Thanks in advance.
EDIT: Permissions/Asset Schema
Tier 0 is actually implied and not in the database, but some things are registered as Tier 0 (Asset ID 0)
Tier 1:
id int(5) unsigned not null primary key auto_increment,
name varchar(32) not null,
disabled tinyint(1) unsigned not null,
Tier 2:
id int(5) unsigned not null primary key auto_increment,
parentId int(5) unsigned not null,
name varchar(32) not null,
abbr varchar(16) not null,
disabled tinyint(1) unsigned not null,
foreign key (parentId) references tier1(id)
Tier 3:
id int(5) unsigned not null primary key auto_increment,
parentId int(5) unsigned not null,
name varchar(32) not null,
abbr varchar(16) not null,
disabled tinyint(1) unsigned not null,
foreign key (parentId) references tier2(id)
Permissions:
id int(5) unsigned not null primary key auto_increment,
permission_key varchar(16) not null,
permission_desc varchar(128) not null
Roles:
id int(5) unsigned not null primary key auto_increment,
name varchar(32) not null,
tier1_id int(5) unsigned not null,
disabled tinyint(1) unsigned not null,
foreign key (tier1_id) references tier1(id)
Role_Permissions:
role_id int(5) unsigned not null,
permission_id int(5) unsigned not null,
disabled tinyint(1) unsigned not null,
primary key (role_id, permission_id),
foreign key (role_id) references roles(id),
foreign key (permission_id) references permissions(id)
User_Role_Permissions:
tier_id tinyint(1) unsigned not null,
asset_id int(5) unsigned not null,
user_id int(5) unsigned not null,
role_id int(5) unsigned not null,
disabled tinyint(1) unsigned not null,
primary key (tier_id, asset_id, user_id, role_id),
foreign key (user_id) references users(id),
foreign key (role_id) references roles(id)
Have you considered breaking up your permissions table into a set of tables that each apply to a separate table of objects, for instance tier_1_assets and tier_1_permissions, and tier_2_assets and tier_2_permissions. This will make it simpler to query the permissions for a tier of assets, but more complex to query for all the permissions of a user (this is a trade off).
I would also challenge the assumption that a relational database is the right storage medium for your data. It may indeed be, but may also not be. (PHP may limit freedom in that respect).
If your data set is small, why not save it to a single serialized file, and have a small server that keeps it in memory and provides a query interface? I am sure there are good reasons why not. (In addition to being in PHP)
Have you considered multiple point tables for your second problem? It may feel like a repetition, but it does make life simpler for some kinds of look ups, and is normalized when you consider each foreign key reference as a different type of object (you get to use foreign key checks on them when the tables are separated).
The multiplicity of the relationships between objects should tell you where to put your keys and references: one-to-many -> (key <- foreign key); many-to-many -> (key <- link-table -> key); one-to-optional -> (key <- foreign key).
I do not know of any patterns that match your problem as it was stated. But I have noticed that most generic solutions to avoid creating another table get really hard to manage very quickly.

Categories