Да, оператор на припокриване && може да използва GIN индекс на масиви
. Това е много полезно за заявки за намиране на редове с даден човек (1 ) сред масив от актьори:
SELECT * FROM eg_assoc WHERE actors && '{1}'::int[]
Въпреки това , логиката на вашата заявка е обратната, търсейки всички лица, изброени в масивите в eg_assoc . GIN индексът е не помогнете тук. Нуждаем се само от btree индекса на PK person.id .
Правилни заявки
Основи:
Следните заявки запазват оригиналните масиви точно както са дадени , включително възможни дублирани елементи и оригинален ред на елементите. Работи за едномерни масиви . Допълнителните размери се сгъват в едно измерение. По-сложно е да се запазят множество измерения (но е напълно възможно):
WITH ORDINALITY в Postgres 9.4 или по-нова версия
SELECT aid, actors
, ARRAY(SELECT name
FROM unnest(e.actors) WITH ORDINALITY a(id, i)
JOIN eg_person p USING (id)
ORDER BY a.i) AS act_names
, benefactors
, ARRAY(SELECT name
FROM unnest(e.benefactors) WITH ORDINALITY b(id, i)
JOIN eg_person USING (id)
ORDER BY b.i) AS ben_names
FROM eg_assoc e;
LATERAL заявки
За PostgreSQL 9.3+ .
SELECT e.aid, e.actors, a.act_names, e.benefactors, b.ben_names
FROM eg_assoc e
, LATERAL (
SELECT ARRAY( SELECT name
FROM generate_subscripts(e.actors, 1) i
JOIN eg_person p ON p.id = e.actors[i]
ORDER BY i)
) a(act_names)
, LATERAL (
SELECT ARRAY( SELECT name
FROM generate_subscripts(e.benefactors, 1) i
JOIN eg_person p ON p.id = e.benefactors[i]
ORDER BY i)
) b(ben_names);
db<>fiddle тук
с няколко варианта.
Стар sqlfiddle
Тънък детайл:Ако човек не бъде намерен, той просто е изпуснат. И двете заявки генерират празен масив ('{}' ), ако не е намерен човек за целия масив. Други стилове на заявка биха върнали NULL . Добавих варианти към цигулката.
Свързани подзаявки
За Postgres 8.4+ (където generate_subsrcipts()
беше въведен):
SELECT aid, actors
, ARRAY(SELECT name
FROM generate_subscripts(e.actors, 1) i
JOIN eg_person p ON p.id = e.actors[i]
ORDER BY i) AS act_names
, benefactors
, ARRAY(SELECT name
FROM generate_subscripts(e.benefactors, 1) i
JOIN eg_person p ON p.id = e.benefactors[i]
ORDER BY i) AS ben_names
FROM eg_assoc e;
Все пак може да се представи най-добре дори в Postgres 9.3.ARRAY конструктор
е по-бърз от array_agg() . Вижте:
Вашата неуспешна заявка
заявката, предоставена от @a_horse изглежда да свърши работата, но е ненадежден, подвеждащ, потенциално неправилен и ненужно скъп.
-
Прокси кръстосано присъединяване поради две несвързани присъединявания. Подъл антимодел. Вижте:
Коригирано повърхностно с
DISTINCTвarray_agg()за елиминиране на генерираните дубликати, но това наистина е като слагане на червило на прасе. Освен това елиминира дубликатите в оригинала защото е невъзможно да се направи разлика в този момент - което е потенциално неправилно. -
Изразът
a_person.id = any(eg_assoc.actors)работи , но елиминира дубликати от резултата (случва се два пъти в тази заявка), което е грешно, освен ако не е посочено. -
Оригиналният ред на елементите на масива не е запазен . Това като цяло е сложно. Но това се влошава в това запитване, тъй като актьорите и благодетелите се умножават и отново се разграничават, което гарантира произволен ред.
-
Няма псевдоними на колони във външния
SELECTводят до дублирани имена на колони, което кара някои клиенти да се провалят (не работят в цигулката без псевдоними). -
min(actors)иmin(benefactors)са безполезни. Обикновено човек просто добавя колоните къмGROUP BYвместо да ги агрегира фалшиво. Ноeg_assoc.aidвсе пак е колоната PK (покриваща цялата таблица вGROUP BY), така че това дори не е необходимо. Самоactors, benefactors.
Обединяването на целия резултат като начало е загуба на време и усилия. Използвайте по-интелигентна заявка, която не умножава основните редове, след което не е необходимо да ги обобщавате обратно.