Pergunta

Estou procurando uma forma de concatenar as strings de um campo dentro de um grupo por consulta.Então, por exemplo, eu tenho uma tabela:

ID   COMPANY_ID   EMPLOYEE
1    1            Anna
2    1            Bill
3    2            Carol
4    2            Dave

e eu queria agrupar por company_id para obter algo como:

COMPANY_ID   EMPLOYEE
1            Anna, Bill
2            Carol, Dave

Existe uma função interna no MySQL para fazer isso grupo_concat

Foi útil?

Solução

PostgreSQL 9.0 ou posterior:

Versões recentes do Postgres (desde o final de 2010) têm o string_agg(expression, delimiter) função que fará exatamente o que a pergunta pediu, permitindo até mesmo especificar a string delimitadora:

SELECT company_id, string_agg(employee, ', ')
FROM mytable
GROUP BY company_id;

O Postgres 9.0 também adicionou a capacidade de especificar um ORDER BY cláusula em qualquer expressão agregada;caso contrário, a ordem será indefinida.Então agora você pode escrever:

SELECT company_id, string_agg(employee, ', ' ORDER BY employee)
FROM mytable
GROUP BY company_id;

Ou mesmo:

SELECT string_agg(actor_name, ', ' ORDER BY first_appearance)

PostgreSQL 8.4 ou posterior:

PostgreSQL 8.4 (em 2009) introduzido a função agregada array_agg(expression) que concatena os valores em uma matriz.Então array_to_string() pode ser usado para dar o resultado desejado:

SELECT company_id, array_to_string(array_agg(employee), ', ')
FROM mytable
GROUP BY company_id;

string_agg para versões anteriores à 9.0:

Caso alguém se depare com isso procurando um shim de compatibilidade para bancos de dados anteriores à 9.0, é possível implementar tudo em string_agg exceto o ORDER BY cláusula.

Portanto, com a definição abaixo, isso deve funcionar da mesma forma que em um banco de dados Postgres 9.x:

SELECT string_agg(name, '; ') AS semi_colon_separated_names FROM things;

Mas isso será um erro de sintaxe:

SELECT string_agg(name, '; ' ORDER BY name) AS semi_colon_separated_names FROM things;
--> ERROR: syntax error at or near "ORDER"

Testado no 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
);

Variações personalizadas (todas as versões do Postgres)

Antes da versão 9.0, não havia função agregada integrada para concatenar strings.A implementação personalizada mais simples (sugerido por Vajda Gabo nesta postagem da lista de discussão, entre muitos outros) é usar o recurso integrado textcat função (que está por trás do || operador):

CREATE AGGREGATE textcat_all(
  basetype    = text,
  sfunc       = textcat,
  stype       = text,
  initcond    = ''
);

Aqui está o CREATE AGGREGATE documentação.

Isso simplesmente cola todas as cordas, sem separador.Para inserir um "," entre eles sem tê-lo no final, você pode querer fazer sua própria função de concatenação e substituí-la pelo "textcat" acima.Aqui está um que eu montei e testei em 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;

Esta versão gerará uma vírgula mesmo se o valor na linha for nulo ou vazio, então você obterá uma saída como esta:

a, b, c, , e, , g

Se você preferir remover vírgulas extras para gerar isso:

a, b, c, e, g

Em seguida, adicione um ELSIF verifique a função assim:

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;

Outras dicas

Que tal usar funções de array integradas no Postgres?Pelo menos no 8.4 isso funciona imediatamente:

SELECT company_id, array_to_string(array_agg(employee), ',')
FROM mytable
GROUP BY company_id;

A partir do PostgreSQL 9.0 você pode usar a função agregada chamada string_agg.Seu novo SQL deve ser parecido com isto:

SELECT company_id, string_agg(employee, ', ')
FROM mytable
GROUP BY company_id;

Não reivindico nenhum crédito pela resposta porque a encontrei após algumas pesquisas:

O que eu não sabia é que o PostgreSQL permite definir suas próprias funções agregadas com CRIAR AGREGADO

Esta postagem na lista do PostgreSQL mostra como é trivial criar uma função para fazer o que é necessário:

CREATE AGGREGATE textcat_all(
  basetype    = text,
  sfunc       = textcat,
  stype       = text,
  initcond    = ''
);

SELECT company_id, textcat_all(employee || ', ')
FROM mytable
GROUP BY company_id;

Como já mencionado, criar sua própria função agregada é a coisa certa a fazer.Aqui está minha função agregada de concatenação (você pode encontrar detalhes em francês):

CREATE OR REPLACE FUNCTION concat2(text, text) RETURNS text AS '
    SELECT CASE WHEN $1 IS NULL OR $1 = \'\' THEN $2
            WHEN $2 IS NULL OR $2 = \'\' THEN $1
            ELSE $1 || \' / \' || $2
            END; 
'
 LANGUAGE SQL;

CREATE AGGREGATE concatenate (
  sfunc = concat2,
  basetype = text,
  stype = text,
  initcond = ''

);

E então use-o como:

SELECT company_id, concatenate(employee) AS employees FROM ...

Este trecho da lista de anúncios mais recente pode ser interessante se você estiver atualizando para a versão 8.4:

Até 8.4 sair com um nativo super efiente, você pode adicionar a função Array_Accum () na documentação do PostGresql para lançar qualquer coluna em uma matriz, que pode ser usada pelo código do aplicativo ou combinada com Array_to_string () para formatar como uma lista:

http://www.postgresql.org/docs/current/static/xaggr.html

Eu colocaria um link para os documentos de desenvolvimento do 8.4, mas eles ainda não parecem listar esse recurso.

Seguindo a resposta de Kev, usando os documentos do Postgres:

Primeiro, crie uma matriz de elementos e, em seguida, use o método integrado array_to_string função.

CREATE AGGREGATE array_accum (anyelement)
(
 sfunc = array_append,
 stype = anyarray,
 initcond = '{}'
);

select array_to_string(array_accum(name),'|') from table group by id;

Seguindo mais uma vez o uso de uma função agregada personalizada de concatenação de strings:você precisa lembrar que a instrução select colocará as linhas em qualquer ordem, então você precisará fazer uma sub selecione no de declaração com um ordenar por cláusula e, em seguida, uma cláusula externa selecione com um agrupar por cláusula para agregar as strings, assim:

SELECT custom_aggregate(MY.special_strings)
FROM (SELECT special_strings, grouping_column 
        FROM a_table 
        ORDER BY ordering_column) MY
GROUP BY MY.grouping_column

Achei esta documentação do PostgreSQL útil: http://www.postgresql.org/docs/8.0/interactive/functions-conditional.html.

No meu caso, procurei SQL simples para concatenar um campo com colchetes, se o campo não estiver vazio.

select itemid, 
  CASE 
    itemdescription WHEN '' THEN itemname 
    ELSE itemname || ' (' || itemdescription || ')' 
  END 
from items;

Usar STRING_AGG função para PostgreSQL e SQL do Google BigQuery:

SELECT company_id, STRING_AGG(employee, ', ')
FROM employees
GROUP BY company_id;

De acordo com a versão PostgreSQL 9.0 e superior você pode usar a função agregada chamada string_agg.Seu novo SQL deve ser parecido com isto:

SELECT company_id, string_agg(employee, ', ')
    FROM mytable GROUP BY company_id;

Você também pode usar a função de formato.O que também pode cuidar implicitamente da conversão de tipo de texto, int, etc.

create or replace function concat_return_row_count(tbl_name text, column_name text, value int)
returns integer as $row_count$
declare
total integer;
begin
    EXECUTE format('select count(*) from %s WHERE %s = %s', tbl_name, column_name, value) INTO total;
    return total;
end;
$row_count$ language plpgsql;


postgres=# select concat_return_row_count('tbl_name','column_name',2); --2 is the value

Estou usando o Jetbrains Rider e foi um incômodo copiar os resultados dos exemplos acima para reexecutar porque parecia envolver tudo em JSON.Isso os une em uma única instrução que foi mais fácil de executar

select string_agg('drop table if exists "' || tablename || '" cascade', ';') 
from pg_tables where schemaname != $$pg_catalog$$ and tableName like $$rm_%$$

Se você estiver no Amazon Redshift, onde string_agg não é compatível, tente usar listagg.

SELECT company_id, listagg(EMPLOYEE, ', ') as employees
FROM EMPLOYEE_table
GROUP BY company_id;
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top