我正在尝试构建一个需要用两个参数(2列)过滤的SQL,第二列需要匹配多个值。
下面给出的是我到目前为止构建的SQL(感谢 Martijn Pieters 的帮助)
import psycopg2
import pandas as pd
import datetime
# Connecting to db
con = psycopg2.connect(db_details)
cur = con.cursor()
cur.execute("select * from sales limit 10")
rows = cur.fetchall()
params = {'earliest': datetime.datetime.today() - datetime.timedelta(days=7),
'store_name': 'store_1', 'store_2'}
df = pd.read_sql("""
select store_name,count(*) from sales
where created_at >= %(earliest)s
and store_name = %(store_name)s""",
params=params, con=con)
上面的SQL有一个date参数,该参数在where子句中使用,我又添加了一个参数 store_name ,其中行与两个值之一匹配。
想知道如何将这个附加参数添加到现有查询中。
我试图创建一个参数(类似于日期过滤器)并将其传递给现有查询,但是当我给它两个值时会出现语法错误:
'store_name': 'store_1', 'store_2'}
^
SyntaxError: invalid syntax
指向params
字段。
答案 0 :(得分:2)
您有两个问题:
您使用了无效的Python语法;字典中的逗号分隔键值对,因此'store_2'
字符串将是另一个键值对,但缺少: value
部分。如果您想使用多个字符串定义一个值,则必须在其中使用元组或列表,如果您明确使用(...)
或[...]
将该语法与{{ 1}}表示法:
key: value, key: value
通常来说,SQL参数只能与单个值一起使用。 params = {
'earliest': datetime.datetime.today() - datetime.timedelta(days=7),
'store_name': ('store_1', 'store_2'), # tuple with two values
}
参数只能给一个值,而不是一个值序列。这是因为SQL参数是SQL查询和该查询中要使用的动态值之间的桥梁,并且这些参数旨在充当每个个体动态值的占位符。
也就是说,store_name
库specifically supports tuples是大多数Python数据库库的例外。
接下来,如果要在匹配psycopg2
或'store_1'
的情况下过滤行,则正确的SQL语法是使用两个'store_2'
测试,并且它们之间使用store_name = ...
(使用OR
将或括起来(以使该部分与通过date
连接到商店名称测试的AND
测试分开)。 store_name IN ('store_1', 'store_2')
测试将列名与IN
括号中列出的多个值进行比较。
鉴于您在此处使用(...)
,可以不用引用元组值的psycopg2
键,但是您的查询确实需要使用store_name
:
IN
另外请注意:params = {
'earliest': datetime.datetime.today() - datetime.timedelta(days=7),
'store_name': ('store_1', 'store_2')
}
df = pd.read_sql("""
SELECT store_name, count(*) FROM sales
WHERE created_at >= %(earliest)s
AND store_name IN %(store_name)s""",
params=params, con=con)
函数[明确指出在使用DBAPI连接时仅支持sqlite](如果为DBAPI2对象,则仅支持sqlite3):
如果是DBAPI2对象,则仅支持sqlite3。
您正在使用这样的对象;大多数Python数据库适配器都是DBAPI2库; DBAPI2是Python standard for such libraries。
您实际上应该使用SQLAlchemy connection string代替。代码之所以能够运行,是因为您从未尝试将任何数据写回到数据库,并且psycopg连接和游标对象与sqlite3库版本在很大程度上兼容,但是您可能会遇到麻烦。
答案 1 :(得分:-1)
我不明白为什么这行不通:
params = {'earliest': datetime.datetime.today() - datetime.timedelta(days=7),
'store_name': '<put what you want here>'}
df = pd.read_sql("""
select store_name,count(*) from sales
where created_at >= %(earliest)s
and store_name = %(store_name)s""",
params=params, con=con)
因为要两个商店,所以有点复杂。
这应该有效:
params = {'earliest': datetime.datetime.today() - datetime.timedelta(days=7),
'store_names': ','.join(('store_1', 'store_2'))}
df = pd.read_sql("""
select store_name,count(*) from sales
where created_at >= %(earliest)s
and store_name in (%(store_names)s)""",
params=params, con=con)