对于连接表的array_agg,Postgres返回[null]而不是[]

And*_*Ray 38 postgresql left-join database-normalization

我正在Postgres中选择一些对象及其标签.架构非常简单,有三个表:

对象 id

引用的Tagging id | object_id | tag_id

标签 id | tag

我正在加入这样的表,array_agg用于将标签聚合到一个字段中:

SELECT objects.*,
    array_agg(tags.tag) AS tags,
FROM objects
LEFT JOIN taggings ON objects.id = taggings.object_id
LEFT JOIN tags ON tags.id = taggings.tag_id
Run Code Online (Sandbox Code Playgroud)

但是,如果对象没有标签,Postgres会返回:

[ null ]
Run Code Online (Sandbox Code Playgroud)

而不是一个空数组.如果没有标签,如何返回空数组?我已经仔细检查过我没有返回null标签.

所述骨料文档说"聚结功能可用于在必要时代替零或空数组空".我试过COALESCE(ARRAY_AGG(tags.tag)) as tags但它仍然返回一个null数组.我试过让第二个参数很多东西(例如COALESCE(ARRAY_AGG(tags.tag), ARRAY()),但它们都会导致语法错误.

Tho*_*erl 31

另一种选择可能array_remove(..., NULL)(在9.3推出 IF)tags.tagNOT NULL(否则你可能想保留NULL值的数组中,但在这种情况下,你不能在一个现有的区分NULL标记和NULL由于标签LEFT JOIN):

SELECT objects.*,
     array_remove(array_agg(tags.tag), NULL) AS tags,
FROM objects
LEFT JOIN taggings ON objects.id = taggings.object_id
LEFT JOIN tags ON tags.id = taggings.tag_id
Run Code Online (Sandbox Code Playgroud)

如果未找到任何标记,则返回空数组.


Ale*_*nov 15

从9.4开始,可以限制聚合函数调用仅继续匹配特定条件的行: array_agg(tags.tag) filter (where tags.tag is not null)

  • 如果所有 `tags.tag` 均为 null,则返回 `null`,而不是空数组。有没有办法默认返回空数组? (2认同)
  • `coalesce(array_agg(tags.tag)过滤器(tags.tag不为null),'{}') (2认同)

Nic*_*nes 13

文档说当你聚合零行时,你得到一个空值,关于使用的注释COALESCE正在解决这个特定的情况.

这并不适用于您的查询,因为的方式LEFT JOIN表现-当它发现零点匹配的行,它返回一个排,用空值填充(和一个空行的聚集物是与一个空元素的数组).

你也许会盲目地更换[NULL][]输出,但你失去之间distiguish的能力,没有标签的物体标签的对象,其中tags.tag为空.您的应用程序逻辑和/或完整性约束可能不允许第二种情况,但如果它确实设法潜入,则更有理由不抑制空标记.

您可以LEFT JOIN通过检查连接条件另一侧的字段是否为空来标识没有标记的对象(或者通常,告知何时找不到匹配项).所以在你的情况下,只需更换

array_agg(tags.tag)
Run Code Online (Sandbox Code Playgroud)

CASE
  WHEN taggings.object_id IS NULL
  THEN ARRAY[]::text[]
  ELSE array_agg(tags.tag)
END
Run Code Online (Sandbox Code Playgroud)

  • 我认为这是一个更好的解释和答案,除了我注意到它要求将`taggings.object_id`添加到`GROUP BY`子句中以避免语法错误`ERROR:42803:列“ taggings.object_id”必须出现在GROUP BY子句中或在聚合函数中使用-添加此子句是否会根本改变最终结果? (2认同)