I made a firm to add a user to my database now I want to have two tables. One table keeps track of the languages the user knows and the other table the design software the user uses.
Would I create 3 tables (profile, languages, software) each with an I'd field and when I add a user add a row to each table?
As you begin to add several many-to-many relationships, you need more tables to 'link' the information together. Here's how I would tackle the problem:
Note The IDs should all be unique indexed columns. Consider using AUTO_INCREMENT.
Table 1: Contains user's profile information
| ProfileID |UserInfo |
|=======================|
| 0 | Info |
|-----------------------|
| 1 | Info2 |
|-----------------------|
Table 2: Stores the possible languages
|LanguageID |LanguageName|
|========================|
| 50 | Python |
|------------------------|
| 51 | Java |
|------------------------|
and so on...
Table 3: Stores the Profile links to the languages
|ProfileID |LanguageID |
|========================|
| 0 | 50 |
|------------------------|
| 0 | 51 |
|------------------------|
| 1 | 50 |
|------------------------|
Every time you wanted to add a language to a user's profile, you'd create an entry in this table.
You would add two more tables for the software a user knows. One table for all the possible types of software, and another to store the links.
When you want to retrieve the information, you would do an operation such as the one below:
SELECT * FROM Table3
LEFT JOIN Table2
ON Table3.LanguageID = Table2.LanguageID
WHERE ProfileID = [TheProfileIDToSearch]
This structure uses JOIN to link tables together to return information from several tables at once. Here is a W3Schools quick explanation about SQL JOINS.
Related
I am trying to display information on a website using PHP and MYSQL that shows all the locations an event can take place, along with the facilities each location includes. For example, a park (location1) may contain toilets (facility1), swings (facility3) and a slide (facility4).
Location1 Location2 Location3 Location4
Facility1 x x
Facility2 x x
Facility3 x x
Facility4 x x
Firstly, I am unsure of the best way to display these as tables in MySQL and then how I would display this clearly using PHP calls onto a webpage.
Any help would be appreciated
Database schema
I would like to recommend you to create 3 tables in your database:
locations
facilities
location_facility
Locations table
+----+------------+
| id | name |
+----+------------+
| 1 | location_1 |
| 2 | location_2 |
+----+------------+
Facilities table
+----+------------+
| id | name |
+----+------------+
| 1 | facility_1 |
| 2 | facility_2 |
| 3 | facility_3 |
+----+------------+
Pivot table (location_facility)
+-------------+-------------+
| location_id | facility_id |
+-------------+-------------+
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 2 | 3 |
+-------------+-------------+
So, in pivot table you can store required information.
PHP application meta code
To get the data from your database in pure PHP - you can use PDO extension.
$sql = 'SELECT locations.name as loc_name,
facilities.name as facility_name
FROM location_facility
INNER JOIN locations ON locations.id = location_facility.location_id
INNER JOIN facilities ON facilities.id = location_facility.facility_id';
foreach ($conn->query($sql) as $row) {
print $row['loc_name'] . "\t";
print $row['facility_name'] . "\n";
}
In the database it is only one additional table, holding both PK from the related tables.
For display you'd choose a "master-detail" view: seleting one item from either table (master), showing all related records from the other (detail).
As others had commented, the tables required a third pivot table to connect the facilities and the location tables so many thanks for that. However, the next issue that arose was using these tables and connecting them to allow searches by facilities.
Hence an SQL query that grouped the facilities into one column within a view alongside all information from the location table.
CREATE VIEW locations AS
SELECT location.*, group_concat(facilities.FacilityName separator ',') AS facility_name
FROM location_facility
INNER JOIN location
ON location.LocationID = location_facility.LocationID
INNER JOIN facilities
ON facilities.FacilitiesID = location_facility.FacilitiesID
GROUP BY location.LocationName
ORDER BY location.LocationID ASC
This table can be queried using
SELECT *
FROM locations
WHERE facility_name LIKE %Toilet% AND facility_name LIKE %Parking%
This solved my issues and allowed the data to be displayed on a webpage exactly how I had desired.
Thanks again for the comments and help all!
I'm wondering if there's a best practice when it comes to having multiple many-to-many relationships between the same tables.
Currently I have a many-to-many relationship between user and item for the items that users have created.
---------------------
| user_id | item_id |
---------------------
| 1 | 3 |
---------------------
I'd like to create another junction table for user and item to reference their watchlist. Should I create separate many-to-many tables?
user_item_inventory user_item_watchlist
--------------------- ---------------------
| user_id | item_id | | user_id | item_id |
--------------------- ---------------------
| 1 | 3 | | 2 | 3 |
--------------------- ---------------------
OR should I create one many-to-many table that has a many-to-one relationship with a user_item_type table?
user_item user_item_type
------------------------------- ------------------
| user_id | item_id | type_id | | id | name |
------------------------------- ------------------
| 1 | 3 | 1 | | 3 | inventory |
------------------------------- ------------------
| 2 | 3 | 2 | | 2 | watchlist |
------------------------------- ------------------
While the decision ultimately rests on just how conceptually different inventory and wishlists are, based on prior experience, I would suggest using separate tables.
Currently, you have no additional data attached to either the inventory or watchlist, but that will not necessarily be the case in the future. Without knowing more details about the inventory and watchlist, it's hard to make predictions, but as soon as you want to start tracking additional data on an inventory relation vs. a watchlist relation, having separate tables will make things much simpler. As soon as you want to add columns that only pertain to one of your types of association, you'll want to have separate tables.
As has been pointed out in the other answer, having separate tables is certainly faster from a pure data storage and retrieval standpoint: you'll have one less column/index to populate/filter by. And if your inventory/wishlist associations table becomes "large", those extra type_id references will start to add up to something significant. (It won't matter for smaller sizes, but besides the obvious disk storage factors, more data requires more memory and more cache to manage, especially when indexes are involved.)
Separate tables would be a complication if you need to know all the items a user has an interest in (the combination of inventory, watchlist, and any other similar tables you might create), but if that is an actual need, then you could generate that list easily with a UNION query on all of the tables. (You could even create another table that contains a copy of all the user - item references as a performance enhancement if necessary.)
It depens on how do you want to use these data. If they are logically separated and there is no releations between them, then you can use two tables.
If you're going to display inventory and watchlist records, in some module, and you want only distinguish type of records, then you can use one table with type of record.
It is clear that for database engine, it is better to do
SELECT * FROM user_item_inventory
reather than
SELECT * FROM user_item WHERE type_id = 1
Here is my pivot table project_group:
+-----+----------+------------+----------+---------+
| ids | group_id | project_id | admin_id | user_id |
+-----+----------+------------+----------+---------+
| 4 | 115 | 1 | 1 | [3,4,5] |
| 5 | 115 | 2 | 1 | [5,2,1] |
| 6 | 115 | 3 | 1 | [1,3,6] |
This table represent group linked to the projects....user_id is which users can see projects/group... Is there any way to display correct projects/group only to the users defined in user_id?
Also content in user_id field can be changed....
The best way to handle this would be to first normalize your database. Storing comma separated lists in a cell is allowed, but generally bad practice, as explained in this question.
If you can have multiple users per project, you should have a linking table with a column for project and a column for user, like this:
project_users:
| project_id | user_id |
and you can make (project_id, user_id) a composite primary key.
That way, you can select the users for a project (say, project 1) like this:
SELECT user_id
FROM project_users
WHERE project_id = 1;
Once you have these, you can display the project data only to users whose id is returned in the above list.
I have built an SQL Fiddle that helps demonstrate this visually, if it helps.
It is good to note that this proper normalization gives the opportunity to a lot of useful data as well, as it becomes easier to search for users by project, but also you can search for project information based on a user.
I am attempting at making a site that has a polling section in it. The users need to be able to take previous polls if they have not voted in them yet. So for each poll they take, it adds a note into the cell.
The table looks something like this:
userid | poll1 | poll2 | poll3 | poll4 /
---------+--------+--------+-------+--------/
001 | Y | Y | | /
---------+--------+--------+-------+--------/
002 | Y | | Y | /
--------------------------------------------/
So now, the code would have to select poll 3 and 4 for user 001, and present that to the user on the page. I have been trying a couple different approaches to this but can't seem to find the right code.
I have looked for something for help online and haven't found anything that addresses this issue.
Users
id | name
---+-------
1 | tyrion
2 | cersei
Polls
id | name
---+-------
1 | first poll
2 | second poll
UserPolls
user_id | poll_id
--------+-------
1 | 1
1 | 2
2 | 2
In the above table structure, you have the two main object tables, and one relational table, called UserPolls. This will scale pretty well, for any number of polls you want.
Then, if you want to know which polls were taken by a given user, you can do:
SELECT poll_id FROM UserPolls WHERE user_id = 1
Or if you want the opposite. Polls not taken by a given user:
SELECT id FROM Polls WHERE Polls.id NOT IN (SELECT poll_id FROM UserPolls WHERE user_id = 1)
Note: this is not tested code, but you get the general idea on how to go about designing and normalizing your tables.
I have one table GAMES and another PLAYERS. Currently each "game" has a column for players_in_game but I have nothing reciprocating in the PLAYERS table. Since this column is an array (Comma separated list of the player's ID #s) I'm thinking that it would probably be better to have each player's record also contain a list of the games they are a member of. On the other hand, duplicating the information in two separate tables might actually require more DB calls.
For perspective, there aren't likely to be more then a dozen players in a game (generally 4-6 is the norm) but there could potentially be a large number of games.
Is there a good way to figure out which would be more efficient?
Thanks.
Normalization is generally a good thing. Comma delimited lists in tables is a sign that a table is in desperate need of a foreign key. If you're worried about extra queries, check out JOINING
dbo.games
+----+----------+
| id | name |
+----+----------+
| 1 | war |
| 2 | invaders |
+----+----------+
dbo.players
+----+----------+---------+
| id | name | game_id |
+----+----------+---------+
| 1 | john | 1 |
| 2 | mike | 1 |
+----+----------+---------+
SELECT games.name, count(players.id) as total_players FROM games INNER JOIN players ON games.id = players.game_id GROUP BY games.name;
Result:
+-----------+--------------+
| name |total_players |
+-----------+--------------+
| war | 2 |
| invaders | 0 |
+-----------+--------------+
Sidenote: Go Hokies :)
Oh god, please don't use CSVs!! I know it's tempting when you're new to SQL, but it becomes unqueryable...
You need 3 tables: games, players, and players_in_games. games and players should each have a primary auto-incrementing key like id, and then players_in_games needs just two fields, player_id and game_id. This is called a "many to many" relationship. A player can play many games, and a game can have many players.
The right answer is a table called PlayersInGames that has a player id and a game id per row.
I would create a third table that links the players and games. Your comma-delimited list is effectively a third table, but parsing your list is almost certainly going to be less efficient than letting the database do it for you.
Ask yourself what happens if you remove a row from the GAME table. Now you'll have to loop over all the PLAYER rows, parse the list, figure out which ones contain a reference to the removed GAME, and then update all the lists.
Bad design. Let SQL do what it was born for. The query will be fast enough if you index it properly. Micro-optimizations like this are the wrong approach.