Pregunta

Estoy buscando una manera de concatenar las cadenas de un campo dentro de un grupo mediante consulta.Entonces, por ejemplo, tengo una tabla:

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

y quería agrupar por company_id para obtener algo como:

COMPANY_ID   EMPLOYEE
1            Anna, Bill
2            Carol, Dave

Hay una función incorporada en MySQL para hacer esto. grupo_concat

¿Fue útil?

Solución

PostgreSQL 9.0 o posterior:

Las versiones recientes de Postgres (desde finales de 2010) tienen la string_agg(expression, delimiter) función que hará exactamente lo que pedía la pregunta, incluso permitiéndole especificar la cadena delimitadora:

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

Postgres 9.0 también agregó la capacidad de especificar un ORDER BY cláusula en cualquier expresión agregada;de lo contrario, el orden no está definido.Entonces ahora puedes escribir:

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

O de hecho:

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

PostgreSQL 8.4 o posterior:

PostgreSQL 8.4 (en 2009) introducido la función agregada array_agg(expression) que concatena los valores en una matriz.Entonces array_to_string() se puede utilizar para dar el resultado deseado:

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

string_agg para versiones anteriores a 9.0:

En caso de que alguien se encuentre con esto buscando una corrección de compatibilidad para bases de datos anteriores a 9.0, es posible implementar todo en string_agg excepto el ORDER BY cláusula.

Entonces, con la siguiente definición, esto debería funcionar igual que en una base de datos Postgres 9.x:

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

Pero esto será un error de sintaxis:

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

Probado en 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
);

Variaciones personalizadas (todas las versiones de Postgres)

Antes de la versión 9.0, no había una función agregada incorporada para concatenar cadenas.La implementación personalizada más simple (sugerido por Vajda Gabo en esta publicación de la lista de correo, entre muchos otros) es utilizar el integrado textcat función (que se encuentra detrás de la || operador):

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

Aquí está el CREATE AGGREGATE documentación.

Esto simplemente pega todas las cuerdas juntas, sin separador.Para poder insertar un "," entre ellos sin tenerlo al final, es posible que desee crear su propia función de concatenación y sustituirla por el "textcat" anterior.Aquí hay uno que armé y probé en 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 versión generará una coma incluso si el valor de la fila es nulo o está vacío, por lo que obtendrá un resultado como este:

a, b, c, , e, , g

Si prefiere eliminar comas adicionales para generar esto:

a, b, c, e, g

Luego agrega un ELSIF verifique la función como esta:

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;

Otros consejos

¿Qué tal si utilizamos las funciones de matriz integradas de Postgres?Al menos en 8.4, esto funciona de inmediato:

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

A partir de PostgreSQL 9.0 puedes utilizar la función agregada llamada cadena_agg.Su nuevo SQL debería verse así:

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

No reclamo ningún crédito por la respuesta porque la encontré después de buscar un poco:

Lo que no sabía es que PostgreSQL te permite definir tus propias funciones agregadas con CREAR AGREGADO

Esta publicación en la lista de PostgreSQL muestra lo trivial que es crear una función para hacer lo que se requiere:

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

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

Como ya se mencionó, crear su propia función agregada es lo correcto.Aquí está mi función agregada de concatenación (puedes encontrar detalles en 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 = ''

);

Y luego úsalo como:

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

Este último fragmento de la lista de anuncios puede ser de interés si actualiza a 8.4:

Hasta que 8.4 salga con uno nativo de Super-Effient, puede agregar la función Array_Accum () en la documentación PostgreSQL para encender cualquier columna en una matriz, que luego se puede usar mediante código de aplicación, o combinado con Array_TO_String () para formatear es como una lista:

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

Me vincularía a los documentos de desarrollo 8.4, pero parece que todavía no incluyen esta característica.

Siguiendo la respuesta de Kev, utilizando los documentos de Postgres:

Primero, cree una matriz de elementos, luego use el incorporado array_to_string función.

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

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

Siguiendo una vez más sobre el uso de una función agregada personalizada de concatenación de cadenas:debe recordar que la instrucción de selección colocará filas en cualquier orden, por lo que deberá hacer una sub seleccionar en el de declaración con un ordenar por cláusula, y luego una exterior seleccionar con un agrupar por cláusula para agregar las cadenas, así:

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

Encontré útil esta documentación de PostgreSQL: http://www.postgresql.org/docs/8.0/interactive/functions-conditional.html.

En mi caso, busqué SQL simple para concatenar un campo con corchetes alrededor, si el campo no está vacío.

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

Usar STRING_AGG función para PostgreSQL y SQL de BigQuery de Google:

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

Según la versión PostgreSQL 9.0 y superior, puede utilizar la función agregada llamada string_agg.Su nuevo SQL debería verse así:

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

También puede utilizar la función de formato.Lo cual también puede encargarse implícitamente de la conversión de tipos de texto, int, etc. por sí solo.

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

Estoy usando Jetbrains Rider y fue una molestia copiar los resultados de los ejemplos anteriores para volver a ejecutarlos porque parecía envolverlo todo en JSON.Esto los une en una sola declaración que fue más fácil de ejecutar.

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

Si está en Amazon Redshift, donde no se admite string_agg, intente usar listagg.

SELECT company_id, listagg(EMPLOYEE, ', ') as employees
FROM EMPLOYEE_table
GROUP BY company_id;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top