在SQL数据库中存储以逗号分隔的列表 - 是不是?

时间:2014-07-18 09:17:49

标签: php mysql sql normalization database-normalization

我有一个包含2个表的MySQL数据库。第一个表格只包含产品。第二个包含用户。我正在使用PHP来处理信息。

在users表中,我想定义允许用户查看哪些产品。

我原本以为我可以通过在用户表中创建一个列来实现这一点,该列包含逗号分隔值列表,这些值对应于用户允许查看的产品(基本上可供该用户使用的product_id列表。

由于我决定使用这种方法,我做了一些研究,我发现人们认为它不遵循数据库规范化的规则,并且可以快速创建冗余。

如果有更好的方法,我肯定会感谢任何能指出我正确方向的人的见解。

6 个答案:

答案 0 :(得分:4)

创建另一个表格,将用户链接到产品,例如users_to_products,并设置字段user_idproduct_id

这将允许您查询数据库以查找用户可以看到的产品:

SELECT u.id, u.name, p.id, p.name FROM users AS u
LEFT JOIN users_to_products AS up ON u.id = up.user_id
LEFT JOIN products AS p ON p.id = up.product_id
WHERE u.id = ?

答案 1 :(得分:3)

这不是最好的方式。您应该再创建一个表,其中包含允许用户使用结构查看的产品:

user_idproduct_id

引用存储用户和产品的表格中的ID。

如果您在MySQL中存储逗号分隔列表,您迟早会遇到在此类表中搜索的问题。例如,如果您想找到被允许查看产品2和5的用户,那么就不可能只在MySQL中

答案 2 :(得分:3)

否!!!请勿使用COMMA分离列表

如果你这样做,你会在未来几年内讨厌自己。处理这种关系的正确方法是使用Junction Table

你会有类似的东西:

CREATE TABLE UserProducts
(
        UserID INT NOT NOT NULL FOREIGN KEY REFERENCES Users (UserID),
        ProductID INT NOT NULL FOREIGN KEY REFERENCES Products (ProductID),
    PRIMARY KEY (UserID, ProductID)
);

这允许正确的参照完整性管理,因为连续分隔列表允许输入任何ProductID,而外键确保输入有效的productID。搜索具有特定产品的用户要容易得多:

SELECT  u.UserID
FROM    Users AS u
        INNER JOIN UserProducts AS up
            ON up.UserID = u.UserID
WHERE   p.ProdcutID = 3;

相反:

SELECT  UserID
FROM    Users
WHERE   CONCAT(',', ProductList, ',') LIKE '%,3,%';

它还允许您使用指标获取产品名称和查询,即如果您想要允许特定用户使用的产品列表,则需要使用效率非常低的谓词加入产品表:

SELECT  p.Name AS Product
FROM    Users AS u
        INNER JOIN Products AS p
            ON CONCAT(',', u.ProductList, ',') LIKE CONCAT('%,', p.ProductID, ',%')
WHERE   u.UserID = 1

使用联结表,您只需使用:

SELECT  p.Name AS Product
FROM    UserProducts AS up
        INNER JOIN Products AS p
            ON p.ProductID = up.ProductID
WHERE   u.UserID = 1;

如果您需要以逗号分隔的列表中的产品ID,则可以使用GROUP_CONCAT轻松完成此操作:

SELECT  up.UserID, GROUP_CONCAT(up.ProductID) AS ProductList
FROM    UserProducts AS up
GROUP BY up.UserID;

答案 3 :(得分:3)

答案中提出了一些好处,但只是为了说明为什么逗号分隔列表可能是个问题。对逗号分隔列表中的项目进行的任何检查都不能使用索引,这意味着任何常规检查都会很慢。

如果您只是检查一行,则不成问题。例如,您已将搜索范围缩小到特定用户,并希望检查该用户是否可以访问单个产品,但是如果要查找有权访问单个产品的所有用户,则表示您正在强制执行非索引表扫描(即,mysql必须读取每一行并处理该字段)。

MySQL确实支持FIND_IN_SET()函数,它确实可以轻松地搜索逗号分隔字段,如果不是快速的话。但它只能在集合中找到单个值。你不能轻易拿一套并将它与另一套比较。

答案 4 :(得分:0)

您必须创建主要有3个表格,例如user, product and user_products

TABLE : user_products
Fields : id, user_id, product_id

其中user_id指的是User Table的ID,product_id指的是Product table的ID。

答案 5 :(得分:0)

你应该制作3张桌子:

用户 制品 User_Products

然后从用户获取UserID,从Products中获取ProductId并将其插入User_Products表,将用户ID与允许的产品ID匹配