python-3.x - 如何使用 psycopg2 以点符号方式使用它们的别名在两个连接表中的许多字段上正确构建 SELECT 查询
问题描述
有没有办法使用 psycopg2 将这些标识符转换为 PostgreSQL 查询:
total_query_fields = (
'p.id',
'p.name',
'p.type',
'p.price',
'o.date', # please, notice the 'o' alias here
'o.transaction', # please, notice the 'o' alias here
'p.warehouse',
'p.location',
)
# they may get split into their own tables if necessary:
product_query_fields = ('id', 'name', 'type', 'price', 'warehouse', 'location',)
order_query_fields = ('date', 'transaction',)
变成这样的东西:
import psycopg2
from psycopg2 import sql
myid = 100
sql_query = sql.SQL("""
SELECT {fields} FROM product p
INNER JOIN owner o ON p.id = o.product_id
WHERE p.id = {jidx} AND (o.dateof_purchase IS NOT NULL
OR o.state = 'checked_out' );"""
).format(
fields = # there should be the readable ***magic***
jidx = sql.Literal(myid)
)
?
即使通过阅读此线程了解了我的问题,我也无法找到一种使用点分符号获取我的字段列表的好方法。我猜可能必须使用两个,并且都使用and 。
或者也许更优雅的东西,例如使用?map()
sql.SQL('.').join(...)
sql.SQL(', ').join(...)
SELECT {}.{}...
因为目前我遇到了麻烦:
fields = sql.SQL(', ').join(map(sql.Identifier, total_query_fields)),
因为它将用双引号转义所有序列“table.fields”,这在 SQL 中绝对无效:
# /!\ INVALID SQL QUERY /!\:
print(sql_query.as_string(conn))
# will print:
# SELECT "p.id", "p.name", "p.type", "p.price", "o.date", "o.transaction", "p.warehouse", "p.location" FROM product p
# INNER JOIN owner o ON p.id = o.product_id
# WHERE p.id = 100 AND (o.dateof_purchase IS NOT NULL
# OR o.state = 'checked_out' );
事实上,如果我直接在我最喜欢的 PostgreSQL 查询工具中复制/粘贴最后一个查询:
ERROR: column "p.id" does not exist
LINE 1: SELECT "p.id", "p.name", "p.type", "p.price", "o.date",...
^
HINT: Perhaps you meant to reference the column "p.id" or the column "o.id".
SQL state: 42703
Character: 8
psycopg2 显然引发了同样的错误:
UndefinedColumn: column "p.id" does not exist
LINE 1: SELECT "p.id", "p.name", "p.type", "p.price", "o.date...
^
HINT: Perhaps you meant to reference the column "p.id" or the column "o.id".
版本控制:
psycopg2.__version__
'2.9.1 (dt dec pq3 ext lo64)'
解决方案
如果我正确理解了这个问题,我认为这里的技巧是sql.Identifier
接受一个或多个字符串 ( *strings
),因此您可以拆分点上的列并将两个部分都传递给sql.Identifier
这将构成所需"alias"."column"
结果。
>>> i = sql.Identifier('a', 'col')
>>> i.strings
('a', 'col')
>>> conn = psycopg2.connect(database='test')
>>> cur = conn.cursor()
>>> i.as_string(cur)
'"a"."col"'
引用所有字段可以这样完成:
fields = sql.SQL(', ').join(sql.Identifier(*f.split('.')) for f in total_query_fields)
sql_query = sql.SQL(
"""
SELECT {fields} FROM product p
INNER JOIN owner o ON p.id = o.product_id
WHERE p.id = {jidx} AND (o.dateof_purchase IS NOT NULL
OR o.state = 'checked_out' );"""
).format(fields=fields, jidx=sql.Literal(myid))
结果查询(来自cursor.mogrify
)是
b'\n SELECT "p"."id", "p"."name", "p"."type", "p"."price", "o"."date", "o"."transaction", "p"."warehouse", "p"."location" FROM product p\n INNER JOIN owner o ON p.id = o.product_id\n WHERE p.id = 100 AND (o.dateof_purchase IS NOT NULL\n OR o.state = \'checked_out\' );'
如果您更喜欢使用map
而不是生成器表达式,则可以使用itertools.starmap
from itertools import starmap
fields = sql.SQL(', ').join(
starmap(sql.Identifier, map(lambda f: f.split('.'), total_query_fields)))
推荐阅读
- powershell - 如何获取 Azure 资源的最新 API 版本
- c# - Android - JSON Parsing 抛出意外的字符异常
- maven - 使用环境变量或命令行参数覆盖 Maven 过滤器值
- kubernetes - 部署和 PVC
- c - 等效的常量数组和指针
- youtube-api - Youtube 视频评论状态不能从“heldForReview”更改为任何其他
- php - 使用不同的电子邮件类型注册
- javascript - 如何在javascript中为元素添加拖放属性?
- python-3.x - 在失败时获得更漂亮的回溯
- android - YouTube Sdk : 从 YouTubePlayer 中删除广告