我试图解决的问题是从 JSON 字段获取
feature
数据,以便保留结果列名称。这意味着尝试了解是否有更好和/或更安全的方法来使用 psycopg(3) 动态定义列别名。
我目前已实施以下解决方案:
# imports
import psycopg
from psycopg import Connection, sql
from psycopg.rows import dict_row
# constants
project = "project_1"
location = "location_1"
data_table = "table_1"
features = ["feature_1", "feature_2"]
start_dt = "2024-04-22T16:00:00"
end_dt = "2024-04-22T17:00:00"
__user = "My"
__password = "I am supposed to be extra-complicated!"
__host = "localhost"
__database = "db"
__port = 5432
# connection
connection = psycopg.connect(
user=__user,
password=__password,
host=__host,
dbname=__database,
port=__port,
row_factory=dict_row,
)
# Adapted from https://github.com/psycopg/psycopg2/issues/791#issuecomment-429459212
def alias_identifier(
ident: str | tuple[str], alias: str | None = None
) -> sql.Composed:
"""Return a SQL identifier with an optional alias."""
if isinstance(ident, str):
ident = (ident,)
if not alias:
return sql.Identifier(*ident)
# fmt: off
return sql.Composed([sql.Literal(*ident), sql.SQL(" AS "),
sql.Identifier(alias)])
# fmt: on
# source query str
QUERY = """SELECT
current_database() AS project,
timestamp,
location,
feature -> {feature}
FROM {data_table}
WHERE lower(location) = {location}
AND timestamp BETWEEN {start_dt} AND {end_dt}
"""
# SQL query
query = sql.SQL(QUERY).format(
feature=sql.SQL(", feature -> ").join([alias_identifier(m, alias=m) for m in features]),
data_table=sql.Identifier(data_table),
location=sql.Literal(location),
start_dt=sql.Literal(start_dt),
end_dt=sql.Literal(end_dt),
)
print(query.as_string(connection))
SELECT
current_database() AS project,
timestamp,
location,
feature -> 'feature_1' AS "feature_1", feature -> 'feature_2' AS "feature_2"
FROM "table_1"
WHERE lower(location) = 'location_1'
AND timestamp BETWEEN '2024-04-22T16:00:00' AND '2024-04-22T17:00:00'
该解决方案提供了预期的结果,尽管我想知道它是否违反了任何 psycopg 指南以及是否有更好的方法来实现我想要的。
在我的用法中我这样做:
... WHERE lower(location) = %(location)s
AND timestamp BETWEEN %(start_dt)s AND %(end_dt)s
然后做:
cur.execute(query, {"location": location , "start_dt": start_date, "end_date": end_dt:})