2017-10-17 4 views
1

Mon objectif avec cette requête est de tout extraire de la table communities, ainsi qu'un objet array_agg de jsonb à partir d'une jointure. Cela fonctionne bien dans le cas unsorted ainsi:trier dans une requête array_agg imbriquée

select communities.*, 
    (
    select array_agg(jsonb_build_object('id', community_permissions.document_id)) 
    from documents as conversations 
     join community_permissions on community_permissions.document_id = conversations.id 
     where conversations.published = true 
     and community_permissions.community_id = communities.id 
) as conversations 
    from communities where communities.id = 110; 

cela renvoie une ligne avec des colonnes normales pour les colonnes en communities, et un tableau de JSON qui ressemble à [{ id: 1 }, { id: 2 } ...] dans la colonne conversations. Tout va bien, la requête marche plutôt bien et je peux déclarer en SQL une ligne et ses relations importantes en même temps. Je peux même filtrer la valeur retournée sur une jointure d'une table différente (seules les conversations publiées sont retournées dans cette liste). Ce que je veux faire maintenant est de trier les conversations basées sur un attribut dans la table de documents (aliasé aux conversations ci-dessus). L'approche naïve:

select communities.*, 
    (
    select array_agg(jsonb_build_object('id', community_permissions.document_id)) 
    from documents as conversations 
     join community_permissions on community_permissions.document_id = conversations.id 
     where conversations.published = true 
     and community_permissions.community_id = communities.id 
     order by conversations.updated_at desc 
) as conversations 
    from communities where communities.id = 110; 

ne fonctionne pas - le promeneur colonne veut la valeur conversations.updated_at être dans la clause group_by. Si j'ajoute un groupe par clause comme ceci:

select communities.*, 
    (
    select array_agg(jsonb_build_object('id', community_permissions.document_id)) 
    from documents as conversations 
     join community_permissions on community_permissions.document_id = conversations.id 
     where conversations.published = true 
     and community_permissions.community_id = communities.id 
     group by conversations.updated_at 
     order by conversations.updated_at desc 
) as conversations 
    from communities where communities.id = 110; 

Il semble que le array_agg n'agrège en fait la réponse - je reçois une erreur « plus d'une ligne retournée par une sous-requête utilisé comme une expression ». Je peux mettre un limit 1 après le order by, et la requête s'exécute, mais alors je reçois seulement une conversation dans la colonne array_agg - la plus récente au moins. Donc, il semble que array_agg ne soit pas en train de s'agrandir dans un tableau, pour ainsi dire.

Je suis capable de faire cette requête en prenant la chose en dehors et en utilisant un CTE:

with q1 as (
    select conversations.id as id, community_permissions.community_id as community_id 
    from documents as conversations 
    join community_permissions on community_permissions.document_id = conversations.id 
    where conversations.published = true order by conversations.updated_at desc 
) 
select communities.id, (
    select array_agg(jsonb_build_object('id', q1.id))) as conversations 
    from communities join q1 on q1.community_id = communities.id 
    where communities.id = 110 
    group by communities.id; 

mais c'est assez inefficace, l'explicateur indique qu'il est le tri de la totalité de la table de conversations chaque fois que la requête est exécutée et une communauté donnée ne partage qu'un petit pourcentage de la table de conversations via community_permissions. Il est également insatisfaisant parce que j'essaie de modulariser mes requêtes afin que le backend puisse décider, "hé, vous aurez besoin de ces valeurs" et les enfoncer dans la requête SQL et tout faire en même temps en une seule fois. requête, au lieu de tirer la communauté et ensuite interroger manuellement pour chaque relation (il y en a généralement plus d'une - les communautés ont des membres, des conversations, des suiveurs, etc.). Dans le pire des cas, je peux le faire - il suffit de gérer la récupération supplémentaire dans le calque de noeud principal et de l'assembler, mais tout mettre dans les sous-requêtes semble plus élégant (jusqu'à ce que j'avais besoin de cette fonction de tri).

Répondre

1

Vous pouvez spécifier l'ordre dans une fonction d'agrégation.

Essayez:

select communities.*, 
    (
    select array_agg(jsonb_build_object('id', community_permissions.document_id) order by conversations.updated_at desc) 
    from documents as conversations 
    join community_permissions on community_permissions.document_id = conversations.id 
    where conversations.published = true 
     and community_permissions.community_id = communities.id 
) as conversations 
from communities where communities.id = 110; 
1

l'une de ces situations "écrire un post long sur stackoverflow et trouver la réponse dix minutes plus tard".

L'astuce, pour référence future, est que je passais la commande au mauvais endroit. Cela fonctionne:

select communities.*, 
    (
    select array_agg(jsonb_build_object('id', community_permissions.document_id) order by conversations.updated_at desc) 
    from documents as conversations 
     join community_permissions on community_permissions.document_id = conversations.id 
     where conversations.published = true 
     and community_permissions.community_id = communities.id 
) as conversations 
    from communities where communities.id = 110; 

Notez que le order by est maintenant dans l'appel array_agg plutôt qu'à l'intérieur du sous-requête.