วิธีการเชื่อมสตริงของฟิลด์สตริงในกลุ่ม PostgreSQL โดยการสืบค้น


351

ฉันกำลังมองหาวิธีที่จะเชื่อมโยงสตริงของเขตข้อมูลภายในกลุ่มโดยใช้แบบสอบถาม ตัวอย่างเช่นฉันมีตาราง:

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

และฉันต้องการจัดกลุ่มโดย company_id เพื่อให้ได้สิ่งที่ชอบ:

COMPANY_ID   EMPLOYEE
1            Anna, Bill
2            Carol, Dave

มีฟังก์ชั่นในตัวใน mySQL เพื่อทำgroup_concatนี้


1
คำตอบของ Markus Döringดีกว่าในทางเทคนิค
pstanton

@pstanton คำตอบของDöringดีกว่าสำหรับ 8.4 และต่ำกว่าเท่านั้น
Jared Beck

คำถามนี้ดูเหมือนจะเหมาะดีกว่าสำหรับdba.stackexchange.com
Dave Jarvis

นี่ควรเป็นคำตอบที่ถูกต้องตอนนี้stackoverflow.com/a/47638417/243233
Jus12

คำตอบ:


542

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:

ในกรณีที่ทุกคนเจอการมองหา shim ที่เข้ากันได้สำหรับฐานข้อมูล pre-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 ไม่มีฟังก์ชันการรวมในตัวเพื่อเชื่อมสตริงเข้าด้วยกัน การปรับใช้ที่ง่ายที่สุด ( แนะนำโดย Vajda Gabo ในโพสต์จดหมายข่าวนี้และอื่น ๆ อีกมากมาย) คือการใช้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;

1
ฉันต้อง S&R varchar เป็นข้อความ (pgsql ที่เสถียรล่าสุด) แต่นี่ยอดเยี่ยมมาก!
Kev

1
คุณสามารถเขียนฟังก์ชันใน SQL เท่านั้นซึ่งจะง่ายต่อการติดตั้ง (ต้องติดตั้งโดย superuser plpgsql) ดูโพสต์ของฉันสำหรับตัวอย่าง
bortzmeyer

11
"ไม่มีฟังก์ชั่นรวมในตัวที่จะต่อสตริง" - ทำไมคุณไม่ใช้array_to_string(array_agg(employee), ',')?
pstanton

2
+1 สำหรับฟังก์ชัน PostgreSQL 9.0 หากคุณจำเป็นต้องกังวลเกี่ยวกับ pre-9.0 คำตอบของ Markus นั้นดีกว่า
แบรดโคช์

7
โปรดทราบว่า Postgres เวอร์ชันล่าสุดยังอนุญาตให้มีOrder Byส่วนคำสั่งในฟังก์ชันการรวมเช่นstring_agg(employee, ',' Order By employee)
IMSoP

99

วิธีการเกี่ยวกับการใช้ฟังก์ชั่นในตัว Postgres? อย่างน้อยใน 8.4 นี่ใช้งานได้ทันที:

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

น่าเศร้าที่นี่ใช้ไม่ได้กับ Greenplum (v8.2) +1 เหมือนกันทั้งหมด
ekkis

ทำงานได้ดีสำหรับฉันบน Greenplum 4.3.4.1 (สร้างบน PostgreSQL 8.2.15)
PhilHibbs

19

ในฐานะที่เป็นจาก PostgreSQL 9.0 คุณสามารถใช้ฟังก์ชั่นรวมเรียกว่าstring_agg SQL ใหม่ของคุณควรมีลักษณะดังนี้:

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


13

ฉันอ้างสิทธิ์เครดิตสำหรับคำตอบเพราะฉันพบหลังจากการค้นหาบางอย่าง:

สิ่งที่ฉันไม่รู้คือ PostgreSQL อนุญาตให้คุณกำหนดฟังก์ชันการรวมของคุณเองด้วยCREATE AGGREGATE

โพสต์นี้ในรายการ PostgreSQL แสดงให้เห็นว่าการสร้างฟังก์ชั่นเล็ก ๆ น้อย ๆ นั้นเป็นสิ่งที่จำเป็น:

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

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

7

ดังที่ได้กล่าวไปแล้วการสร้างฟังก์ชั่นรวมของคุณเองเป็นสิ่งที่ถูกต้อง นี่คือฟังก์ชั่นการรวมการเรียงต่อกันของฉัน (คุณสามารถหารายละเอียดเป็นภาษาฝรั่งเศส ):

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 = ''

);

จากนั้นใช้เป็น:

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

5

ตัวอย่างข้อมูลประกาศล่าสุดนี้อาจเป็นที่สนใจหากคุณกำลังอัปเกรดเป็น 8.4:

จนกว่า 8.4 จะออกมาพร้อมกับ native super-effient คุณสามารถเพิ่ม array_accum () ฟังก์ชั่นในเอกสาร PostgreSQL สำหรับการพับคอลัมน์ใด ๆ ลงในอาร์เรย์ซึ่งสามารถใช้โดยรหัสแอปพลิเคชันหรือรวมกับ array_to_string () เพื่อจัดรูปแบบ มันเป็นรายการ:

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

ฉันต้องการลิงก์ไปยังเอกสารการพัฒนา 8.4 แต่พวกเขายังไม่ได้แสดงรายการคุณลักษณะนี้


5

ติดตามคำตอบของ Kev โดยใช้เอกสาร Postgres:

ขั้นแรกให้สร้างอาร์เรย์ขององค์ประกอบจากนั้นใช้array_to_stringฟังก์ชันในตัว

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

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

5

การติดตามอีกครั้งเกี่ยวกับการใช้ฟังก์ชันการรวมแบบกำหนดเองของการต่อสตริง: คุณต้องจำไว้ว่าคำสั่ง select จะวางแถวในลำดับใด ๆ ดังนั้นคุณจะต้องทำการเลือกย่อยในคำสั่งจากคำสั่งที่มีคำสั่งย่อยและ จากนั้นตัวเลือกภายนอกที่มีกลุ่มตามข้อเพื่อรวมสตริงดังนั้น:

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

3

ผมพบว่าเอกสารนี้เป็นประโยชน์ PostgreSQL: http://www.postgresql.org/docs/8.0/interactive/functions-conditional.html

ในกรณีของฉันฉันค้นหา SQL ธรรมดาเพื่อเชื่อมโยงเขตข้อมูลที่มีวงเล็บอยู่รอบ ๆ ถ้าเขตข้อมูลไม่ว่างเปล่า

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


0

ตามเวอร์ชั่น PostgreSQL 9.0 ขึ้นไปคุณสามารถใช้ฟังก์ชันการรวมที่เรียกว่า string_agg SQL ใหม่ของคุณควรมีลักษณะดังนี้:

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

0

นอกจากนี้คุณยังสามารถใช้ฟังก์ชั่นการจัดรูปแบบ ซึ่งยังสามารถดูแลการแปลงข้อความโดยปริยาย int ฯลฯ ด้วยตนเอง

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

1
สิ่งนี้เกี่ยวข้องกับการใช้การรวมเพื่อเชื่อมค่าสตริงหรือไม่
a_horse_with_no_name

0

ฉันใช้ Jetbrains Rider และมันก็เป็นความยุ่งยากในการคัดลอกผลลัพธ์จากตัวอย่างข้างต้นเพื่อดำเนินการอีกครั้งเพราะมันดูเหมือนจะห่อหุ้มมันทั้งหมดใน JSON สิ่งนี้จะรวมไว้ในคำสั่งเดียวที่เรียกใช้ง่ายขึ้น

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

0

หากคุณอยู่ใน Amazon Redshift โดยที่ไม่รองรับ string_agg ให้ลองใช้ listagg

SELECT company_id, listagg(EMPLOYEE, ', ') as employees
FROM EMPLOYEE_table
GROUP BY company_id;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.