Databricks里用PySpark统计指定表和字段中各字段的空值、空字符串或零值比例

python 复制代码
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, coalesce, trim, when, lit, sum
from pyspark.sql.types import StringType, NumericType

# 在 Databricks 中,spark 会话通常已经存在,无需重新创建
# 如果需要显式创建,使用:
# spark = SparkSession.builder.getOrCreate()

# 配置参数
database_name = "your_database"  # 替换为实际数据库名
result_list = []

# 获取数据库中的所有表和视图(包括 Delta 表)
tables = spark.catalog.listTables(database_name)

for table in tables:
    table_name = table.name
    full_table_name = f"{database_name}.{table_name}"
    
    try:
        # 读取表或视图(Delta 表和普通表均可)
        df = spark.table(full_table_name)
        
        # 快速判断是否为空表,避免不必要的缓存
        total_count = df.count()
        if total_count == 0:
            continue
        
        # 缓存数据以便多次引用(实际上我们只需扫描一次,缓存并非必需)
        df.cache()
        
        # 构建所有字段的聚合表达式(一次性计算)
        agg_exprs = []
        field_meta = []  # 用于记录每个字段的类型和名称
        
        for field in df.schema.fields:
            col_name = field.name
            col_type = field.dataType
            
            if isinstance(col_type, StringType):
                # 字符串类型:统计 null 或 trim 后为空字符串
                modified_col = trim(coalesce(col(col_name), lit("")))
                condition = (modified_col == lit(""))
                count_expr = sum(when(condition, 1).otherwise(0)).alias(f"cnt_{col_name}")
            elif isinstance(col_type, NumericType):
                # 数值类型:统计 null 或零值
                modified_col = coalesce(col(col_name), lit(0))
                condition = (modified_col == lit(0))
                count_expr = sum(when(condition, 1).otherwise(0)).alias(f"cnt_{col_name}")
            else:
                # 其他类型:仅统计 null
                condition = col(col_name).isNull()
                count_expr = sum(when(condition, 1).otherwise(0)).alias(f"cnt_{col_name}")
            
            agg_exprs.append(count_expr)
            field_meta.append((col_name, str(col_type)))
        
        # 执行一次聚合,获取所有字段的统计值
        stats_row = df.agg(*agg_exprs).collect()[0]
        
        # 整理结果
        for col_name, col_type in field_meta:
            stat_count = stats_row[f"cnt_{col_name}"]
            percentage = round((stat_count / total_count) * 100, 2) if total_count > 0 else 0.0
            result_list.append((
                database_name,
                table_name,
                col_name,
                col_type,
                stat_count,
                total_count,
                float(percentage)
            ))
        
        df.unpersist()  # 释放缓存
        
    except Exception as e:
        print(f"Error processing table {table_name}: {str(e)}")
        continue

# 创建结果 DataFrame
result_columns = [
    "database_name",
    "table_name",
    "column_name",
    "column_type",
    "stat_count",
    "total_rows",
    "percentage"
]

result_df = spark.createDataFrame(result_list, result_columns)

# 显示结果
result_df.show(truncate=False)

# 可选:将结果保存到 Delta 表
# result_df.write.format("delta").mode("overwrite").saveAsTable("your_audit_table")

主要改动说明

原代码 修改后 原因
显式创建 SparkSession 并启用 Hive 直接使用 Databricks 内置 spark Databricks 已预配置,无需额外初始化
对每个字段分别执行 df.agg() 构建所有字段聚合表达式,一次 df.agg() 减少表扫描次数,大幅提高性能
缓存 df 后多次扫描 缓存后仅一次聚合扫描 配合优化,降低缓存开销
spark.catalog.listTables() 同样使用,能返回表和视图 无需修改,原生支持 Delta 表和视图
异常处理与结果收集 保持不变 逻辑通用

注意事项

  • 数据库名称 :根据实际环境修改 database_name,如果使用默认数据库,可设为 "default"
  • 视图spark.table() 可以读取持久化视图,但临时视图不会出现在 listTables 中。如需处理临时视图,可额外指定名称列表。
  • 性能 :对于大型表,df.count() 和聚合仍会触发一次完整扫描,这是必要的。如果表极大,可考虑分批处理或使用抽样估算。
  • Delta 表:Delta 表在 Catalog 中通常显示为普通表名,读取方式与 Hive 表一致,无需特殊处理。
相关推荐
默_笙1 天前
🍙 给每个请求过安检:FastAPI 是怎么把校验写进类型注解的
python
qq_426003961 天前
启动playwright录制codegen生成自动化测试脚本
python·自动化
虎头金猫1 天前
4K 视频总卡在公网带宽?用 N1 + OpenList 把网盘播放链路重新理顺
运维·服务器·网络·python·容器·beautifulsoup·pandas
长沙三为智能科技1 天前
家政小程序开发从0到上线:五阶段交付流程与验收清单
python
伞伞悦读1 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
只睡四小时1 天前
Canvas 弹道联机实战:700 行 + 固定时间步长
python·websocket·html5·游戏开发·canvas
AI职业加油站1 天前
AI智能体应用工程师证书:政策红利下的职业新风口
大数据·运维·人工智能·学习·职场发展
奇思妙想聪明勤奋的小羊1 天前
DeepAgents第5章:子Agent 与上下文隔离—让 Agent学会委派
人工智能·python·学习·语言模型
西木莉1 天前
数据仓库概述
数据仓库
lpfasd1231 天前
2026年第38周GitHub趋势周报
python·科技·github