实训4 ETL构建中间层

《豆瓣图书数据ETL实训手册》

------基于MySQL的中间层数据构建与清洗实践


一、实训目标

本实训通过真实场景中的图书数据(豆瓣图书表 book_info),完成以下核心任务:

  1. 构建中间层表 ,作为数据清洗与转换的桥梁;
    保护原始数据不被破坏
    统一杂乱数据格式
    清洗脏数据与异常值
    降低上层分析难度
  2. 实现 ETL 抽取(Extract)、转换(Transform)、加载(Load) 流程;
  3. 解决字段拆分中的常见问题,如:
  4. 多个分隔符 / 的嵌套结构;
  5. 价格字段含非数字字符(如"元");
  6. 掌握正则表达式、字符串函数、类型转换、空值处理等核心技术。

二、背景与数据说明

数据来源

  • 表名:doubanbooks.book_info
  • 字段说明:
字段名 类型 说明
id INT 图书唯一标识
book_name VARCHAR(500) 书名
book_info TEXT 信息字段,内容整合了作者、出版社、出版日期、价格等,格式为:作者 / 出版社 / 出版日期 / 价格

✅示例数据:

"余华 / 北京十月文艺出版社 / 2018-05-01 / 39.00元"


三、实训环境准备

软件与工具要求

项目 要求
数据库系统 MySQL 8.0+
客户端工具 Navicat / DBeaver / MySQL Workbench / 命令行
权限要求 CREATE, INSERT, SELECT, DROP 权限
表空间 已创建数据库 doubanbooks

💡 提示:若未创建数据库,请先执行:

sql 复制代码
CREATE DATABASE IF NOT EXISTS doubanbooks CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE doubanbooks;

四、操作步骤详解


步骤 1:创建中间层表

目的:保留原始字段 + 新增拆分后的独立字段,便于后续分析与建模。

sql 复制代码
-- 1. 删除已存在的中间表(防止冲突)
DROP TABLE IF EXISTS doubanbooks.book_info_mid;

-- 2. 创建中间层表
CREATE TABLE doubanbooks.book_info_mid (
    id INT PRIMARY KEY,
    book_name VARCHAR(500) NOT NULL,
    book_info TEXT NOT NULL,
    author VARCHAR(255),
    publisher VARCHAR(255),
    publish_date VARCHAR(50),
    price DECIMAL(10,2)
);

⚠️ 说明:

  • id 为主键,保证唯一;
  • book_name 保留原文;
  • book_info 保留原始字符串用于溯源;
  • author, publisher, publish_date, price 分别为拆分后字段;
  • price 使用 DECIMAL(10,2) 精确存储金额。

步骤 2:ETL 抽取、拆分与插入

核心逻辑 :从 book_info 中提取并清洗信息,使用 SUBSTRING_INDEXREGEXP_REPLACE 实现拆分与数据清洗。

sql 复制代码
INSERT INTO doubanbooks.book_info_mid (
    id, book_name, book_info,
    author, publisher, publish_date, price
)
SELECT
    id,
    book_name,
    book_info,

    -- 拆分作者(倒数第3个/前的内容)
    TRIM(
        SUBSTRING_INDEX(
            book_info,
            '/',
            LENGTH(book_info) - LENGTH(REPLACE(book_info, '/', '')) - 2
        )
    ) AS author,

    -- 拆分出版社(倒数第3段,取第一部分)
    TRIM(
        SUBSTRING_INDEX(
            SUBSTRING_INDEX(book_info, '/', -3),
            '/',
            1
        )
    ) AS publisher,

    -- 拆分出版日期(倒数第2段,取第一部分)
    TRIM(
        SUBSTRING_INDEX(
            SUBSTRING_INDEX(book_info, '/', -2),
            '/',
            1
        )
    ) AS publish_date,

    -- 修复价格:移除"元"字符,过滤非数字,强制转为小数
    CAST(
        NULLIF(
            TRIM(
                REGEXP_REPLACE(
                    REPLACE(SUBSTRING_INDEX(book_info, '/', -1), '元', ''),
                    '[^0-9.]', ''
                )
            ),
            ''
        ) AS DECIMAL(10,2)
    ) AS price

FROM doubanbooks.book_info;

拆解关键函数说明

函数 作用说明
REPLACE(str, old, new) 替换"元"为""(空),便于清洗
REGEXP_REPLACE(str, pattern, replacement) 使用正则删除所有非数字和小数点字符
SUBSTRING_INDEX(str, delim, n) 按分隔符分割字符串,取第 n 项(支持负数,从右数)
LENGTH(str) - LENGTH(REPLACE(str, '/', '')) 计算"/"的数量
TRIM() 去除首尾空格
NULLIF(expr, '') 若字段为空字符串,则返回 NULL,避免插入空值
CAST(... AS DECIMAL(10,2)) 强制类型转换,保证价格数据一致性

步骤 3:验证中间层结果

sql 复制代码
-- 查看前10条数据,确认清洗效果
SELECT * FROM doubanbooks.book_info_mid LIMIT 10;
预期输出样例:
id book_name book_info author publisher publish_date price
1 《活着》 余华 / 北京十月文艺出版社 / 2018-05-01 / 39.00元 余华 北京十月文艺出版社 2018-05-01 39.00
2 《许三观卖血记》 余华 / 上海文艺出版社 / 2012-07-01 / 45.00元 余华 上海文艺出版社 2012-07-01 45.00

✅ 检查点:

  • 所有字段均拆分正确;
  • price 无"元"符号,为标准小数;
  • NULLIF 保证了异常值(如空价格)被安全处理;
  • 所有字段不为空(即未插入非法值)。

五、异常处理与调试技巧

常见错误及解决方案

问题 原因 解决方案
Error: Cannot convert string to decimal 价格字段含"元"或特殊符号(如"¥"、空格) 使用 REPLACE + REGEXP_REPLACE 清洗
结果有 NULL 但应有值 分隔符不一致(如没有 / 或多余空格) 使用 TRIM + LENGTH(REPLACE(...)) 优化计数逻辑
拆分不准确(如作者错位) SUBSTRING_INDEX 偏移错误 通过观察原始数据确定层级,例如 -2 是日期,-1 是价格
数据类型转换失败 输入内容为纯字母或 NaN NULLIF(..., '') 防止空字符串传入 CAST

六、后续建议(进阶拓展)

  1. 数据质量检查脚本(增加)

    sql 复制代码
    -- 统计各字段空值情况
    SELECT 
        SUM(CASE WHEN author IS NULL THEN 1 ELSE 0 END) AS null_author,
        SUM(CASE WHEN publisher IS NULL THEN 1 ELSE 0 END) AS null_publisher,
        SUM(CASE WHEN publish_date IS NULL THEN 1 ELSE 0 END) AS null_publish_date,
        SUM(CASE WHEN price IS NULL THEN 1 ELSE 0 END) AS null_price
    FROM doubanbooks.book_info_mid;
  2. 创建索引提升查询效率

    sql 复制代码
    CREATE INDEX idx_publisher ON doubanbooks.book_info_mid(publisher);
    CREATE INDEX idx_price ON doubanbooks.book_info_mid(price);
  3. 构建维度表(用于BI分析)

    • dim_book:book_id, book_name, author, publish_date
    • dim_publisher:publisher_name, region
    • fact_book_sales:book_id, price, sales_count

附:完整SQL脚本包

将以下内容保存为 .sql 文件,可直接运行。

sql 复制代码
-- ===============================
-- 豆瓣图书ETL实训完整脚本
-- 作者:实训指导组
-- 时间:2025年4月
-- ===============================

-- 1. 创建数据库(如未存在)
CREATE DATABASE IF NOT EXISTS doubanbooks CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE doubanbooks;

-- 2. 创建中间层表
DROP TABLE IF EXISTS book_info_mid;
CREATE TABLE book_info_mid (
    id INT PRIMARY KEY,
    book_name VARCHAR(500) NOT NULL,
    book_info TEXT NOT NULL,
    author VARCHAR(255),
    publisher VARCHAR(255),
    publish_date VARCHAR(50),
    price DECIMAL(10,2)
);

-- 3. ETL抽取与加载
INSERT INTO book_info_mid (
    id, book_name, book_info,
    author, publisher, publish_date, price
)
SELECT
    id,
    book_name,
    book_info,

    TRIM(
        SUBSTRING_INDEX(
            book_info,
            '/',
            LENGTH(book_info) - LENGTH(REPLACE(book_info, '/', '')) - 2
        )
    ) AS author,

    TRIM(
        SUBSTRING_INDEX(
            SUBSTRING_INDEX(book_info, '/', -3),
            '/',
            1
        )
    ) AS publisher,

    TRIM(
        SUBSTRING_INDEX(
            SUBSTRING_INDEX(book_info, '/', -2),
            '/',
            1
        )
    ) AS publish_date,

    CAST(
        NULLIF(
            TRIM(
                REGEXP_REPLACE(
                    REPLACE(SUBSTRING_INDEX(book_info, '/', -1), '元', ''),
                    '[^0-9.]', ''
                )
            ),
            ''
        ) AS DECIMAL(10,2)
    ) AS price

FROM book_info;

-- 4. 查看结果
SELECT * FROM book_info_mid LIMIT 10;

-- 5. (可选)数据质量检查
SELECT 
    SUM(CASE WHEN author IS NULL THEN 1 ELSE 0 END) AS null_author,
    SUM(CASE WHEN publisher IS NULL THEN 1 ELSE 0 END) AS null_publisher,
    SUM(CASE WHEN publish_date IS NULL THEN 1 ELSE 0 END) AS null_publish_date,
    SUM(CASE WHEN price IS NULL THEN 1 ELSE 0 END) AS null_price
FROM book_info_mid;

结语

通过本实训,你已掌握了从原始杂乱数据 → 结构化中间表的全过程,是构建企业级数据仓库的重要基石。

相关推荐
苛子4 小时前
ETL与ELT的区别与选择:企业数据集成方案深度对比
数据仓库·etl
清水白石0085 小时前
Python 日志采集到数据仓库 ETL 流程设计实战:从基础语法到生产级可靠运维
数据仓库·python·etl
2501_933329555 小时前
企业舆情处置系统设计与实践:Infoseek数字公关AI中台技术解析
数据仓库·人工智能·重构·架构·数据库开发
莫叫石榴姐21 小时前
字节广告数开一面 | 实习
大数据·数据仓库·面试
2501_933329551 天前
AI驱动媒介宣发:Infoseek舆情系统的技术架构与公关实战
数据仓库·人工智能·重构·数据库开发
heimeiyingwang1 天前
【架构实战】数据仓库分层架构(ODS/DWD/DWS/ADS)
数据仓库·架构
APguantou1 天前
NCRE-三级数据库技术-第14章-数据仓库与数据挖掘
数据库·数据仓库·数据挖掘
IOFsmLtzR2 天前
cursor cli 执行 ETL 数据同步任务探索
数据仓库·etl
achi0103 天前
Apache Beam 详细入门指南
etl·批处理·流处理·apache beam·dataflow 模型·pcollection·批流融合