如何将列的默认值指定为占位符?
How to specify a column's default value as a placeholder?
我有一个包含多列的 table,其中有几列是可选的。我正在从外部源读取记录,其中每条记录都可以指定可选列的值,也可以不指定。对于每条记录,我想在数据库中插入一行,其中包含给定值加上任何未指定列的列默认值。
如果指定了所有的列,我显然只是使用了一个基本的 INSERT 语句:
db_cursor.execute("insert into table (col1, col2, col3, col4, col5) " +
"values (%s, %s, %s, %s, %s)",
(value_1, value_2, value_3, value_4, value_5))
但是,如果未指定某些值,似乎没有一种简单的方法可以仅对这些值使用默认值。您可以在 SQL 中使用 DEFAULT 关键字(或者,等效地,将这些列完全排除在插入语句之外),例如
db_cursor.execute("insert into table (col1, col2, col3, col4, col5) " +
"values (%s, %s, %s, DEFAULT, %s)",
(value_1, value_2, value_3, value_5))
但是您不能将 'DEFAULT'
作为占位符值传递;它会变成那个字符串。
到目前为止我只能想到三种解决这个问题的方法:
根据输入数据在 运行 时构造 SQL 查询字符串本身,而不是使用参数化。由于通常的 SQL 注入原因,这是一个非常强大的反模式。 (这个应用程序实际上并不是安全关键的,但我不想在我的代码中出现这样的反模式。)
为指定参数和未指定参数的每种可能组合编写不同的查询字符串。在这里,如果其中四列是可选的,那么 2^4 = 16 个不同的命令 运行ning 同一个查询。这显然是行不通的。
让应用程序知道默认值,并让它在未指定列的情况下显式发送它们。这打破了默认值的 SPOT,伴随着所有维护和互操作性的麻烦(多个应用程序读取数据库)。在我能想到的方法中,这可能是最不糟糕的方法,但我仍然宁愿不必这样做。
是否有更简单的方法来管理动态发送默认值?
我通常处理这个问题的方法是用占位符代替列列表和字符串 format()
列列表。这是安全的,因为列列表由开发人员控制,并且不是不受信任的用户输入。
stmt_without_col_names = 'INSERT INTO table ({}) VALUES ({})'
input_values = [1, None, 1, None, None]
columns = ('col1', 'col2', 'col3', 'col4', 'col5')
columns_to_keep = {k: v for k, v in zip(columns, input_values) if v is not None}
# note: relies on dict key ordering remaining the same
# this is true if the dict is not modified *at all* between creation
# and the statement execution - use an OrderedDict or other data
# structure instead if you're worried
format_str = ','.join(['%s'] * len(columns_to_keep))
stmt = stmt_without_col_names.format(columns_to_keep.keys(), format_str)
# stmt looks like "INSERT INTO table (['col3', 'col1']) VALUES (%s,%s)"
cursor.execute(stmt, columns_to_keep.values())
我有一个包含多列的 table,其中有几列是可选的。我正在从外部源读取记录,其中每条记录都可以指定可选列的值,也可以不指定。对于每条记录,我想在数据库中插入一行,其中包含给定值加上任何未指定列的列默认值。
如果指定了所有的列,我显然只是使用了一个基本的 INSERT 语句:
db_cursor.execute("insert into table (col1, col2, col3, col4, col5) " +
"values (%s, %s, %s, %s, %s)",
(value_1, value_2, value_3, value_4, value_5))
但是,如果未指定某些值,似乎没有一种简单的方法可以仅对这些值使用默认值。您可以在 SQL 中使用 DEFAULT 关键字(或者,等效地,将这些列完全排除在插入语句之外),例如
db_cursor.execute("insert into table (col1, col2, col3, col4, col5) " +
"values (%s, %s, %s, DEFAULT, %s)",
(value_1, value_2, value_3, value_5))
但是您不能将 'DEFAULT'
作为占位符值传递;它会变成那个字符串。
到目前为止我只能想到三种解决这个问题的方法:
根据输入数据在 运行 时构造 SQL 查询字符串本身,而不是使用参数化。由于通常的 SQL 注入原因,这是一个非常强大的反模式。 (这个应用程序实际上并不是安全关键的,但我不想在我的代码中出现这样的反模式。)
为指定参数和未指定参数的每种可能组合编写不同的查询字符串。在这里,如果其中四列是可选的,那么 2^4 = 16 个不同的命令 运行ning 同一个查询。这显然是行不通的。
让应用程序知道默认值,并让它在未指定列的情况下显式发送它们。这打破了默认值的 SPOT,伴随着所有维护和互操作性的麻烦(多个应用程序读取数据库)。在我能想到的方法中,这可能是最不糟糕的方法,但我仍然宁愿不必这样做。
是否有更简单的方法来管理动态发送默认值?
我通常处理这个问题的方法是用占位符代替列列表和字符串 format()
列列表。这是安全的,因为列列表由开发人员控制,并且不是不受信任的用户输入。
stmt_without_col_names = 'INSERT INTO table ({}) VALUES ({})'
input_values = [1, None, 1, None, None]
columns = ('col1', 'col2', 'col3', 'col4', 'col5')
columns_to_keep = {k: v for k, v in zip(columns, input_values) if v is not None}
# note: relies on dict key ordering remaining the same
# this is true if the dict is not modified *at all* between creation
# and the statement execution - use an OrderedDict or other data
# structure instead if you're worried
format_str = ','.join(['%s'] * len(columns_to_keep))
stmt = stmt_without_col_names.format(columns_to_keep.keys(), format_str)
# stmt looks like "INSERT INTO table (['col3', 'col1']) VALUES (%s,%s)"
cursor.execute(stmt, columns_to_keep.values())