探索者的博客
2025-09-30 02:48:36 63

用户关注和互关mysql数据库设计

作者头像 探索者
用户关注和互关mysql数据库设计

1. 创建users表

CREATE TABLE `users` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '用户唯一ID',
  `username` VARCHAR(50) NOT NULL COMMENT '用户名',
  `email` VARCHAR(100) NOT NULL COMMENT '用户邮箱',
  `password_hash` VARCHAR(255) NOT NULL COMMENT '密码哈希值',
  `avatar` VARCHAR(255) DEFAULT NULL COMMENT '用户头像URL',
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间',
  `updated_at` TIMESTAMP DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '信息更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_username` (`username`),  -- 用户名唯一索引
  UNIQUE KEY `uk_email` (`email`)         -- 邮箱唯一索引
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

2. 创建follows表

CREATE TABLE `follows` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '关注记录ID',
  `follower_id` INT UNSIGNED NOT NULL COMMENT '关注者ID(关联users.id)',
  `followed_id` INT UNSIGNED NOT NULL COMMENT '被关注者ID(关联users.id)',
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '关注时间',
  PRIMARY KEY (`id`),
  -- 联合唯一索引:防止重复关注
  UNIQUE KEY `uk_follow_relation` (`follower_id`, `followed_id`),
  -- 外键约束:确保关联的用户存在
  CONSTRAINT `fk_follower` FOREIGN KEY (`follower_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
  CONSTRAINT `fk_followed` FOREIGN KEY (`followed_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='关注关系表';

3. 插入数据



4. 关键查询语句
SELECT 
            f.followed_id AS user_id,
            u.username,
            u.avatar,
            EXISTS (SELECT 1 FROM follows WHERE follower_id = f.followed_id AND followed_id =1) AS is_mutual FROM follows f 
         JOIN users u ON f.followed_id = u.id
        WHERE f.follower_id = 1
        ORDER BY f.created_at DESC

语句各部分含义
主查询字段(SELECT 部分)
f.followed_id AS user_id,  -- 被关注用户的ID,别名user_id
u.username,                -- 被关注用户的用户名(来自users表)
u.avatar,                  -- 被关注用户的头像(来自users表)
EXISTS (...) AS is_mutual  -- 判断是否互相关注的标志,结果为1(是)或0(否)

子查询(判断互相关注)
EXISTS (
  SELECT 1 FROM follows 
  WHERE follower_id = f.followed_id  -- 条件1:被关注的用户作为关注者
    AND followed_id = 1              -- 条件2:该用户关注的对象是用户1
) AS is_mutual
EXISTS 函数:如果子查询有结果(即存在满足条件的记录),返回1;否则返回0。
作用:判断 “被关注的用户” 是否也关注了 “用户 1”。例如,用户 1 关注了用户 2,子查询会检查follows表中是否有 “用户 2 关注用户 1” 的记录,有则is_mutual=1(互关),无则is_mutual=0(单方面关注)。
 

表关联与条件(FROM / JOIN / WHERE 部分)
FROM follows f  -- 从关注表follows查询,别名f
JOIN users u ON f.followed_id = u.id  -- 关联用户表users(别名u),通过被关注用户ID关联
WHERE f.follower_id = 1  -- 筛选条件:只查询“关注者是用户1”的记录
follows f:follows表是存储关注关系的表,f是别名,简化后续引用。
JOIN users u:通过f.followed_id = u.id将关注表与用户表关联,目的是从用户表中获取被关注用户的username(用户名)和avatar(头像)。
WHERE f.follower_id = 1:只保留 “用户 1 主动关注他人” 的记录,即筛选出用户 1 的关注列表。

排序(ORDER BY 部分)
ORDER BY f.created_at DESC  -- 按关注时间倒序排列,最新关注的用户显示在最前面
f.created_at:follows表中记录关注时间的字段(假设表中有该字段,用于记录 “用户 1 何时关注对方”)。
DESC:倒序排列,确保最新关注的用户在列表顶部。

列表代码:

<?php
// 数据库连接配置
$servername = "localhost";
$username = "your_username";
$password = "your_password";
$dbname = "your_database";

// 创建连接
$conn = new mysqli($servername, $username, $password, $dbname);

// 检查连接
if ($conn->connect_error) {
    die("连接失败: " . $conn->connect_error);
}

// 当前用户ID
$currentUserId = 888;

// 查询关注列表及互关状态
$sql = "SELECT 
            f.followed_id AS user_id,
            u.username,
            u.avatar,
            EXISTS(SELECT 1 FROM follows WHERE follower_id = f.followed_id AND followed_id = ?) AS is_mutual
        FROM follows f
        JOIN users u ON f.followed_id = u.id
        WHERE f.follower_id = ?
        ORDER BY f.created_at DESC";  // 按关注时间倒序排列,最新关注的在前

$stmt = $conn->prepare($sql);
$stmt->bind_param("ii", $currentUserId, $currentUserId);
$stmt->execute();
$result = $stmt->get_result();

$followList = [];
while ($row = $result->fetch_assoc()) {
    $followList[] = $row;
}

$stmt->close();
$conn->close();
?>

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>我的关注列表</title>
    <style>
        .follow-list {
            max-width: 600px;
            margin: 20px auto;
            padding: 0 20px;
        }
        
        .follow-item {
            display: flex;
            align-items: center;
            padding: 15px;
            border-bottom: 1px solid #eee;
        }
        
        .avatar {
            width: 60px;
            height: 60px;
            border-radius: 50%;
            object-fit: cover;
            margin-right: 15px;
            border: 1px solid #ddd;
        }
        
        .user-info {
            flex: 1;
        }
        
        .username {
            font-size: 18px;
            margin: 0 0 5px 0;
        }
        
        .status {
            padding: 3px 10px;
            border-radius: 12px;
            font-size: 14px;
        }
        
        .status-following {
            background-color: #e3f2fd;
            color: #1976d2;
        }
        
        .status-mutual {
            background-color: #e8f5e9;
            color: #388e3c;
        }
    </style>
</head>
<body>
    <div class="follow-list">
        <h2>我的关注列表</h2>
        
        <?php if(empty($followList)): ?>
            <p>您还没有关注任何人</p>
        <?php else: ?>
            <?php foreach($followList as $user): ?>
                <div class="follow-item">
                    <!-- 头像显示,如果没有头像使用默认图片 -->
                    <img src="<?php echo $user['avatar'] ? $user['avatar'] : 'default-avatar.png'; ?>" 
                         alt="<?php echo htmlspecialchars($user['username']); ?>的头像" 
                         class="avatar">
                     
                    <div class="user-info">
                        <h3 class="username"><?php echo htmlspecialchars($user['username']); ?></h3>
                        <span class="status <?php echo $user['is_mutual'] ? 'status-mutual' : 'status-following'; ?>">
                            <?php echo $user['is_mutual'] ? '互关' : '已关注'; ?>
                        </span>
                    </div>
                </div>
            <?php endforeach; ?>
        <?php endif; ?>
    </div>
</body>
</html>
  




评论区

评论者头像

张三

2025-06-16

非常实用的文章,我学到了很多关于前端性能优化的知识。特别是缓存策略部分,对我帮助很大。

评论者头像

李四

2025-06-15

代码分割确实是提高前端性能的重要手段,请问作者有没有实际项目中遇到的代码分割最佳实践可以分享?

作者头像
探索者 作者
2025-06-16

感谢提问!在实际项目中,我通常会根据路由、组件和第三方库进行代码分割。对于大型组件库,可以考虑使用动态导入来实现按需加载。