У меня есть таблица friend
, используемая для хранения отношений между двумя пользователями.
Например: (1,2)
означает, что user1 и user2 являются друзьями.(2,1)
означает то же самое, но там мы не будем хранить это, делая uid1 < uid2
вручную:
Как правило, вы реализуете это с включенным PRIMARY KEY
(udi1, uid2)
и ограничение CHECK
принудительное применение uid1 < uid2
.
CREATE TABLE public.friend (
uid1 integer
, uid2 integer
, PRIMARY KEY (uid1, uid2)
, CONSTRAINT uid2_gt_uid1 CHECK (uid1 < uid2)
);
CREATE INDEX index_uid2 ON friend USING BTREE (uid2);
Вам не нужен другой индекс, он покрывается индексом PK;
<strike>CREATE INDEX index_uid1 ON friend USING BTREE (uid1);</strike>
Тогда не может быть дубликатов (включая переключенные дубликаты), и никто не может быть другом с самим собой, и ваш запрос может быть просто:
SELECT * FROM friend WHERE 2 IN (uid1, uid2);
... что означает:
SELECT * FROM friend WHERE uid1 = 2 OR uid2 = 2;
И вариант UNION
теперь логически идентичен:
SELECT * FROM friend WHERE uid1=2
UNION
SELECT * FROM friend WHERE uid2=2;
Но вы бы UNION ALL
вместо просто UNION
, так как для начала нет дубликатов и UNION ALL
дешевле.Но все же немного дороже, чем один SELECT
выше.
Дубликаты?
В UNION ALL
имеется три возможных источников дубликатов* query:
- Дублирующиеся строки в базовой таблице (исключены PK).
- Строки выбираются несколько раз болеечем одна
SELECT
ветвь. - В вашем конкретном случае: логический дублируется с переключенными идентификаторами (исключено ограничением
CHECK
).
Как только вы поймете это, вы также поймете значение различных методов запросов.При предложенной настройке в качестве возможной проблемы остается только 2.
.