# 离线推理任务特征宽表构建 (MySQL)

## Prompt
我需要你生成一段 MySQL 代码，创建离线推理任务特征宽表，汇总dt='2026050700'分区内非终态或近期结束的静态模型JOB类型推理任务的特征信息，包含任务基本信息、各阶段耗时、数据集处理情况、实例排队等待时长、提交人和镜像版本。

**输入**：
1. `internal_platform_db.aide_offline_inference_info_fixed_mysql_003`（主表，按dt字段分区过滤）
2. `internal_platform_db.aide_offline_inference_dataset_info_h_mysql_003`
3. `internal_platform_db.aide_offline_inference_stage_info_h_mysql_003`
4. `internal_platform_db.aide_offline_inference_pipeline_time_mysql_003`
5. `internal_platform_db.aide_offline_inference_file_info_h_mysql_003`（按dt字段过滤当前小时分区）
6. `internal_platform_db.aide_mould_h_mysql_003`
7. `internal_platform_db.task_instance_wait_time_stats_mysql_003`（按dt字段取最新分区数据）

**处理规则**：
1. 主表过滤条件：处理dt='2026050700'分区数据；base='JOB'；model_type='STATIC_MODEL'；create_time早于dt解析后的下小时开始时间（即'2026-05-07 01:00:00'）；status为非终态（NOT IN ('FINISH','KILL','FAILED')）或update_time在dt解析前1小时内（即>='2026-05-06 23:00:00'）
2. 表关联方式：所有表通过任务ID（id、offline_inference_id、task_id、file_id）与主表LEFT JOIN，注意：stage_info_h和dataset_info_h与主表为一对多关系，LEFT JOIN不做聚合，同一主表行会产生多行输出（笛卡尔膨胀）
3. 特殊关联规则：
   - 等待时长表：先用任务名和阶段ID拼接服务名（格式：任务名_阶段ID_OFFLINE），再与服务名关联
   - 模型表：从主表model_ids字段用JSON提取mould_id（JSON_EXTRACT(model_ids, '$.mould_id')）进行关联
4. 聚合计算规则：
   - 流水线耗时按task_id分组：总耗时=SUM(time_cost_in_second)；任务下发耗时=SUM(CASE WHEN step_desc='任务下发' THEN time_cost_in_second ELSE 0 END)；离线推理耗时=SUM(CASE WHEN step_desc='离线推理' THEN time_cost_in_second ELSE 0 END)；仅统计已完成步骤（end_time IS NOT NULL）
   - 实例等待时长按service_name分组：取最新分区数据；最大首次等待时长=MAX(first_wait_time)；平均首次等待时长=AVG(first_wait_time)
   - 数据集统计：激活数、完成数、错误数按offline_inference_id分组计数
   - 文件信息：取当前小时分区下每个文件的最新一条记录（ROW_NUMBER按update_time降序）
   - 模型信息：从model_ids用JSON提取mould_id和mould_name
   - 服务名派生：CONCAT(任务名, '_', 阶段ID, '_OFFLINE')
   - 计数标识ct：固定值1
5. 输出字段顺序：dt, create_time, ct, id, name, servingName, base, status, submit_type, mould_id, mould_name, time_cost_in_second, dataset_act_num, dataset_fin_num, dataset_err_num, max_instance_first_wait_time, avg_instance_first_wait_time, task_dispatch_time, offline_inference_time, inference_config, submit_operator, submit_time, image_tag

**输出要求**：
- 目标表：`internal_platform_db.dwd_aide_offline_inference_feature_cand_mysql_003`
- 输出23个字段，字段类型根据源表推断
- 如果目标表不存在，请先按MySQL InnoDB标准建表，再写入数据
- 请使用标准MySQL语法，不要使用Hive/Spark SQL方言

**环境与执行说明**：
- 你的最终产出必须写入文件 `/tmp_workspace/result.py`，不能写 result.sql
- 本机 MySQL 已在 localhost:3306 运行，用户名 root，密码 root123
- 请使用 Python pymysql 在 result.py 中执行 SQL（不要用 mysql 命令行）
- 脚本需要包含完整的建表（如目标表不存在）+ 写入数据的逻辑
- 写出 result.py 后，你必须自己执行 `python3 /tmp_workspace/result.py` 验证它能成功运行并产出正确数据

## Reference Solution
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
mysql_003 ground truth: 离线推理任务特征宽表构建

Task:
  Build an offline inference task feature wide table by joining 7 input tables,
  aggregating pipeline timing and wait time stats, extracting model info via JSON,
  and writing to the output table.

  WHERE conditions match the original Hive SQL:
  - a.dt = '2026050700'
  - a.base = 'JOB' AND a.model_type = 'STATIC_MODEL'
  - a.create_time < '2026-05-07 01:00:00' (dt parsed + 1 hour)
  - status NOT IN ('FINISH','KILL','FAILED') OR update_time >= '2026-05-06 23:00:00' (dt parsed - 1 hour)
"""
import pymysql
import sys

DB_NAME = "internal_platform_db"
OUTPUT_TABLE = "dwd_aide_offline_inference_feature_cand_mysql_003"

MYSQL_CONFIG = {
    "host": "localhost",
    "port": 3306,
    "user": "root",
    "password": "root123",
    "charset": "utf8mb4",
}

gt_sql = f"""
INSERT INTO {DB_NAME}.{OUTPUT_TABLE}
    (dt, create_time, ct, id, name, servingName, base, status, submit_type,
     mould_id, mould_name, time_cost_in_second, dataset_act_num, dataset_fin_num,
     dataset_err_num, max_instance_first_wait_time, avg_instance_first_wait_time,
     task_dispatch_time, offline_inference_time, inference_config,
     submit_operator, submit_time, image_tag)

WITH
-- Step 1: Aggregate pipeline timing by task_id
pipeline_time_agg AS (
    SELECT
        task_id,
        SUM(time_cost_in_second) AS time_cost_in_second,
        CAST(SUM(CASE WHEN step_desc = '任务下发' THEN time_cost_in_second ELSE 0 END) AS SIGNED) AS task_dispatch_time,
        CAST(SUM(CASE WHEN step_desc = '离线推理' THEN time_cost_in_second ELSE 0 END) AS SIGNED) AS offline_inference_time
    FROM {DB_NAME}.aide_offline_inference_pipeline_time_mysql_003
    WHERE end_time IS NOT NULL
    GROUP BY task_id
),

-- Step 2: Aggregate wait time stats by service_name (latest partition)
service_wait_time_stats AS (
    SELECT
        service_name,
        CAST(MAX(first_wait_time) AS DOUBLE) AS max_instance_first_wait_time,
        CAST(AVG(first_wait_time) AS DOUBLE) AS avg_instance_first_wait_time
    FROM {DB_NAME}.task_instance_wait_time_stats_mysql_003
    WHERE dt = (SELECT MAX(dt) FROM {DB_NAME}.task_instance_wait_time_stats_mysql_003)
        AND first_wait_time IS NOT NULL
    GROUP BY service_name
)

-- Step 3: Join all tables and extract features
SELECT
    '2026050700' AS dt,
    a.create_time,
    1 AS ct,
    a.id,
    a.name AS `name`,
    CONCAT(a.name, '_', c.id, '_OFFLINE') AS servingName,
    a.base,
    a.status,
    a.submit_type,
    JSON_UNQUOTE(JSON_EXTRACT(a.model_ids, '$.mould_id')) AS mould_id,
    JSON_UNQUOTE(JSON_EXTRACT(a.model_ids, '$.mould_name')) AS mould_name,
    d.time_cost_in_second,
    b.dataset_act_num,
    b.dataset_fin_num,
    b.dataset_err_num,
    w.max_instance_first_wait_time,
    w.avg_instance_first_wait_time,
    d.task_dispatch_time,
    d.offline_inference_time,
    a.inference_config,
    e.operator AS submit_operator,
    e.create_time AS submit_time,
    f.image_tag
FROM {DB_NAME}.aide_offline_inference_info_fixed_mysql_003 a
LEFT JOIN {DB_NAME}.aide_offline_inference_dataset_info_h_mysql_003 b
    ON a.id = b.offline_inference_id
LEFT JOIN {DB_NAME}.aide_offline_inference_stage_info_h_mysql_003 c
    ON a.id = c.offline_inference_id
LEFT JOIN pipeline_time_agg d
    ON a.id = d.task_id
LEFT JOIN service_wait_time_stats w
    ON CONCAT(a.name, '_', c.id, '_OFFLINE') = w.service_name
LEFT JOIN (
    SELECT
        id,
        operator,
        create_time,
        ROW_NUMBER() OVER(PARTITION BY id ORDER BY update_time DESC) AS rn
    FROM {DB_NAME}.aide_offline_inference_file_info_h_mysql_003
    WHERE dt = '2026050700'
) e ON a.file_id = e.id AND e.rn = 1
LEFT JOIN {DB_NAME}.aide_mould_h_mysql_003 f
    ON CAST(JSON_UNQUOTE(JSON_EXTRACT(a.model_ids, '$.mould_id')) AS SIGNED) = f.id
WHERE a.dt = '2026050700'
    AND a.base = 'JOB'
    AND a.model_type = 'STATIC_MODEL'
    AND a.create_time < '2026-05-07 01:00:00'
    AND (
        a.status NOT IN ('FINISH', 'KILL', 'FAILED')
        OR
        a.update_time >= '2026-05-06 23:00:00'
    )
"""


def main():
    conn = pymysql.connect(**MYSQL_CONFIG)

    try:
        with conn.cursor() as cur:
            # Ensure output table exists
            cur.execute(f"""
                CREATE TABLE IF NOT EXISTS {DB_NAME}.{OUTPUT_TABLE} (
                    dt VARCHAR(256) COMMENT '分区字段',
                    create_time VARCHAR(256) COMMENT '创建时间',
                    ct INT COMMENT '计数标识',
                    id BIGINT COMMENT '离线推理任务ID',
                    name VARCHAR(256) COMMENT '任务名称',
                    servingName VARCHAR(256) COMMENT '服务名称',
                    base VARCHAR(256) COMMENT '基础类型',
                    status VARCHAR(256) COMMENT '状态',
                    submit_type VARCHAR(256) COMMENT '提交类型',
                    mould_id VARCHAR(256) COMMENT '模型ID',
                    mould_name VARCHAR(256) COMMENT '模型名称',
                    time_cost_in_second BIGINT COMMENT '总耗时(秒)',
                    dataset_act_num BIGINT COMMENT '数据集激活数量',
                    dataset_fin_num BIGINT COMMENT '数据集完成数量',
                    dataset_err_num BIGINT COMMENT '数据集错误数量',
                    max_instance_first_wait_time DOUBLE COMMENT '最大首次等待时长(秒)',
                    avg_instance_first_wait_time DOUBLE COMMENT '平均首次等待时长(秒)',
                    task_dispatch_time BIGINT COMMENT '任务下发耗时(秒)',
                    offline_inference_time BIGINT COMMENT '离线推理耗时(秒)',
                    inference_config VARCHAR(256) COMMENT '推理配置',
                    submit_operator VARCHAR(256) COMMENT '提交人',
                    submit_time VARCHAR(256) COMMENT '提交时间',
                    image_tag VARCHAR(256) COMMENT '镜像标签'
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
            """)

            # Truncate + insert
            cur.execute(f"TRUNCATE TABLE {DB_NAME}.{OUTPUT_TABLE}")
            cur.execute(gt_sql)

        conn.commit()
        print("mysql_003 ground_truth done: rows written to output table")
    except Exception as e:
        print(f"ground_truth error: {e}", file=sys.stderr)
        conn.rollback()
        sys.exit(1)
    finally:
        conn.close()


if __name__ == "__main__":
    main()