实训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;

结语

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

相关推荐
喻师傅3 小时前
Apache Hudi 概述:从 Parquet 更新难题到数据湖 Upsert
大数据·数据仓库·big data
宽海智能仓储物流3 天前
航发自动化立体库异构系统改造:宽海智能30天联调技术全解析
运维·数据仓库·自动化
2601_962284173 天前
基于Apache Cassandra与Python的数据建模及ETL实践
python·nosql·etl·数据建模·apachecassandra
程序员小羊!4 天前
医疗数据仓库ODS层敏感信息防护实战:7大关键策略
数据仓库
欢迎来到祖安!4 天前
企业微信 API 接口能力介绍:支持消息收发、图片发送、表情互动、联系人管理等多场景应用
java·前端·数据仓库·spring cloud·企业微信
想吃披萨5 天前
Kettle下载地址
etl
RestCloud6 天前
飞书多维表格落地新思路:ETL做数据导入,豆包做业务智能分析
数据仓库·飞书·etl·数据传输·api对接·豆包
AgentMaster6 天前
元数据、血缘、质量、安全四大模块能力拆解,数据治理方案对比:4 种技术路线深度评测
大数据·数据库·数据仓库·人工智能·原型模式
Gl�ria6 天前
Hadoop Hive 和 YARN 的关系
数据仓库·hive·hadoop
MC丶科7 天前
软考架构师90天冲刺|DAY44·Redis高级应用
数据库·数据仓库·redis·缓存·oracle·容器·规格说明书