DataWorks 体验笔记 :MaxCompute 用 Python 对数据进行二次处理

一. 前言

前面2篇已经体验了 DataWorks 的简单使用,但是一些复杂的业务通过纯 SQL 是没有办法处理的。

这个时候就需要进行代码处理了 ,通常像 Flink 支持 Java 工具包上传的方式对是数据进行处理 ,另外很多大数据工具类会考虑通过脚本语言进行处理 。Python 就是一个很好的实现方式。

另外注意一下 ,这里为了方便 ,把 MaxCompute 和 DataWorks 是看成一体了 ,MaxCompute 也可以是一个独立的组件,这里没有单独区分开。

二. MaxCompute 中使用 Python

首先官方文档里面对这些都有详细的描述 :@ 官方文档

使用方式 :

  • S1 : 在数据开发模块中 ,创建对应的 PyODPS 就行 ,分为 PyODPS2PyODPS3 ,分别对应着 python2 / python3
  • S2 : 原生集成了 odps 包 ,以及一些常用包 ,可以像写 Python 代码一样去写 Ops

三. 实现一个简单的 Python 处理数据的流程

在 DataWorks 里面,是通过 PyODPS 类型来进行 Python 层面的数据处理 。 我这边基于昨天的操作来替换其中的一个关键节点 ,来实现类似的功能 :

3.1 原始 SQL 实现方式

sql 复制代码
CREATE TABLE IF NOT EXISTS self_user_info_1d (
  uid STRING COMMENT '用户ID',
  username STRING COMMENT '用户名称',
  email STRING COMMENT '用户',
  password STRING COMMENT '密码 ',
  first_name STRING COMMENT '名称',
  last_name STRING COMMENT '名称',
  full_name STRING COMMENT '全名',
  created_at STRING COMMENT '年龄段',
  updated_at STRING COMMENT '星座'
)


COMMENT '用户行为分析案例-用户画像数据'
PARTITIONED BY (
  dt STRING  COMMENT '业务日期, 格式yyyymmdd'
)
LIFECYCLE 7;

INSERT OVERWRITE TABLE self_user_info_1d 
SELECT id,
    username,
    email,
    password,
    first_name,
    last_name,
    first_name AS full_name,
    created_at,
    updated_at 
FROM 
    users where pt = 20250313;


SELECT * FROM self_user_info_1d

3.2 改 PyODPS 实现

python 复制代码
from odps import ODPS  
from odps.models import Schema, Column, Partition  

# 板块:创建表  
try:  
    # 定义表结构  
    columns = [  
        Column(name='uid', type='string', comment='用户ID'),  
        Column(name='username', type='string', comment='用户名称'),  
        Column(name='email', type='string', comment='用户'),  
        Column(name='password', type='string', comment='密码'),  
        Column(name='first_name', type='string', comment='名称'),  
        Column(name='last_name', type='string', comment='名称'),  
        Column(name='full_name', type='string', comment='全名'),  
        Column(name='created_at', type='string', comment='年龄段'),  
        Column(name='updated_at', type='string', comment='星座')  
    ]  
    partitions = [Partition(name='dt', type='string', comment='业务日期, 格式yyyymmdd')]  
    schema = Schema(columns=columns, partitions=partitions)  

    # 创建表  
    table = o.create_table('self_user_info_1d_002', schema, comment='用户行为分析案例-用户画像数据', lifecycle=7)  
    print(f"INFO: 表创建成功 - 表名: {table.name}")  

except Exception as e:  
    print(f"ERROR: 创建表失败 - 错误信息: {str(e)}")  

# 板块:插入数据  
try:  
    # 执行SQL插入操作  
    insert_sql = """  
    INSERT OVERWRITE TABLE self_user_info_1d_002 PARTITION(dt='20250315')  
    SELECT id, username, email, password, first_name, last_name, first_name AS full_name,  
           created_at, updated_at   
    FROM users WHERE pt = '20250315';  
    """  
    o.execute_sql(insert_sql)  
    print("INFO: 数据插入成功")  



except Exception as e:  
    print(f"ERROR: 数据插入失败 - 错误信息: {str(e)}")  

3.3 深入 ,对多种数据进行处理

python 复制代码
from odps import ODPS  
from odps.models import Schema, Column, Partition  
import random  

# 板块:创建表  
try:  
    # 定义表结构  
    columns = [  
        Column(name='uid', type='string', comment='用户ID'),  
        Column(name='username', type='string', comment='用户名称'),  
        Column(name='email', type='string', comment='用户'),  
        Column(name='password', type='string', comment='密码'),  
        Column(name='first_name', type='string', comment='名称'),  
        Column(name='last_name', type='string', comment='名称'),  
        Column(name='full_name', type='string', comment='全名'),  
        Column(name='roles', type='string', comment='用户角色'),  # 添加角色字段  
        Column(name='created_at', type='string', comment='年龄段'),  
        Column(name='updated_at', type='string', comment='星座')  
    ]  
    partitions = [Partition(name='dt', type='string', comment='业务日期, 格式yyyymmdd')]  
    schema = Schema(columns=columns, partitions=partitions)  

    # 创建表  
    o.delete_table('self_user_info_1d_002', if_exists=True) 
    table = o.create_table('self_user_info_1d_002', schema, comment='用户行为分析案例-用户画像数据', lifecycle=7)  
    print(f"INFO: 表创建成功 - 表名: {table.name}")  

except Exception as e:  
    print(f"ERROR: 创建表失败 - 错误信息: {str(e)}")  

# 板块:查询数据并处理  
try:  
    # 查询用户数据  
    select_sql_users = "SELECT id, username, email, password, first_name, last_name, created_at, updated_at FROM users WHERE pt = '20250315';"  
    records = []  
    
    with o.execute_sql(select_sql_users).open_reader() as reader:  
        for record in reader:  
            # 模拟查询用户角色  
            user_id = record['id']  
            # 伪代码:根据用户ID获取角色  
            # 假设我们有一个字典来模拟角色数据  
            simulated_user_roles = {  
                '1': ['admin', 'editor'],  
                '2': ['viewer'],  
                '3': ['editor', 'contributor'],  
                '4': ['admin', 'viewer', 'editor'],  
                '5': ['contributor', 'viewer'],  
                # 其他用户ID及其角色...  
            }  
            roles = simulated_user_roles.get(user_id, [])  # 获取角色,默认为空列表  
            
            # 随机选择一个角色,如果没有角色则设为'无角色'  
            selected_role = random.choice(roles) if roles else '无角色'  

            # 在这里进行数据处理,例如添加后缀和整合角色信息  
            processed_record = {  
                'uid': record['id'],  
                'username': f"{record['username']}_suffix",  # 添加后缀  
                'email': record['email'],  
                'password': record['password'],  
                'first_name': record['first_name'],  
                'last_name': record['last_name'],  
                'full_name': f"{record['first_name']} {record['last_name']}",  # 组合名称  
                'roles': selected_role,  # 随机选择的角色  
                'created_at': record['created_at'],  
                'updated_at': record['updated_at']  
            }  
            records.append(processed_record)  

    print("INFO: 数据查询和处理完成")  

except Exception as e:  
    print(f"ERROR: 数据查询失败 - 错误信息: {str(e)}")  

# 板块:写入处理后的数据  
try:  
    # 准备写入数据  
    write_records = [  
        [record['uid'], record['username'], record['email'], record['password'],   
         record['first_name'], record['last_name'], record['full_name'],   
         record['roles'],  # 写入随机选择的角色信息  
         record['created_at'], record['updated_at']]  
        for record in records  
    ]  

    # 写入数据到新表  
    o.write_table('self_user_info_1d_002', write_records, partition='dt=20250315', create_partition=True)  
    print("INFO: 数据写入成功")  

except Exception as e:  
    print(f"ERROR: 数据写入失败 - 错误信息: {str(e)}")  

3.4 执行结果

3.5 阶段总结

四. MaxCompute PyODPS 涉及到的核心概念

  • 项目空间 : 项目空间是MaxCompute的基本组织单元 ,用于隔离和管理不同的数据开发和治理活动
    • 备注 :与一组相关的资源,如表、函数、任务、调度等
    • 功能 :资源隔离 / 权限管理 / 协作开发 / 独立调度
  • Schema : 项目空间内的一个逻辑分组,用于组织和管理表
    • 备注 :Schema 可以看作是表的命名空间,用于对表进行分类和管理
    • 作用 :表的分类 / 权限管理 / 命名冲突解决
  • 表(Table) : 表是数据存储的基本单元,由行和列组成
    • 备注 :表用于存储具体的数据,是数据开发和分析的核心对象
    • 作用 :数据存储 / 数据操作 / 数据共享
  • SQL : 这个就不用说了 ,基本上都是差不多的
  • 任务实例 : 任务实例是 DataWorks 中任务调度和执行的基本单元
    • 备注 : 一个任务实例代表了一次具体的任务执行
    • 作用 : 任务调度 / 任务执行 / 任务监控 / 任务管理
  • 函数 : 函数是 DataWorks 中用于封装和重用的代码块
    • 备注 : 函数可以是系统内置函数,也可以是用户自定义函数
    • 作用 : 代码复用 / 功能扩展 / 简化查询

五. 常见用法整理

python 复制代码
from odps import ODPS
from odps.models import Schema, Column, Partition
import random

# # 板块 : 运行环境 - 项目空间查询
try:
    # 获取特定项目空间  
    project = o.get_project('DF_cs_624101')  
    print(f"INFO: 获取特定项目空间成功 - 项目名称: {project.name}")  

    # 获取当前项目空间  
    current_project = o.get_project()  
    print(f"INFO: 获取当前项目空间成功 - 项目名称: {current_project.name}")  

    # 验证项目空间是否存在  
    project_name = 'DF_cs_624101'  
    is_exist = o.exist_project(project_name)  
    print(f"INFO: 验证项目空间是否存在完成 - 项目名称: {project_name}, 是否存在: {is_exist}")  

except Exception as e:
    print(f"ERROR: 运行环境获取异常{e}",e)


# # 板块 : Schema 环境准备
try:  

    #- 列举所有Schema  
    schemas = o.list_schemas()  
    if not schemas:  
        print("INFO: 当前没有任何Schema")  
    else:  
        for schema in schemas:  
            print(f"INFO: Schema名称: {schema.name}")  
            if schema.name == 'my_new_schema':
                o.delete_schema('my_new_schema', if_exists=True)  

    #- 创建Schema  
    schema_name = 'my_new_schema'  
    schema = o.create_schema(schema_name)  
    print(f"INFO: 创建Schema成功 - Schema名称: {schema_name}")  

    #- 删除Schema  
    delete_schema_name = 'my_new_schema'  
    o.delete_schema(delete_schema_name, if_exists=True)  
    print(f"INFO: 删除Schema成功 - Schema名称: {delete_schema_name}")  

except Exception as e:  
    print(f"ERROR: Schema操作失败 - 错误信息: {str(e)}")  


# 板块 : Schema 创建表操作
try:  
    # 定义表结构(此处定义了Columns 和 分区信息)  
    columns = [  
        Column(name='id', type='bigint', comment='the column'),  
        Column(name='age', type='double', comment='the column2'),
        Column(name='name', type='string', comment='the column3')  
    ]  
    partitions = [Partition(name='pt', type='string', comment='the partition')]
    schema = Schema(columns=columns, partitions=partitions)
    # 打印列信息  
    for column in columns:  
        print(f"INFO 列 name='{column.name}', type='{column.type}', comment='{column.comment}'")  
    
    # 打印分区信息  
    for partition in partitions:  
        print(f"INFO 分区 name='{partition.name}', type='{partition.type}', comment='{partition.comment}'")  
    
    # 打印字段类型信息  
    for column in schema.columns:  
        print(f"INFO 类型 name='{column.name}', type='{column.type}'")  

    # schema 创建表
    o.delete_table('my_new_table', if_exists=True) 
    table = o.create_table('my_new_table', schema)   
    print(f"INFO: 表创建完成")   
    print(f"INFO: 表的shema :{table.table_schema}")   

    # #- 列举指定Schema下的所有表  
    # tables = o.list_tables(schema=table.schema)  
    # if not tables:  
    #     print(f"INFO: Schema {specified_schema} 下没有任何表")  
    # else:  
    #     for table in tables:  
    #         print(f"INFO: 表名称: {table.name}")  

  
except Exception as e:  
    print(f"ERROR: Schema操作失败 - 错误信息: {str(e)}")  


# 模块 : 插入数据
try:  
   # 获取表对象  
    table = o.get_table('my_new_table')  

    records = []  
    for _ in range(5):  
        id = random.randint(100000, 999999)  # 随机生成id  
        age = random.randint(20, 60)       # 随机生成年龄  
        name = f"test_{id}"                   # 生成测试name  
        records.append([id, age, name])  

    # 插入数据  
    o.write_table(table, records, partition='pt=test', create_partition=True)  
    print(f"INFO: 插入数据成功 - {len(records)} 条记录") 

    # 通过read_table方法读取数据  
    for record in o.read_table('my_new_table',partition='pt=test'):  
        print(record)  

    # 通过表对象读取数据  
    table = o.get_table('my_new_table')  
    with table.open_reader(partition='pt=test') as reader:  
        for record in reader:  
            print(record)  

    # 删除表中的所有数据  
    # table.truncate()  


except Exception as e:  
    print(f"ERROR: Table 操作失败 - 错误信息: {str(e)}")  



# 板块 :SQL 数据处理篇
try:  

    # 执行 SQL 语句  
    sql = "select id,age,name from my_new_table where pt = 'test' "  
    result = o.execute_sql(sql)  
    print(f"INFO: SQL 执行成功 - SQL: {sql}")  

    # 执行 SQL 语句  
    sql = "select id,age,name from my_new_table where pt = 'test' "
    with o.execute_sql(sql).open_reader() as reader:  
        for record in reader:  
            print(record)  

except Exception as e:  
    print(f"ERROR: SQL 操作失败 - 错误信息: {str(e)}")  

六. 使用注意事项

6.1 Schema 默认没有开启

@ 操作指南

6.2 处理Debug

在执行的过程中 ,可以通过单个节点的执行 ,也可以线性处理 , 其中比较麻烦的是确认代码写的对不对。

一般需要计算的数据最终会放在 MaxCompute 里面 ,这些可以直接在 MaxCompute 控制台里面查看 :

  • 其他的则可以通过操作日志查看到一些线索 (比如这里就是字段有问题)

总结 :

到了这一步 ,基本上常见的业务都可以通过 Python 进行二次处理 ,从而达到预期的需求。

后面如果有空 ,会做一些更复杂的案例,总结一下。

最后的最后 ❤️❤️❤️👇👇👇

相关推荐
uzong1 小时前
技术故障复盘模版
后端
GetcharZp1 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
桦说编程2 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
IT毕设实战小研2 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi2 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
阿华的代码王国3 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
Jimmy3 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
喂完待续4 小时前
Apache Hudi:数据湖的实时革命
大数据·数据仓库·分布式·架构·apache·数据库架构
AntBlack4 小时前
不当韭菜V1.1 :增强能力 ,辅助构建自己的交易规则
后端·python·pyqt
青云交4 小时前
Java 大视界 -- 基于 Java 的大数据可视化在城市交通拥堵治理与出行效率提升中的应用(398)
java·大数据·flink·大数据可视化·拥堵预测·城市交通治理·实时热力图