Php 在SQL数据库中存储逗号分隔的列表-是否正确?

Php 在SQL数据库中存储逗号分隔的列表-是否正确?,php,mysql,sql,normalization,database-normalization,Php,Mysql,Sql,Normalization,Database Normalization,我有一个MySQL数据库,它包含两个表。第一个表仅包含产品。第二个由用户组成。我使用PHP来处理信息 在users表中,我想定义允许用户查看的产品 我最初认为我可以通过在users表中创建一列来实现这一点,该列包含一个逗号分隔值列表,该列表将对应于用户可以看到的产品(基本上是该用户可用的产品id列表) 自从我决定使用这种方法以来,我做了一些研究,我看到有人建议它不遵循数据库规范化的规则,并且可以快速创建冗余 如果有更好的方法,我肯定会感谢任何人能给我指出正确方向的一些见解。创建另一个表,将用户链

我有一个MySQL数据库,它包含两个表。第一个表仅包含产品。第二个由用户组成。我使用PHP来处理信息

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

我最初认为我可以通过在users表中创建一列来实现这一点,该列包含一个逗号分隔值列表,该列表将对应于用户可以看到的产品(基本上是该用户可用的产品id列表)

自从我决定使用这种方法以来,我做了一些研究,我看到有人建议它不遵循数据库规范化的规则,并且可以快速创建冗余


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

创建另一个表,将用户链接到产品,例如
用户到产品
,并具有
用户id
产品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 = ?

创建另一个表,将用户链接到产品,例如
users\u to\u products
,并具有
user\u id
product\u 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 = ?

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

用户id
产品id

两者都引用存储用户和产品的表中的ID


如果您在MySQL中存储逗号分隔的列表,您迟早会在搜索此类表时遇到问题。例如,如果您想找到允许查看产品2和产品5的用户,仅在MySQL中是不可能的,这不是最好的方法。您应该再创建一个表,其中包含允许用户使用以下结构查看的产品:

用户id
产品id

两者都引用存储用户和产品的表中的ID


如果您在MySQL中存储逗号分隔的列表,您迟早会在搜索此类表时遇到问题。例如,如果您想找到允许查看产品2和5的用户,那么仅在MySQL中是不可能的,您必须主要创建3个表,如
user、product和user\u products

TABLE : user_products
Fields : id, user_id, product_id

其中user\u id指的是user表的id,product\u id指的是product表的id。

您必须主要创建3个表,如
user、product和user\u products

TABLE : user_products
Fields : id, user_id, product_id

其中用户id指的是用户表的id,产品id指的是产品表的id。

您应该制作3个表:

使用者 产品 用户产品


然后,从用户获取用户id,从产品获取产品id,并将它们插入到User_Products表中,将用户id与允许的产品id相匹配。您应该创建3个表:

使用者 产品 用户产品


然后从用户和产品中获取UserID和ProductId,并将它们插入到User\u Products表中,将用户id与允许的产品id匹配

否!!!不要使用逗号分隔的列表执行此操作

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

你会有这样的想法:

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)
);
这允许适当的引用完整性管理,因为conmma分隔列表允许输入任何
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;

不!!!不要使用逗号分隔的列表执行此操作

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

你会有这样的想法:

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)
);
这允许适当的引用完整性管理,因为conmma分隔列表允许输入任何
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;

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

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

MySQL确实支持FIND_IN_SET()函数,该函数使搜索逗号分隔的字段变得简单(如果不是很快的话)。但它只能在一个集合中找到一个值。你不可能轻易地拿一套和另一套做比较。

这是很有道理的