Postgresでいくつかのオブジェクトとそのタグを選択しています。スキーマは非常にシンプルで、3つのテーブルがあります。
オブジェクトid
タグ付け_id | object_id | tag_id
_
タグ_id | tag
_
_array_agg
_を使用してタグを1つのフィールドに集約して、次のようにテーブルを結合しています。
_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
_
ただし、オブジェクトにタグがない場合、Postgresはこれを返します。
_[ null ]
_
空の配列の代わりに。 タグがないときに空の配列を返すにはどうすればよいですか?nullタグが返されないことを再確認しました。
集合ドキュメント 「必要に応じて、合体機能を使用してゼロまたは空の配列をヌルに置き換えることができます」と言います。 COALESCE(ARRAY_AGG(tags.tag)) as tags
を試しましたが、それでもnullの配列を返します。私は2番目のパラメーターをさまざまなもの(COALESCE(ARRAY_AGG(tags.tag), ARRAY())
など)にしようとしましたが、それらはすべて構文エラーになります。
別のオプションはarray_remove(..., NULL)
( 9.3で導入 )if tags.tag
はNOT 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
タグが見つからない場合、空の配列が返されます。
9.4以降では、集約関数呼び出しを制限して、特定の基準に一致する行のみを続行できます:array_agg(tags.tag) filter (where tags.tag is not null)
ドキュメントでは、ゼロ行を集約するとnull値が得られると述べており、COALESCE
の使用に関する注意事項はこの特定のケースに対処しています。
LEFT JOIN
の動作方法により、これはクエリには適用されません。一致する行をzeroが検出されると、oneヌルで満たされた行(および1つのヌル行の集合は、1つのヌル要素を持つ配列です)。
出力で[NULL]
を[]
に盲目的に置き換えたいと思うかもしれませんが、それからタグのないオブジェクトとタグ付きオブジェクトを区別する能力を失いますtags.tag
はnullです。アプリケーションロジックや整合性の制約により、この2番目のケースが許可されない場合がありますが、それが何とか忍び込んだ場合にnullタグを抑制しない理由です。
結合条件の反対側のフィールドがnullであるかどうかをチェックすることにより、タグのないオブジェクトを識別できます(または、一般に、LEFT JOIN
が一致しなかった場合に通知します)。だからあなたの場合、単に置き換える
array_agg(tags.tag)
と
CASE
WHEN taggings.object_id IS NULL
THEN ARRAY[]::text[]
ELSE array_agg(tags.tag)
END
私はこれがそれを行うことがわかった:
COALESCE(ARRAY_AGG(tags.tag), ARRAY[]::TEXT[])
...仮定して tags.tag
はテキストタイプです。
Postgresの古いバージョンでこれが機能しないかどうかはわかりませんが、ver。 9.6そしてそれは動作しているようで、CASE WHEN x IS NULL... GROUP BY...
以前に提供されたソリューション。
ドキュメントには、NULL
を含む配列が返されると書かれています。それを空の配列に変換したい場合、ちょっとした魔法をかける必要があります:
SELECT objects.id,
CASE WHEN length((array_agg(tags.tag))[1]) > 0
THEN array_agg(tags.tag)
ELSE ARRAY[]::text[] END AS tags
FROM objects
LEFT JOIN taggings ON objects.id = taggings.object_id
LEFT JOIN tags ON tags.id = taggings.tag_id
GROUP BY 1;
これは、タグがtext
タイプ(またはそのバリアントのいずれか)であると想定しています。必要に応じてキャストを変更します。
ここでのコツは、[NULL]
配列の最初の(そして唯一の)要素の長さが0であるため、tags
からデータが返された場合は集約を返し、そうでない場合は空の配列を作成します正しいタイプ。
ちなみに、coalesce()
の使用に関するドキュメントの記述は少し不器用です。つまり、結果としてNULL
が必要ない場合は、coalesce()
を使用してそれを0
または選択した他の出力に変換します。ただし、これを配列の代わりにarray elementsに適用する必要があります。これは、あなたの場合、解決策を提供しません。