Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
MySQL数据库中的逗号分隔列表_Mysql_Database - Fatal编程技术网

MySQL数据库中的逗号分隔列表

MySQL数据库中的逗号分隔列表,mysql,database,Mysql,Database,我正在为数据库中的用户实现一个好友列表,该列表将存储好友帐户ID 在我的成就数据库中,我已经有了一个类似的结构,其中我有一个单独的表,其中有一对accountID to AchieventId,但我对这种方法的担心是,它效率低下,因为如果有100万用户每个都有100个成就,那么这个表中就有1亿个条目。然后,试图为具有特定accountID的用户获得每一项成就将是对表的线性扫描(我认为) 我正在考虑为我的好友列表表设置一个逗号分隔的accountID字符串,我意识到将数据作为字符串处理是多么烦人,

我正在为数据库中的用户实现一个好友列表,该列表将存储好友帐户ID

在我的成就数据库中,我已经有了一个类似的结构,其中我有一个单独的表,其中有一对accountID to AchieventId,但我对这种方法的担心是,它效率低下,因为如果有100万用户每个都有100个成就,那么这个表中就有1亿个条目。然后,试图为具有特定accountID的用户获得每一项成就将是对表的线性扫描(我认为)

我正在考虑为我的好友列表表设置一个逗号分隔的accountID字符串,我意识到将数据作为字符串处理是多么烦人,但至少可以保证用户使用accountID作为主键,第二列作为列表字符串时的日志(n)搜索时间


对于这两种不同结构的搜索时间,我错了吗?

MySQL可以有效地使用适当的索引,用于设计为使用这些索引的查询,避免对表进行“扫描”操作

如果您总是为用户处理一整套成就,检索整个成就集并存储整个成就集,那么在单个列中使用逗号分隔的列表可能是一种可行的方法

然而……当你想处理个人成就时,这种设计就失败了。例如,如果要检索具有特定成就的用户列表。现在,你要对所有用户的所有成就进行昂贵的全面扫描,进行“字符串搜索”,依赖于格式正确的字符串,而MySQL无法使用索引扫描有效地检索该集合

因此,根据经验法则,如果您从不需要单独访问一项成就,从不需要从数据库中的用户中删除一项成就,从不需要为用户添加一项个人成就,那么您将仅将这些成就作为一个整体拉取,并且只将它们作为一个完整的集合存储在数据库内外,逗号分隔列表是可行的


我不太愿意推荐这种方法,因为它永远不会变成那样。不可避免地,您需要一个查询来获得具有特定成就的用户列表

使用逗号分隔的列表列,您会陷入一些难看的SQL:

SELECT a.user_id
  FROM user_achievement_list a
 WHERE CONCAT(',',a.list,',') LIKE '%,123,%'
丑陋的是MySQL不能使用索引范围扫描来满足谓词;MySQL必须查看每个成就列表,然后从头到尾对每个成就进行字符串扫描,以确定一行是否匹配

如果您想使用该列表中的单个值来执行联接操作,或者“查找”另一个表中的行,这将是非常痛苦的。这种SQL变得非常丑陋

数据完整性的声明性实施是不可能的;您不能定义任何外键约束来限制添加到列表中的值,也不能从每个列表中删除特定
成就id

基本上,您正在“放弃”关系数据存储的优势;因此,不要期望数据库能够处理这种类型的列。就数据库而言,它只是一个数据块,也可能是存储在该列中的.jpg图像,MySQL不会帮助检索或维护该列表的内容

另一方面,如果您采用存储各行的设计,每个用户的每个成就都作为一个单独的行存储,并且您有一个适当的可用索引,那么数据库返回列表的效率会更高,SQL也更简单:

SELECT a.user_id
  FROM user_achievements a
 WHERE a.achievement_id = 123
覆盖索引适用于该查询:

... ON user_achievements (achievement_id, user_id)
user\u id
作为前导列的索引适用于其他查询:

... ON user_achievements (user_id, achievement_id)

跟进

使用
EXPLAIN-SELECT…
查看MySQL生成的访问计划

例如,在检索给定用户的所有成就时,MySQL可以对索引进行范围扫描,以快速找到一个用户的行集。MySQL不需要查看索引中的每个页面,索引是以树的形式构造的(至少在B-tree索引的情况下是这样),因此它基本上可以消除一大堆它“知道”您要查找的行不可能存在的页面。通过索引中的
eaccessment\u id
,MySQL可以直接从索引返回结果集,而无需访问基础表中的页面。(对于InnoDB引擎,主键是表的集群键,因此表本身实际上是一个索引。)

使用两列InnoDB表
(用户id,成就id)
,将这两列作为复合主键,只需在
(成就id,用户id)
上添加一个二级索引


跟进


Q:次级索引指的是第三列,其中包含复合表(userID,AchieventId)的键。我的createtable查询如下所示

CREATE TABLE `UserFriends`
(`AccountID`       BIGINT(20) UNSIGNED NOT NULL
,`FriendAccountID` BIGINT(20) UNSIGNED NOT NULL
,`Key`             BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT
, PRIMARY KEY (`Key`)
, UNIQUE KEY `AccountID` (`AccountID`, `FriendAccountID`)
);
A:不,我不是说增加第三列。如果表中只有两列是另一个表的外键(看起来它们指的是同一个表,并且这些列都不是空的,并且对列的组合有一个独特的约束……并且表上没有其他属性,我将考虑不使用代理作为主键。我将把唯一的密钥设为主键。

就个人而言,我将使用InnoDB,启用了
InnoDB_file_per_table
选项。我的表定义如下所示:

CREATE TABLE user_friend
( account_id            BIGINT(20) UNSIGNED NOT NULL COMMENT 'PK, FK ref account.id'
, friend_account_id     BIGINT(20) UNSIGNED NOT NULL COMMENT 'PK, FK ref account.id'
, PRIMARY KEY (account_id, friend_account_id)
, UNIQUE KEY user_friend_UX1 (friend_account_id, account_id)
, CONSTRAINT FK_user_friend_user FOREIGN KEY (account_id)
    REFERENCES account (id) ON UPDATE CASCADE ON DELETE CASCADE
, CONSTRAINT FK_user_friend_friend FOREIGN KEY (friend_account_id)
    REFERENCES account (id) ON UPDATE CASCADE ON DELETE CASCADE
) Engine=InnoDB;

MySQL可以有效地使用适当的索引,用于设计为使用这些索引的查询,避免对t进行“扫描”操作