CREATE TABLE public.products ( id SERIAL, title VARCHAR NOT NULL, CONSTRAINT products_pkey PRIMARY KEY(id) );
CREATE TABLE public.filters ( id SERIAL, title VARCHAR NOT NULL, CONSTRAINT filters_pkey PRIMARY KEY(id) );
CREATE TABLE public.products_ref_filters ( id_product INTEGER NOT NULL, id_filter INTEGER NOT NULL, CONSTRAINT products_ref_filters_pkey PRIMARY KEY(id_product, id_filter), CONSTRAINT products_ref_filters_fk FOREIGN KEY (id_product) REFERENCES public.products(id) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT products_ref_filters_fk1 FOREIGN KEY (id_filter) REFERENCES public.filters(id) ON DELETE CASCADE ON UPDATE CASCADE );
ALTER TABLE public.products ADD COLUMN filters INTEGER[] DEFAULT ARRAY[]::integer[] NOT NULL;
CREATE OR REPLACE FUNCTION public.products_ref_filters__insert_tr () RETURNS trigger AS' BEGIN UPDATE products SET filters = filters + NEW.id_filter --push element onto array WHERE id = NEW.id_product; RETURN NEW; END; 'LANGUAGE 'plpgsql'; CREATE TRIGGER products_ref_filters__insert_tr AFTER INSERT ON public.products_ref_filters FOR EACH ROW EXECUTE PROCEDURE public.products_ref_filters__insert_tr();
CREATE OR REPLACE FUNCTION public.products_ref_filters__delete_tr () RETURNS trigger AS' BEGIN UPDATE products SET filters = filters - OLD.id_filter --remove entries matching right argument from array WHERE id = OLD.id_product; RETURN OLD; END; 'LANGUAGE 'plpgsql';
CREATE OR REPLACE FUNCTION public.products_ref_filters__update_tr () RETURNS trigger AS' BEGIN UPDATE products SET filters = filters - OLD.id_filter WHERE id = OLD.id_product; UPDATE products SET filters = filters + NEW.id_filter WHERE id = NEW.id_product; RETURN NEW; END; 'LANGUAGE 'plpgsql'; CREATE TRIGGER products_ref_filters__update_tr AFTER UPDATE ON public.products_ref_filters FOR EACH ROW EXECUTE PROCEDURE public.products_ref_filters__update_tr();
CREATE OR REPLACE FUNCTION public.products_ref_filters__truncate_tr () RETURNS trigger AS' BEGIN UPDATE products SET filters = ARRAY[]::INTEGER[]; RETURN NULL; END; 'LANGUAGE 'plpgsql'; CREATE TRIGGER products_ref_filters__truncate_tr AFTER TRUNCATE ON public.products_ref_filters FOR EACH STATEMENT EXECUTE PROCEDURE public.products_ref_filters__truncate_tr();
CREATE EXTENSION intarray;
INSERT INTO filters (title) SELECT 'filter_' || num FROM generate_series(1, 10000) as num; INSERT INTO products (title) SELECT 'product_' || num FROM generate_series(1, 100000) as num; DO $$ DECLARE idp INTEGER; BEGIN FOR idp IN SELECT id FROM products LOOP INSERT INTO products_ref_filters SELECT idp, f.id FROM filters f ORDER BY random() LIMIT 10; END LOOP; END$$;
CREATE INDEX products_idx ON public.products USING gin (filters public.gin__int_ops);
SELECT id_filter FROM products_ref_filters GROUP BY id_filter ORDER BY count(*) DESC LIMIT 10
-- JOIN SELECT t1.* FROM products t1, products_ref_filters t2 WHERE t1.id = t2.id_product AND t2.id_filter = 7267 -- 140 rows returned (execution time: 125 ms; total time: 141 ms) -- SUB SELECT SELECT * FROM products WHERE id IN ( SELECT id_product FROM products_ref_filters WHERE id_filter = 7267 ) -- 140 rows returned (execution time: 125 ms; total time: 141 ms) -- SELECT * FROM products WHERE filters @> ARRAY[7267] -- 140 rows returned (execution time: 0 ms; total time: 0 ms)
-- JOIN SELECT DISTINCT t1.* FROM products t1, products_ref_filters t2 WHERE t1.id = t2.id_product AND t2.id_filter IN (7267,4889,6364,5376,3556,7292,11188,2643,9005,10235) -- 1347 rows returned (execution time: 297 ms; total time: 297 ms) -- SUB SELECT SELECT * FROM products WHERE id IN ( SELECT id_product FROM products_ref_filters WHERE id_filter IN (7267,4889,6364,5376,3556,7292,11188,2643,9005,10235) ) -- 1347 rows returned (execution time: 234 ms; total time: 250 ms) -- INTARRAY SELECT * FROM products WHERE filters && ARRAY[7267,4889,6364,5376,3556,7292,11188,2643,9005,10235] -- 1347 rows returned (execution time: 16 ms; total time: 16 ms)
-- JOIN -- , ;) -- SELECT * FROM products WHERE filters @> ARRAY[9844,9957]; -- 1 rows returned (execution time: 0 ms; total time: 16 ms)
-- 1000 INSERT INTO filters (title) SELECT 'filter_' || num FROM generate_series(1, 10000) as num; -- 10 000 000 INSERT INTO products (title) SELECT 'product_' || num FROM generate_series(1, 10000000) as num; -- .. , ID- DO $$ DECLARE idp INTEGER; BEGIN FOR idp IN SELECT id FROM products LOOP UPDATE products SET filters = (SELECT array_agg(id) FROM (SELECT id FROM filters OFFSET random()*1000 LIMIT 10) as foo) WHERE id = idp; END LOOP; END$$; -- ~20
CREATE INDEX products_idx ON public.products USING btree (filters); -- execution time: 00:02:36 -- DB size ~ 3.8Gb SELECT * FROM products WHERE filters @> '{842}'::INTEGER[]; -- Seq Scan on public.products (cost=0.00..357908.00 rows=10000 width=80) ... -- 99798 rows returned (execution time: 5,594 sec; total time: 5,594 sec) SELECT * FROM products WHERE filters = '{842,843,844,845,846,847,848,849,850,851}'::INTEGER[]; -- Bitmap Heap Scan on public.products (cost=487.94..32940.13 rows=9726 width=80) -- 9940 rows returned (execution time: 46 ms; total time: 62 ms) SELECT * FROM products WHERE filters && '{842,843,844,845,846,847,848,849,850,851}'::INTEGER[] -- Seq Scan on public.products (cost=0.00..357908.00 rows=10000 width=80) -- 189853 rows returned (execution time: 6,281 sec; total time: 6,296 sec)
CREATE INDEX products_idx ON public.products USING gist (filters public.gist__int_ops); -- execution time: 00:26:55; -- DB size ~ 4.5Gb SELECT * FROM products WHERE filters @> '{842}'::INTEGER[]; -- Bitmap Heap Scan on public.products (cost=833.92..34097.44 rows=10000 width=80) -- 99798 rows returned (execution time: 2,234 sec; total time: 2,234 sec) SELECT * FROM products WHERE filters = '{842,843,844,845,846,847,848,849,850,851}'::INTEGER[]; -- Bitmap Heap Scan on public.products (cost=811.79..33263.99 rows=9726 width=80) -- 9940 rows returned (execution time: 234 ms; total time: 234 ms) SELECT * FROM products WHERE filters && '{842,843,844,845,846,847,848,849,850,851}'::INTEGER[]; -- Bitmap Heap Scan on public.products (cost=833.92..34097.44 rows=10000 width=80) -- 189853 rows returned (execution time: 5,234 sec; total time: 5,234 sec)
CREATE INDEX products_idx ON public.products USING gin (filters public.gin__int_ops); -- execution time: 56,344 sec; SELECT * FROM products WHERE filters @> '{842}'::INTEGER[]; -- Bitmap Heap Scan on public.products (cost=97.50..33361.02 rows=10000 width=80) -- 99798 rows returned (execution time: 2,204 sec; total time: 2,219 sec) SELECT * FROM products WHERE filters = '{842,843,844,845,846,847,848,849,850,851}'::INTEGER[]; -- Bitmap Heap Scan on public.products (cost=211.37..32663.57 rows=9726 width=80) -- 9940 rows returned (execution time: 297 ms; total time: 312 ms) SELECT * FROM products WHERE filters && '{842,843,844,845,846,847,848,849,850,851}'::INTEGER[]; -- Bitmap Heap Scan on public.products (cost=213.50..33477.02 rows=10000 width=80) -- 189853 rows returned (execution time: 4,500 sec; total time: 4,515 sec)
Source: https://habr.com/ru/post/269823/