กำจัดค่าอาร์เรย์ที่ซ้ำกันใน postgres


87

ฉันมีอาร์เรย์ประเภทbigintฉันจะลบค่าที่ซ้ำกันในอาร์เรย์นั้นได้อย่างไร

เช่น: array[1234, 5343, 6353, 1234, 1234]

ฉันควรจะได้รับ array[1234, 5343, 6353, ...]

ฉันทดสอบตัวอย่างSELECT uniq(sort('{1,2,3,2,1}'::int[]))ในคู่มือ postgres แต่ใช้งานไม่ได้

คำตอบ:


94

ฉันต้องเผชิญกับสิ่งเดียวกัน แต่อาร์เรย์ในกรณีของฉันถูกสร้างขึ้นผ่านarray_aggฟังก์ชัน และโชคดีที่อนุญาตให้รวมค่าDISTINCTเช่น:

  array_agg(DISTINCT value)

สิ่งนี้ใช้ได้กับฉัน


5
โปรดทราบว่า DISTINCT ไม่ได้รับการสนับสนุนสำหรับฟังก์ชันหน้าต่าง
คิดได้

tks guytrim(string_agg(distinct to_char(z.dat_codigo,'0000000000'),'')) as dat_codigo,
Fábio Zangirolami

4
เลือก array_agg (DISTINCT Array [1,2,2,3]) ให้ "{{1,2,2,3}}"
user48956

@ user48956 นั่นเป็นตรรกะเมื่อคุณป้อนอาร์เรย์เป็นค่าคุณต้องตั้งค่าคอลัมน์เดียวเป็นค่าที่จัดกลุ่มโดยในแบบสอบถาม
Daniël Tulp

83

sort(int[])และuniq(int[])ฟังก์ชั่นที่มีให้โดยintarrayโมดูล contrib

ต้องการเปิดใช้งานการใช้งานคุณจะต้องติดตั้งโมดูล

หากคุณไม่ต้องการใช้โมดูลการสนับสนุน intarray หรือหากคุณต้องลบรายการที่ซ้ำกันออกจากอาร์เรย์ประเภทต่างๆคุณมีอีกสองวิธี

หากคุณมี PostgreSQL 8.4 เป็นอย่างน้อยคุณสามารถใช้ประโยชน์จากunnest(anyarray)ฟังก์ชันได้

SELECT ARRAY(SELECT DISTINCT UNNEST('{1,2,3,2,1}'::int[]) ORDER BY 1);
 ?column? 
----------
 {1,2,3}
(1 row)

หรือคุณสามารถสร้างฟังก์ชันของคุณเองเพื่อทำสิ่งนี้

CREATE OR REPLACE FUNCTION array_sort_unique (ANYARRAY) RETURNS ANYARRAY
LANGUAGE SQL
AS $body$
  SELECT ARRAY(
    SELECT DISTINCT $1[s.i]
    FROM generate_series(array_lower($1,1), array_upper($1,1)) AS s(i)
    ORDER BY 1
  );
$body$;

นี่คือตัวอย่างการเรียกร้อง:

SELECT array_sort_unique('{1,2,3,2,1}'::int[]);
 array_sort_unique 
-------------------
 {1,2,3}
(1 row)

1
วิธีการแก้ปัญหาของปัญหา ( "ขจัดค่าอาร์เรย์ที่ซ้ำกัน") ไม่จำเป็นต้องเรียงลำดับ แม้ว่าโดยปกติจะเป็นคุณสมบัติที่มีประโยชน์ แต่ก็ไม่จำเป็น (ต้นทุน CPU) ในบริบท / ข้อกำหนดนี้
Peter Krauss

27

... ในกรณีที่statandardห้องสมุด (?) สำหรับชนิดของนี้ยูทิลิตี้อาร์เรย์ _X ??

ลองค้นหาดู ... ดูบ้าง แต่ไม่มีมาตรฐาน:

  • postgres.cz/wiki/Array_based_functions : ข้อมูลอ้างอิงที่ดี!

  • JDBurnZ / postgresql-anyarray เป็นความคิดริเริ่มที่ดี แต่ต้องการการทำงานร่วมกันเพื่อปรับปรุง

  • wiki.postgresql.org/Snippetsความคิดริเริ่มที่น่าผิดหวัง แต่ "วิกินอกระบบ" ต้องการการทำงานร่วมกันเพื่อปรับปรุง

  • MADlib : ดี! .... แต่มันคือช้างไม่ใช่ "pure SQL snippets lib"


array_distinct()ฟังก์ชัน snippet-lib ที่ง่ายและเร็วกว่า

นี่คือการใช้งานที่ง่ายที่สุดและอาจเร็วกว่าสำหรับarray_unique()หรือarray_distinct():

CREATE FUNCTION array_distinct(anyarray) RETURNS anyarray AS $f$
  SELECT array_agg(DISTINCT x) FROM unnest($1) t(x);
$f$ LANGUAGE SQL IMMUTABLE;

หมายเหตุ: ทำงานได้ตามที่คาดไว้กับประเภทข้อมูลยกเว้นอาร์เรย์อาร์เรย์

SELECT  array_distinct( array[3,3,8,2,6,6,2,3,4,1,1,6,2,2,3,99] ), 
        array_distinct( array['3','3','hello','hello','bye'] ), 
        array_distinct( array[array[3,3],array[3,3],array[3,3],array[5,6]] );
 -- "{1,2,3,4,6,8,99}",  "{3,bye,hello}",  "{3,5,6}"

"ผลข้างเคียง" คือการระเบิดอาร์เรย์ทั้งหมดในชุดขององค์ประกอบ

PS: ด้วยอาร์เรย์ JSONB ทำงานได้ดี

SELECT array_distinct( array['[3,3]'::JSONB, '[3,3]'::JSONB, '[5,6]'::JSONB] );
 -- "{"[3, 3]","[5, 6]"}"

แก้ไข: พารามิเตอร์ "drop nulls" ที่ซับซ้อนกว่า แต่มีประโยชน์

CREATE FUNCTION array_distinct(
      anyarray, -- input array 
      boolean DEFAULT false -- flag to ignore nulls
) RETURNS anyarray AS $f$
      SELECT array_agg(DISTINCT x) 
      FROM unnest($1) t(x) 
      WHERE CASE WHEN $2 THEN x IS NOT NULL ELSE true END;
$f$ LANGUAGE SQL IMMUTABLE;

คุณช่วยอธิบายได้ไหมว่า t (x) กำลังทำอะไรจากค่าที่ผิดปกติ ($ 1) t (x) ... และฉันจะรักษาลำดับของรายการที่ใส่เข้าไปได้อย่างไร
abhirathore2006

@ abhirathore2006 คำตอบนี้เป็น Wiki คุณสามารถเขียนคำอธิบายที่คุณแนะนำได้ เกี่ยวกับ "รักษาคำสั่งซื้อ" ไม่เป็นวิธีการทำลายล้างโปรดดูโซลูชัน PLpgSQL ในหน้านี้เพื่อรักษาลำดับอาร์เรย์ดั้งเดิม นอกจากนี้ยังเป็นที่ยอมรับของข้อกำหนดสองประการการเรียงลำดับและความแตกต่าง (ดูความสำเร็จของคำตอบหลักที่นี่และความคิดเห็นของฉันที่นั่น)
Peter Krauss

ไม่ต้องกังวลฉันพบวิธีแก้ปัญหาแล้วจากที่อื่นใช่นั่นคือโซลูชัน plsql
abhirathore2006

13

ผมได้รวบรวมชุดของวิธีการจัดเก็บ (ฟังก์ชั่น) การขาดการต่อสู้ของ PostgreSQL anyarrayของอาร์เรย์งานกับประกาศเกียรติคุณ ฟังก์ชั่นเหล่านี้ได้รับการออกแบบมาเพื่อทำงานกับข้อมูลอาร์เรย์ทุกประเภทไม่ใช่แค่จำนวนเต็มเหมือนที่ intarray ทำ: https://www.github.com/JDBurnZ/anyarray

anyarray_uniq.sqlในกรณีของคุณทั้งหมดที่คุณต้องการจริงๆจำเป็นต้องเป็น คัดลอกและวางเนื้อหาของไฟล์นั้นลงในแบบสอบถาม PostgreSQL และดำเนินการเพื่อเพิ่มฟังก์ชัน anyarray_sort.sqlหากคุณต้องการอาร์เรย์เรียงลำดับเช่นกันนอกจากนี้ยังเพิ่ม

จากนั้นคุณสามารถสร้างแบบสอบถามง่ายๆดังนี้:

SELECT ANYARRAY_UNIQ(ARRAY[1234,5343,6353,1234,1234])

ส่งคืนสิ่งที่คล้ายกับ: ARRAY[1234, 6353, 5343]

หรือหากคุณต้องการการจัดเรียง:

SELECT ANYARRAY_SORT(ANYARRAY_UNIQ(ARRAY[1234,5343,6353,1234,1234]))

กลับตรง: ARRAY[1234, 5343, 6353]


13

การใช้DISTINCTเรียงลำดับอาร์เรย์โดยปริยาย หากต้องรักษาลำดับสัมพัทธ์ขององค์ประกอบอาร์เรย์ในขณะที่ลบรายการที่ซ้ำกันฟังก์ชันสามารถออกแบบได้ดังต่อไปนี้: (ควรใช้งานได้ตั้งแต่ 9.4 เป็นต้นไป)

CREATE OR REPLACE FUNCTION array_uniq_stable(anyarray) RETURNS anyarray AS
$body$
SELECT
    array_agg(distinct_value ORDER BY first_index)
FROM 
    (SELECT
        value AS distinct_value, 
        min(index) AS first_index 
    FROM 
        unnest($1) WITH ORDINALITY AS input(value, index)
    GROUP BY
        value
    ) AS unique_input
;
$body$
LANGUAGE 'sql' IMMUTABLE STRICT;

1
คำตอบที่ดีที่สุด! ดูเพิ่มเติม: dba.stackexchange.com/questions/211501/…
fjsj

9

นี่คือวิธี "แบบอินไลน์":

SELECT 1 AS anycolumn, (
  SELECT array_agg(c1)
  FROM (
    SELECT DISTINCT c1
    FROM (
      SELECT unnest(ARRAY[1234,5343,6353,1234,1234]) AS c1
    ) AS t1
  ) AS t2
) AS the_array;

อันดับแรกเราสร้างชุดจากอาร์เรย์จากนั้นเราเลือกเฉพาะรายการที่แตกต่างกันจากนั้นรวมกลับเข้าสู่อาร์เรย์


9
หรือ "อินไลน์เพิ่มเติม" ;-) SELECT array_agg(DISTINCT c1) FROM unnest(ARRAY[1234,5343,6353,1234,1234]) t(c1)
Peter Krauss

4

ในแบบสอบถามเดียวฉันทำสิ่งนี้:

SELECT (select array_agg(distinct val) from ( select unnest(:array_column) as val ) as u ) FROM :your_table;


3

สำหรับคนอย่างฉันที่ยังต้องจัดการกับ postgres 8.2 ฟังก์ชันการเรียกซ้ำนี้สามารถกำจัดรายการที่ซ้ำกันได้โดยไม่ต้องแก้ไขการเรียงลำดับของอาร์เรย์

CREATE OR REPLACE FUNCTION my_array_uniq(bigint[])
  RETURNS bigint[] AS
$BODY$
DECLARE
    n integer;
BEGIN

    -- number of elements in the array
    n = replace(split_part(array_dims($1),':',2),']','')::int;

    IF n > 1 THEN
        -- test if the last item belongs to the rest of the array
        IF ($1)[1:n-1] @> ($1)[n:n] THEN
            -- returns the result of the same function on the rest of the array
            return my_array_uniq($1[1:n-1]);
        ELSE
            -- returns the result of the same function on the rest of the array plus the last element               
            return my_array_uniq($1[1:n-1]) || $1[n:n];
        END IF;
    ELSE
        -- if array has only one item, returns the array
        return $1;
    END IF;
END;
$BODY$
  LANGUAGE 'plpgsql' VOLATILE;

สำหรับตัวอย่าง:

select my_array_uniq(array[3,3,8,2,6,6,2,3,4,1,1,6,2,2,3,99]);

จะให้

{3,8,2,6,4,1,99}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.