Python|将 csv 文件转化为 sql 的 INSERT INTO 语句

最近有一个开发场景,需要将从 Hue 中导出的 csv 文件,转化为 INSERT INTO 语句并写入到测试数据库。在处理数据时,存在如下具体需求:

  • 需要自动将 HUe 导出的 csv 中 “表名.列名” 的表头清洗为 “列名”
  • 需要自动忽略部分字段(例如分区字段)

在具体的实现上:

  • 为了兼容从文件中读取 TextIO 以及直接处理字符串,我们允许入参为 TextIO 或字符串,并将字符串类型的入参统一为 TextIO 类型
  • 我们使用 csv.reader 解析 csv 文件,这是因为 readercsv 模块中性能最高的
  • 为了兼容需要跳过的字段名可能是 “表名.列名” 的格式,也有可能是 “列名” 的格式,所以我们在原始字段名和清洗后的字段名中分别查找需要过滤的字段名是否存在
  • 为了避免使用保留字的列名引发问题,所以我们将所有列名都是用引号框住
  • 为了避免不同类型的变量值引发问题,所以我们将所有的变量值都用引号框住

具体实现如下:

"""
将 csv 文件转化为 sql 的 INSERT INTO 语句
"""

import csv
from typing import List, Optional, TextIO, Union


def change_to_textio(s: Union[TextIO, str]) -> TextIO:
    """将字符串或 TextIO 转化为 TextIO

    Parameters
    ----------
    s : Union[TextIO, str]
        字符串或 TextIO

    Returns
    -------
    TextIO
    """
    if isinstance(s, str):
        return io.StringIO(s)  # 将字符串转化为 TextIO
    return s


def change_csv_to_sql(f: Union[TextIO, str],
                      table_name: Optional[str] = None,
                      ignore_columns: Optional[List[str]] = None
                      ) -> str:
    """将 csv 文件转化为 sql 的 INSERT INTO 语句

    1. 可以将 "表名.列名" 的表头清洗为 "列名"
    2. 可以设置忽略的字段列表(可以是原始字段名,也可以是清洗后的字段名)

    Parameters
    ----------
    f : Union[TextIO, str]
        字符串或 TextIO(打开的文件)
    table_name : Optional[str], default = None
        如果为 None,则 INSERT 语句中将使用 Table 代替表名
    ignore_columns : Optional[List[str]] = None
        需要忽略的字段列表,使用清洗后的字段名,如果为 None,则不忽略任何字段

    Returns
    -------
    str
        生成的 INSERT INTO 语句
    """
    f = change_to_textio(f)
    if table_name is None:
        table_name = "Table"
    if ignore_columns is None:
        ignore_columns = []

    reader = csv.reader(f)  # 解析 csv 文件

    # 解析字段名
    source_columns = next(reader)
    clean_columns = [column[column.index(".") + 1:] if "." in column else column for column in source_columns]

    # 计算需要忽略的字段下标
    ignore_idx_set = set()
    for ignore_column in ignore_columns:
        if ignore_column in source_columns:  # 先在原始字段名中检索
            ignore_idx = source_columns.index(ignore_column)
            ignore_idx_set.add(ignore_idx)
        elif ignore_column in clean_columns:  # 然后在清洗字段名中检索
            ignore_idx = clean_columns.index(ignore_column)
            ignore_idx_set.add(ignore_idx)

    # 格式化标题行
    columns = ", ".join([f"`{column}`" for i, column in enumerate(clean_columns) if i not in ignore_idx_set])

    # 格式化数据行
    values = ", ".join(["(" + ", ".join([f"'{v}'" if v != "NULL" else v
                                         for i, v in enumerate(row) if i not in ignore_idx_set]) + ")"
                        for row in csv.reader(f)])

    return f"INSERT INTO `{table_name}` ({columns}) VALUES {values};"

使用方法:

if __name__ == "__main__":
    with open(r"file_path", encoding="UTF-8") as file:
        print(change_csv_to_sql(file.read(), table_name="table_name", ignore_columns=["pt"]))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值