I've got a question on how to "prettify" data entry and removal for a many to many relationship.
Imagine if you will a DB with 3 tables. Tables called Users, Roles, and RoleAssignment.
-- `Users`
CREATE TABLE `Users`
(
`userID` int NOT NULL AUTO_INCREMENT ,
`userName` varchar(40) NOT NULL ,
`realName` varchar(40) NOT NULL ,
PRIMARY KEY (`userID`),
UNIQUE KEY `username` (`username`)
) AUTO_INCREMENT=1;
-- `Roles`
CREATE TABLE `Roles`
(
`roleID` int AUTO_INCREMENT NOT NULL ,
`roleName` VARCHAR(40) NOT NULL ,
PRIMARY KEY (`roleID`)
);
-- `RoleAssignment`
CREATE TABLE `RoleAssignment`
(
roleAssignID int NOT NULL AUTO_INCREMENT ,
roleID int NOT NULL ,
userID int NOT NULL ,
PRIMARY KEY (roleAssignID),
FOREIGN KEY (userID) REFERENCES Users(userID),
FOREIGN KEY (roleID) REFERENCES Roles(roleID)
) AUTO_INCREMENT=1;
This is a database where RoleAssignment is essentially a child table only meant to connect two Parent tables together. I made them this way because the user<->role relationship is many to many.
I found a way of getting this to output just relationships between users and their roles… Both queries output the same data but query 2 is better organized.
SELECT
Users.userName,
Users.realName,
Roles.roleName
FROM Users
JOIN RoleAssignment ON Users.userID = RoleAssignment.userID
JOIN Roles ON Roles.roleID = RoleAssignment.roleID;
SELECT
Users.userName,
Users.realName,
GROUP_CONCAT(Roles.roleName)
FROM Users
JOIN RoleAssignment ON Users.userID = RoleAssignment.userID
JOIN Roles ON Roles.roleID = RoleAssignment.roleID
GROUP BY Users.userID;
Is there an easy way to add an entry to the RoleAssignment table by name instead of ID in a single query? i.e. "Add user jack to role moderator"? Or would I need to write a script to find the ID of the user, then the ID of the role, and finally add an entry to RoleAssignment?
Any help or guidance would be appreciated!
Best Answer
3 tables is the standard way to implement many:many between two "entities" (users and roles, in your example).
However, if
Roles
is just an id and arole
, you could get rid of it by simply putting the role name in the mapping table. ("Tagging" is another example -- there is no real need to instantiate the "tags" table; just put the tags separately in the mapping table.)Get rid of the
id
in the mapping table; it serves no purpose. With those changes, you getThe
FOREIGN KEYs
are of little use in this context.More on mapping table design: http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table
Don't worry about using
VARCHARs
in indexes. Sure, you pay a little in speed, but gain a lot in simplicity, and even more if you are not having to map to an INT before using the table.By using the
INSERT..SELECT
syntax, it is not very complex; a single query suffices:If you choose to have Roles in a separate table, a different approach could be used. This takes advantage of being able to use a parenthesized
SELECT
in place of a literal.