投票表应建三张表:polls存主题,poll_options存选项,poll_votes存记录;用唯一索引(user_id,poll_id)防重复投票;统计用left join+group by,加复合索引优化性能。

投票表结构怎么建才不翻车
核心是分离「选项」和「投票记录」,别把所有数据塞进一张表。常见翻车点是用 ENUM 存选项或直接在用户表加 voted_option 字段——改选项、查统计、加新投票都得改表结构或写复杂 SQL。
推荐三张表:
-
polls:存投票主题,字段如id,title,start_time,end_time -
poll_options:存每个投票的选项,字段如id,poll_id,option_text,sort_order -
poll_votes:存用户每次投票,字段如id,poll_id,option_id,user_id,created_at(加联合唯一索引(user_id, poll_id)防重复投)
如何防止用户重复投票
靠数据库约束比靠应用层判断更可靠。在 poll_votes 表上建唯一索引:
CREATE UNIQUE INDEX idx_user_poll ON poll_votes (user_id, poll_id);
插入时用 INSERT IGNORE 或 INSERT ... ON DUPLICATE KEY UPDATE,避免报错中断流程。别只依赖 session 或 cookie 判断——清缓存、换设备、多开浏览器都会绕过。
如果业务要求「同一投票允许多选」,就把唯一索引改成 (user_id, poll_id, option_id),并把 poll_votes 的 option_id 设为非空。
这是一款比较精美的企业网站管理系统源码,功能比较完整,比较适合新手学习交流使用,也可以作为毕业设计或者课程设计使用,感兴趣的朋友可以下载看看哦。功能介绍:该源码主要包括前台和后台两大部分,具体功能如下:网站前台模块:主要包括企业简介、新闻中心、产品展示、公司证书、工程业绩、联系我们、客户系统、人才招聘等信息的浏览,以及客户留言的功能。网站后台模块1、常规管理:企业简介、链接管理、投票管理、系统设置
统计各选项得票数的 SQL 怎么写才高效
别用子查询套子查询,也别在应用层循环查。直接 JOIN + GROUP BY:
SELECT o.option_text, COUNT(v.id) AS vote_count FROM poll_options o LEFT JOIN poll_votes v ON o.id = v.option_id AND v.poll_id = 1 WHERE o.poll_id = 1 GROUP BY o.id, o.option_text ORDER BY vote_count DESC;
关键点:
- 用
LEFT JOIN保证没得票的选项也显示(COUNT(v.id)会返回 0) - 把
v.poll_id = 1放在ON条件里,不是WHERE,否则会过滤掉零票项 - 给
poll_votes(poll_id, option_id)加复合索引,加速聚合
MySQL 8.0+ 可以用窗口函数简化排名逻辑
如果要实时显示「当前投票 TOP 3」或带名次的榜单,不用应用层排序。例如按得票降序排并加名次:
SELECT option_text, vote_count, RANK() OVER (ORDER BY vote_count DESC) AS rank_num FROM ( SELECT o.option_text, COUNT(v.id) AS vote_count FROM poll_options o LEFT JOIN poll_votes v ON o.id = v.option_id AND v.poll_id = 1 WHERE o.poll_id = 1 GROUP BY o.id, o.option_text ) t;
注意 RANK() 会并列(比如两个第一,下一个就是第三),如果需要连续序号(1,2,3…),换成 ROW_NUMBER();如果要跳过并列(1,1,3…),保留 RANK()。
实际部署时,poll_votes 表增长快,记得定期归档历史投票数据,不然 COUNT 和 GROUP BY 会越来越慢。









