如何在 Django 数据库查询中查询列的元组?

Posted

技术标签:

【中文标题】如何在 Django 数据库查询中查询列的元组?【英文标题】:How to query tuples of columns in Django database queries? 【发布时间】:2020-07-08 17:49:10 【问题描述】:

我有一些表 ports(switch_ip, slot_number, port_number, many, more, columns) 并希望使用 Django 实现以下 PostgreSQL 查询:

SELECT switch_ip, array_agg((slot_number, port_number, many, more, columns) ORDER BY slot_number, port_number) info
FROM ports
GROUP BY switch_ip
ORDER BY switch_ip

使用django.contrib.postgres.aggregates 这是我目前得到的结果:

Port.objects \
    .values('switch_ip') \
    .annotate(
        info=ArrayAgg('slot_number', ordering=('slot_number', 'port_number'))
    ) \
    .order_by('switch_ip')

我无法在ArrayAgg 中包含多于一列。 ArrayAgg(a, b, c)ArrayAgg((a, b, c))ArrayAgg([a, b, c]) 似乎都不起作用。解决方法可能涉及为每列单独的ArrayAggs,并且每列都具有相同的顺序。我会鄙视这一点,因为我有很多专栏。有没有更好的解决方法,可能更底层?

我怀疑这不是 ArrayAgg 本身的问题,而是一般的元组表达式。有没有办法在 Django 查询中使用元组?例如,对应的 Django 是什么:

SELECT switch_ip, (slot_number, port_number, many, more, columns) info
FROM ports

如果这在 Django 中还不能实现,那么实现起来有多可行?

【问题讨论】:

只是为了理解你的问题,为什么要在一个元组中返回这些值,而不是仅仅在python中构建元组? @GrandPhuba 解决我最初的问题:不必使用多个 info1=ArrayAgg(a, ordering=o), info2=ArrayAgg(b, ordering=o), info3=ArrayAgg(c, ordering=o) 进行注释,其中 o 每次都是相同的顺序。如果ArrayAgg 可以在元组上工作,那么它只是类似于info=ArrayAgg((a, b, c), ordering=o)。因为我怀疑这不是 ArrayAgg 特有的问题,所以我将问题扩大到 Django 中的元组。 【参考方案1】:

做了更多的研究后,我想可以添加缺少的元组功能,如下所示:

    创建一个名为TupleField 的新模型字段类型。该实现可能看起来有点类似于django.contrib.postgres.fields.ArrayFieldTupleField 会很尴尬,因为我认为任何 RDBMS 都不允许将复合类型用作列类型,因此 TupleField 的使用将仅限于(可能是中间?)查询结果。 创建一个新的django.db.models.Expression 子类,它自己包装多个表达式(通常类似于Func,因此查看Func 的实现可能是值得的)并计算为TupleField。例如,将此子类命名为 TupleExpression

然后我可以简单地用ArrayAgg(TupleExpression('slot_number', 'port_number', 'many', 'more', 'columns'), ordering=('slot_number', 'port_number')) 注释来解决我原来的问题。这将使用正确排序的元组数组注释每个switch_ip,其中每个元组代表一个交换机端口。

【讨论】:

否决这一点,因为采用任何 NoSQL 数据库可能比使用 Django 内部实现像元组这样基本的东西更有效率和满足感。哦,等等,我不能。那为什么会显示向下的箭头呢?【参考方案2】:

我花了很多时间寻找可行的解决方案,这里有一个完整的代码示例。

    您需要在模板中用方括号定义数组“函数”
from django.db.models.expressions import Func

class Array(Func):
    template = '%(function)s[%(expressions)s]'
    function = 'ARRAY'
    您需要定义输出字段格式(它必须是一些 django 字段的数组)。例如字符串数组
from django.contrib.postgres.fields import ArrayField
from django.db.models.fields import CharField

out_format = ArrayField(CharField(max_length=200))
    最后制作一个 ArrayAgg 表达式
from django.db.models import F

annotate = '2-fields': ArrayAgg(Array(F('field1'), F('field2'), output_field=out_format), distinct=True) 
model.objects.all().annotate(**annotate)
    (可选)如果 field1 或 field2 不是 CharFields,您可以包含 Cast 作为 Array 的参数
from django.db.models.functions import Cast


annotate = '2-fields': ArrayAgg(Array(Cast(F('field1'), output_field=CharField(max_length=200)), F('field2'), output_field=out_format), distinct=True) 

【讨论】:

以上是关于如何在 Django 数据库查询中查询列的元组?的主要内容,如果未能解决你的问题,请参考以下文章

一个SQL查询不会写

python 如何获取从数据库查询返回的元组并将它们转换为命名元组。

选择包含混合单引号和双引号的元组的查询

如何将`inSet`与列的元组一起使用?

PostgreSQL中如何查询索引的元数据

数据库面试系列之一:内连接和外连接