2017-10-04 2 views
0

L'idée est d'interroger une table article lorsqu'un article possède un tag donné, puis STRING_AGG tous les balises (même non apparentées) qui appartiennent à cette ligne d'article.SELECT à partir de la sous-requête sans avoir à spécifier toutes les colonnes dans GROUP BY

tables Exemple et requête:

CREATE TABLE article (id SERIAL, body TEXT); 
CREATE TABLE article_tag (article INT, tag INT); 
CREATE TABLE tag (id SERIAL, title TEXT); 

SELECT DISTICT ON (id) 
    q.id, q.body, STRING_AGG(q.tag_title, '|') tags 
FROM (
    SELECT a.*, tag.title tag_title 
    FROM article a 
     LEFT JOIN article_tag x ON a.id = tag.article 
     LEFT JOIN tag ON tag.id = x.tag 
    WHERE tag.title = 'someTag' 
) q 
GROUP BY q.id 

L'exécution de ce qui précède, postgres exigent que le q.body doit être inclus dans GROUP BY:

ERROR: column "q.body" must appear in the GROUP BY clause or be used in an aggregate function 

Si je comprends bien, il est parce que la sous-requête q ne le fait pas inclure n'importe quelle touche PRIMAIRE.

Je pensais naïvement que le DISTINCT ON complèterait cela, mais cela ne semble pas le cas.

Existe-t-il un moyen de marquer une colonne dans une sous-requête comme PRIMARY afin que nous ne devons pas répertorier toutes les colonnes dans la clause GROUP BY?

Si nous devons lister toutes les colonnes dans la clause GROUP BY, cela implique-t-il un coût de perf significatif?

EDIT: d'élaborer, depuis PostgreSQL 9.1 vous n'avez pas à offre les clés non-primaire (à savoir dépendant fonctionnellement) lors de l'utilisation GROUP BY, par exemple requête suivante fonctionne très bien:

SELECT a.id, a.body, STRING_AGG(tag.title, '|') tags 
FROM article a 
    LEFT JOIN article_tag x ON a.id = tag.article 
    LEFT JOIN tag ON tag.id = x.tag 
GROUP BY a.id 

Je me demandais si je peux tirer parti du même comportement, mais avec une sous-requête (en indiquant en quelque sorte que q.id est une clé primaire).

+1

double possible de [fonction GROUP BY/agrégat confusion dans SQL] (https://stackoverflow.com/questions/4611897/group-by-aggregate-function-confusion-in-sql) – waka

+1

Vous devez * grouper * sur toutes les colonnes non-agrégées, en raison de l'utilisation d'une fonction d'agrégat. – eurotrash

+0

édité pour expliquer ce que je veux dire par ne pas avoir à spécifier les clés non primaires dans GROUP BY (donc pourquoi ce n'est pas un doublon de la question ci-dessus) – Dwelle

Répondre

1

Malheureusement, cela ne fonctionne pas lorsque vous enveloppez votre clé primaire dans la sous-requête et je ne connais aucun moyen de la "marquer" comme vous l'avez suggéré.

Vous pouvez essayer cette solution de contournement à l'aide window function et distinct:

CREATE TABLE test1 (id serial primary key, name text, value text); 
CREATE TABLE test2 (id serial primary key, test1_id int, value text); 

INSERT INTO test1(name, value) 
      values('name1', 'test01'), ('name2', 'test02'), ('name3', 'test03'); 

INSERT INTO test2(test1_id, value) 
      values(1, 'test1'), (1, 'test2'), (3, 'test3'); 

SELECT DISTINCT ON (id) id, name, string_agg(value2, '|') over (partition by id) 
    FROM (SELECT test1.*, test2.value AS value2 
      FROM test1 
      LEFT JOIN test2 ON test2.test1_id = test1.id) AS sub; 

id name string_agg 
1 name1 test1|test2 
2 name2 null 
3 name3 test3 

Demo

0

Le problème est dans SELECT externe - vous devez agréger les colonnes soit par eux. Postgres veut que vous spécifiez quoi faire avec q.body - groupez-le ou calculez l'agrégat. Ça a l'air un peu bizarre mais ça devrait marcher.

SELECT DISTICT ON (id) 
    q.id, q.body, STRING_AGG(q.tag_title, '|') tags 
FROM (
    SELECT a.*, tag.title tag_title 
    FROM article a 
     LEFT JOIN article_tag x ON a.id = tag.article 
     LEFT JOIN tag ON tag.id = x.tag 
    WHERE tag.title = 'someTag' 
) q 
GROUP BY q.id, q.body 
--    ^^^^^^ 

Une autre façon est de faire une requête pour obtenir id et agrégées tags puis rejoindre body à elle. Si vous voulez, je peux faire un exemple.

+0

Pas besoin, je comprends cela.Ma question était de savoir si je pouvais contourner le problème de la même façon que si je pouvais sélectionner directement dans la table 'article', et non dans une sous-requête. – Dwelle