Choose a database table to join with, using a field value - mysql

The website I'm building has a table which stores all the information of uploaded images on the site. These uploaded images can come from different resources such as a guestbook, news section or an item from an agenda.
Ofcourse I want the image to inherit the rights of the resource it is part of. For example: if user A isn't allowed to view the guestbook I don't want him to be able to view an image posted on the guestbook by going to image/view/id/12 (which would be the image request used it in the guestbook).
What I have now is that the system remembers the resources used (in this case the guestbook) the image-id is coupled to the resource-id. However I don't know to which guestbook post the image is connected (I do ofcourse know it the other way around).
Is there a way in SQL to connect one table field to a field in another table, where which table I connect to can vary based on one of the first table's field values?
In my case I would like to connect an image to a resource this could be a guestbook post in the table gb_posts or an agenda item in the table agenda_items.
Or is this all a stupid way of solving the problem and should I not use one table for the uploaded images but keep the image attached to the resource (as a column in the table for example)? It sounds like using one table is at least a lot slower in use (but I would have a great overview of all the images in one place).
I hope you guys can help me out.
EDIT: extra explanation: db model
I will try to explain how it all works the best I can.
First of all: I use Zend Framework, and therefor I also use Zend_Acl for working with priveleges.
My DB structure:
- Users are connected to roles (directly or by being connected to a group that is connected to a role)
- There is a table resources containing all the resources which is connected to priveleges. For example: guestbook is a resource, view or edit are the priveleges. Next to the controllers/actions there can also be other resources in this table such as a category within the agenda or a file location.
- roles are connected to a privelege
When for example the guestbook is requested for viewing I can check if the user is allowed to.
In short something like:
users -> roles -> priveleges <- resources
When a user adds a guestbook post with an image, the used resources (in this case guestbook is saved):
guestbook_posts -> images -> resources
I hope this explains my DB model for a bit, if it doesn't I will try to create an image of the tables.

I have to admit I'm failing to completely understand the model you wish to implement, but there is an interesting quote...
However I don't know to which
guestbook post the image is connected
(I do ofcourse know it the other way
around).
If you know an association one way, you should be able to use the associaton in both directions? I'm assuming you have a table that includes "post_id, image_id", or something?
It may be that the table is only indexed post_id first, in which case querying that table by image_id may be slow, but then you can just include a new index with image_id first?
If you can give examples of the table structure you have at present, and an example of the query you can't fullfil, we may be able to help you further.

Sounds like you want a foreign key constraint.
Update: Completely misunderstood the question, apparently.
There are two approaches here:
As it currently stands, there is nothing in the schema that would prohibit linking the same image from multiple resources. If that is desired, then a foreign key constraint and an index for the backreference is probably the best solution, although it will not scale well, and requires additional computation (because the rights on the image need to be the union of the rights of the refering resources).
The alternative is to create some kind of inheritance schema, where there is a table listing "resources" (that effectively just contains identifiers) that is referenced as a foreign key from the actual resource tables and the images table; the only constraint that cannot be expressed in plain SQL is that different resources may not share the same identifier.

Create two SELECT clauses, each having the correct joins to the correct tables, and then combine the output of the two SELECT clauses together using a UNION statement.
SELECT field1, field2
FROM table1
JOIN table2 on table1.PK = table2.FK
WHERE table1.selector = 1
UNION SELECT field1, field2
FROM table1
JOIN table3 on table1.PK = table3.FK
WHERE table1.selector = 2

Related

How to set up relational database tables for this many-to-many relationship?

I have a type of data called a chain. Each chain is made up of a specific sequence of another type of data called a step. So a chain is ultimately made up of multiple steps in a specific order. I'm trying to figure out the best way to set this up in MySQL that will allow me to do the following:
Look up all steps in a chain, and get them in the right order
Look up all chains that contain a step
I'm currently considering the following table set up as the appropriate solution:
TABLE chains
id date_created
TABLE steps
id description
TABLE chains_steps (this would be used for joins)
chain_id step_id step_position
In the table chains_steps, the step_position column would be used to order the steps in a chain correctly. It seems unusual for a JOIN table to contain its own distinct piece of data, such as step_position in this case. But maybe it's not unusual at all and I'm just inexperienced/paranoid.
I don't have much experience in all this so I wanted to get some feedback. Are the three tables I suggested the correct way to do this? Are there any viable alternatives and if so, what are the advantages/drawback?
You're doing it right.
Consider a database containing the Employees and Projects tables, and how you'd want to link them in a many-to-many fashion. You'd probably come up with an Assignments table (or Project_Employees in some naming conventions).
At some point you'd decide you want not only to store each project assignment, but you'd also want to store when the assignment started, and when it finished. The natural place to put that is in the assignment itself; it doesn't make sense to store it either with the project or with the employee.
In further designs you might even find it necessary to store further information about the assignment, for example in an employee review process you may wish to store feedback related to their performance in that project, so you'd make the assignment the "one" end of a relationship with a Review table, which would relate back to Assignments with a FK on assignment_id.
So in short, it's perfectly normal to have a junction table that has its own data.
That looks fine, and it's not unusual for the join table to contain a position/rank field.
Look up all steps in a chain, and get them in the right order
SELECT * FROM chains_steps
LEFT JOIN steps ON steps.id = chains_steps.step_id
WHERE chains_steps.chain_id = ?
ORDER BY chains_steps.step_position ASC
Look up all chains that contain a step
SELECT DISTINCT chain_id FROM chains_steps
LEFT JOIN chains ON chains.id = chains_steps.chain_id
I think that the plan you've outlined is the correct approach. Don't worry too much about the presence of step_position on your mapping table. After all the step_position is a bit of data that is directly related to a step in the context of a chain. So the chains_steps table is the right place for it IMHO.
Some things to think about:
Foreign keys - use 'em!
Unique key on the chains_steps table - can a step be present in more than one position in a single chain? What about in different chains?
Good luck!

Performance of modelling inheritance in database using superclass table

My Question, is actually a question about the usability / performance of a concept / idea I had:
The Setup:
Troughout my Database, two (actually three) fields always re-appear constantly: title and description (and created). The title is always a VARCHAR(100) and the description always a TEXT.
Now, to simplify those tables, I thought about something (and changed it in that way): Wouldnt it be more useful to just create a table named content, with id, title, description and created as only fields, and always point to that table from all others?
Example:
table tab has id, key and content_id (instead of title, description and created)
table chapter has id, story_id and content_id (" ")
etc
The Question:
Everything works fine so far, but my only fear is performance. Will I run into a bottleneck, doing it this way, or should I be fine? I have about 23 different tables pointing to content right now, and some of them will hold user-defined content (journals, comments, etc) - so the number of entries in content could get quite high.
Is this setup better, or equal to having title and description in every separate table?
Edit: And if it turns out to be a bad idea, what are alternatives to mantain/copying certain fields like title and description into ~25 tables?
Thanks in advance for the help!
There is no clear answer for your question because it mainly depends on usage of the tables, so just consider following points:
How often will you need write to the tables? In case of many inserts/updates having data in one big table can cause problems because all write operations will target the same table.
How often do you need data stored in table with common data? If title or description are not needed most of the time for your select this can be OK. If you need title every time then take into account that you wile always have to JOIN table with common data.
How do you manage your database schema? It can be easier to write some simple tool for creation/checking table structure. In MySQL you can easily access data dictionary with DESCRIBE table_name or through INFORMATION_SCHEMA database.
I'm working on project with 700+ tables where some of the fields have to be present in every table (when was record created, timestamp of last modification). We have simple script that helps with this, because having all data in one table would be disastrous.

Implementing Comments and Likes in database

I'm a software developer. I love to code, but I hate databases... Currently, I'm creating a website on which a user will be allowed to mark an entity as liked (like in FB), tag it and comment.
I get stuck on database tables design for handling this functionality. Solution is trivial, if we can do this only for one type of thing (eg. photos). But I need to enable this for 5 different things (for now, but I also assume that this number can grow, as the whole service grows).
I found some similar questions here, but none of them have a satisfying answer, so I'm asking this question again.
The question is, how to properly, efficiently and elastically design the database, so that it can store comments for different tables, likes for different tables and tags for them. Some design pattern as answer will be best ;)
Detailed description:
I have a table User with some user data, and 3 more tables: Photo with photographs, Articles with articles, Places with places. I want to enable any logged user to:
comment on any of those 3 tables
mark any of them as liked
tag any of them with some tag
I also want to count the number of likes for every element and the number of times that particular tag was used.
1st approach:
a) For tags, I will create a table Tag [TagId, tagName, tagCounter], then I will create many-to-many relationships tables for: Photo_has_tags, Place_has_tag, Article_has_tag.
b) The same counts for comments.
c) I will create a table LikedPhotos [idUser, idPhoto], LikedArticles[idUser, idArticle], LikedPlace [idUser, idPlace]. Number of likes will be calculated by queries (which, I assume is bad). And...
I really don't like this design for the last part, it smells badly for me ;)
2nd approach:
I will create a table ElementType [idType, TypeName == some table name] which will be populated by the administrator (me) with the names of tables that can be liked, commented or tagged. Then I will create tables:
a) LikedElement [idLike, idUser, idElementType, idLikedElement] and the same for Comments and Tags with the proper columns for each. Now, when I want to make a photo liked I will insert:
typeId = SELECT id FROM ElementType WHERE TypeName == 'Photo'
INSERT (user id, typeId, photoId)
and for places:
typeId = SELECT id FROM ElementType WHERE TypeName == 'Place'
INSERT (user id, typeId, placeId)
and so on... I think that the second approach is better, but I also feel like something is missing in this design as well...
At last, I also wonder which the best place to store counter for how many times the element was liked is. I can think of only two ways:
in element (Photo/Article/Place) table
by select count().
I hope that my explanation of the issue is more thorough now.
The most extensible solution is to have just one "base" table (connected to "likes", tags and comments), and "inherit" all other tables from it. Adding a new kind of entity involves just adding a new "inherited" table - it then automatically plugs into the whole like/tag/comment machinery.
Entity-relationship term for this is "category" (see the ERwin Methods Guide, section: "Subtype Relationships"). The category symbol is:
Assuming a user can like multiple entities, a same tag can be used for more than one entity but a comment is entity-specific, your model could look like this:
BTW, there are roughly 3 ways to implement the "ER category":
All types in one table.
All concrete types in separate tables.
All concrete and abstract types in separate tables.
Unless you have very stringent performance requirements, the third approach is probably the best (meaning the physical tables match 1:1 the entities in the diagram above).
Since you "hate" databases, why are you trying to implement one? Instead, solicit help from someone who loves and breathes this stuff.
Otherwise, learn to love your database. A well designed database simplifies programming, engineering the site, and smooths its continuing operation. Even an experienced d/b designer will not have complete and perfect foresight: some schema changes down the road will be needed as usage patterns emerge or requirements change.
If this is a one man project, program the database interface into simple operations using stored procedures: add_user, update_user, add_comment, add_like, upload_photo, list_comments, etc. Do not embed the schema into even one line of code. In this manner, the database schema can be changed without affecting any code: only the stored procedures should know about the schema.
You may have to refactor the schema several times. This is normal. Don't worry about getting it perfect the first time. Just make it functional enough to prototype an initial design. If you have the luxury of time, use it some, and then delete the schema and do it again. It is always better the second time.
This is a general idea
please donĀ“t pay much attention to the field names styling, but more to the relation and structure
This pseudocode will get all the comments of photo with ID 5
SELECT * FROM actions
WHERE actions.id_Stuff = 5
AND actions.typeStuff="photo"
AND actions.typeAction = "comment"
This pseudocode will get all the likes or users who liked photo with ID 5
(you may use count() to just get the amount of likes)
SELECT * FROM actions
WHERE actions.id_Stuff = 5
AND actions.typeStuff="photo"
AND actions.typeAction = "like"
as far as i understand. several tables are required. There is a many to many relation between them.
Table which stores the user data such as name, surname, birth date with a identity field.
Table which stores data types. these types may be photos, shares, links. each type must has a unique table. therefore, there is a relation between their individual tables and this table.
each different data type has its table. for example, status updates, photos, links.
the last table is for many to many relation storing an id, user id, data type and data id.
Look at the access patterns you are going to need. Do any of them seem to made particularly difficult or inefficient my one design choice or the other?
If not favour the one that requires the fewer tables
In this case:
Add Comment: you either pick a particular many/many table or insert into a common table with a known specific identifier for what is being liked, I think client code will be slightly simpler in your second case.
Find comments for item: here it seems using a common table is slightly easier - we just have a single query parameterised by type of entity
Find comments by a person about one kind of thing: simple query in either case
Find all comments by a person about all things: this seems little gnarly either way.
I think your "discriminated" approach, option 2, yields simpler queries in some cases and doesn't seem much worse in the others so I'd go with it.
Consider using table per entity for comments and etc. More tables - better sharding and scaling. It's not a problem to control many similar tables for all frameworks I know.
One day you'll need to optimize reads from such structure. You can easily create agragating tables over base ones and lose a bit on writes.
One big table with dictionary may become uncontrollable one day.
Definitely go with the second approach where you have one table and store the element type for each row, it will give you a lot more flexibility. Basically when something can logically be done with fewer tables it is almost always better to go with fewer tables. One advantage that comes to my mind right now about your particular case, consider you want to delete all liked elements of a certain user, with your first approach you need to issue one query for each element type but with the second approach it can be done with only one query or consider when you want to add a new element type, with the first approach it involves creating a new table for each new type but with the second approach you shouldn't do anything...

Database organization

This isn't much of a coding question as opposed to seeking help on going down the right path. So, I have my users set up with username, password, email, posts, & id. The basic forum stuff. But, if I were to add more, say some games in the website that you play and store data with your forum account (being your account for the whole website, in other words), would it be wiser to add those fields to the existing table (i.e.: game1_money) or make another table for each individual side project, then create and link it to each user upon starting the "game" or whatever it is?
If I'm too vague, tell me and I'll try to clarify.
Use separate tables and then a left join when you want the data for the specific game or application. Then if 10% of the users are signed up for the game you don't waste the table space for the other 90%. You also don't need to keep fiddling with the user table.
Anonymous,
Always try to keep your primary table clean and relate everything back to a unique, individual index. In this case, whether you add games, comments, documents, whatever...user User_ID (for example) as the related index field that points back to your main users table.
Here's a good reference on building normalized tables
http://dev.mysql.com/tech-resources/articles/intro-to-normalization.html
Don't store that information in the Users table. Normalise into separate tables.
Create a separate table, say GameSavedInfo, to store this information and reference the users ID from the user table.
I would also create a GameDetails table and use its Id in the GameSavedInfo table.

Saving multiple attributes in a single MySQL row

I have a MySQL table , each row representing a Person. For each person I would like to show a list of related links (when generating his page on my website), so I created a column called "RelatedLinks" which there I save raw HTML content such as:
Related Link 1 Related Link2
Then I just use PHP to echo $row["RelatedLinks"]; within the HTML itself.
The problem with this implementation is that I have little control over the links, for example in changing their order of appearance on the website, deleting/editing a specific link and so on.
The second idea I thought about was to create a column in the table for each related link, such as "RelatedLink1", "RelatedLink2" and then use PHP to echo $row["RelatedLink1"] . "<br />" . $row["RelatedLink2"].
Although I do have much more control with this implementation, I think I am creating a big overhead in the table.
What is an efficient way to achieve this functionality?
EDIT
Referring to Gabriel's answer, the question now is about complexity:
With the current implementation, once I select the person I have all the links in my hand - complexity of O(1). What will be the complexity of running a query to select all foreign keys of the current Person, as suggested by Gabriel (and Haim) below?
Thanks,
Joel
If you only want to store and display the links, and the edit actions are very few, the first option is the best one. In this case, you will have to save the new values on edit.
If you think you'll have more edit operations, you could try to create a second tabel, to contain id_pearson, link, text, order . And you can use this tabel to edit the links for a specific pearson.
Also, if you need the same link for two or more persons, it's preferable to use 3 tables.
persons table : id_person, name (and all other info)
links table : id_link, text, link
person_links : id_person, id_link, order
Using this setup, you can edit the links in one place, and all persons will have access to the same version.
LATER EDIT IN RESPONSE TO COMMENT
Okey, regarding complexity of operations, nothing could beat the first solution. But you must take in account several things regarding what you need:
how often do you expect to change the links ?
who is gone change the links (the admin of the site, or the end user) ?
how many cases of the same link to appear for different persons (using the first solution, you will have to edit all persons that contain that link )
To answer your question of complexity, the solutions proposed by me are O(n) if I'm not mistaking. Using foreign keys and proper indexing in MySql, you shouldn't have a problem with this.
Summary: if performance is very important to you and the links will be edited only by the admin, use the first solution. If not, I would use the third one.
Hope this helps,
Gabriel
You need a secondary table that contains a PersonID (foreign Key), RelatedLink(ID) and maybe an OrderID.
Something like
Table PersonRelatedLinks
PersonID
RelatedLink or RelatedLinkID
OrderID
SpecialFormat maybe