Sql query to return elements with same ID as comma separated string
I have two tables, table1
has a entry_ID
, entry_date
and other entry information. table2
has entry_ID
and entry_subject
. Each entry_ID
can have arbitrarily many entry_subjects
.
I want a query that will return an entry_ID
, entry_date
, and a list of the subjects corresponding to that entry separated by commas.
The first step in this seems to be just getting a query that returns an entry_ID
and a comma separated list of subjects from table2
. Once I have that the join should be easy.
WITH RECURSIVE CTE (entry_ID, subjectlist, subject, length)
AS ( SELECT entry_ID, cast( '' as varchar(8000))
, cast( '' as varchar(8000)), 0
FROM table2
GROUP BY entry_ID
UNION ALL
SELECT t2.entry_ID,
cast(subjectlist || CASE length = 0 THEN '' ELSE ', ' END
|| entry_subject AS varchar(8000) ),
cast (t2.entry_subject as varchar(8000)),
length +1
FROM CTE c
INNER JOIN table2 t2
on c.entry_ID=t2.entry_ID where t2.entry_subject > c.subject)
SELECT entry_ID, subjectlist FROM (
SELECT entry_ID, subjectlist, RANK() OVER (
PARTITION BY entry_ID order by length DESC)
FROM CTE) D (entry_ID, subjectlist, rank) where rank = 1;
And it works, I get the response I expect. To achieve my final goal the query I use is this:
SELECT t1.* t2.subjectlist FROM table1
JOIN (ABOVE QUERY) AS t2 on t1.entry_ID=t2.entry_ID;
This seems very unwieldy. Is this really 开发者_运维知识库the best way to do this?
If I understand correctly, then there should be a much simpler solution.
Test setup
According to your description - you could have done that for us:
CREATE TABLE table1 (
entry_id int4 PRIMARY KEY
, entry_date date
);
CREATE TABLE table2 (
entry_id int4 REFERENCES table1 (entry_id)
, entry_subject text
, PRIMARY KEY (entry_id, entry_subject)
);
INSERT INTO table1 VALUES (1, '2011-09-01'), (2, '2011-09-02'),(3, '2011-09-03');
INSERT INTO table2 VALUES (1, 'foo1'), (2, 'foo2'), (2, 'bar2')
, (3, 'foo3'), (3, 'baz3'), (3, 'bar3');
Answer
string_agg()
requires Postgres 9.0+
SELECT t1.entry_id, t1.entry_date
, string_agg(t2.entry_subject, ', ') AS entry_subjects
FROM table1 t1
JOIN table2 t2 USING (entry_id)
GROUP BY 1,2
ORDER BY 1;
entry_id | entry_date | entry_subjects
----------+------------+------------------
1 | 2011-09-01 | foo1
2 | 2011-09-02 | bar2, foo2
3 | 2011-09-03 | baz3, bar3, foo3
Or, if you want the entry_subjects sorted:
SELECT DISTINCT ON (1)
t1.entry_id
, t1.entry_date
, string_agg(t2.entry_subject, ', ') OVER (
PARTITION BY t1.entry_id ORDER BY t2.entry_subject
RANGE BETWEEN UNBOUNDED PRECEDING
AND UNBOUNDED FOLLOWING) AS entry_subjects
FROM table1 t1
JOIN table2 t2 USING (entry_id)
ORDER BY 1;
entry_id | entry_date | entry_subjects
----------+------------+------------------
1 | 2011-09-01 | foo1
2 | 2011-09-02 | bar2, foo2
3 | 2011-09-03 | bar3, baz3, foo3
You could do the same with a subselect on table2
to first ORDER BY entry_subject
.
精彩评论