简介:覆盖2003年2月23日至2025年4月15日的全部3287期双色球开奖记录,每期包含期号、开奖日期、6个红球(1–33)与1个蓝球(1–16)等完整字段,时间跨度超过22年。数据包面向彩票数据分析爱好者、Python开发者和自建预测模型的研究者:Excel文件便于筛选排序、透视图表与快速预览;MySQL结构化脚本可直接导入本地数据库,支持批量查询、频次统计、冷热号追踪、连号及区间分布等深度分析场景。压缩包共5个文件,以xlsx、sql和py为主,附带的Python辅助脚本可帮助读取或预处理数据,整体体积仅213KB,轻量便捷。数据已按时间顺序连续整理,无缺失、无重复,适合各类分析工具和模型直接调用。目前已有1269人学习下载,是一份颇具实用价值的历史数据素材。 从2003年首次摇奖到2025年,双色球这二十多年累计开出了3287期数据。整理这份数据起初是因为后台需要一套体量适中的结构化样本,用来验证Excel透视表的处理能力,顺便练练MySQL的批量导入和索引优化。结果一动手才发现,真正的工作量不在导入,而是"预处理"这一层:源站抓回来的数据是夹杂着标签和空白的HTML片段,直接贴进Excel会遭遇字段错位,转成CSV又暴露编码和格式问题;等到两边都能正常打开、字段对齐之后,还得逐期核对期号连续性,搜索历史数据中的近似重复。这个项目做下来,我对数据从采集、清洗、存储到查询的"全流程"有了比看十遍教程都深的理解。
这份数据本身不复杂,每一期开奖就是6个红球(1到33范围内取6个)加1个蓝球(1到16范围内取1个),但正是这种"结构单纯、体量适中、规律可循"的特点,让它成了一个非常理想的数据处理练习样本。不管你是刚开始学Excel函数,还是正在练MySQL的建表和查询,这套数据都能给你提供足够的操作空间。
1. 数据范围与整体设计思路
1.1 双色球数据的基本结构
双色球每一期的开奖结果可以抽象成一条固定格式的记录:期号、开奖日期、6个红球号码、1个蓝球号码。期号是唯一标识,从2003年第一期开始编号,一直顺延到2025年,共3287条记录。这个量级不大不小——比课堂上的学生成绩表大得多,又不需要上Hadoop,对Excel和MySQL来说都在舒适区。
设计字段时我遵循了一个原则:只存原始事实,不存计算结果。也就是说,表格里只保存开奖号码这些基础信息,至于奇偶比、大小号个数、号码和值这些衍生指标,全部用公式或SQL实时计算。这样做的好处是数据不会冗余,将来如果算法口径变了,不需要回头改历史数据,重跑一遍查询就行。
原始HTML源里数据大概是这种结构:一个div标签里套着一串p标签,每个号码一个节点,夹杂着换行和缩进。我在写脚本做清洗时主要做了四件事:去掉标签、统一日期格式、把两位数的期号补零(比如2025001这样的格式必须统一成6位,否则排序会乱)、筛掉重复抓取产生的脏记录。
1.2 为什么同时输出Excel和MySQL两个版本
整理成Excel格式,是为了让不熟悉数据库的人也能直接打开使用;生成MySQL版本,则是为了满足查询分析和程序调用的需求。这两个版本我压测过很多场景,结论很明确:Excel适合交互式浏览、小规模筛选和可视化构图,MySQL适合复杂条件查询、多表关联和自动化脚本调用。
把同一份数据同时做成两个版本,本身也是一道很好的"一致性"考题——导入完成后我先统计总行数,再用两份数据做抽样比对,确保Excel和MySQL里的期号、号码完全一致。这个环节看起来很基础,但实际执行时极其容易出错,后面第4部分我会专门说排查经验。
图表之外,我还额外导出了一套CSV中间文件,字段分隔用英文逗号,编码采用UTF-8。这个文件的价值在于给其他工具留了一个通用入口:如果你不想用Excel也不想用MySQL,拿这个CSV直接喂给Python的pandas也能干活。很多朋友问我为什么非要绕一道CSV,直接给Excel文件不够方便吗?原因是Excel的xlsx格式是个压缩包,读起来还得依赖库,而CSV是纯文本,任何脚本语言都能处理,通用性上一个台阶。
2. Excel版数据文件的制作与实操细节
2.1 字段顺序与单元格格式设计
Excel版本的表格我使用了这样一套列设计,从A到I列依次是:期号、开奖日期、红1、红2、红3、红4、红5、红6、蓝球。这里有一个非常容易踩的坑:期号和号码一定要设成文本格式,不能是常规或数值。原因是期号如2025001,如果存成数值格式,前面的"2025"会被保留但末尾的"001"会变成"1",期号就会显示成20251,直接破坏唯一性。
我最开始导入时就吃过这个亏,打开Excel发现最后一百期全部对不上号,排查了半天才意识到是格式问题。设置方法是在建立表格前,先把对应列选中,右键设置为"文本"格式,之后再粘贴或导入数据。如果你已经在常规格式下粘贴进去了,也不要慌,可以用分列功能补救:选中该列,数据标签页里点"分列",向导第三步把列类型改成"文本",点完成,数据就回来了。
红球和蓝球号码区域,我顺手加了一层条件格式:红球列用暖色填充,蓝球列用冷色填充。这不只是为了好看,实际筛选数据的时候,视觉上能更快定位到某一列的范围,尤其是滚动到第2000期以后,表格变长,这个区分度能节省不少扫视时间。
2.2 用函数做号码统计
数据到手之后,大部分人第一件事就是统计每个红球号码出现的次数。这个场景在Excel里面最常用的函数组合是COUNTIF。假设号码范围在D列到I列,你想统计红球"01"在整个历史数据里出现了多少次,可以先用辅助列把所有红球合并成单个值列表,再用条件计数。比如把每个单元格单独拆出来:
=COUNTIF($D$2:$I$3288, "01")这个公式会统计"01"在全部红球区域出现的次数。这里要提醒一个细节:数字1和文本"01"在COUNTIF里会被当成不同内容,所以号码列必须是统一的文本格式,否则统计结果会偏小。我通常的做法是先在旁边做一个号码映射表(从01到33),然后对每个号码跑一次COUNTIF,得到一张频率表,再用柱状图展示。
如果要用数据透视表来做,同样可行:把所有红球号码合并到一列,给这列起个字段名叫"号码",在旁边再放一个标记字段叫"类型"(全部填红球),选中这两列插入透视表,把"号码"拖到行区域,把"号码"再拖到值区域,并修改值字段设置为"计数",一分钟就能得到分布结果。相比写公式,透视表胜在不需要维护长长的公式列,而且数据刷新之后右键"刷新"就能重算。
2.3 日期筛选与数据验证
Excel版还有一个实用的功能是利用日期列做时间切片。比如你想看近一年的号码趋势,可以选中日期列,在"数据"选项卡里点击"筛选",然后按日期范围筛选。这一步操作简单,但对Excel新手来说,最常见的困惑是筛选出的行号不连续,导致公式计算范围错误。解决方法是把数据区域定义成正式表格,也就是按Ctrl+T转成表格对象,之后写公式和透视表都引用表格名,而不是固定单元格区间,这样筛选后统计范围依然正确。
我还加了一项"数据验证"来提高可用性:选中红球列区域,在数据验证里设置"允许"为"自定义",公式填=AND(D2>=1, D2<=33)。这样的话,如果后续有人往表格里录入数据填了个35,单元格会直接报错,防止无效数据混入。
3. MySQL版本的数据建表与导入实践
3.1 表结构设计
MySQL版本的建表语句我做了比较充分的考虑。字段类型的选择上,号码用TINYINT就够,范围是1到33,完全落在无符号TINYINT可表示范围内(0到255),没必要用INT占4个字节。期号需要字符串类型,因为保留前导零是必要的,用VARCHAR(10)合适。开奖日期用DATE类型,方便后续做日期函数运算。
CREATE TABLE ssq_history ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, issue_no VARCHAR(10) NOT NULL COMMENT '期号', draw_date DATE NOT NULL COMMENT '开奖日期', red1 TINYINT UNSIGNED NOT NULL, red2 TINYINT UNSIGNED NOT NULL, red3 TINYINT UNSIGNED NOT NULL, red4 TINYINT UNSIGNED NOT NULL, red5 TINYINT UNSIGNED NOT NULL, red6 TINYINT UNSIGNED NOT NULL, blue TINYINT UNSIGNED NOT NULL, UNIQUE KEY uk_issue (issue_no) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;这里有几个值得解释的点。第一,期号字段加唯一索引是必须的,防止重复导入。第二,我故意没有添加total_sum、odd_even这类衍生字段,而是在查询时用表达式即时计算,这样表结构更简洁,后续如果算法定义变化不需要重建表。第三,引擎用InnoDB,虽然数据量不大,但InnoDB支持事务和行级锁,安全性更好。
3.2 数据导入的两种方式
第一种方式是使用LOAD DATA LOCAL INFILE从CSV文件批量导入,速度最快。假设你已经有了前面提到的UTF-8编码CSV文件:
LOAD DATA LOCAL INFILE 'D:/data/ssq_history.csv' INTO TABLE ssq_history FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 1 LINES (issue_no, draw_date, @red1, @red2, @red3, @red4, @red5, @red6, @blue) SET red1 = LPAD(@red1, 2, '0'), red2 = LPAD(@red2, 2, '0'), red3 = LPAD(@red3, 2, '0'), red4 = LPAD(@red4, 2, '0'), red5 = LPAD(@red5, 2, '0'), red6 = LPAD(@red6, 2, '0'), blue = LPAD(@blue, 2, '0');等等,这里有一个细节要注意。号码字段类型是整数,所以不能直接插入字符串"01",实际上LOAD DATA会自动把'01'转成1存进去,所以不需要LPAD操作。这个写法反而会报类型不匹配的错误。实际执行时直接对应字段即可,整数字段会自动处理前导零:
LOAD DATA LOCAL INFILE 'D:/data/ssq_history.csv' INTO TABLE ssq_history FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 1 LINES (issue_no, draw_date, red1, red2, red3, red4, red5, red6, blue);把"02"读入TINYINT字段时,MySQL会把它转成数值2并去掉前导零。这正是我们需要的存储方式。查询时如果想按两位格式展示,用LPAD(red1, 2, '0')实时处理即可。
第二种方式是使用客户端工具(如Navicat、MySQL Workbench)直接把Excel文件导入。这种方式对不熟悉SQL命令的朋友更友好,但有一个潜在问题:Excel里如果有合并单元格或表头复杂,导入会失败。我的建议是导入前先在Excel里把数据整理成标准的一维表,表头只保留一行,所有列都是原始值,不要有任何合并单元格。
3.3 常用查询用例
导入完成后,我整理了三个最常见的查询需求:查询某期完整开奖、统计蓝球出现次数、计算每个红球的总出现次数。下面给出前两个示例:
查询某一期开奖:
SELECT * FROM ssq_history WHERE issue_no = '2025050';统计蓝球出现频次并排序:
SELECT blue, COUNT(*) AS cnt FROM ssq_history GROUP BY blue ORDER BY cnt DESC, blue ASC;这个红球频率查询虽然SQL也简单,但实际使用时要注意:如果直接对red1到red6做六次COUNT再相加,会很啰嗦。推荐用UNION ALL把六个列纵向合并成一行值,再统一分组:
SELECT num, COUNT(*) AS freq FROM ( SELECT red1 AS num FROM ssq_history UNION ALL SELECT red2 FROM ssq_history UNION ALL SELECT red3 FROM ssq_history UNION ALL SELECT red4 FROM ssq_history UNION ALL SELECT red5 FROM ssq_history UNION ALL SELECT red6 FROM ssq_history ) t GROUP BY num ORDER BY freq DESC;这段查询用子查询把6个红球列拆成了6倍行数的长表,再做聚合。虽然在这个数据量下性能差别不大,但对于理解"宽表转长表再聚合"的思路非常有帮助,这也是MySQL数据分析常用的套路。
数据导入后,我建了三个索引:期号唯一索引、开奖日期普通索引、蓝球普通索引。期号索引前面建表时已经包含,日期索引是为了按时间范围查询更快,蓝球索引则为按蓝球号码分类统计加速。虽然3287行数据量不大,无索引也能秒查,但养成合理建索引的习惯,是以后面对更大数据时必须具备的素养。
4. 数据正确性验证与常见问题排查
4.1 数据完整性校验
数据如果不可信,后续一切分析都没有意义,所以我设计了一套验证流程。第一步是验证总行数:确认Excel数据行数为3287行,MySQL中SELECT COUNT(*) FROM ssq_history返回3287。第二步验证连续性:期号应该从2003001开始,以后每隔一段增加一个固定数值。用SQL可以这样查缺失期号:
SELECT a.issue_no + 1 AS missing_start FROM ssq_history a LEFT JOIN ssq_history b ON a.issue_no + 1 = b.issue_no WHERE b.issue_no IS NULL;这里用了自连接,把每期和它的下一期做匹配,如果某期后面没有紧邻的下一期,说明期号不连续。注意期号是字符串,直接加1需要转成整数再操作:CAST(a.issue_no AS UNSIGNED) + 1。实际查询时要写完整。
第三步是字段范围验证。红球号码范围应在1到33之间,蓝球号码在1到16之间。检查语句:
SELECT COUNT(*) FROM ssq_history WHERE red1 NOT BETWEEN 1 AND 33 OR red2 NOT BETWEEN 1 AND 33 OR blue NOT BETWEEN 1 AND 16;如果这个查询返回0,说明号码都在合法范围。对于Excel版,我用的是条件格式加筛选:选中号码列,用"不等于"的自定义筛选填上"0",看是否还有可见行。
最后一步是交叉抽样。我在Excel里随机抽了20行记录,去源网站核对开奖号码,同时在MySQL里再查同样的期号做比对。三份数据一致,我才确认这份数据可用。
4.2 实际操作中踩过的坑
第一个坑是CSV文件编码问题。第一次导出的CSV没有显式指定UTF-8,用Excel双击打开后中文日期标题全部变成乱码。解决方法是使用UTF-8 BOM编码导出,或者在导入MySQL时间指定字符集。Excel对UTF-8无BOM的支持一直不友好,这是它自己的历史遗留问题,不是数据本身的错,但处理时要主动规避。
第二个坑是期号前导零丢失。这个问题在CSV转Excel时尤其高发,因为Excel默认把数字字符串按数值处理。我一个同事帮我校验数据时,直接把期号列设成"常规"格式粘贴,结果2025001变成了2025001吗?不对,是变成2025001还在?实际是2025001被显示成2025001,看着好像没丢,但如果你把它复制到文本编辑器里,会看到科学计数法或者截断。更常见的丢零场景是2003001变成2003001也还好,但2003008这类含0的尾号,如果被转成数值再转回文本,末尾的0可能就没了。
第三个坑是MySQL导入时外键或唯一键冲突。因为是重复导入了两次,第二次导入时期号唯一索引直接报错。解决方案是导入前先TRUNCATE表,或者使用INSERT IGNORE跳过重复值。我的习惯是建一个原始数据临时表,清洗校验无误后再用INSERT ... SELECT灌入正式表,这样不会误伤已有数据。
第四个坑是Excel“文本”列被自动转成数字。具体表现在号码颜色列出现绿色的小三角,这是Excel的"数字以文本形式存储"提示。这个问题本身不致命,但如果后续要使用VLOOKUP,文本格式和数值格式匹配不上会导致查询失败。解决方法是把那列全部选中,用分列功能转成真正的文本格式,或统一转成数值。
4.3 排查思路的顺序分享
这里想多说一句排查的心法。数据出问题时,不要急着改数据,先判断是源数据错了、导入过程错了、还是查询语句错了。我的排查顺序是:先看原始文件(CSV或Excel)里的值对不对,再看数据库里的值对不对,最后才看查询语句返回的结果对不对。如果原始文件正确而数据库不对,问题一定在导入环节;如果数据库正确但查询结果不对,问题才在查询语句。
这套顺序看起来简单,但能帮你省下大量时间。我见过太多人一上来就怀疑SQL写错,反复修改查询条件,结果最后发现是数据导入时字段错位了一列。
5. 基于这份数据可以做的延伸分析
5.1 号码分布与频率可视化
数据准备妥当后,可以做的最基础分析是历史红球和蓝球出现频次分布。用MySQL跑出频率统计,导出到Excel画柱状图,几分钟就能完成。我做完之后发现几个有趣的现象:蓝球号码虽然理论上是等概率的,但历史样本中某些号码的出现次数会比均值高一些,也有个别号码偏低。这种波动完全是随机性在有限样本里的正常表现,不构成任何规律。
我自己更喜欢做的是奇偶比例和大小号比例的统计分析。双色球每期红球包含6个数字,可以算出奇偶比(奇数个数比偶数个数),也可以按1到16为小号、17到33为大号来统计大小比。这串字段在原始数据里没有,但我用SQL可以一次性生成:
SELECT issue_no, red1+red2+red3+red4+red5+red6 AS total_sum, (red1%2)+(red2%2)+(red3%2)+(red4%2)+(red5%2)+(red6%2) AS odd_cnt, 6 - (red1%2 + red2%2 + red3%2 + red4%2 + red5%2 + red6%2) AS even_cnt FROM ssq_history;这类派生指标对Excel用户同样友好,把原始列导入Excel后,新增两列写公式即可。算完之后用数据透视表统计"奇偶比组合"出现过多少期,你会发现某些组合出现的期数明显多于其他组合——这在大量样本里是正常的概率密度分布,不代表下期会继续沿袭这个分布。
5.2 走势图与趋势观察
接下来可以按时间维度做走势图。用日期列做横轴、某个红球号码做纵轴,画出它的历史出现时间线,再叠加一个简单的移动平均线观察密集区间。Excel里做这种趋势叠加图很方便,把两列数据选中,插入散点图加趋势线即可。
这里必须强调一点:所有历史数据趋势都只是在描述"过去发生过什么",不是"未来会发生什么"。如果你想抱着预测的心态来学数据分析,我建议换一个数据集,因为开奖过程是独立随机事件,历史频率对下一次开奖没有任何影响。我整理这套数据的初衷是给数据分析爱好者提供一个练手样本,用来熟悉Excel函数、透视表、SQL查询、数据可视化这些基本功,而不是用来做预测。把预期摆正,这份数据会是个特别趁手的工具。
5.3 数据集后续如何扩展
这套数据集还有不少可以扩展的方向。比如你可以在MySQL里增加两张关联表,一张记录用户录入的每期投注号码,另一张记录号码命中情况,通过关联查询实现"历史命中回溯"功能。再比如你在学Python数据分析时,可以直接读取CSV文件,用pandas做更复杂的分布检验和可视化。如果你在练习API开发,也可以基于MySQL表写一个查询接口,返回指定期号的JSON格式数据。
我见过不少学习者把这类数据当作第一个完整项目的"种子数据",后端、前端、数据库全都围绕它来练,效果很好。关键不在于数据本身有多值钱,而在于数据结构足够简单,让你可以把精力放在工具链和流程上,而不是消耗在理解业务规则上。
6. 一些整理数据的小经验
最后分享几点我在这个项目里沉淀下来的通用经验,不限彩票数据,做什么数据整理项目都能用上。
第一,先定格式、再做清洗。在动手抓数据之前,先明确最终要交付什么字段、什么类型、什么编码,后面每一步都向这个标准看齐,可以省掉很多返工。第二,版本管理不可忽视。清洗过程中会产生多个中间文件,建议文件名里带上日期或版本号,比如ssq_raw_20250601.csv、ssq_clean_v1.0.xlsx,这样后面复盘才知道每一步做了什么。第三,在源头加校验。导入数据库之后,永远先在目标环境做一次总行数、去重数、范围检查,再开始分析。不要相信"我刚才导出的时候是对的"这种话——导入过程中字段错位、换行符差异、编码转换这类问题,比你想象中常见得多。
如果你手里正好有类似"看起来简单但数据量不小"的整理需求,不管是传感器数据、商品记录还是订单流水,这套流程完全可以套用。核心思路就一句话:源数据、中间过程、最终结果,三层都要可见、可查、可回溯。把这一条做扎实了,数据质量自然就稳了。
本文还有配套的精品资源,点击获取