银行卡BIN数据从Excel到MySQL导入与查询实践
发布时间:2026/9/25 14:26:07来源:尧图网络
简介银联官方2020年4月25日发布的银行卡BIN数据共收录9868条记录面向支付系统开发、金融风控、商户对接及数据分析人员可快速完成银行卡发卡行识别与卡类型判断适用于卡Bin校验、交易路由和用户画像等场景。资源包共7个文件、约1.12MB包含5个Excel分表卡表、农民工卡表、跨行转账卡表、单位结算卡卡表、非标卡表和1个SQL文件Excel按卡类型拆分便于分场景查阅SQL文件已整理成可直接导入MySQL的格式省去手工清洗环节。字段涵盖银行卡BIN、BIN长度、发卡行、银行卡名、银行卡类型、银行卡长度等核心信息支持按卡BIN快速匹配发卡行与卡种既适合日常查询也方便批量导入数据库管理。目前已有3489人学习使用数据来源于银联官方渠道权威性与完整性有保障是搭建本地银行卡信息库、校验交易卡BIN或补充既有卡表数据时的可靠基础。1. 银行卡 BIN 数据为什么值得本地存一份Excel 与 MySQL 的真实使用场景做支付后端或风控规则时银行卡BIN几乎是绕不开的第一个判断点。卡号前6位能告诉你发卡行、卡种、卡组织路由和风控都靠它。我见过不少团队每次判断卡种都去调第三方接口结果接口一抖动整个下单流程跟着超时。标题里这份“银行卡bin数据(ExcelMySQL)”的价值就在这把银联口径的BIN表落到本地Excel便于人工核对和补录MySQL负责线上毫秒级查询。适合谁支付SDK开发者、风控策略工程师、对账和数据分析岗。你不需要一次性吃下全部字段先把卡BIN、卡类型、发卡行三列用起来后面再逐步扩。2. 先搞懂 BIN 表的结构再动手银联官方 BIN 数据的字段逻辑与版本差异2.1 一张 BIN 表到底长什么样卡品牌、卡类型、卡组织与发卡行字段BIN是Bank Identification Number的缩写指银行卡号前6位但实际使用时要区分“发卡行标识码”和“卡产品标识码”。银联官方口径的BIN表通常一个BIN就是一行同一发卡行会占据连续多个BIN。字段一般包括BIN号、卡类型借记卡/贷记卡/准贷记卡、卡品牌或卡组织银联、Visa、Mastercard、发卡行名称与行号、卡等级、币种以及一些扩展标记比如是否支持闪付、是否仅境外发行。先看常见的表格结构。| 字段 | 示例 | 说明 | | bin | 622848 | 卡号前6位定长字符串 | | card_type | 借记卡 | 区分借记/贷记/准贷记/预付 | | card_org | 银联 | 卡组织 | | bank_name | 中国农业银行 | 发卡行名称 | | bank_code | 0103 | 行号联行号或银联行号 | | card_level | 金卡 | 普卡/金卡/白金/钻石等 |字段顺序不重要真正决定查询效率的只有BIN这一列。卡类型和发卡行字段用于返回展示和风控规则不要用卡类型列去做精确过滤因为同一张卡在不同时期的BIN表里可能被标注成不同卡种线上判断时预留容错。需要说明的是银联官方发布的Excel里可能不带“卡组织”这一列因为整张表默认就是银联卡但如果要把外卡数据合并进来卡组织这列最好自己补上。Visa、Mastercard各自的BIN表格式完全不同不要指望一张Excel能统一三家的字段口径。实际项目中我会把银联作为主表外卡数据单独一张表或者在同一张表里用card_org字段区分索引设计上保持每张表的BIN列都唯一。还要注意行粒度。有些版本按BIN发放区间给一行比如“622848000000~622848099999”有些版本每一行就是一个具体BIN前6位。如果是区间格式入库前必须展开成单BIN否则查询时要先判断范围SQL写起来别扭索引也没法走到点查。展开区间属于纯体力活但漏掉边界值会造成线上查不到后面避坑章我会单独讲。2.2 2020 版银联 BIN 数据与旧版的差异为什么“最新最全”不等于字段更全标题里“2020最新最全”这个描述需要拆开看。常见网络流传的BIN表版本很多2015、2017、2019、2020各家整理版都有口径差异不小。新版通常比旧版多两类变化第一是新增了更多62开头的银联卡BIN同时补了部分9开头的老BIN。9开头是老一批双标卡或早期银联卡62开头是银联标准卡。查到9开头别急着删很多旧卡还在有效期内线上还在正常交易。第二是发卡行名称逐步规范化。早期的“xx银行xx支行”被收拢成总行名称有的版本还补了卡等级和产品线字段。这里有个实际价值如果你们的风控规则里要根据发卡行维度做限额行名口径统一后分组统计才能准确否则“农业银行”和“中国农业银行”会分成两个组。但“最新最全”更大意义是行数覆盖更全而不是字段更多。有的2020版反而删掉了一些非标准字段比如旧版里常见的“卡片有效期”列新版可能改成了“是否长期有效”。对于做风控和路由的团队字段越少越不容易出错因为Excel数据不像数据库没有一个强约束保证字段名不打架。这里有个容易被忽略的点银联官方并不直接发一份公开的完整Excel给所有开发者市面上流传的“银联官方发布”大多是某个机构或论坛整理后的导出版。拿到之后第一件事不是建表而是先随机抽几个真实卡号去验证BIN是否一致。拿自己手里的工资卡、信用卡各两位多个渠道交叉验证防止整理者漏了行或错位。验证通过再谈导入。如果发现某个BIN在表里找不到先确认是不是新发的卡或联名卡。银联卡BIN的新增在2020年依然频繁尤其互联网金融公司的联名卡、数字银行卡BIN段的增量更新往往赶不上发卡速度。所以“最全”只是相对时点线上还得留一个“命中不到时跳转人工或按卡组织前缀兜底”的逻辑。2.3 Excel 打开 BIN 数据的三个坑编码、列宽与身份证号式数字失真标题里特别标注了Excel说明这份数据的主要交付形态就是Excel。用Excel打开、整理BIN表有3个坑是每次都要交代一遍的。第一个是编码。银联相关Excel在Windows环境下大多是GBK/ANSI编码直接拖进一些数据工具或MySQL客户端会出现中文乱码。解决方式是先确认文件编码再用iconv转成UTF-8。用Excel自身另存为CSV也是常见方式但另存为CSV时中文版Excel默认输出GBK后面做LOAD DATA还要再转一次。我一般拿到文件先执行file命令看编码确认之后再动。file bin_info.xlsx # 如果是xlsx用python读取时指定engine如果是csv直接用iconv转码 iconv -f GBK -t UTF-8 bin_info.csv bin_info_utf8.csv第二个是列宽。BIN列虽然是6位数字但很多Excel版本默认把长数字列显示成科学计数法比如622848显示成6.23E05。这只是显示问题单元格值还是准的。怕的是某些工具导出时把单元格转成文本或数值后精度丢失所以用Excel处理时先把BIN列设成文本格式再填数或者从源头就保持“文本导入”。第三个是长数字失真的变体。如果Excel里除了BIN列还附带测试卡号、完整卡号样本16位以上数字会被Excel截断或变成科学计数法末几位变成0。这时候Excel已经不可逆地改了数据退回原始文件重新导一次就行千万别在修改后的Excel上继续清洗。最稳妥的做法是Excel只当查看器清洗和导入一律走脚本处理原始文件。处理Excel的辅助手段是转成Markdown表格或CSV后再做差异对比。把Excel转成CSV文件再用diff对比新旧版本BIN表的差异比肉眼扫Excel快得多。数据核对比的是行数变化和特定发卡行BIN段是否存在不是比哪个单元格居中、加粗。3. 把 Excel 里的 BIN 表清洗成 MySQL 可导入的格式转换脚本与参数选择3.1 先定主键和索引BIN 字段做唯一键还是普通索引建表之前先把主键策略定下来。很多人一看BIN列在表里是唯一的就直接把bin设成主键。这在银联BIN表成立的前提是“一个BIN只有一行”。但实际数据里经常出现同一个BIN对应两个卡产品比如同一张卡既有借记账户又有贷记账户或者某家银行同一个BIN段同时发普卡和金卡。如果BIN列直接做主键导入时会丢行。我一般建议用自增主键bin列建普通唯一索引或普通索引。理由有两个自增主键保证每一行都有稳定标识后续做增量更新、关联发卡行扩展表时不会因为BIN重复而JOIN出脏数据。BIN列即使不唯一只要建了索引点查性能也足够。BIN是定长6位索引大小很小10万级数据量不构成压力。如果你坚持用bin做主键先做一次去重统计SELECT bin, COUNT(*) FROM bin_raw GROUP BY bin HAVING COUNT(*) 1 LIMIT 20;有重复就要换主键方案。没有重复再用bin做主键也不迟。这个检查放在清洗之前做省得导完再返工。3.2 用 Python 完成 Excel 到 MySQL 的转换参数字段与前后类型校验清洗和转换我通常用Python一次性处理不手动改Excel。脚本需要完成的事是读取Excel、去掉空行和全空格、把BIN列统一成字符串、剔除明显异常的行然后输出一个UTF-8编码的CSV或者直连MySQL批量写入。import pandas as pd df pd.read_excel(bin_2020.xlsx, dtype{bin: str, bank_code: str}) df.columns [c.strip().lower() for c in df.columns] df df.dropna(subset[bin]) df[bin] df[bin].str.strip() df[bin] df[bin].str.replace(r\.0$, , regexTrue) df df[df[bin].str.match(r^\d{6}$)] df[card_type] df[card_type].fillna(未知) df[bank_name] df[bank_name].fillna().str.strip() df.to_csv(bin_2020_clean.csv, indexFalse, encodingutf-8) print(df.shape)参数说明read_excel里第一个参数是文件路径dtype指定bin和bank_code按字符串读取避免pandas默认把纯数字列读成int64或float64str.replace(r.0$)是处理某些Excel导出后数字变成622848.0的脏数据match正则过滤掉长度不足或带字母的行。这一步最关键的是把BIN统一成6位定长字符串后面进MySQL用CHAR(6)存储才有意义。输出CSV之后再用LOAD DATA导入比Python逐行INSERT快很多LOAD DATA LOCAL INFILE /path/bin_2020_clean.csv INTO TABLE bin_info CHARACTER SET utf8mb4 FIELDS TERMINATED BY , OPTIONALLY ENCLOSED BY IGNORE 1 LINES (bin, card_type, bank_name, bank_code, card_level);导入后立刻做三个校验总行数是否和Excel一致、bin列是否有空、是否有重复。用Python写个校验脚本也行直接在MySQL里跑三条SQL更快。很多血泪经验证明Excel里看起来正常的数据经CSV中转后行数对不上大多是因为换行符嵌入在字段里LOAD DATA遇到被引号包裹的换行符会误判成新行所以清洗时最好把字段内的换行符替换掉。3.3 MySQL 表结构设计字段类型、字符集与导入后的数据校验MySQL表结构按实际查询习惯建不为用不到的字段过度设计。下面这个DDL适合大多数支付场景CREATE TABLE bin_info ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, bin CHAR(6) NOT NULL COMMENT 卡BIN定长6位, card_type VARCHAR(16) NOT NULL DEFAULT 未知 COMMENT 卡类型, card_org VARCHAR(16) NOT NULL DEFAULT 银联 COMMENT 卡组织, bank_name VARCHAR(64) NOT NULL DEFAULT COMMENT 发卡行名称, bank_code CHAR(8) NOT NULL DEFAULT COMMENT 发卡行代码, card_level VARCHAR(16) NOT NULL DEFAULT COMMENT 卡等级, updated_at DATE DEFAULT NULL COMMENT 数据版本日期, PRIMARY KEY (id), KEY idx_bin (bin), KEY idx_bank_code (bank_code) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;参数说明bin用CHAR(6)而不是VARCHAR(6)因为定长字段省去长度前缀存储配合等值查询时性能更稳定。card_type用VARCHAR就够因为实际值只有几个固定枚举但没有必要用ENUM后续数据源换口径时ENUM会成约束。bank_code用CHAR(8)是为了对齐银联行号规范前导零不会丢。updated_at不是交易时间字段记录的是这批BIN数据属于哪个发布版本排查线上问题时能看出是不是数据太旧。字符集统一utf8mb4。如果只存中文发卡行名utf8也够但bin_info这种表未来可能关联商户名称、外卡卡组织描述直接utf8mb4避免二次迁移。导入后的校验我用下面三条SQLSELECT COUNT(*) FROM bin_info; SELECT bin, COUNT(*) FROM bin_info GROUP BY bin HAVING COUNT(*) 1 LIMIT 10; SELECT * FROM bin_info WHERE bin NOT REGEXP ^[0-9]{6}$;第一条看总数对照Excel的行数。第二条查重复BIN如果存在你要判断是继续保留还是清洗掉。第三条查非6位数字看有没有隐藏的换行或空格被带进来。全部通过后再随机抽三个真实卡号验证SELECT * FROM bin_info WHERE bin LEFT(6228480402564890018, 6);这一步必须做否则整张表只是“看起来正确”。另外说一句MySQL安装和配置层面的事。如果本地还没装MySQL直接下载官网的社区版安装包一路默认配置即可。BIN表数据量很小不需要调什么性能参数唯一建议是把默认字符集在my.cnf里改成utf8mb4否则建表时容易忘记指定。4. 避坑/排查BIN 数据导入 MySQL 后续与查询中的常见问题4.1 现象导入后卡号前缀变成了科学计数法从Excel导出的CSV里BIN列如果曾经被Excel处理过可能会以数值格式存储导出时变成622848但Python读取后再写CSV某些场景下会出现“622848.0”或“6.23E05”。导入MySQL后bin字段的值就变成622848.0等值查询时匹配不上。原因是Excel的单元格格式在保存CSV时无法保留“文本”属性数字格式的列会按数值序列化。只要Excel里该列被重新编辑过精度和格式就可能被改掉。解决方法是清洗脚本里强制把bin列转成字符串并做正则校验。我在3.2里写的str.replace和str.match就是专门处理这个问题的。如果已经导坏了就把bin_info清空重新导入不要手动在MySQL里UPDATE十几万行逐条改不现实。4.2 现象同一张卡查出多条BIN记录用完整卡号前6位去查bin_info返回两行或多行而且卡类型或发卡行不一样。最常见的原因是这份BIN表里同一BIN对应多个卡产品比如信用卡和借记卡共用一个BIN段或者普卡和金卡共用一个BIN。原因在于2020年之后的银联BIN表为了覆盖更多卡产品不再保证BIN唯一。处理方式不是去重而是根据业务需要加一个优先级字段比如线上查询时优先返回贷记卡记录或者优先返回卡等级最高的记录。SQL上可以用ORDER BY加LIMIT但更稳妥的是在应用层指定规则SELECT * FROM bin_info WHERE bin LEFT(6228480402564890018, 6) ORDER BY FIELD(card_type, 贷记卡, 借记卡, 预付费卡) LIMIT 1;FIELD函数只是临时排序真正要长期用建表时就应该加一个priority TINYINT列手动维护优先级。4.3 现象查询直接全表扫描几万行也慢BIN表几万行在MySQL里跑SELECT * FROM bin_info WHERE bin LIKE 6228%速度看起来还行但一旦并发上来或者表里数据扩大到几十万行问题就暴露。原因是用LIKE前置通配符写BIN匹配索引用不上只能全表扫。正确写法是定长前缀匹配先取完整卡号前6位再做等值查询。用LEFT函数取前缀不会让索引失效因为LEFT(6228480402564890018, 6)的结果是常量等于直接查WHERE bin 622848。SELECT * FROM bin_info WHERE bin LEFT(6228480402564890018, 6);如果还是慢用EXPLAIN看一下是否走到索引。大概率问题是3.3里没建idx_bin索引补上就好。4.4 现象MySQL 8.0 导入报错 Invalid default value for updated_at把DDL里的updated_at定义成DATE DEFAULT 0000-00-00然后导入时MySQL 8.0报Invalid default value。原因是MySQL 8.0默认开启了sql_mode里的NO_ZERO_IN_DATE和NO_ZERO_DATE不允许日期字段使用全零默认值。解决方法有三个。一是改sql_mode不建议全局改会影响其他表。二是把DDL里的默认值改成NULL我在3.3里写的DATE DEFAULT NULL就是为避开这个坑。三是用DEFAULT (CURRENT_DATE)但这会让字段语义变成“插入日期”而不是“数据版本日期”容易误导后人。这个坑在MySQL 5.7时代不存在很多人迁移到8.0后翻车。所有从旧环境带过来的建表语句都要检查日期字段的默认值。4.5 现象Excel 里看到的是脱敏 BIN导入后对不上有的整理版Excel会把BIN列处理成“622848******”或者只给前4位加星号这种脱敏数据用于展示没问题但没法直接当查询字典用。如果你拿到的文件是这种导入后查询永远匹配不上不只是索引的问题。原因在于数据源在导出时就已经丢失了精确的BIN信息。解决方法是回到原始渠道找未脱敏版本。上头传下来的Excel如果只剩脱敏版可以用公开的卡BIN规则兜底比如银联标准卡62开头但发卡行和卡类型无法精确判断风控规则就得放宽。另一个操作是拿完整卡号反推反推出来的BIN可以补录但只能补到你能接触到的样本范围覆盖不全。尽量避免在这种数据上做二次整理脱敏版再怎么清洗都补不回来信息。最好的做法是把原始Excel归档脱敏版只用于展示和文档。5. 用 BIN 表做卡 BIN 识别一次 JOIN 查询与查询速度验证5.1 卡 BIN 识别的最短 SQL定长前缀匹配而不是 LIKE 开头线上识别卡BIN核心SQL只有一行。我见过最靠谱的写法是传入完整卡号用LEFT取前6位直接点查SELECT bin, card_type, bank_name, card_level FROM bin_info WHERE bin LEFT(6228480402564890018, 6) LIMIT 1;LEFT函数只截取字符串不影响索引使用。很多新手写成WHERE bin LIKE 622848%这会让MySQL放弃索引。BIN是定长6位做等值匹配是最优解。如果卡号长度可能不足6位应用层先做校验避免传入空字符串。5.2 给 BIN 表加一个覆盖索引让 10 万级 BIN 查询稳定在毫秒级日常查询只需要返回卡类型、发卡行、卡组织、卡等级为了不回头查主键直接建覆盖索引ALTER TABLE bin_info ADD INDEX idx_bin_cover (bin, card_type, bank_name, card_level);执行EXPLAIN看效果EXPLAIN SELECT bin, card_type, bank_name, card_level FROM bin_info WHERE bin 622848;如果key列显示idx_bin_coverExtra列显示Using index说明查询不需要回表。10万行数据量下这种查询每次都在毫秒级压力测试并发2000也不会有问题。等值匹配加上覆盖索引是BIN表查询性能的兜底方案。5.3 一张表搞定多个卡组织同时维护银联、Visa、Mastercard 的字段约定结合标题里的“银联官方发布”定位这张表以银联卡为主但完全可以在同一张表里维护外卡数据。我通常在清洗阶段加上card_org列银联卡统一填“银联”Visa和Mastercard的数据单独追加BIN转成6位定长格式后直接插入。查询时只在这一列上做等值过滤不影响BIN索引。SELECT bin, card_org, card_type, bank_name FROM bin_info WHERE card_org VISA AND bin 411111 LIMIT 1;如果外卡BIN和银联BIN存在重复区间加一个org优先级的判断避免一条卡号查出两条。这里我的习惯是永远不在card_org上做前置通配符匹配M卡、V卡字段只要统一成标准名等值查询就够。最后一个习惯是备份。BIN表从Excel导入MySQL后原始文件不要丢按照日期归档。每次拿到新版本先对比行数再随机验证真实卡号最后再替换线上表。替换时我习惯用RENAME TABLE做原子切换先导入bin_info_new确认无误后把旧表改名备份再把新表切到线上这样线上查询不会出现几秒钟的数据空洞。希望这份从Excel到MySQL的BIN库落地路径能帮到你少走我踩过的弯路。本文还有配套的精品资源点击获取
网站建设高端定制企业官网