SQLAlchemy 与 Postgresql 结合使用构建来自具有未知长度表名列表的查询

47 阅读3分钟

我们正在处理时序数据,并试图避免在所有记录混合在一起的单个记录表上的每一行中存在多对一关系(因为这些时序数据系列都具有不同的列数)。 相反,我们希望在多个表之间拆分记录,以将各个数据系列分开。

我们有很多这样的表:

系列 1 记录表:

CREATE TABLE records_1 (
    id BIGSERIAL PRIMARY KEY,
    level DOUBLE PRECISION
    #... more columns
);
CREATE TABLE records_2 (
    id BIGSERIAL PRIMARY KEY,
    level DOUBLE PRECISION
    #... more columns
);
CREATE TABLE records_34 (
    id BIGSERIAL PRIMARY KEY,
    level DOUBLE PRECISION
    #... more columns
);

系列 2 记录表

CREATE TABLE records_101 (
    id BIGSERIAL PRIMARY KEY,
    level DOUBLE PRECISION,
    height DOUBLE PRECISION
    #... more columns
);
CREATE TABLE records_102 (
    id BIGSERIAL PRIMARY KEY,
    level DOUBLE PRECISION,
    height DOUBLE PRECISION
    #... more columns
);

我们有一个类,用字符串表示与该类关联的表名:

class Serie(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    record_tables = db.Column(db.String(255))

示例:

s1 = Serie() 
s1.record_tables = 'records_1, records_2, records_34'
s2 = Serie()
s2.record_tables = 'records_101, records_102'

我们希望能够根据 record_tables 字符串选择与给定系列相关联的所有记录。拆分并去除空格很容易,但我们如何动态构建查询?我们尝试用“对于 record_tables_list 中的每个表,从表中选择所有记录并从列表中的所有表中返回所有记录”来构建查询,但我们希望在一次 SQL 查询中完成,而不是在 Python 中将结果追加到列表中(我们认为这会更慢)。我们认为我们应该使用 UNION,因为我们在 SQL 中手动创建了这样的查询。但如何用 Serie.record_tables 中数量未知的表名来实现?

更新: 好吧,所以原始查询似乎在连接已清除的 record_tables 列表时有效(它依赖于用户输入(我们自己的输入),因此我们必须清除它以消除错别字等):

tables = ', '.join([x.strip() for x in record_tables.split(',')])
raw_query = 'SELECT * FROM ' + tables
results = db.engine.execute(raw_query)
for row in raw_query:
    print row    # (1L, 123.0, 1L, 456.0)

编辑:不,这不起作用。

2、解决方案

我们可以使用 SQLAlchemy 的 text() 函数和 bindparams 参数来构建动态查询。以下是如何实现的:

from sqlalchemy import text

def get_records(record_tables):
    # 清理表名列表
    tables = ', '.join([x.strip() for x in record_tables.split(',')])

    # 构建查询字符串
    query = text(f'SELECT * FROM {tables}')

    # 执行查询
    results = db.engine.execute(query)

    # 返回结果
    return results

现在,我们可以使用此函数从给定系列中获取所有记录。例如:

record_tables = 'records_1, records_2, records_34'
results = get_records(record_tables)

for row in results:
    print(row)

这将打印出所有来自表 records_1、records_2 和 records_34 的记录。

代码例子

from sqlalchemy import text

def get_records(record_tables):
    # 清理表名列表
    tables = ', '.join([x.strip() for x in record_tables.split(',')])

    # 构建查询字符串
    query = text(f'SELECT * FROM {tables}')

    # 执行查询
    results = db.engine.execute(query)

    # 返回结果
    return results

# 示例
record_tables = 'records_1, records_2, records_34'
results = get_records(record_tables)

for row in results:
    print(row)

这个解决方案同时考虑了查询性能和代码简洁性。我们使用 SQLAlchemy 的 text() 函数和 bindparams 参数来构建动态查询,这是一种非常灵活和强大的方法,可以构建任意复杂的查询。