使用 SQLAlchemy 从 PostgreSQL 行对象中获取单个值

Posted

技术标签:

【中文标题】使用 SQLAlchemy 从 PostgreSQL 行对象中获取单个值【英文标题】:Get single value from PostgreSQL row object using SQLAlchemy 【发布时间】:2013-05-10 18:57:59 【问题描述】:

我正在将 Python 与 SQLAlchemy(在我的特定情况下是 GeoAlchemy)一起使用,并且我有一个查询会产生单列的行对象。我希望从这些行对象中提取特定值,但 SQLAlchemy 将行对象解释为单个实体(如果我是正确的,将它们作为字符串返回)。如何在不使用 Python 解析它们的情况下更干净地取回这些单独的值?

我的现实生活用例: PostgreSQL 的 PostGIS 扩展提供了一个名为 ST_IsValidDetail 的函数。此函数旨在返回一个valid_detail 行,该行由布尔值valid、一个字符串reason 和一个发生无效的几何location 组成。我已经放弃了 PostGIS 标签,因为我觉得这个问题比这更笼统。我的查询类似于SELECT ST_IsValidDetail('POINT(1 1)'::GEOMETRY);(当然,几何形状更复杂)。

【问题讨论】:

【参考方案1】:

你可以使用:

SELECT (ST_IsValidDetail(the_value)).* FROM the_table;

... 但不幸的是,PostgreSQL 实际上对每一行执行一次 ST_IsValidDetail 函数。作为一种解决方法,您可以稍微修改查询,通过公共表表达式实现,然后在第二遍中提取元组:

WITH interim_result(v) AS (
    SELECT ST_IsValidDetail(the_value) FROM the_table
)
SELECT (v).* FROM interim_result;

(v) 周围的括号需要告诉解析器您指的是一个值,而不是表名。

演示:

CREATE OR REPLACE FUNCTION multirows(x IN integer, a OUT integer, b OUT integer, c OUT integer) AS
$$
BEGIN
    RAISE NOTICE 'multirows(%) invoked', x;
    a := x;
    b := x+1;
    c := x+2;
    RETURN;
END;
$$ LANGUAGE plpgsql;

craig=> SELECT multirows(x) FROM generate_series(1,2) x;
NOTICE:  multirows(1) invoked
NOTICE:  multirows(2) invoked
 multirows 
-----------
 (1,2,3)
 (2,3,4)
(2 rows)

craig=> SELECT (multirows(x)).* FROM generate_series(1,2) x;
NOTICE:  multirows(1) invoked
NOTICE:  multirows(1) invoked
NOTICE:  multirows(1) invoked
NOTICE:  multirows(2) invoked
NOTICE:  multirows(2) invoked
NOTICE:  multirows(2) invoked
 a | b | c 
---+---+---
 1 | 2 | 3
 2 | 3 | 4
(2 rows)




craig=> WITH interim(v) AS (SELECT multirows(x) FROM generate_series(1,2) x)
SELECT (v).* FROM interim;
NOTICE:  multirows(1) invoked
NOTICE:  multirows(2) invoked
 a | b | c 
---+---+---
 1 | 2 | 3
 2 | 3 | 4
(2 rows)

【讨论】:

非常感谢您的努力。不幸的是,我一直在寻找一种通过 Python 实现此目的的方法,特别是 SQLAlchemy ORM。对困惑感到抱歉;如果您对使问题更清楚有任何建议,请告诉我。 通常使用 ORM 的人可以将原生 SQL 方法转换回他们的 ORM 的查询语言方言。我倾向于说明它在 Pg 中是如何工作的,以防万一。 真;我也养成了一个习惯,或者首先直接针对数据库编写查询。我已经知道.columnname 语法,虽然我不知道.* 语法。不过,我不知道这些是如何转化为 SQLAlchemy 的。 我今天遇到了这个问题,很久以前(但在这个问题之后),我开始意识到我试图构建具有中等复杂性的查询是多么荒谬和不可维护SQLAlchemy 对象。我不应该如此害怕在代码中使用原始 SQL。正确参数化,它不会带来任何安全风险,并且更加灵活和可维护。因此,我已将接受的答案更改为这个答案。要运行原始 SQL,请参阅 here。 @jpmc26 是的。 ORM 非常适合自动化简单的事情,但是当您有棘手的、性能关键的、复杂的或特定于平台的代码时,能够绕过它们是很好的。【参考方案2】:

我从ST_IsValidDetail 页面上的示例中找到了一种方法。显然,以下语法是有效的:

SELECT gid, reason(ST_IsValidDetail(the_geom)), ST_AsText(location(ST_IsValidDetail(the_geom)))

注意函数调用周围的reasonlocation“调用”; ST_IsValidDetail 返回的行中的列名几乎被视为函数。事实证明,你可以欺骗 SQLAlchemy 做同样的事情。 (假设 session 是之前设置的 Session 对象,db_geom 是 GeoAlchemy 几何对象。)

from sqlalchemy import func as sqlfunc
result = session.query(sqlfunc.reason(sqlfunc.ST_IsValidDetail(db_geom)), sqlfunc.ST_AsText(sqlfunc.location(sqlfunc.ST_IsValidDetail(db_geom)))).one()

result[0] 将包含原因,result[1] 将包含位置的 WKT。 (我们可以使用label 为列指定实际名称。)

在不使用 PostGIS 功能的情况下对其进行修剪:

from sqlalchemy import func as sqlfunc
result = session.query(sqlfunc.columnname(sqlfunc.myrowfunc('some input string')).label('mylabel')).one()

这使得 SQLAlchemy 认为columnname 是一个函数,并以表格形式向数据库发送 SQL

SELECT columnname(myrowfunc('some input string')) AS mylabel;

我还没有尝试过它,但是如果有一种方法可以让 SQLAlchemy 将我们的行视为我们选择FROM 的表,那也可以。 (请参阅ST_IsValidDetail 页面的最底部。)

【讨论】:

每次看到这样的事情都会让我想起为什么我尽可能避免使用 ORM ;-)

以上是关于使用 SQLAlchemy 从 PostgreSQL 行对象中获取单个值的主要内容,如果未能解决你的问题,请参考以下文章

Postgresq9.6主从部署

PostgreSQ 连接问题 FATAL: no pg_hba.conf entry for host

无法从 sqlalchemy 插入到 mysql

使用 SQLAlchemy 从 PostgreSQL 行对象中获取单个值

使用 sqlalchemy 从 PostgreSQL 查询返回 Pandas 数据框

使用 SQLAlchemy 从反射表中删除的行