SQLite3:如何提供为 EXCEPT 子句指定多行的占位符值

问题描述 投票:0回答:1

作为背景,我追求的最终目标是“给定 python 中的 id 列表,找到当前未映射到 SQLite3 表中的行的所有 id”

我目前正在尝试使用

EXCEPT
运算符来实现此目的

例如

-- if the table currently stores id1 and id3 would only return id2
WITH cte(id) as VALUES ('id1'), ('id2'), ('id3') 
SELECT * from cte EXCEPT SELECT id FROM some_table

但是我想从 python 列表中动态指定 ids 列表

我已经能够通过使用 python 格式字符串来构建硬编码值列表来实现这一点

例如

query = (
    "with cte(id) as " +
    f"(values {",".join(f"('{id}')" for id in ids)}) " +
    "select * from cte except select id from some_table"
    )
print(query)
res = cursor.execute(query)

但据我了解,通常不鼓励这样做,因为它容易受到 SQL 注入的攻击。相反,占位符语法是首选。 python sqlite3 文档展示了一些使用

executemany
进行
INSERT
操作的很好的例子,但我很难将其应用到 SELECT+EXCEPT 这只是一个查询(因此必须使用
execute
而不是
executemany
)。

我该怎么办?或者,是否有更好的 SQL/SQLite3 机制可以用来过滤表中不存在的输入列表?谢谢。

这是我的问题的包含示例,以防有帮助

import sqlite3

db = sqlite3.connect(":memory:")

cursor = db.cursor()

#
# First create a table of video-id,video-title pairs
#

cursor.execute("CREATE TABLE IF NOT EXISTS videos(id TEXT PRIMARY KEY, title TEXT)")
dummy_data = [
    ("vid1", "Video 1"),
    ("vid2", "Video 2"),
    ("vid3", "Video 3"),
]

# use executemany to insert multiple rows via placeholder VALUES
cursor.executemany("INSERT INTO videos VALUES(?, ?)", dummy_data)
db.commit()

# sanity check that we see the expected videos
res = cursor.execute("SELECT * FROM videos")
print(f"select* result: {res.fetchall()}")

#
# Next, given a set of video ids, find all of the ids not already stored in the DB
#

new_video_ids = ["vid1", "vid2", "vid5"] # vid1 and vid2 already exist in db. only vid5 should be returned
new_video_ids_str = ",".join(f"('{id}')" for id in new_video_ids)
print(new_video_ids_str)

# The following query uses python string formatting and is therefore vulnerable to SQL injection attacks
query = (
    "with cte(id) as " +
    f"(values {new_video_ids_str}) " +
    "select * from cte except select id from videos"
    )
print(query)
res = cursor.execute(query)
print(f"filter result: {res.fetchall()}")

# I'd like to use SQLite3 placeholder values but can't figure out the syntax. The following doesn't work.
# it fails since it's trying to all of the `new_video_ids` values as a single row rather than multiple rows.
#
# query = (
#     "with cte(id) as " +
#     "(values (?)) " +
#     "select * from cte except select id from videos"
#     )
# res = cursor.execute(query, new_video_ids)
# print(f"filter result: {res.fetchall()}")


db.close()
python sqlite sqlite3-python
1个回答
1
投票
new_video_ids = ["vid1", "vid2", "vid5"] # vid1 and vid2 already exist in db. only vid5 should be returned

new_video_ids_str = ",".join(
   ["(?)"] * len(new_video_ids)
)

print(new_video_ids_str)

query = (
    "with cte(id) as " +
    + f"(values {new_video_ids_str}) " +
    + "select * from cte except select id from videos"
    )

print(query)

res = cursor.execute(query, *new_video_ids)

print(f"filter result: {res.fetchall()}")
© www.soinside.com 2019 - 2024. All rights reserved.