0

0

SQL数据清洗的实用方法:如何在SQL中处理缺失与异常数据

雪夜

雪夜

发布时间:2025-08-05 17:09:01

|

793人浏览过

|

来源于php中文网

原创

识别缺失值需使用is null、trim()=''或nullif函数统一处理null和空字符串;2. 处理缺失值可采用coalesce填充默认值、update设置统计值(如平均值)或删除缺失行;3. 检测异常值可通过范围检查(如年龄合法性)、格式匹配(如正则或like)、逻辑校验(如日期顺序)和频率分析;4. 修正异常值常用trim/upper等函数标准化、case语句条件替换、cast类型转换;5. 利用窗口函数(如row_number、avg over)实现分组填充和去重,结合cte提升复杂清洗逻辑的可读性;6. 组合使用nullif与coalesce可高效处理多类型缺失数据,配合索引优化确保清洗效率。整个数据清洗过程需结合业务规则迭代执行,确保数据准确完整。

SQL数据清洗的实用方法:如何在SQL中处理缺失与异常数据

SQL数据清洗,说白了,就是在数据库里把那些不规矩、不完整的、甚至是错得离谱的数据找出来,然后让它们变得规规矩矩、完整可用。这过程里,我们主要面对的就是缺失值和异常值。我的经验是,SQL本身提供了相当多的武器来应对这些挑战,从简单的过滤到复杂的窗口函数,都能派上用场。核心思路就是利用SQL的强大查询和更新能力,识别问题数据,然后根据业务规则进行修正或剔除。

解决方案

处理SQL中的缺失与异常数据,通常可以归结为识别、分析和修正三个阶段。具体到SQL操作,我们主要依赖以下几种策略:

对于缺失数据(通常表现为NULL值或空字符串):

  • 识别: 使用
    IS NULL
    IS NOT NULL
    来筛选出含有或不含有NULL值的行。对于空字符串,则用
    TRIM(column_name) = ''
    LENGTH(TRIM(column_name)) = 0
    (不同数据库函数可能略有差异,如SQL Server的
    LEN
    )。
  • 处理:
    • 填充默认值: 使用
      COALESCE(column_name, default_value)
      ISNULL(column_name, default_value)
      (SQL Server特有)在查询时替换NULL。如果需要持久化到表中,则使用
      UPDATE table_name SET column_name = default_value WHERE column_name IS NULL;
    • 删除: 如果缺失数据占比很小且对分析影响不大,或者无法有效填充,可以直接删除含有缺失值的行:
      DELETE FROM table_name WHERE column_name IS NULL;
      或在查询时
      SELECT * FROM table_name WHERE column_name IS NOT NULL;
    • 基于统计值填充: 例如用平均值、中位数填充。这通常需要子查询或窗口函数来计算统计值:
      UPDATE table_name SET column_name = (SELECT AVG(column_name) FROM table_name WHERE column_name IS NOT NULL) WHERE column_name IS NULL;
      (这只是一个简单示例,实际应用中可能需要按组填充)。

对于异常数据(包括格式不一致、超出范围、逻辑错误等):

  • 识别:
    • 范围检查:
      WHERE numeric_column < min_value OR numeric_column > max_value
    • 格式检查:
      WHERE string_column NOT LIKE '%[0-9]%'
      (检查非数字字符),或者使用正则表达式函数(如PostgreSQL的
      ~
      或 MySQL的
      REGEXP
      )进行更复杂的模式匹配。
    • 逻辑检查:
      WHERE start_date > end_date
      WHERE quantity < 0
    • 频率分析:
      SELECT column_name, COUNT(*) FROM table_name GROUP BY column_name HAVING COUNT(*) < threshold;
      找出出现频率异常低的“离群”值。
  • 处理:
    • 标准化/修正:
      • 字符串:
        UPDATE table_name SET string_column = TRIM(LOWER(string_column));
        REPLACE(string_column, 'old_text', 'new_text')
      • 类型转换:
        CAST(string_column AS INT)
        CONVERT(VARCHAR, date_column, 120)
      • 条件修正: 使用
        CASE
        语句根据不同条件更新数据:
        UPDATE table_name
        SET status = CASE
            WHEN status = 'active ' THEN 'Active'
            WHEN status = 'inactive' THEN 'Inactive'
            ELSE status
        END;
    • 隔离或标记: 不直接修正,而是将异常数据标记出来,或将其移动到单独的“问题数据”表中进行后续分析。

在我看来,数据清洗不是一蹴而就的,它更像是一个迭代的过程,需要结合业务理解和数据探索。

如何识别和处理SQL中的缺失值?

识别SQL中的缺失值,说实话,比很多人想象的要复杂一点。我们通常认为的缺失值就是

NULL
,但很多时候,空字符串
''
或者只有空格的字符串
' '
也代表着信息缺失。我习惯把它们都当作缺失值来处理,毕竟它们都不能提供有效信息。

识别缺失值: 最直接的方式当然是

IS NULL

-- 查找所有订单金额为空的记录
SELECT *
FROM Orders
WHERE OrderAmount IS NULL;

但如果数据录入不规范,可能会有空字符串:

-- 查找所有客户名是空字符串或只有空格的记录
SELECT *
FROM Customers
WHERE TRIM(CustomerName) = '' OR CustomerName IS NULL;
-- 或者更简洁地利用NULLIF,将空字符串转为NULL再判断
SELECT *
FROM Customers
WHERE NULLIF(TRIM(CustomerName), '') IS NULL;

NULLIF
这个函数非常实用,它会在两个表达式相等时返回
NULL
,否则返回第一个表达式。这对于统一处理空字符串和
NULL
非常有效。

处理缺失值: 处理方式的选择,很大程度上取决于数据的重要性和业务场景。

  1. 直接过滤掉: 这是最简单粗暴的方法,如果缺失值占比很小,或者你只关心完整数据,那么直接在查询中排除它们是效率最高的。

    -- 只统计有有效订单金额的订单
    SELECT SUM(OrderAmount)
    FROM Orders
    WHERE OrderAmount IS NOT NULL;
  2. 填充默认值: 当缺失值需要被某个固定值替代时,

    COALESCE
    是我的首选。它会返回参数列表中第一个非
    NULL
    的表达式。

    -- 查询订单金额,如果为空则显示为0.00
    SELECT OrderID, COALESCE(OrderAmount, 0.00) AS DisplayAmount
    FROM Orders;
    
    -- 更新表中NULL值为特定默认值
    UPDATE Orders
    SET OrderAmount = 0.00
    WHERE OrderAmount IS NULL;

    对于字符串,可以填充为 'N/A' 或 '未知':

    UPDATE Products
    SET ProductDescription = 'N/A'
    WHERE NULLIF(TRIM(ProductDescription), '') IS NULL;
  3. 基于统计值填充: 比如用平均值、中位数来填充数值型缺失值,或者用出现频率最高的值来填充类别型缺失值。这种方式在SQL中实现起来会稍微复杂一些,通常需要子查询或窗口函数。

    -- 假设我们要用产品的平均价格填充缺失价格
    UPDATE Products
    SET Price = (SELECT AVG(Price) FROM Products WHERE Price IS NOT NULL)
    WHERE Price IS NULL;

    但要注意,这种全局平均值填充可能不够精确。更高级的做法是按产品类别进行分组平均填充,这就要用到窗口函数了:

    -- 假设按 ProductCategory 分组填充平均价格
    WITH AvgPrices AS (
        SELECT
            ProductID,
            Price,
            ProductCategory,
            AVG(Price) OVER (PARTITION BY ProductCategory) AS CategoryAvgPrice
        FROM Products
    )
    UPDATE P
    SET P.Price = AP.CategoryAvgPrice
    FROM Products P
    JOIN AvgPrices AP ON P.ProductID = AP.ProductID
    WHERE P.Price IS NULL;

    这有点像在说:“如果这个产品的价格不知道,那就看看它同类产品的平均价格是多少,然后填上去。”这通常比简单地填一个全局平均值要合理得多。

针对异常数据,SQL有哪些实用的检测与修正技巧?

异常数据,这东西真是让人头疼。它不像是

NULL
那样一眼就能看出来,它可能只是格式不对,或者值超出了常理。检测异常数据,我觉得更像是在数据里玩“大家来找茬”。

检测异常数据:

动态WEB网站中的PHP和MySQL:直观的QuickPro指南第2版
动态WEB网站中的PHP和MySQL:直观的QuickPro指南第2版

动态WEB网站中的PHP和MySQL详细反映实际程序的需求,仔细地探讨外部数据的验证(例如信用卡卡号的格式)、用户登录以及如何使用模板建立网页的标准外观。动态WEB网站中的PHP和MySQL的内容不仅仅是这些。书中还提到如何串联JavaScript与PHP让用户操作时更快、更方便。还有正确处理用户输入错误的方法,让网站看起来更专业。另外还引入大量来自PEAR外挂函数库的强大功能,对常用的、强大的包

下载
  1. 范围检测: 这是最常见的。比如,年龄不可能为负数,库存量不可能为负数,日期不可能在未来太远。

    -- 查找年龄小于0或大于150的用户
    SELECT UserID, Age
    FROM Users
    WHERE Age < 0 OR Age > 150;
    
    -- 查找订单日期在未来或发货日期早于订单日期的订单
    SELECT OrderID, OrderDate, ShipDate
    FROM Orders
    WHERE OrderDate > GETDATE() OR ShipDate < OrderDate; -- GETDATE()是SQL Server,其他数据库可能用 NOW() 或 CURRENT_DATE
  2. 格式与类型检测: 有时候数字字段存了字母,或者日期格式五花八门。

    -- 查找看起来不像有效电话号码的记录(假设电话号码都是数字)
    SELECT CustomerID, PhoneNumber
    FROM Customers
    WHERE PhoneNumber LIKE '%[^0-9]%'; -- 查找包含非数字字符的电话号码
    -- 对于更复杂的模式,可能需要正则表达式(如果数据库支持)
    -- WHERE PhoneNumber ~ '[^0-9]' -- PostgreSQL示例

    如果一个文本字段应该只有特定的几个值(比如 '男', '女'),那就可以这样:

    -- 查找性别字段不规范的记录
    SELECT UserID, Gender
    FROM Users
    WHERE Gender NOT IN ('男', '女', 'Male', 'Female'); -- 考虑到多种可能规范
  3. 频率与离群值检测: 某些数值可能远远偏离平均水平,或者某个分类值出现频率极低,这可能就是录入错误。

    -- 查找销售额远超平均水平的订单(简单离群点检测)
    SELECT OrderID, SaleAmount
    FROM Sales
    WHERE SaleAmount > (SELECT AVG(SaleAmount) * 3 FROM Sales); -- 销售额超过平均值3倍的订单

    当然,更严谨的离群点检测会用到统计学方法,比如Z-score或IQR,但这些在纯SQL中实现起来会比较复杂,通常需要多步操作或结合编程语言。

修正异常数据:

  1. 标准化与格式统一:

    TRIM
    ,
    LOWER
    ,
    UPPER
    ,
    REPLACE
    是我的老朋友。

    -- 统一产品名称大小写并去除前后空格
    UPDATE Products
    SET ProductName = TRIM(UPPER(ProductName));
    
    -- 将地址中的“路”统一替换为“路”
    UPDATE Addresses
    SET Street = REPLACE(Street, '大道', '路'); -- 假设“大道”是错的,应为“路”
  2. 条件修正:

    CASE
    语句是修正异常数据的瑞士军刀,它能根据不同的条件执行不同的更新逻辑。

    -- 修正年龄异常值:将负数年龄设为NULL,过大年龄设为100
    UPDATE Users
    SET Age = CASE
        WHEN Age < 0 THEN NULL
        WHEN Age > 150 THEN 100
        ELSE Age
    END;
    
    -- 修正不规范的性别表示
    UPDATE Users
    SET Gender = CASE
        WHEN Gender IN ('M', 'man') THEN 'Male'
        WHEN Gender IN ('F', 'woman') THEN 'Female'
        ELSE Gender
    END;
  3. 类型转换与验证: 当数据类型不匹配时,

    CAST
    CONVERT
    就派上用场了。

    -- 尝试将一个字符串列转换为日期,如果转换失败(异常数据),则设为NULL
    UPDATE Transactions
    SET TransactionDate = TRY_CAST(TransactionDateString AS DATE); -- TRY_CAST是SQL Server特有,转换失败返回NULL
    -- 其他数据库可能需要更复杂的逻辑,例如先判断是否是有效日期格式

    说实话,在SQL里处理所有异常情况有点像在玩拼图,你需要把各种函数和逻辑碎片拼起来,才能得到你想要的结果。有时候,我甚至觉得这种“修修补补”的工作比从头写一个新查询更有挑战性。

如何利用SQL函数和高级特性提升数据清洗效率?

提升数据清洗效率,不仅仅是让查询跑得更快,更重要的是让清洗逻辑更清晰、更可维护。我发现,善用SQL的一些高级特性,能让这个过程变得优雅很多。

  1. 窗口函数: 这是我个人最喜欢也觉得最强大的工具之一。它能让你在分组内部进行计算,而不需要聚合整个组,这在数据清洗中尤其有用。

    • 重复数据处理:
      ROW_NUMBER()
      是去除重复数据的利器。
      -- 查找并保留每个CustomerID下最新的一条订单记录
      WITH RankedOrders AS (
          SELECT
              OrderID,
              CustomerID,
              OrderDate,
              ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY OrderDate DESC) as rn
          FROM Orders
      )
      SELECT OrderID, CustomerID, OrderDate
      FROM RankedOrders
      WHERE rn = 1;

      这比

      GROUP BY
      配合
      MAX()
      MIN()
      更灵活,因为你可以保留整行数据。

    • 组内填充: 之前提到的按类别平均值填充,窗口函数就能很自然地实现。
      -- 假设我们想用部门的平均工资来填充缺失的工资
      UPDATE Employees
      SET Salary = (
          SELECT AVG(e2.Salary)
          FROM Employees e2
          WHERE e2.DepartmentID = Employees.DepartmentID AND e2.Salary IS NOT NULL
      )
      WHERE Salary IS NULL;
      -- 使用窗口函数可以更简洁地在子查询中完成
      WITH EmployeeSalaries AS (
          SELECT
              EmployeeID,
              Salary,
              DepartmentID,
              AVG(Salary) OVER (PARTITION BY DepartmentID) AS AvgDeptSalary
          FROM Employees
      )
      UPDATE E
      SET E.Salary = ES.AvgDeptSalary
      FROM Employees E
      JOIN EmployeeSalaries ES ON E.EmployeeID = ES.EmployeeID
      WHERE E.Salary IS NULL AND ES.AvgDeptSalary IS NOT NULL; -- 确保有平均值可供填充
    • 序列检查:
      LAG()
      LEAD()
      可以比较当前行与前一行或后一行的数据,这在检查时间序列数据(比如连续的异常值)时非常有用。
  2. 公共表表达式 (CTEs -

    WITH
    子句): 当清洗逻辑变得复杂时,CTEs 能极大地提高SQL的可读性和可维护性。你可以把复杂的清洗步骤拆分成多个逻辑块,每个块处理一部分数据,然后像乐高积木一样组合起来。

    WITH CleanedNames AS (
        SELECT
            UserID,
            TRIM(UPPER(FirstName)) AS CleanedFirstName,
            TRIM(UPPER(LastName)) AS CleanedLastName
        FROM Users
    ),
    ValidatedAges AS (
        SELECT
            UserID,
            CASE
                WHEN Age BETWEEN 0 AND 120 THEN Age
                ELSE NULL -- 将异常年龄设为NULL
            END AS ValidAge
        FROM Users
    )
    SELECT
        CN.UserID,
        CN.CleanedFirstName,
        CN.CleanedLastName,
        VA.ValidAge
    FROM CleanedNames CN
    JOIN ValidatedAges VA ON CN.UserID = VA.UserID;

    这样,每一步清洗的目的都非常明确,排查问题也方便很多。我经常用CTE来构建一个“中间数据集”,这样可以避免写一堆嵌套的子查询,那看起来简直是噩梦。

  3. NULLIF
    COALESCE
    的组合拳:
    这两个函数一起用,能非常优雅地处理多种形式的缺失。

    -- 将空字符串和只有空格的字符串都视为NULL,并用'未知'填充
    SELECT COALESCE(NULLIF(TRIM(CustomerName), ''), '未知') AS NormalizedCustomerName
    FROM Customers;

    这行代码,在我看来,就是数据清洗中的一个“小确幸”,因为它简洁而强大。

  4. 索引和查询优化: 虽然这不直接是“清洗”方法,但一个效率低下的清洗查询,在面对大数据量时会让人崩溃。确保你的

    WHERE
    子句和
    JOIN
    条件涉及的列有合适的索引。有时候,将复杂的清洗逻辑分步执行,或者在ETL工具中完成部分清洗,反而比在一个巨型SQL查询中完成所有事情更高效。

总的来说,SQL在数据清洗方面提供了相当多的灵活性和功能。关键在于理解你的数据,然后选择最适合的工具。这就像是修补一件旧家具,你不能指望一个锤子解决所有问题,你需要各种工具,并且知道什么时候用哪一个。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

727

2023.10.12

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

327

2023.10.27

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

350

2024.02.23

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

1242

2024.03.06

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

360

2024.03.06

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

820

2024.04.07

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

581

2024.04.29

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

423

2024.04.29

俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

158

2026.01.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
SQL 教程
SQL 教程

共61课时 | 3.6万人学习

SQL优化与排查(MySQL版)
SQL优化与排查(MySQL版)

共26课时 | 2.3万人学习

MySQL索引优化解决方案
MySQL索引优化解决方案

共23课时 | 2.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号