SQL 中动态列格式化及其存储后端

82 阅读3分钟

我想在 Python 中创建一个系统,用户可以在其中从一组表中选择多行,并以用户定义的方式对其进行格式化。假设表 a 有一组列,其中一些列包括日期或时间戳值。每列的用户定义格式应存储在另一个表中,并在运行时对其进行查询并应用于主查询。

举个例子:有不同的方法可以格式化日期列,例如使用 PostgreSQL 中的 SELECT to_char(column, 'YYYY-MM-DD') FROM table;

我希望 to_char() 内置函数的第二个参数可以在运行时从另一个表中动态查询,然后在有值时应用该参数。从表中读取定义并不是什么大问题,而是创建一个数据库模式,该模式将接收来自用户界面的数据,用户可以在其中选择要应用于不同列的格式化说明。用户应该能够选择要包含在查询中的列集,以及每列的用户自定义格式化。

我已经考虑了几种优雅高效的方法来为这个问题建立解决方案,但目前还没有成功。让用户在文本字段中输入用户所需的定义并将其包含在查询中,基本上会生成一个 SQL 注入攻击的邀请(尽管可以使用 escape() 函数),而存储所有可能的组合对我来说似乎也不可行。

2、解决方案

为了安全、高效地解决上述问题,可以使用以下解决方案:

  1. 创建用于存储格式化指令的数据库表。 该表将包含以下列:

    • 列名
    • 数据类型
    • 格式化指令
  2. 创建用于存储用户查询的数据库表。 该表将包含以下列:

    • 查询名称
    • 查询文本
    • 用户 ID
  3. 创建一个 Python 脚本,用于将用户查询转换为 SQL 查询。 该脚本将执行以下步骤:

    • 从用户查询表中获取查询文本。
    • 解析查询文本,以识别要格式化的列。
    • 从格式化指令表中获取相应列的格式化指令。
    • 将格式化指令应用于查询文本。
  4. 创建一个 Python 脚本,用于执行 SQL 查询并格式化结果。 该脚本将执行以下步骤:

    • 将 SQL 查询发送到数据库。
    • 接收查询结果。
    • 使用格式化指令格式化查询结果。
    • 将格式化后的结果返回给用户。

以下是一个代码示例,展示了如何使用此解决方案:

import pandas as pd

# 连接到数据库
connection = pymysql.connect(host='localhost',
                             user='root',
                             password='',
                             db='my_database')

# 从格式化指令表中获取格式化指令
formatting_instructions = pd.read_sql('SELECT * FROM formatting_instructions', connection)

# 从用户查询表中获取查询文本
query_text = 'SELECT * FROM my_table'

# 创建 Python 脚本,用于将用户查询转换为 SQL 查询
def convert_query_to_sql(query_text, formatting_instructions):
    # 解析查询文本,以识别要格式化的列
    columns_to_format = [column for column in query_text.split() if column in formatting_instructions['column_name'].values]

    # 从格式化指令表中获取相应列的格式化指令
    formatting_instructions = formatting_instructions[formatting_instructions['column_name'].isin(columns_to_format)]

    # 将格式化指令应用于查询文本
    for column, formatting_instruction in zip(columns_to_format, formatting_instructions['formatting_instruction'].values):
        query_text = query_text.replace(column, f"{column} {formatting_instruction}")

    return query_text

# 将用户查询转换为 SQL 查询
sql_query = convert_query_to_sql(query_text, formatting_instructions)

# 执行 SQL 查询
results = pd.read_sql(sql_query, connection)

# 格式化查询结果
formatted_results = results.applymap(lambda x: x.strftime('%Y-%m-%d') if isinstance(x, datetime.datetime) else x)

# 将格式化后的结果返回给用户
print(formatted_results)