Reputation: 19528
I have a MySQL table as follow:
id, user, culprit, reason, status, ts_register, ts_update
I was thinking of using the reason as an int field and store just the id of the reason that could be selected by the user and the reason itself could be increased by the admin.
What I meant by increased is that the admin could register new reason, for example currently we have:
Flood, Racism, Hacks, Other
But the admin could add a new reason for instance:
Refund
Now my problem is that I would like to allow my users to select multiple reasons, for example:
The report 01 have the reasons Flood and Hack.
How should I store the reason field so that I could select multiple reasons while maintaining a good table format?
Should I just go ahead and store it as a string and cast it as an INT when I am searching thru it or there are better forms to store it?
UPDATE Based on Jonathan's reply:
SELECT mt.*, group_concat(r.reason separator ', ') AS reason
FROM MainTable AS mt
JOIN MainReason AS mr ON mt.id = mr.maintable_ID
JOIN Reasons AS r ON mr.reason = r.reason_id
GROUP BY mt.id
Upvotes: 0
Views: 281
Reputation: 753525
The normalized solution is to have a second table containing one row for each reason:
CREATE TABLE MainReasons
(
MainTable_ID INTEGER NOT NULL REFERENCES MainTable(ID),
Reason INTEGER NOT NULL REFERENCES Reasons(ID),
PRIMARY KEY(MainTable_ID, Reason)
);
(Assuming your main table is called MainTable and you have a table defining valid reason codes called Reasons.)
From a comment:
[W]ould you be [so] kind [as] to show me an example of selecting something to retrieve a report's reason? I mean if I simple select it SELECT * FROM MainTABLE I would never get any reasons since MainTable doesnt know it right? Because it is only linked to the MainReasons and Reasons table so I would need to do something like SELECT * FROM MainTable LEFT JOIN MainReasons USING (MainTable_ID) or something alike but how would I go about getting all the reasons if multiples?
SELECT mt.*, r.reason
FROM MainTable AS mt
JOIN MainReason AS mr ON mt.id = mr.maintable_ID
JOIN Reasons AS r ON mr.reason = r.reason_id
This will return one row per reason - so it would return multiple rows for a single report (recorded in what I called MainTable). I omitted the reason ID number from the results - you can include it if you wish.
You can add criteria to the query, adding terms to a WHERE clause. If you want to see the reports where a specific reason is specified:
SELECT mt.*
FROM MainTable AS mt
JOIN MainReason AS mr ON mt.id = mr.maintable_ID
JOIN Reasons AS r ON mr.reason = r.reason_id
WHERE r.reason = 'Flood'
(You don't need the reason in the results - you know what it is.)
If you want to see the reports where 'Floods' and 'Hacks' were the reasons given, then you can write:
SELECT mt.*
FROM MainTable AS mt
JOIN (SELECT f.MainTable_ID
FROM (SELECT MainTable_ID
FROM MainReason AS mr1
JOIN Reasons AS r1 ON mr1.reason = r1.reason_ID
WHERE r1.reason = 'Floods'
) AS f ON f.MainTable_ID = mt.MainTable_ID
JOIN (SELECT f.MainTable_ID
FROM (SELECT MainTable_ID
FROM MainReason AS mr2
JOIN Reasons AS r2 ON mr2.reason = r2.reason_ID
WHERE r1.reason = 'Hacks'
) AS h ON h.MainTable_ID = mt.MainTable_ID
Upvotes: 2
Reputation: 1321
To do a one-to-many relationship, I would spin reason off into it's own table, like so:
id, parent_id, reason
parent_id would refer back into your current table's id.
You could store it as an INT, but it would be a continual pain to have to parse it every time you wanted to read the data. This way would just take one more join.
Upvotes: 1