Ефективне об'єднання (видалення дублікатів) масивів


10

У мене дві таблиці, left2і right2. Обидві таблиці будуть великими (1-10М рядків).

CREATE TABLE left2(id INTEGER, t1 INTEGER, d INTEGER);
ALTER TABLE left2 ADD PRIMARY KEY (id,t1);

CREATE TABLE right2( t1 INTEGER, d INTEGER, arr INTEGER[] );
ALTER TABLE right2 ADD PRIMARY KEY(t1,d);

Я виконуватиму такий тип запиту:

SELECT l.d + r.d,
       UNIQ(SORT((array_agg_mult(r.arr)))
FROM left2 l,
     right2 r
WHERE l.t1 = r.t1
GROUP BY l.d + r.d
ORDER BY l.d + r.d;

Де для агрегації масивів я використовую функцію:

CREATE AGGREGATE array_agg_mult(anyarray) (
SFUNC=array_cat,
STYPE=anyarray,
INITCOND='{}');

Після об'єднання масивів я використовую UNIQфункцію intarrayмодуля. Чи є більш ефективний спосіб зробити це? Чи є індекс на arrполі для прискорення злиття (з видаленням дублікатів)? Чи може функція сукупності видаляти дублікати безпосередньо? Оригінальні масиви можуть вважатися відсортованими (і вони унікальні), якщо це допомагає.

Скрипка SQL тут :


Ви збираєтесь запитувати мільйони рядків одночасно? Що ти робиш з результатом? Або будуть предикати для вибору кількох? Чи може right2.arr бути NULL так, як пропонує ваша демо-схема? Вам потрібні відсортовані масиви як результат?
Ервін Брандстеттер

Відповіді:


9

Правильні результати?

Перш за все: правильність. Ви хочете створити масив унікальних елементів? Ваш поточний запит цього не робить. Функція uniq()з модуля intarray обіцяє лише:

видалити сусідні дублікати

Як і інструкція в посібнику , вам знадобиться:

SELECT l.d + r.d, uniq(sort(array_agg_mult(r.arr)))
FROM   ...

Також надає відсортовані масиви - припускаючи, що ви цього хочете, ви не уточнили.

Я бачу, що ви маєте sort() свою загадку , тож це може бути просто помилка у вашому запитанні.

Постгрес 9.5

Так чи інакше, вам сподобається новий Postgres 9.5 (зараз бета-версія). Це забезпечує можливості array_agg_mult()поза коробкою і набагато швидше:

Також було покращено продуктивність для обробки масиву.

Запит

Основна мета array_agg_mult()- агрегувати багатовимірні масиви, але ви все одно створюєте лише одновимірні масиви. Тож я б принаймні спробував цей альтернативний запит:

SELECT l.d + r.d AS d_sum, array_agg(DISTINCT elem) AS result_arr
FROM   left2  l
JOIN   right2 r USING (t1)
     , unnest(r.arr) elem
GROUP  BY 1
ORDER  BY 1;

Що також стосується вашого питання:

Чи може функція сукупності видаляти дублікати безпосередньо?

Так, може, с DISTINCT. Але це не швидше, ніж uniq()для цілих масивів, оптимізованих для цілих масивів, в той час як DISTINCTє загальним для всіх кваліфікованих типів даних.

Не потрібен intarrayмодуль. Однак результат не обов'язково сортується. Postgres використовує різні алгоритми для DISTINCT(IIRC), великі набори типово хешовані, тоді результат не сортується, якщо ви не додаєте явного ORDER BY. Якщо вам потрібні відсортовані масиви, ви можете безпосередньо додати ORDER BYдо функції сукупності:

array_agg(DISTINCT elem ORDER BY elem)

Але це зазвичай повільніше, ніж подача попередньо відсортованих даних array_agg()(один великий сорт проти багатьох малих сортів). Тож я б сортував підзапит і потім агрегував:

SELECT d_sum, uniq(array_agg(elem)) AS result_arr
FROM  (
   SELECT l.d + r.d AS d_sum, elem
   FROM   left2  l
   JOIN   right2 r USING (t1)
        , unnest(r.arr) elem
   ORDER  BY 1, 2
   ) sub
GROUP  BY 1
ORDER  BY 1;

Це був найшвидший варіант у моєму побіжному тесті на Postgres 9.4.

SQL Fiddle на основі тієї, яку ви надали.

Покажчик

Я не бачу тут великого потенціалу для будь-якого індексу. Єдиний варіант:

CREATE INDEX ON right2 (t1, arr);

Має сенс, лише якщо ви отримаєте сканування, що стосуються лише індексу, - це станеться, якщо нижня таблиця right2істотно ширша, ніж лише ці два стовпці, і ваша установка може відповідати скануванню лише для індексів. Детальніше у Postgres Wiki.


Дякую +1 Мені доведеться пізніше UNNEST, але хочу перевірити, чи видалення дублікатів у масивах, а потім UNNEST швидше.
Олександрос

0

Я дуже розчарований, це легко зробити в Microsoft Access. Ви можете створити запит "видалити дублікати", а потім подивіться на SQL, щоб побачити, як це робиться. Мені доведеться підпалити машину Windows, щоб подивитися. Вони різняться, майстер запитів виконує це.

Я думаю, що я працюю над тим, щоб завантажити всі ваші дані в одну таблицю, а потім вибрати SELECT DISTINCT в нову таблицю. Ви також можете дотримуватися замовлення за допомогою пункту, поки ви перебуваєте в ньому. Я це зробив якось рік тому, так і повинно бути.

Я поєдную температурні дані на 2 роки, датчик щохвилини надсилає 2 копії однієї і тієї ж точки даних як зайву захисну систему. Іноді хтось потрапляє у сміття, але я хочу лише його зберегти. У мене також є перекриття між файлами.

Якщо дані мають однаковий формат протягом усього циклу, на Unix-машині ви можете зробити щось подібне

cat *.tab > points.txt
sort -n < points.txt > sorted.txt
uniq -u sorted.txt unique.txt

Але uniq порівнює рядки як рядки, і, наприклад, 18.7000 - це не те, що 18.7. Я змінив програмне забезпечення протягом двох років, тому у мене є обидва формати.


Розчарований від Postgres? У Access навіть є масиви?
ypercubeᵀᴹ

Я не знаю, але це може видалити дублікати, це досить поширена проблема в очищенні даних. Вибір чіткого достатньо близький. Ви не завжди маєте контроль над своїми необробленими даними з реального світу.
Алан Корі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.