Запрос Sql для возврата элементов с тем же идентификатором, что и через запятую - PullRequest
2 голосов
/ 19 сентября 2011

У меня есть две таблицы, table1 имеет entry_ID, entry_date и другую информацию для ввода. table2 имеет entry_ID и entry_subject. Каждый entry_ID может иметь произвольно много entry_subjects.

Мне нужен запрос, который вернет entry_ID, entry_date и список предметов, соответствующих этой записи, через запятую.

Первым шагом в этом, похоже, является получение запроса, который возвращает entry_ID и разделенный запятыми список субъектов из table2. Как только у меня будет это, соединение должно быть легким.
Я адаптировал рекурсивный метод CTE с этого сайта : для моего случая:

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;

И это работает, я получаю ожидаемый ответ. Для достижения моей конечной цели я использую следующий запрос:

SELECT t1.* t2.subjectlist FROM table1 
    JOIN (ABOVE QUERY) AS t2 on t1.entry_ID=t2.entry_ID; 

Это кажется очень громоздким. Это действительно лучший способ сделать это?

1 Ответ

2 голосов
/ 19 сентября 2011

Если я правильно понимаю, тогда должно быть намного более простое решение.

Настройка теста

Согласно вашему описанию - вы могли бы сделать это для нас:

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');  

Ответ

string_agg() требуется 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

Или, если вы хотите, чтобы entry_subjects отсортировано :

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

Вы можете сделать то же самое с подвыбором от table2 до первого ORDER BY entry_subject.

...