插入可变数量的列时,正确格式化SQL查询的格式

时间:2018-11-27 15:02:35

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

我正在使用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查询构造方法,但是我不确定如何在不使用字符串格式的情况下对其进行调整以适应未知数量的变量。

如何优雅地安全地构造一个具有未知数量的指定插入列的查询?

1 个答案:

答案 0 :(得分:1)

让您担心的是,当前使用.replace()的方法容易出现极端情况,其中字段或值包含[]'。无论如何,它们都会被替换为 ,并且可能使您的查询混乱。

您始终可以使用.join()来联接列表中变量个值。最重要的是,在%s之后用VALUESformat进行适当的查询,然后将参数传递到.execute()中。

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

## for completeness
import psycopg2
conn = psycopg2.connect("dbname=test user=postgres")
cur = conn.cursor()

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

def createMyQuery(user_ids, dictOfUnknownLength):

    ## this may be quicker
    fields, values = list(dictOfUnknownLength.keys()), list(dictOfUnknownLength.values())

    if len(fields) != len(values):
        ## raise an error? sql won't work in this case anyway...
        pass

    fieldsParam = ','.join(fields)  ## key1, key2 ## directly stringify it
    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