Здравствуйте Уважаемый
che_aa,
Вот SQL, который указывает, что кроме UNIQUE CONSTRAINT необходимо создать ещё и UNIQUE INDEX на тех же полях. Без UNIQUE INDEX план выполнения запроса действительно, не оптимален.
Я не уверен в причине этого поведения, вот цитата из
документации продукта:
Adding a unique constraint will automatically create a unique B-tree index on the column or group of columns listed in the constraint. A uniqueness restriction covering only some rows cannot be written as a unique constraint, but it is possible to enforce such a restriction by creating a unique partial index.
В дополнение, в определение индекса добавлено поле unix_time_check в части INCLUDE.
Если Вы будете выполнять скрипт, будьте внимательны, чтобы провести эксперимент в отдельной базе данных, специально созданной для эксперимента. Я постарался оградить запросы так, чтобы не повредить существующие данные и объекты, но не могу гарантировать, так что будьте внимательны.
-- DROP TABLE IF EXISTS public.friends_info;
CREATE TABLE IF NOT EXISTS public.friends_info (
user_id bigint
, friend_id bigint
, unix_time_check bigint
);
ALTER TABLE IF EXISTS public.friends_info
ADD CONSTRAINT friends_info_user_id_friend_id_key UNIQUE (user_id, friend_id);
CREATE INDEX IF NOT EXISTS ix_friends_friend_id ON public.friends_info (friend_id);
CREATE INDEX IF NOT EXISTS ix_friends_user_id ON public.friends_info (user_id);
WITH EncodedData AS (
SELECT gs.rownum
, gen_random_bytes(4) AS RandBytes
FROM generate_series(1,1000000) gs (rownum)
WHERE NOT EXISTS (
SELECT 1
FROM public.friends_info
LIMIT 1
)
), DecodedData AS (
SELECT rownum
,(get_byte(RandBytes, 0)::bigint + (get_byte(RandBytes, 1)::bigint << 8) + (get_byte(RandBytes, 2)::bigint << 16) + (get_byte(RandBytes, 3)::bigint << 24))::bigint
AS RandBigInt
FROM EncodedData
), PreparedData AS (
SELECT rownum AS user_id
, (RandBigInt % 1000000) + 1 AS friend_id
, RandBigInt AS unix_time_check
FROM DecodedData
)
INSERT INTO public.friends_info (
user_id
, friend_id
, unix_time_check
) SELECT
user_id
, friend_id
, unix_time_check
FROM PreparedData;
-- EXPLAIN (ANALYZE,BUFFERS)
SELECT *
FROM friends_info
LEFT JOIN friends_info i ON friends_info.friend_id = i.user_id
WHERE friends_info.user_id = 1;
CREATE UNIQUE INDEX IF NOT EXISTS friends_info_user_id_friend_id_key2
ON public.friends_info (user_id, friend_id) INCLUDE (unix_time_check);
-- EXPLAIN (ANALYZE,BUFFERS)
SELECT *
FROM friends_info
LEFT JOIN friends_info i ON friends_info.friend_id = i.user_id
WHERE friends_info.user_id = 1;
-- EXPLAIN (ANALYZE,BUFFERS)
SELECT *
FROM friends_info
LEFT JOIN friends_info i
ON friends_info.friend_id = i.user_id
WHERE friends_info.friend_id=592737;
Вообще, есть интересные возможности решить запрос такого типа используя следующее
GraphDB расширение СУБД Postgres. Это было бы неплохим испытанием на прочность. Если Вы интересуетесь, оставайтесь на связи.