Context

You might be needed a bug tracking application for products. Your original design allowed only one user to be the contact for each product.
 
CREATE TABLE Products (
    ...
    account_id BIGINT UNSIGNED, # many-to-one relationship
    FOREIGN KEY (account_id) REFERENCES Accounts(account_id)
);

Antipattern

As project growth, you were requested to support assigning multiple users. You choose to store a list of user account identifiers separated by commas.
 
CREATE TABLE Products (
    ...
    account_id VARCHAR(100), #comma-separated list
);    
 
SELECT * FROM Products 
    WHERE account_id REGEXP '[[:<:]]12[[:>:]]';    

Bugs

Soon your boss approaches: - They tell me they can add five people only!

SOLUTION

Use an intersection table with foreign keys. Some people resist queries that contain a join, thinking that they perform poorly. However, this query uses indexes much better.
 
CREATE TABLE Contacts (
    product_id BIGINT UNSIGNED NOT NULL,
    account_id BIGINT UNSIGNED NOT NULL,
    PRIMARY KEY (product_id, account_id),
    FOREIGN KEY (product_id) REFERENCES Products(product_id),
    FOREIGN KEY (account_id) REFERENCES Accounts(account_id)
);

SELECT p.*
FROM Products AS p JOIN Contacts AS c ON (p.account_id = c.account_id)
WHERE c.account_id = 34;    
Be conservative if you decide to employ denormalization. Start by using a normalized database organization.





Questions and answers:
Clink on Option to Answer




1. Comma-separated lists as values in DB are used ...

  • a) to avoid creating an intersection table
  • b) to avoid a many-to-one relationship

2. What's the problem when using comma-separated lists?

  • a) you cannot use use indexes
  • b) you cannot use denormalization

3. Which one doesn't refer to this antipattern

  • a) account_id BIGINT
  • b) account_id VARCHAR(100)

4. Programmers correct this antipattern with:

  • a) regex
  • b) intersection table and foreign keys


References: