Domanda

Sto cercando un modo per concatenare le stringhe di un campo all'interno di un gruppo tramite query.Quindi, ad esempio, ho una tabella:

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

e volevo raggruppare per company_id per ottenere qualcosa del tipo:

COMPANY_ID   EMPLOYEE
1            Anna, Bill
2            Carol, Dave

C'è una funzione integrata in MySQL per farlo gruppo_concat

È stato utile?

Soluzione

PostgreSQL 9.0 o successivo:

Le versioni recenti di Postgres (dalla fine del 2010) hanno l'estensione string_agg(expression, delimiter) funzione che farà esattamente ciò che la domanda richiedeva, permettendoti anche di specificare la stringa delimitatrice:

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

Postgres 9.0 ha anche aggiunto la possibilità di specificare un file ORDER BY clausola in qualsiasi espressione aggregata;in caso contrario, l'ordine non è definito.Quindi ora puoi scrivere:

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

O addirittura:

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

PostgreSQL 8.4 o successivo:

PostgreSQL 8.4 (nel 2009) introdotto la funzione aggregata array_agg(expression) che concatena i valori in un array.Poi array_to_string() può essere utilizzato per ottenere il risultato desiderato:

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

string_agg per le versioni precedenti alla 9.0:

Nel caso qualcuno si imbattesse in questo cercando uno shim di compatibilità per i database precedenti alla 9.0, è possibile implementare tutto in string_agg tranne il ORDER BY clausola.

Quindi con la definizione seguente dovrebbe funzionare come in un DB Postgres 9.x:

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

Ma questo sarà un errore di sintassi:

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

Testato su 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
);

Variazioni personalizzate (tutte le versioni di Postgres)

Prima della versione 9.0, non esisteva alcuna funzione di aggregazione incorporata per concatenare le stringhe.L'implementazione personalizzata più semplice (suggerito da Vajda Gabo in questo post della mailing list, tra molti altri) è utilizzare il file built-in textcat funzione (che sta dietro il || operatore):

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

Ecco il CREATE AGGREGATE documentazione.

Questo incolla semplicemente tutte le corde insieme, senza separatore.Per inserire un ", " tra di loro senza averlo alla fine, potresti voler creare la tua funzione di concatenazione e sostituirla al "textcat" sopra.Eccone uno che ho messo insieme e testato l'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;

Questa versione genererà una virgola anche se il valore nella riga è null o vuoto, quindi otterrai un output come questo:

a, b, c, , e, , g

Se preferisci rimuovere le virgole extra per ottenere questo risultato:

a, b, c, e, g

Quindi aggiungi un ELSIF controlla la funzione in questo modo:

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;

Altri suggerimenti

Che ne dici di utilizzare le funzioni di array integrate di Postgres?Almeno su 8.4 funziona immediatamente:

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

A partire da PostgreSQL 9.0 è possibile utilizzare la funzione di aggregazione chiamata stringa_agg.Il tuo nuovo SQL dovrebbe assomigliare a questo:

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

Non rivendico alcun merito per la risposta perché l'ho trovata dopo alcune ricerche:

Quello che non sapevo è che PostgreSQL ti consente di definire le tue funzioni aggregate con CREA AGGREGATO

Questo post nell'elenco PostgreSQL mostra quanto sia banale creare una funzione per fare ciò che è richiesto:

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

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

Come già accennato, creare la propria funzione aggregata è la cosa giusta da fare.Ecco la mia funzione di aggregazione di concatenazione (puoi trovare dettagli in francese):

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 poi usarlo come:

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

Questo ultimo snippet dell'elenco degli annunci potrebbe interessarti se esegui l'aggiornamento alla versione 8.4:

Fino a 8.4 non esce con uno nativo super-effetti, è possibile aggiungere la funzione Array_Accum () nella documentazione PostgreSQL per farle fare qualsiasi colonna in un array, che può quindi essere utilizzato dal codice dell'applicazione o combinata con Array_to_String () per formattare come un elenco:

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

Mi collegherei ai documenti di sviluppo 8.4 ma non sembrano ancora elencare questa funzionalità.

In seguito alla risposta di Kev, utilizzando i documenti Postgres:

Innanzitutto, crea un array di elementi, quindi utilizza il built-in array_to_string funzione.

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

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

Seguendo ancora una volta l'uso di una funzione aggregata personalizzata di concatenazione di stringhe:devi ricordare che l'istruzione select posizionerà le righe in qualsiasi ordine, quindi dovrai fare un sub Selezionare nel da dichiarazione con un ordinato da clausola, e poi un esterno Selezionare con un raggruppa per clausola per aggregare le stringhe, quindi:

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

Ho trovato utile questa documentazione PostgreSQL: http://www.postgresql.org/docs/8.0/interactive/functions-conditional.html.

Nel mio caso, ho cercato un semplice SQL per concatenare un campo racchiuso tra parentesi, se il campo non è vuoto.

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

Utilizzo STRING_AGG funzione per PostgreSQL E Google BigQuerySQL:

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

Secondo la versione PostgreSQL 9.0 e successive è possibile utilizzare la funzione aggregata chiamata string_agg.Il tuo nuovo SQL dovrebbe assomigliare a questo:

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

Puoi anche utilizzare la funzione di formattazione.Che può anche occuparsi implicitamente della conversione del tipo di text, int, ecc.

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

Sto utilizzando Jetbrains Rider ed è stata una seccatura copiare i risultati degli esempi precedenti per rieseguirli perché sembrava racchiudere tutto in JSON.Questo li unisce in un'unica istruzione più facile da eseguire

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

Se utilizzi Amazon Redshift, dove string_agg non è supportato, prova a utilizzare listagg.

SELECT company_id, listagg(EMPLOYEE, ', ') as employees
FROM EMPLOYEE_table
GROUP BY company_id;
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top