PostgreSQL 9.0 или более поздняя версия:
В последних версиях Postgres (с конца 2010 года) есть string_agg(expression, delimiter)
функция, которая будет выполнять именно то, о чем спрашивался вопрос, даже позволяя указать строку разделителя:
SELECT company_id, string_agg(employee, ', ')
FROM mytable
GROUP BY company_id;
В Postgres 9.0 также добавлена возможность указывать ORDER BY
предложение в любом агрегированном выражении ; в противном случае порядок не определен. Теперь вы можете написать:
SELECT company_id, string_agg(employee, ', ' ORDER BY employee)
FROM mytable
GROUP BY company_id;
Или действительно:
SELECT string_agg(actor_name, ', ' ORDER BY first_appearance)
PostgreSQL 8.4 или более поздняя версия:
PostgreSQL 8.4 (в 2009 году) представил функцию агрегирования,array_agg(expression)
которая объединяет значения в массив. Затем array_to_string()
можно использовать, чтобы дать желаемый результат:
SELECT company_id, array_to_string(array_agg(employee), ', ')
FROM mytable
GROUP BY company_id;
string_agg
для версий до 8.4:
В случае, если кто-то сталкивается с этим в поисках прокладки совместимости для баз данных до 9.0, можно реализовать все, string_agg
кромеORDER BY
пункта.
Таким образом, с приведенным ниже определением это должно работать так же, как в 9.x Postgres DB:
SELECT string_agg(name, '; ') AS semi_colon_separated_names FROM things;
Но это будет синтаксическая ошибка:
SELECT string_agg(name, '; ' ORDER BY name) AS semi_colon_separated_names FROM things;
--> ERROR: syntax error at or near "ORDER"
Протестировано на PostgreSQL 8.3.
CREATE FUNCTION string_agg_transfn(text, text, text)
RETURNS text AS
$$
BEGIN
IF $1 IS NULL THEN
RETURN $2;
ELSE
RETURN $1 || $3 || $2;
END IF;
END;
$$
LANGUAGE plpgsql IMMUTABLE
COST 1;
CREATE AGGREGATE string_agg(text, text) (
SFUNC=string_agg_transfn,
STYPE=text
);
Пользовательские варианты (все версии Postgres)
До 9.0 не было встроенной агрегатной функции для объединения строк. Простейшая пользовательская реализация ( предложенная Вайда Габо в этом посте списка рассылки , среди многих других) заключается в использовании встроенной textcat
функции (которая скрывается за ||
оператором):
CREATE AGGREGATE textcat_all(
basetype = text,
sfunc = textcat,
stype = text,
initcond = ''
);
Вот CREATE AGGREGATE
документация.
Это просто склеивает все строки без разделителя. Чтобы вставить «,» между ними, не имея его в конце, вы можете создать собственную функцию конкатенации и заменить ее на «textcat» выше. Вот один, который я собрал и протестировал на 8.3.12:
CREATE FUNCTION commacat(acc text, instr text) RETURNS text AS $$
BEGIN
IF acc IS NULL OR acc = '' THEN
RETURN instr;
ELSE
RETURN acc || ', ' || instr;
END IF;
END;
$$ LANGUAGE plpgsql;
Эта версия будет выводить запятую, даже если значение в строке равно нулю или пусто, поэтому вы получите такой вывод:
a, b, c, , e, , g
Если вы предпочитаете удалить лишние запятые для вывода этого:
a, b, c, e, g
Затем добавьте ELSIF
проверку к функции следующим образом:
CREATE FUNCTION commacat_ignore_nulls(acc text, instr text) RETURNS text AS $$
BEGIN
IF acc IS NULL OR acc = '' THEN
RETURN instr;
ELSIF instr IS NULL OR instr = '' THEN
RETURN acc;
ELSE
RETURN acc || ', ' || instr;
END IF;
END;
$$ LANGUAGE plpgsql;