大数据组件(一)快速入门调治组件Airflow

打印 上一主题 下一主题

主题 841|帖子 841|积分 2523

大数据组件(一)快速入门调治组件Airflow



  • DolphinScheduler和 Airflow是数据范畴很盛行的两款开源使命调治体系。DolphinScheduler 致力于用可视化的方式去完成一个 DAG 工作流,而 Airflow 则想的是用类似于编程的方式完成一个 DAG 工作流。

    • Apache DolphinScheduler 可以直接在页面上完成对 DAG 工作流的开发。
    • 而 Apache Airflow 须要提交一个 Python 文件到背景服务器上,由 Apache Airflow 去剖析这个 Python 文件,进而天生一个 DAG 工作流。

  • 我们,本日以几个简单的案例,快速了解下基于python的调治组件Airflow。

    • 官网地点:https://airflow.incubator.apache.org/

1 Airflow的安装(单机版)

这里,我们利用conda举行安装。
首先,创建情况:
  1. conda create -n airflow  python=3.8 -y
  2. conda activate airflow
复制代码
然后,利用pip举行安装,须要注意的是:安装时候,须要指定约束文件,否则很容易会出现依赖冲突。
  1. CONSTRAINT_URL="https://raw.githubusercontent.com/apache/airflow/constraints-2.7.2/constraints-3.8.txt"
  2. pip install "apache-airflow==2.7.2" --constraint "${CONSTRAINT_URL}"
复制代码
我们这里利用MySQL数据库举行配置:
  1. # 3、查询版本
  2. (airflow) [root@centos01 ~]# airflow version
  3. 2.7.2
  4. (airflow) [root@centos01 airflow]# pwd
  5. /root/airflow
  6. # 4、数据库初始化、改为Mysql数据库
  7. (airflow) [root@centos01 airflow]# pip install mysql-connector-python
  8. (airflow) [root@centos01 airflow]# airflow db init
  9. (airflow) [root@centos01 airflow]# vim airflow.cfg
  10. #sql_alchemy_conn = sqlite:root/airflow/airflow.db
  11. sql_alchemy_conn = mysql+mysqlconnector://root:123456@127.0.0.1:3306/airflow_db
  12. # 再次初始化
  13. (airflow) [root@centos01 airflow]# airflow db init
  14. # 报错如下:
  15. sqlalchemy.exc.ProgrammingError: (mysql.connector.errors.ProgrammingError) 1067 (42000): Invalid default value for 'updated_at'
  16. [SQL:
  17. CREATE TABLE dataset (
  18.         id INTEGER NOT NULL AUTO_INCREMENT,
  19.         uri VARCHAR(3000) COLLATE latin1_general_cs NOT NULL,
  20.         extra JSON NOT NULL,
  21.         created_at TIMESTAMP(6) NOT NULL,
  22.         updated_at TIMESTAMP(6) NOT NULL,
  23.         is_orphaned BOOL NOT NULL DEFAULT '0',
  24.         CONSTRAINT dataset_pkey PRIMARY KEY (id)
  25. )]
  26. (Background on this error at: https://sqlalche.me/e/14/f405)
  27. # 解决方案
  28. 原因:字段 'update_at' 为 timestamp类型,取值范围是:1970-01-01 00:00:00 到 2037-12-31 23:59:59(UTC +8 北京时间从1970-01-01 08:00:00 开始),而这里默认给了空值,所以导致失败。
  29. 推荐修改mysql存储时间戳格式:
  30. mysql> set GLOBAL sql_mode ='STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'
  31. 重启MySQL会造成参数失效,推荐将参数写入到配置文件my.cnf中。
  32. # 在[mysqld]添加下面两行
  33. [root@centos01 apps]# vim /etc/my.cnf
  34. [mysqld]
  35. skip_ssl
  36. sql_mode=STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
  37. # 重启mysql
  38. [root@centos01 apps]# systemctl restart mysqld
  39. # 5、添加admin用户
  40. (airflow) [root@centos01 airflow]# airflow users create \
  41. > --username admin \
  42. > --firstname Undo \
  43. > --lastname Undo \
  44. > --role Admin \
  45. > --email undo@163.com
  46. # 输入密码
  47. [2024-12-27T16:28:17.107+0800] {manager.py:555} INFO - Added Permission %s to role %s
  48. Password:
  49. # 6、启动airflow web服务和调度器, 启动后浏览器访问http://centos01:8080
  50. (airflow) [root@centos01 airflow]# airflow webserver -p 8080 -D
  51. (airflow) [root@centos01 airflow]# airflow scheduler -D
复制代码

上图是默认安装时候,会出现的两条信息:


  • Airflow利用SQLite数据库,建议改为Mysql数据库,我们已经修改;
  • SequentialExecutor按顺序运行使命实例,不能并行执行使命,我们下面修改;
  • 注意:右上角默认是UTC时间,我们肯定要点击修改时区,同时要修改airflow.cfg文件。
  1. # 7、修改airflow的配置文件
  2. (airflow) [root@centos01 airflow]# vim airflow.cfg
  3. # 可以使用官方推荐的几种执行器,也可以自定义。这里我们选择本地执行器即可。
  4. [core]
  5. # 存放python调度脚本的目录
  6. dags_folder = /root/airflow/dags
  7. # The executor class that airflow should use. Choices include
  8. # ``SequentialExecutor``, ``LocalExecutor``, ``CeleryExecutor``, ``DaskExecutor``,
  9. # ``KubernetesExecutor``, ``CeleryKubernetesExecutor`` or the
  10. # full import path to the class when using a custom executor.
  11. executor = LocalExecutor
  12. # 修改时区信息
  13. # default_ui_timezone = UTC
  14. default_ui_timezone = Asia/Shanghai
  15. # default_timezone = utc
  16. default_timezone = Asia/Shanghai
  17. # 创建目录,用来存放python调度脚本
  18. (airflow) [root@centos01 airflow]# mkdir dags
复制代码
我们把airflow启动、关闭封装为脚本:
  1. (airflow) [root@centos01 ~]# vim af.sh
  2. (airflow) [root@centos01 ~]# chmod +x af.sh
  3. #!/bin/bash
  4. case $1 in
  5. "start"){
  6.    
  7.     echo " --------start airflow-------"
  8.     conda activate airflow;airflow webserver -p 8080 -D;airflow scheduler -D;conda deactivate
  9. };;
  10. "stop"){
  11.    
  12.     echo " --------stop airflow-------"
  13.     ps -ef | egrep 'scheduler|airflow-webserver' | grep -v grep | awk '{print $2}' | xargs kill -15
  14. };;
  15. esac
  16. # 然后重启
  17. (airflow) [root@centos01 ~]# ./af.sh stop
  18. --------stop airflow-------
  19. (airflow) [root@centos01 ~]# ./af.sh start
  20. --------start airflow-------
  21. # 查看是否启动成功
  22. (airflow) [root@centos01 ~]# ps -ef | grep 8080
  23. root       1935      1  1 20:55 ?        00:00:00 /opt/apps/minoconda3/envs/airflow/bin/python /opt/apps/minoconda3/envs/airflow/bin/airflow webserver -p 8080 -D
  24. root       2618   1630  0 20:55 pts/0    00:00:00 grep --color=auto 8080
  25. # 访问http://centos01:8080/页面,页面上会出现很多官方示例
  26. # 当然,我们也能通过命令查看
  27. (airflow) [root@centos01 ~]# airflow dags list
  28. dag_id                                                  | filepath                                                                                                                             | owner   | paused
  29. ========================================================+======================================================================================================================================+=========+=======
  30. dataset_consumes_1                                      | /opt/apps/minoconda3/envs/airflow/lib/python3.8/site-packages/airflow/example_dags/example_datasets.py                               | airflow | True  
  31. dataset_consumes_1_and_2                                | /opt/apps/minoconda3/envs/airflow/lib/python3.8/site-packages/airflow/example_dags/example_datasets.py                               | airflow | True  
  32. dataset_consumes_1_never_scheduled                      | /opt/apps/minoconda3/envs/airflow/lib/python3.8/site-packages/airflow/example_dags/example_datasets.py                               | airflow | True  
  33. ......
复制代码
2 Airflow入门案例

2.1 BashOperator



  • Airflow中最紧张的还是各种Operator,其允许天生特定类型的使命,这个使命在实例化时称为DAG中的使命节点,所有的Operator均派生自BaseOparator,而且继承了许多属性和方法。



  • BashOperator紧张执行bash脚本或下令。
  • Operator参考:https://airflow.apache.org/docs/
  1. (airflow) [root@centos01 shell_jobs]# cat first_shell.sh
  2. #!/bin/bash
  3. dt=$1
  4. echo "==== execute first shell ===="
  5. echo "---- first : time is ${dt}"
  6. (airflow) [root@centos01 shell_jobs]# cat second_shell.sh
  7. #!/bin/bash
  8. dt=$1
  9. echo "==== execute second shell ===="
  10. echo "---- second : time is ${dt}"
复制代码


  • python脚本如下(可以用VS Code远程连接),注意:要放在配置的目次下:

  1. #!/usr/bin/python
  2. from datetime import datetime, timedelta
  3. from airflow import DAG
  4. from airflow.operators.bash import BashOperator
  5. default_args = {
  6.     'owner':'root',
  7.     'start_date':datetime(2024, 12, 27, 22, 0),   # 调度开始时间
  8.     'retries': 1,                                 # 失败重试次数
  9.     'retry_delay': timedelta(minutes=5)           # 失败重试间隔
  10. }
  11. dag = DAG(
  12.     dag_id='MyShellTest',
  13.     default_args=default_args,
  14.     schedule_interval='*/15 * * * *'    # 每15min运行一次
  15. )
  16. first=BashOperator(
  17.     task_id='first',
  18.     # 脚本路径建议写绝对路径
  19.     bash_command='sh /root/shell_jobs/first_shell.sh %s'%datetime.now().strftime("%Y-%m-%d-%H"),
  20.     dag=dag
  21. )
  22. second=BashOperator(
  23.     task_id='second',
  24.     # 脚本路径建议写绝对路径
  25.     bash_command='sh /root/shell_jobs/second_shell.sh %s'%datetime.now().strftime("%Y-%m-%d-%H"),
  26.     dag=dag
  27. )
  28. first >> second
复制代码



2.2 SSHOperator



  • 在实际的调治使掷中,使命脚本大多分布在差别的呆板上,我们可以利用SSHOperator来调用远程呆板上的脚本使命。
  • Airflow中提供了很多的providers,须要通过pip安装apache-airflow-providers-ssh包。

    • 安装哪个版本的apache-airflow-providers-ssh包呢?
    • 须要检察我们安装时候的https://raw.githubusercontent.com/apache/airflow/constraints-2.7.2/constraints-3.8.txt约束文件


  • 最后,配置SSH Connection连接。登录airflow webui ,选择“Admin”->“Connections”。

  1. # 首先停止airflow webserver与scheduler
  2. # 我们在https://raw.githubusercontent.com/apache/airflow/constraints-2.7.2/constraints-3.8.txt找相应的版本安装
  3. (airflow) [root@centos01 airflow]# pip install apache-airflow-providers-ssh==3.7.3
  4. # 然后启动airflow webserver与scheduler
  5. 我们在另一台机器上创建两个shell脚本:
  6. #!/bin/bash
  7. # 获取主机名
  8. HOSTNAME=$(hostname)
  9. echo "==== 执行脚本主机是: $HOSTNAME, execute first shell ===="
  10. #!/bin/bash
  11. # 获取主机名
  12. HOSTNAME=$(hostname)
  13. echo "==== 执行脚本主机是: $HOSTNAME, execute second shell ===="
  14. [root@centos02 shell_jobs]# ll
  15. total 8
  16. -rw-r--r--. 1 root root 122 Dec 28 16:34 first_shell.sh
  17. -rw-r--r--. 1 root root 123 Dec 28 16:34 second_shell.sh
复制代码
然后,创建Python脚本
  1. from datetime import datetime, timedelta
  2. from airflow import DAG
  3. from airflow.operators.bash import BashOperator
  4. from airflow.providers.ssh.operators.ssh import SSHOperator
  5. default_args = {
  6.     'owner':'root',
  7.     'start_date': datetime(2024, 12, 28, 16),   # 开始执行时间
  8.     'retries': 1,                               # 失败重试次数
  9.     'retry_delay': timedelta(minutes=5)         # 失败重试间隔
  10. }
  11. # 声明任务图, Airflow使用的是“前一个周期”来调度 DAG 运行
  12. # 即:在2024-12-28 00:00:00时,Airflow会执行的调度账期是:2024-12-27 00:00:00
  13. # dag = DAG('MyTaskTest', default_args=default_args, schedule_interval=timedelta(days=1))
  14. dag = DAG(
  15.     dag_id = 'Myssh2centos02',
  16.     default_args=default_args,
  17.     schedule_interval=timedelta(minutes=10)  # 10min执行一次
  18. )
  19. first=SSHOperator(
  20.     task_id='first',
  21.     ssh_conn_id='AA-My-ssh-centos02',                  # 配置在Airflow webui Connection中配置的SSH Conn id
  22.     command='sh /root/shell_jobs/first_shell.sh ',     # 注意:带一个空格
  23.     dag = dag
  24. )
  25. second=SSHOperator(
  26.     task_id='second',
  27.     ssh_conn_id='AA-My-ssh-centos02',                     # 配置在Airflow webui Connection中配置的SSH Conn id
  28.     command='sh /root/shell_jobs/second_shell.sh ',    # 注意:带一个空格
  29.     remote_host="192.168.42.102",                      # 如果配置remote_host,将会替换Connection中的SSH配置的host
  30.     dag=dag
  31. )
  32. first >> second
复制代码

2.3 PythonOperator



  • PythonOperator可以调用Python函数,由于Python基本可以调用任何类型的使命,假如着实找不到符合的Operator,将使命转为Python函数,利用PythonOperator即可。
  1. import random
  2. from datetime import datetime, timedelta
  3. from airflow import DAG
  4. from airflow.operators.python import PythonOperator
  5. import pytz
  6. def print__hello1(*a,**b):
  7.     """
  8.         *  关键字参数允许传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple
  9.         ** 关键字参数允许传入0个或任意个含【参数名】的参数,这些关键字参数在函数内部自动组装为一个dict
  10.     """
  11.     print("hello airflow1")
  12.     print(a)
  13.     print(b)
  14.     # 返回的值只会打印到日志中
  15.     return {"sss1":"print__hello1"}
  16. def print__hello2(execution_ds, execution_ts, random_base):
  17.     print("hello airflow2")
  18.     print("Today:{}, 当前执行的账期为:{}, 随机数为:{}".format(execution_ds, execution_ts, random_base))
  19.     # 将字符串解析为带有时区信息的datetime对象
  20.     utc_dt = datetime.strptime(execution_ts, "%Y-%m-%dT%H:%M:%S%z")
  21.     # 定义目标时区(上海时区)
  22.     shanghai_tz = pytz.timezone('Asia/Shanghai')
  23.     # 将UTC时间转换为目标时区的时间
  24.     shanghai_dt = utc_dt.astimezone(shanghai_tz)
  25.     # 格式化输出
  26.     execution_ts_sh = shanghai_dt.strftime('%Y-%m-%d %H:%M:%S')
  27.     print("Today:{}, 当前执行的账期为:{}, 随机数为:{}".format(execution_ds, execution_ts_sh, random_base))
  28.    
  29.     # 返回的值只会打印到日志中
  30.     return {"sss2":"print__hello2"}
  31. default_args = {
  32.     'owner':'root',
  33.     'start_date':  datetime(2024, 12, 28, 14), # 开始执行时间
  34.     'retries': 1,  # 失败重试次数
  35.     'retry_delay': timedelta(minutes=5) # 失败重试间隔
  36. }
  37. dag = DAG(
  38.     dag_id = 'MyPythonOperator',
  39.     default_args=default_args,
  40.     schedule_interval='*/10 * * * *'  # 每10min运行一次
  41. )
  42. first=PythonOperator(
  43.     task_id='MyPython_first',
  44.     #填写print__hello1方法时,不要加上()
  45.     python_callable=print__hello1,
  46.     # op_args对应print_hello1方法中的a参数
  47.     op_args=[1,2,3,"hello","world"],
  48.     # op_kwargs对应print__hello1方法中的b参数,带参数名称
  49.     op_kwargs={"id":"1","name":"zs","age":18},
  50.     dag = dag
  51. )
  52. second=PythonOperator(
  53.     task_id='MyPython_second',
  54.     # 同样,填写print__hello2 方法时,不要加上()
  55.     python_callable=print__hello2,
  56.    
  57.     op_kwargs={
  58.         # {{ ds }} 是一个Airflow Jinja模板变量,表示当前的执行日期(格式为 YYYY-MM-DD)
  59.         'execution_ds': '{{ ds }}',
  60.         # {{ ts }} 是一个Airflow Jinja模板变量,表示当前的执行时间戳,格式为 YYYY-MM-DDTHH:MM:SS
  61.         # 注意:默认传递是UTC时间,例如:2024-12-28T07:30:00+00:00
  62.         'execution_ts': '{{ ts }}',
  63.         # random_base参数对应print_hello2方法中参数的random_base
  64.         "random_base": random.randint(0, 9)
  65.     },
  66.     dag=dag
  67. )
  68. first >> second
复制代码

2.4 HiveOperator



  • 可以通过HiveOperator直接操作Hive SQL
  • 在airflow中利用HiveOperator调用Hive使命,首先须要安装以下依赖并配置Hive Metastore
  1. # 在https://raw.githubusercontent.com/apache/airflow/constraints-2.7.2/constraints-3.8.txt找相应的版本安装
  2. pip install apache-airflow-providers-apache-hive==6.1.6
复制代码


  • 启动HDFS、Hive Metastore,在Hive中创建下面表,并加载文件。
  1. -- 1、创建表
  2. create table ods_person_info(id int,name string,age int, city_id int)
  3. row format delimited fields terminated by '\t';
  4. create table dim_city_info(city_id int,city_name string)
  5. row format delimited fields terminated by '\t';
  6. -- 2、准备两个文件
  7. ods_person_info.csv
  8. 1        John        30        1001
  9. 2        Jane        25        1002
  10. 3        Mike        40        1001
  11. 4        Sarah        35        1003
  12. 5        Liam        22        1004
  13. dim_city_info.csv
  14. 1001        New York
  15. 1002        Los Angeles
  16. 1003        Chicago
  17. 1004        Houston
  18. 1005        Phoenix
  19. -- 3、加载数据
  20. LOAD DATA LOCAL INPATH '/root/input_doris_data/ods_person_info.csv'
  21. INTO TABLE ods_person_info;
  22. LOAD DATA LOCAL INPATH '/root/input_doris_data/dim_city_info.csv'
  23. INTO TABLE dim_city_info;
复制代码


  • 同样,登录Airflow webui并设置Hive Metastore



  • 编写Python脚本
  1. from datetime import datetime, timedelta
  2. from airflow import DAG
  3. from airflow.providers.apache.hive.operators.hive import HiveOperator
  4. from airflow.operators.python_operator import PythonOperator
  5. import pytz
  6. default_args = {
  7.     'owner': 'root',
  8.     'start_date': datetime(2024, 12, 30, 12),
  9.     'retries': 1,
  10.     'retry_delay': timedelta(minutes=5)
  11. }
  12. dag = DAG(
  13.     dag_id='MyHiveOperator',
  14.     default_args=default_args,
  15.     schedule_interval=timedelta(hours=1)
  16. )
  17. # 1、利用PythonOperator封装分区变量
  18. def get_partition_hour(**context):
  19.     execution_dt = context['execution_date']
  20.     # 转换Asia/Shanghai分区时间
  21.     shanghai_tz = pytz.timezone('Asia/Shanghai')
  22.     shanghai_dt = execution_dt.astimezone(shanghai_tz)
  23.     # 分区时间
  24.     partition_hour = shanghai_dt.strftime('%Y%m%d%H')
  25.     context['ti'].xcom_push(key='partition_hour', value=partition_hour)
  26.     return {'partition_hour': partition_hour}
  27. time_prep = PythonOperator(
  28.     task_id='prepare_times',
  29.     python_callable=get_partition_hour,
  30.     provide_context=True,
  31.     dag=dag
  32. )
  33. # 2、创建结果表
  34. create_table = HiveOperator(
  35.     task_id='create_table',
  36.     hive_cli_conn_id="AA-My-centos01-hive",
  37.     hql="""
  38.     CREATE TABLE IF NOT EXISTS dwd_person_city_info (
  39.         id INT,
  40.         name STRING,
  41.         age INT,
  42.         city_id INT,
  43.         city_name STRING,
  44.         insert_time STRING
  45.     )
  46.     PARTITIONED BY (p_hour STRING)
  47.     ROW FORMAT DELIMITED
  48.     FIELDS TERMINATED BY '\t'
  49.     """,
  50.     dag=dag
  51. )
  52. # 3、利用PythonOperator来预处理HQL语句,并将处理后的HQL语句传递给HiveOperator
  53. def get_insert_time():
  54.     # 获取当前的执行时间
  55.     now = datetime.now()
  56.     formatted_time = now.strftime('%Y-%m-%d %H:%M:%S')
  57.     return formatted_time
  58. def prepare_hql(**context):
  59.     # Airflow中可以XCom功能来共享数据
  60.     # 通过XCom,一个任务可以将数据推送到一个临时存储中,其他任务可以从这个存储中拉取数据
  61.     partition_hour = context['ti'].xcom_pull(task_ids='prepare_times', key='partition_hour')
  62.     # 调用本地方法
  63.     formatted_time = get_insert_time()
  64.     # 组装hive-sql
  65.     hql = f"""
  66.     INSERT OVERWRITE TABLE dwd_person_city_info
  67.     PARTITION(p_hour='{partition_hour}')
  68.     SELECT
  69.           a.id
  70.         , a.name
  71.         , a.age
  72.         , a.city_id
  73.         , b.city_name
  74.         , '{formatted_time}' AS insert_time
  75.     FROM
  76.         ods_person_info a
  77.     JOIN
  78.         dim_city_info b
  79.     ON a.city_id = b.city_id
  80.     """
  81.     return hql
  82. prepare_hql_task = PythonOperator(
  83.     task_id='prepare_hql',
  84.     python_callable=prepare_hql,
  85.     provide_context=True,
  86.     dag=dag
  87. )
  88. # 4、执行hive-sql插入到相应分区
  89. insert_data = HiveOperator(
  90.     task_id='insert_partitioned_data',
  91.     hive_cli_conn_id="AA-My-centos01-hive",
  92.     # 注意,这里使用了task_instance.xcom_pull而不是ti.xcom_pull
  93.     # 因为在HiveOperator的模板上下文中,ti可能不是直接可用的
  94.     # 而task_instance是Airflow在模板渲染时提供的一个全局变量,用于访问当前任务实例。
  95.     hql="{{ task_instance.xcom_pull(task_ids='prepare_hql', key='return_value') }}",
  96.     dag=dag
  97. )
  98. time_prep >> create_table >> prepare_hql_task >> insert_data
复制代码
  1. 0: jdbc:hive2://192.168.42.101:10000> show tables;
  2. +-----------------------+
  3. |       tab_name        |
  4. +-----------------------+
  5. | dim_city_info         |
  6. | dwd_person_city_info  |
  7. | ods_person_info       |
  8. +-----------------------+
  9. 3 rows selected (0.931 seconds)
  10. 0: jdbc:hive2://192.168.42.101:10000> show partitions dwd_person_city_info;
  11. +--------------------+
  12. |     partition      |
  13. +--------------------+
  14. | p_hour=2024123012  |
  15. | p_hour=2024123013  |
  16. | p_hour=2024123014  |
  17. | p_hour=2024123015  |
  18. | p_hour=2024123016  |
  19. | p_hour=2024123017  |
  20. +--------------------+
  21. 6 rows selected (0.307 seconds)
  22. 0: jdbc:hive2://192.168.42.101:10000> select * from  dwd_person_city_info a  where p_hour=2024123017;
  23. +-------+---------+--------+------------+--------------+----------------------+-------------+
  24. | a.id  | a.name  | a.age  | a.city_id  | a.city_name  |    a.insert_time     |  a.p_hour   |
  25. +-------+---------+--------+------------+--------------+----------------------+-------------+
  26. | 1     | John    | 30     | 1001       | New York     | 2024-12-30 18:00:10  | 2024123017  |
  27. | 2     | Jane    | 25     | 1002       | Los Angeles  | 2024-12-30 18:00:10  | 2024123017  |
  28. | 3     | Mike    | 40     | 1001       | New York     | 2024-12-30 18:00:10  | 2024123017  |
  29. | 4     | Sarah   | 35     | 1003       | Chicago      | 2024-12-30 18:00:10  | 2024123017  |
  30. | 5     | Liam    | 22     | 1004       | Houston      | 2024-12-30 18:00:10  | 2024123017  |
  31. +-------+---------+--------+------------+--------------+----------------------+-------------+
  32. 5 rows selected (2.301 seconds)
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

滴水恩情

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表