简介:一套以MySQL为核心的万年历数据脚本,面向需要快速搭建农历、黄历查询系统的开发者,资源覆盖1970年至2100年共约4.7万天的公历农历对照、二十四节气、财神方位、每日宜忌、星座、天干地支与五行信息,并提供了相应字段的完整数据记录。文件共2个,核心为wnl.csv结构化数据文件和万年历.sql建表、导入脚本,压缩包仅1.86MB;已有1609人学习下载。借助该脚本可省去手工整理多年日期数据的繁琐过程:先执行SQL脚本建立calendar、lucky_direction、suit_and_taboo等数据表,再通过LOAD DATA INFILE将CSV批量导入MySQL;CSV中每行对应一天,包含公历日期、农历年月日、节气、财神方位、宜忌说明、星座、干支与五行属性等字段。导入后即可按日期查询节气、宜忌、财神方位等结果,也可进一步扩展成个人日历应用、择日工具或数据库课程实验的完整数据层。
1. 万年历脚本准备的 MySQL 库:为什么先确认数据边界
做内容类或业务类系统时,日历功能只要带上农历、节气、黄历宜忌,体验立刻不一样。但真正下手的人都知道,这套数据比想象中麻烦:公历转农历不是简单查表,节气涉及天文计算,宜忌又是另一套民俗规则。这份“最全万年历脚本 mysql 数据库黄历”恰好补了这个空档——它把农历、节气、干支、宜忌整理成可直接导入的 MySQL 脚本,省掉自己爬数据、对数据、清洗数据的漫长征途。适合正在做日历、生活服务、内容管理系统的开发者和运维,也适合想在本地快速搭一套带黄历查询能力的数据库的从业者。使用前先确认一件事:你要的是查询能力还是数据处理能力,这决定了后面导入和使用的姿势。
2. 黄历库的表结构设计:从节气到宜忌的字段取舍
2.1 数据分层:把“万年历”拆成可维护的多张表
拿到这份 MySQL 脚本后,第一件事不是急着 source,而是先看它的表结构设计。一份合格的万年历库不会把所有字段塞在一张表里,常见做法是按数据维度拆成多张表,用日期字段作为关联主键。以这套脚本为例,它覆盖公历日期、农历日期、节气、干支纪年、生肖、宜忌、冲煞等常见黄历元素,每类数据对应一张或两张表。
拆表的好处很明显:数据更新时不用动整库,查询时也能按需 JOIN。比如节气表通常只存 24 条或者每年 24 条,干支表也不复杂,真正的核心高表是“每日黄历表”,它以公历日期为唯一键,把每天的农历月、日、干支、宜忌字段归拢在一起。这种设计在业务端很好用——查某天黄历只需一条带索引的主查询,不需要跨表多次拼接。
2.2 字段与类型选型:date、varchar、json 各自的位置
看脚本时重点关注字段类型是否合理。日期字段用 DATE 类型而不是 VARCHAR,这是底线要求。VARCHAR 存日期会带来排序、范围查询和索引失效问题,直接拉低查询性能。农历月和农历日这类字段,用 VARCHAR 或 TINYINT 都行,视脚本是否带闰月标记而定。闰月这种特殊日期,最好有独立的标记字段,比如is_leap_month,否则查询时无法区分“闰四月”和“四月”。
宜忌字段是这套脚本的亮点,也是容易出问题的地方。常见的设计有两种:一种是一个字段存逗号分隔的字符串,比如“宜:祭祀、祈福、求嗣;忌:开市、动土”;另一种是拆成yi和ji两个字段,分别存 JSON 数组或逗号分隔文本。推荐后者,查询时用FIND_IN_SET或 JSON 函数都能处理。重点确认脚本里宜忌字段用的是哪种方案,这直接影响你后续的业务查询逻辑。如果脚本用 JSON,要注意 MySQL 版本不能低于 5.7;用逗号分隔则兼容性更好。
表结构确认完毕后,别急着导入。先做一次全库扫描,看有没有明显空缺的日期段。万年历脚本最怕数据从 1900 年跳到 2025 年中间断档,一旦断了,业务端查不到数据就只能干瞪眼。
3. SQL 脚本的导入链路:两种稳妥方式与编码陷阱
3.1 命令行导入:source 与编码参数的真实作用
导入这份 MySQL 脚本,最基础的方式是命令行。用 source 命令时要注意:source 是在 MySQL 会话内执行的,不是 shell 命令。进入 MySQL 后先选库,再执行source /path/to/wanianli.sql;。但实际干活时,我更推荐直接重定向导入,这样能绕开会话内路径和权限问题:
mysql -uroot -p --default-character-set=utf8mb4 wnl < /path/to/wanianli.sql这段命令的逻辑是:用 root 身份连接 MySQL,指定默认字符集为 utf8mb4,把脚本文件重定向到wnl数据库中执行。--default-character-set=utf8mb4这参数不能省,黄历脚本里含有大量中文和生僻字,有些地区用字,比如“巳”“戌”“廿”,如果字符集不对,导入后不是乱码就是报错。<是 shell 重定向,把文件内容作为 mysql 客户端的标准输入,这样做的好处是不用手动进入交互模式,脚本出错时也能在终端直接看到日志。
导入前还要确认数据库存在,如果wnl库不存在,得先创建:
mysql -uroot -p -e "CREATE DATABASE IF NOT EXISTS wnl DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"COLLATE utf8mb4_unicode_ci指定排序规则,如果脚本里有中文排序需求,这个配置比默认的utf8mb4_general_ci更符合语言习惯。从执行经验看,unicode_ci 对中文生僻字和特殊符号的处理也更稳定。
3.2 图形化工具导入:Navicat 里最容易忽略的两处设置
不习惯命令行的同学,用 Navicat 或 MySQL Workbench 导入也是常见路径。但图形化工具导入 SQL 文件时有几个容易翻车的地方。Navicat 里右键数据库选择“运行 SQL 文件”,弹窗里第一件事就是检查文件编码。UTF-8 和 UTF-8 with BOM 不一样,黄历脚本如果带 BOM 头,某些版本会多一个不可见字符,导致第一条 SQL 直接报错。
另一处是“遇到错误时继续运行”这个选项,默认是关闭的,意味着脚本中途任何一条 SQL 出错,整个导入就停在那里。这不是坏事——出错时停下来反而能帮你定位问题。如果确认脚本在别的环境跑过,只是想快速恢复数据,再勾选这个选项也不迟。Workbench 则要注意导入超时设置,在 Edit -> Preferences -> SQL Execution 里把 timeout 调大,避免大文件导入到一半连接断开。
图形化工具导入完成后,建议顺手查一下表行数,和生产环境之前验证过的数字做对比。这一步看着多余,但实操中踩过太多坑——导入成功但数据量不对,或者部分表缺失。花三十秒确认,省下后面排查的半小时。
4. 避坑排查:导入报错与数据异常的六个真实案例
4.1 报错 1064 / 1366:字符集与 SQL 版本的连带问题
坑一:error 1366 Incorrect string value。现象是导入到一半报错,看具体行指向某个中文或生僻字。原因是表或库的字符集不是 utf8mb4,而是默认的 latin1 或者 utf8mb3。utf8mb3 存不了 4 字节字符,部分冷门汉字正好撞上。解决方式是建库时明确指定 utf8mb4,且检查脚本里的建表语句是否显式声明CHARSET=utf8mb4。如果脚本里没写,全局改一样要出事。
坑二:error 1064 syntax error。现象是某条 INSERT 语句报语法错误,而且错得莫名其妙。原因大概率是 MySQL 版本差异。脚本里如果用了INSERT IGNORE、ON DUPLICATE KEY UPDATE或窗口函数,MySQL 5.6 及以下版本就理解不了。解决方式先确认本地 MySQL 版本,再决定是否手动拆分低版本不支持的语句。血泪教训是拿到脚本先看第一行有没有SET NAMES utf8mb4,没有就先加上再导入。
坑三:导入成功但中文乱码。现象是数据能查到,但农历月份显示成乱码或问号。原因是连接层字符集和表的字符集不一致。解决方式是客户端连接时加--default-character-set=utf8mb4,同时检查表字符集,两者对齐。Navicat 用户在连接属性里也要手动设为 utf8mb4,否则连接池默认用 latin1。
4.2 数据正确性:节气时间、宜忌字段与空白日期
坑四:节气时间只到日,没有具体时分。现象是查询某年小寒,发现时间显示为当天日期,但真实节气可能在前一天或后一天的某个时辰。原因是脚本里的节气数据只做了按日归集,没有按天文精确时刻归集。这不算脚本错误,但业务端如果要求精确到时辰,就得自己补数据。我一般做法是节气精确到日即可用于绝大多数业务场景,如果需求要时辰级,再单独外挂节气时辰表。
坑五:宜忌字段存在“宜:诸事不宜”或空值。现象是查询某天黄历,yi字段返回 “诸事不宜” 或空字符串。原因是原始数据里有些日子确实无宜事,脚本原样保留。业务端要处理这种语义,不能让前端直接把空值展示给用户。常见做法是查询后做兜底:如果把“诸事不宜”理解为“可做之事很少”,那yi字段为空时业务层补一句“今日无特别适宜事项”。这里没有标准答案,按产品语义来定。
坑六:日期维度存在空洞。现象是按月查询时发现某些天没有记录。原因是脚本数据源本身可能只覆盖了某段年份范围,或导入时中途出错跳过了部分行。解决方式是先跑一段 SQL 查空洞:
SELECT a.dt AS missing_date FROM wnl_base a LEFT JOIN wnl_daily b ON a.dt = b.dt WHERE b.dt IS NULL AND a.dt BETWEEN '2024-01-01' AND '2024-12-31';这段查询的逻辑是:在wnl_base里生成一整年的日期序列,左连接每日黄历表,连接不到的行就是缺失日期。跑完如果结果为空,说明该年份数据完整;有结果就针对性补数据。这个技巧在验收任何日期类数据集时都值得先用一遍,能快速定位空洞。
5. 查询与联动:把黄历数据用进业务系统的 SQL 实践
5.1 单日完整查询:日期、农历、干支与宜忌一次取全
导入成功后,最核心的查询是“给一个公历日期,返回完整黄历信息”。推荐直接用 JOIN 方式从多张表组装数据,而不是每张表分开查再在应用层拼接。示例如下:
SELECT b.dt AS solar_date, d.lunar_year, d.lunar_month, d.lunar_day, g.ganzhi_year, g.ganzhi_month, g.ganzhi_day, d.yi, d.ji, s.shengxiao FROM wnl_base b LEFT JOIN wnl_daily d ON b.dt = d.dt LEFT JOIN wnl_ganzhi g ON b.dt = g.dt LEFT JOIN wnl_shengxiao s ON b.dt = s.dt WHERE b.dt = '2024-02-10';这里每条 JOIN 都通过日期字段关联,字段名根据脚本实际定义调整。LEFT JOIN 而不是 INNER JOIN 的原因是:某张表可能因数据缺失而没有该日期的记录,用 LEFT JOIN 可以保留主表行,业务端查询仍然有结果,只是部分字段为空,方便排查。日期范围查询时把WHERE b.dt =改成BETWEEN '2024-01-01' AND '2024-12-31'即可,配合索引性能没有问题。
5.2 与业务表联动:JOIN、排序与缓存策略
黄历数据真正用进业务,通常是和业务表做联动。比如一个预约系统,用户选了某天来店,你想同时展示当天宜忌,那么预约表关联黄历表就是典型场景:
SELECT r.id AS reservation_id, r.appointment_date, d.lunar_month || '月' || d.lunar_day AS lunar_text, d.yi, d.ji FROM reservations r LEFT JOIN wnl_daily d ON r.appointment_date = d.dt WHERE r.user_id = 123 ORDER BY r.appointment_date DESC;这里的||拼接语法在 MySQL 里默认不生效,需要改成CONCAT(d.lunar_month, '月', d.lunar_day)。如果按日期倒序查最近记录并关联黄历,建议在wnl_daily.dt上建联合索引,索引字段就是dt一个,但要注意 SQL 里关联条件用=或范围查询时索引才生效,对dt做函数运算会导致索引失效。
分页查询时,大表 JOIN 会有性能瓶颈,常见做法是先分页查出业务表的数据集,再一次性关联黄历表,而不是把 JOIN 放在分页子查询里。或者更简单:黄历数据一年只有三百多条,完全可以在应用启动时加载到本地缓存或 Redis,按日期查询直接走缓存,数据库只负责业务数据。这套方案既能减轻数据库压力,又能避免 JOIN 链路过深。
6. 校验与更新:让黄历数据长期可信的几条硬习惯
数据入库后不能直接撒手不管。我习惯每次导入后跑一组抽样校验,挑几个典型日期人工核对,比如春节、清明、某年闰月日期,对比公开日历确认农历和节气是否一致。这套脚本的数据源如果年份跨度大,个别年份因天文计算基准不同可能出现一天的偏差,抽样能把这层风险控住。
更新也是必须关注的环节。黄历脚本本质是一份静态数据集,不可能自动跟进未来的新数据。遇到跨年或新数据发布,需要增量补充,常见做法是先把新数据导入临时表,再通过INSERT ... ON DUPLICATE KEY UPDATE合入正式表,避免重复键报错。我从使用这套脚本的经验看,每次更新前看一眼增量文件里的日期段,确认和现有数据没有重叠空洞,再执行合入。
从那以后我每次处理这类数据资源,都强制先核对边界、再导入、再抽样验证,这套流程走完才敢交给业务侧调用。希望这套验证方法也能帮你在自己的库里少走弯路。
本文还有配套的精品资源,点击获取