# metabase数据分析及可视化-智能建表


上一篇我们对开源免费的 BI 工具 MetaBase 进行了介绍。

在开始正式学习之前，我们需要解决最重要的一个问题：`导数`。

我们本节将介绍，如何根据数据文件创建正确数据类型的表，并将数据导入数据库。

{{< figure src="/ox-hugo/2023-08-17_21-52-42_screenshot.png" >}}

例如，我有一个data.csv文件，数据如上图所示。

很多朋友喜欢使用 Navicat 的导入向导来导数，确实相对来说，它是比较简单方便的。

{{< figure src="/ox-hugo/2023-08-17_21-55-53_screenshot.png" >}}

但是有一个问题是，就算我们用导入向导，如果想后续使用 MetaBase ，就需要设置正确的数据类型，否则 MetaBase 无法对文本进行计算。

那么我们就需要手工一个一个的修改数据类型了，这就非常慢。尤其是当字段特别多的时候，修改数据类型就能浪费不少时间。


## 自动建表导数脚本 {#自动建表导数脚本}

这里让 chatgpt 给我写了一个 Python 脚本，让它能一键完成。

{{< figure src="/ox-hugo/2023-08-17_22-08-06_data2sql.gif" >}}

我们运行 Python 代码：

{{< highlight bash >}}
python3 data2sql.py
{{< /highlight >}}


### 输入表名称 {#输入表名称}

运行代码后，会出现提示，输入想创建的表的名称：例如我们输入 `test`

{{< highlight bash >}}
Enter the table name: test
{{< /highlight >}}


### 输入数据文件路径 {#输入数据文件路径}

接着会提示，我们的数据文件路径：例如我们输入 `data.csv`

{{< highlight bash >}}
Enter the file path: data.csv
{{< /highlight >}}


### 选择数据类型 {#选择数据类型}

这里会提示两个选项：

1.  是根据数据文件第二列的数据自动生成类型。
2.  是全部设置成varchar(255)文本类型。

例如我们输入 1 ，让其自动根据数据设置类型：

{{< highlight bash >}}
Choose data type inference:
1. Automatic (from sample row)
2. All VARCHAR(255)
Select an option (1/2): 1
{{< /highlight >}}

完事后，就会打印出建表语句和导数语句，同时该语句已经复制到粘贴板上，因此，我们只需要在 Navicat 等图形化工具中`Ctrl+v`粘贴语句，运行即可。

{{< figure src="/ox-hugo/2023-08-17_22-17-53_screenshot.png" >}}

整个过程只需要几秒钟，实际上能比导入向导快不少时间。

数据量越大，节约的时间越多。

细心的朋友会发现 InvoiceDate 字段的值`12/1/2010 8:26`并没有识别成`DateTime`格式。

当然我们可以修改`data2sql.py`中 `guess_data_type()` 函数的正则表达式，让其能适配这种格式，但实际上就算我把该字段设置成`datetime`类型，导入 mysql 时并不能正确识别。

这就需要我们导入前对该日期格式进行数据清洗。

对于文本文件的数据数据清洗，我感觉最好用的还是 Linux 系统终端自带的一些命令，它们可以非常简洁、高效的完成处理。

然而，绝大多数同事都是使用的 Windows 系统，那么可以在 Windows 系统上使用 Linux 终端命令吗？

答案是可以的！

下一节，我们将先介绍如何在 Windows 上使用 Linux 终端命令，并完成数据清洗工作。

要知道，数据清洗工作其实能占不少时间的，而且这方面的资料是相对比较少一点的。

我将把平时遇到并总结的经验分享读者，我相信对于平时做数据分析的朋友，会有一些帮助。


## 脚本代码 {#脚本代码}

上面用的data2sql.py文件代码在运行前，需要先安装使用到的 pandas 和 clipboard 库。

安装方法：

{{< highlight bash >}}
pip install pandas clipboard
{{< /highlight >}}

`data2sql.py` 的完整代码如下：

{{< highlight python >}}
#!/usr/bin/python3
# -*- coding:UTF-8 -*-
# Author: nigo
import pandas as pd
import re
import clipboard
import os

def guess_data_type(value):
    if re.match(r'^\d+$', value):  # Check for integer
        return 'INT'
    elif re.match(r'^\d+\.\d+$', value):  # Check for float
        return 'FLOAT'
    elif re.match(r'^\d{4}-\d{2}-\d{2}$', value):  # Check for date (YYYY-MM-DD)
        return 'DATE'
    elif re.match(r'^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$', value):  # Check for datetime (YYYY-MM-DD HH:MM:SS)
        return 'DATETIME'
    else:
        return 'VARCHAR(255)'  # Default to string type

def generate_create_table_sql(table_name, header, sample_row):
    create_table_query = f"CREATE TABLE {table_name} (\n"
    for column_name, value in zip(header, sample_row):
        data_type = guess_data_type(str(value))  # Convert value to string before guessing data type
        create_table_query += f"    `{column_name}` {data_type},\n"
    create_table_query = create_table_query.rstrip(",\n") + "\n);"
    return create_table_query

def generate_load_table_sql(table_name, file_path):
    abs_file_path = os.path.abspath(file_path)
    load_table_query = f"""
LOAD DATA INFILE '{abs_file_path}'
INTO TABLE {table_name}
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\\n'
IGNORE 1 LINES;
"""
    return load_table_query

def generate_create_table_sql_varchar(table_name, header):
    create_table_query = f"CREATE TABLE {table_name} (\n"
    for column_name in header:
        create_table_query += f"    `{column_name}` VARCHAR(255),\n"
    create_table_query = create_table_query.rstrip(",\n") + "\n);"
    return create_table_query

def main():
    table_name = input("Enter the table name: ")
    file_path = input("Enter the file path: ")
    df = pd.read_csv(file_path, nrows=1)  # Read only the first row to get column names and sample data
    header = df.columns.tolist()
    sample_row = df.iloc[0].tolist()

    # User choice for data type inference
    data_type_choice = input("Choose data type inference:\n1. Automatic (from sample row)\n2. All VARCHAR(255)\nSelect an option (1/2): ")

    if data_type_choice == '1':
        create_table_sql = generate_create_table_sql(table_name, header, sample_row)
    elif data_type_choice == '2':
        create_table_sql = generate_create_table_sql_varchar(table_name, header)  # Create all fields as VARCHAR(255)
    else:
        print("Invalid choice.")
        return

    load_table_sql = generate_load_table_sql(table_name, file_path)
    print("-- Create Table SQL:")
    print(create_table_sql)
    print("\n-- Load Table SQL:")
    print(load_table_sql)

    try:
        # Copy load_table_sql to clipboard
        clipboard.copy(create_table_sql+load_table_sql)
        print("\nLoad Table SQL has been copied to the clipboard.")
    except Exception as e:
        print("\nError copying to clipboard:", e)

if __name__ == "__main__":
    main()
{{< /highlight >}}


## 脚本及数据下载 {#脚本及数据下载}

将本文使用的`data2sql.py`脚本文件，以及示例数据`data.csv`文件供读者下载，练习。

<https://wwds.lanzouq.com/i1iGw15ob2fe>

虽然本文的导数脚本是针对 mysql 的，实际上大家可以修改去适用于其它数据库。

如果对 mysql 和 navicat 不了解的朋友，可以先看之前的相关文章。

