【问题标题】:Properly format SQL query when insert into variable number of columns插入可变数量的列时正确格式化 SQL 查询
【发布时间】:2019-04-29 08:37:31
【问题描述】:

我正在使用psycopg2PostgreSQL 数据库进行交互。我有一个函数可以插入表中的任意数量的列(从单列到所有列)。我的问题是:如何正确、动态地构造这个查询?

目前我正在使用字符串格式和连接,我知道这是绝对最糟糕的方法。考虑下面的代码,在这种情况下,我未知的列数(即来自 dict 的键实际上是 2):

dictOfUnknownLength = {'key1': 3, 'key2': 'myString'}

def createMyQuery(user_ids, dictOfUnknownLength):
    fields, values = list(), list()

    for key, val in dictOfUnknownLength.items():
        fields.append(key)
        values.append(val)

    fields = str(fields).replace('[', '(').replace(']', ')').replace("'", "")
    values = str(values).replace('[', '(').replace(']', ')')

    query = f"INSERT INTO myTable {fields} VALUES {values} RETURNING someValue;"

query = INSERT INTO myTable (key1, key2) VALUES (3, 'myString') RETURNING someValue;

这提供了格式正确的查询,但当然容易受到 SQL 注入等的影响,因此,这不是实现我的目标的可接受方法。

在其他查询中,我在处理已知数量的变量(%s 和包含变量的 .execute() 的单独参数)时使用查询构造的 recommended 方法,但我不确定如何调整它以适应不使用字符串格式的未知数量的变量。

我怎样才能优雅地安全地构造一个包含未知数量的指定插入列的查询?

【问题讨论】:

    标签: python sql python-3.x postgresql psycopg2


    【解决方案1】:

    更令人担忧的是,当前使用 .replace() 的方法容易出现字段或值包含 []' 的极端情况。它们将被替换无论如何,并且可能会弄乱您的查询。

    您始终可以使用.join() 来加入列表中变量 数量的值。补充一下,formatVALUES 之后适当地使用%s 查询,并将您的参数传递给.execute()

    注意:您可能还需要考虑字段数不等于数值的情况。

    import psycopg2
    
    
    conn = psycopg2.connect("dbname=test user=postgres")
    cur = conn.cursor()
    
    dictOfUnknownLength = {'key1': 3, 'key2': 'myString'}
    
    
    def createMyQuery(user_ids, dictOfUnknownLength):
        # Directly assign keys/values.
        fields, values = list(dictOfUnknownLength.keys()), list(dictOfUnknownLength.values())
    
        if len(fields) != len(values):
            # Raise an error? SQL won't work in this case anyways...
            pass
    
        # Stringify the fields and values.
        fieldsParam = ','.join(fields) # "key1, key2"
        valuesParam = ','.join(['%s']*len(values))) # "%s, %s"
    
        # "INSERT ... (key1, key2) VALUES (%s, %s) ..."
        query = 'INSERT INTO myTable ({}) VALUES ({}) RETURNING someValue;'.format(fieldsParam, valuesParam)
    
        # .execute('INSERT ... (key1, key2) VALUES (%s, %s) ...', [3, 'myString'])
        cur.execute(query, values) # Anti-SQL-injection: pass placeholder
                                   # values as second argument.
    

    【讨论】:

    • 我相信这种方法会导致列名被视为字符串,因此被封装在' ' 中,因此是无效的 SQL 语法?
    • 确保len(fields)==len(values) 是一个很好的观点,谢谢。
    猜你喜欢
    • 2018-11-18
    • 1970-01-01
    • 1970-01-01
    • 2013-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多