আমার কাছে নিম্নলিখিত সারণী রয়েছে (সাকিলা ডাটাবেস থেকে নেওয়া):
- ফিল্ম: ফিল্ম_আইডি পিকে
- অভিনেতা: অভিনেতা_ পিকে আছেন
- ফিল্ম_অ্যাক্টর: ফিল্ম_আইডি এবং অভিনেতা_আইডি ফিল্ম / অভিনেতার প্রতি অভিনেতা
আমি একটি নির্দিষ্ট চলচ্চিত্র নির্বাচন করছি। এই ফিল্মের জন্য, আমিও চাই যে সমস্ত অভিনেতা সেই ছবিতে অংশ নেবেন। এর জন্য আমার দুটি প্রশ্ন রয়েছে: একটি সাথে একটি LEFT JOIN
এবং একটি এর সাথে একটি LEFT JOIN LATERAL
।
select film.film_id, film.title, a.actors
from film
left join
(
select film_actor.film_id, array_agg(first_name) as actors
from actor
inner join film_actor using(actor_id)
group by film_actor.film_id
) as a
on a.film_id = film.film_id
where film.title = 'ACADEMY DINOSAUR'
order by film.title;
select film.film_id, film.title, a.actors
from film
left join lateral
(
select array_agg(first_name) as actors
from actor
inner join film_actor using(actor_id)
where film_actor.film_id = film.film_id
) as a
on true
where film.title = 'ACADEMY DINOSAUR'
order by film.title;
ক্যোয়ারী পরিকল্পনার তুলনা করার সময়, প্রথম ক্যোয়ারী দ্বিতীয়টির চেয়ে অনেক খারাপ (20x) সম্পাদন করে:
Merge Left Join (cost=507.20..573.11 rows=1 width=51) (actual time=15.087..15.089 rows=1 loops=1)
Merge Cond: (film.film_id = film_actor.film_id)
-> Sort (cost=8.30..8.31 rows=1 width=19) (actual time=0.075..0.075 rows=1 loops=1)
Sort Key: film.film_id
Sort Method: quicksort Memory: 25kB
-> Index Scan using idx_title on film (cost=0.28..8.29 rows=1 width=19) (actual time=0.044..0.058 rows=1 loops=1)
Index Cond: ((title)::text = 'ACADEMY DINOSAUR'::text)
-> GroupAggregate (cost=498.90..552.33 rows=997 width=34) (actual time=15.004..15.004 rows=1 loops=1)
Group Key: film_actor.film_id
-> Sort (cost=498.90..512.55 rows=5462 width=8) (actual time=14.934..14.937 rows=11 loops=1)
Sort Key: film_actor.film_id
Sort Method: quicksort Memory: 449kB
-> Hash Join (cost=6.50..159.84 rows=5462 width=8) (actual time=0.355..8.359 rows=5462 loops=1)
Hash Cond: (film_actor.actor_id = actor.actor_id)
-> Seq Scan on film_actor (cost=0.00..84.62 rows=5462 width=4) (actual time=0.035..2.205 rows=5462 loops=1)
-> Hash (cost=4.00..4.00 rows=200 width=10) (actual time=0.303..0.303 rows=200 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 17kB
-> Seq Scan on actor (cost=0.00..4.00 rows=200 width=10) (actual time=0.027..0.143 rows=200 loops=1)
Planning time: 1.495 ms
Execution time: 15.426 ms
Nested Loop Left Join (cost=25.11..33.16 rows=1 width=51) (actual time=0.849..0.854 rows=1 loops=1)
-> Index Scan using idx_title on film (cost=0.28..8.29 rows=1 width=19) (actual time=0.045..0.048 rows=1 loops=1)
Index Cond: ((title)::text = 'ACADEMY DINOSAUR'::text)
-> Aggregate (cost=24.84..24.85 rows=1 width=32) (actual time=0.797..0.797 rows=1 loops=1)
-> Hash Join (cost=10.82..24.82 rows=5 width=6) (actual time=0.672..0.764 rows=10 loops=1)
Hash Cond: (film_actor.actor_id = actor.actor_id)
-> Bitmap Heap Scan on film_actor (cost=4.32..18.26 rows=5 width=2) (actual time=0.072..0.150 rows=10 loops=1)
Recheck Cond: (film_id = film.film_id)
Heap Blocks: exact=10
-> Bitmap Index Scan on idx_fk_film_id (cost=0.00..4.32 rows=5 width=0) (actual time=0.041..0.041 rows=10 loops=1)
Index Cond: (film_id = film.film_id)
-> Hash (cost=4.00..4.00 rows=200 width=10) (actual time=0.561..0.561 rows=200 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 17kB
-> Seq Scan on actor (cost=0.00..4.00 rows=200 width=10) (actual time=0.039..0.275 rows=200 loops=1)
Planning time: 1.722 ms
Execution time: 1.087 ms
কেন? আমি এটি সম্পর্কে যুক্তি জানাতে চাই, তাই আমি কী চলছে তা বুঝতে পারি এবং ডেটা আকার বাড়লে কোয়েরিটি কী আচরণ করবে এবং পরিকল্পনাকারী নির্দিষ্ট শর্তে কোন সিদ্ধান্ত নেবে তা অনুমান করতে পারি।
আমার চিন্তাভাবনা: প্রথম LEFT JOIN
ক্যোয়ারিতে দেখে মনে হচ্ছে যে উপাত্তটি ডাটাবেসের সমস্ত ফিল্মের জন্য নির্বাহ করা হয়েছে, বাইরের ক্যোয়ারীতে ফিল্টারিংকে বিবেচনা না করেই আমরা কেবল একটি নির্দিষ্ট ফিল্মে আগ্রহী। পরিকল্পনাকারী কেন সাব-কোয়রিতে সেই জ্ঞান রাখতে সক্ষম নয়?
ইন LEFT JOIN LATERAL
ক্যোয়ারী, আমরা বেশী বা কম 'ঠেলাঠেলি' যে ফিল্টারিং নীচের দিকে আছে। সুতরাং আমাদের প্রথম প্রশ্নের মধ্যে যে সমস্যাটি ছিল তা এখানে উপস্থিত নেই, সুতরাং আরও ভাল পারফরম্যান্স।
আমি অনুমান করি যে আমি মূলত থাম্বস, সাধারণ বুদ্ধি , এবং নিয়মের সন্ধান করছি ... সুতরাং এই পরিকল্পনাকারী যাদুটি দ্বিতীয় প্রকৃতিতে পরিণত হয় - যদি তা বোঝা যায়।
আপডেট (1)
LEFT JOIN
নিম্নলিখিত হিসাবে পুনরায় লেখাই ভাল কর্মক্ষমতা দেয় (এর চেয়ে কিছুটা ভাল LEFT JOIN LATERAL
):
select film.film_id, film.title, array_agg(a.first_name) as actors
from film
left join
(
select film_actor.film_id, actor.first_name
from actor
inner join film_actor using(actor_id)
) as a
on a.film_id = film.film_id
where film.title = 'ACADEMY DINOSAUR'
group by film.film_id
order by film.title;
GroupAggregate (cost=29.44..29.49 rows=1 width=51) (actual time=0.470..0.471 rows=1 loops=1)
Group Key: film.film_id
-> Sort (cost=29.44..29.45 rows=5 width=25) (actual time=0.428..0.430 rows=10 loops=1)
Sort Key: film.film_id
Sort Method: quicksort Memory: 25kB
-> Nested Loop Left Join (cost=4.74..29.38 rows=5 width=25) (actual time=0.149..0.386 rows=10 loops=1)
-> Index Scan using idx_title on film (cost=0.28..8.29 rows=1 width=19) (actual time=0.056..0.057 rows=1 loops=1)
Index Cond: ((title)::text = 'ACADEMY DINOSAUR'::text)
-> Nested Loop (cost=4.47..19.09 rows=200 width=8) (actual time=0.087..0.316 rows=10 loops=1)
-> Bitmap Heap Scan on film_actor (cost=4.32..18.26 rows=5 width=4) (actual time=0.052..0.089 rows=10 loops=1)
Recheck Cond: (film_id = film.film_id)
Heap Blocks: exact=10
-> Bitmap Index Scan on idx_fk_film_id (cost=0.00..4.32 rows=5 width=0) (actual time=0.035..0.035 rows=10 loops=1)
Index Cond: (film_id = film.film_id)
-> Index Scan using actor_pkey on actor (cost=0.14..0.17 rows=1 width=10) (actual time=0.011..0.011 rows=1 loops=10)
Index Cond: (actor_id = film_actor.actor_id)
Planning time: 1.833 ms
Execution time: 0.706 ms
আমরা কীভাবে এ বিষয়ে যুক্তি করতে পারি?
আপডেট (2)
আমি কয়েকটি পরীক্ষা-নিরীক্ষা চালিয়ে গিয়েছি এবং আমার কাছে মনে হয় থাম্বের একটি আকর্ষণীয় নিয়মটি: যতগুলি সম্ভব উচ্চ / দেরীতে সমষ্টিগত ফাংশনটি প্রয়োগ করুন । আপডেট (1) এ থাকা ক্যোয়ারী সম্ভবত আরও ভাল সম্পাদন করেছে কারণ আমরা বাহ্যিক ক্যোয়ারিতে একত্রিত করছি, অভ্যন্তরীণ কোয়েরিতে আর নেই।
আমরা যদি LEFT JOIN LATERAL
উপরের অংশটিকে আবার নিম্নরূপ লিখি তবে একই প্রয়োগ করা হবে বলে মনে হচ্ছে :
select film.film_id, film.title, array_agg(a.first_name) as actors
from film
left join lateral
(
select actor.first_name
from actor
inner join film_actor using(actor_id)
where film_actor.film_id = film.film_id
) as a
on true
where film.title = 'ACADEMY DINOSAUR'
group by film.film_id
order by film.title;
GroupAggregate (cost=29.44..29.49 rows=1 width=51) (actual time=0.088..0.088 rows=1 loops=1)
Group Key: film.film_id
-> Sort (cost=29.44..29.45 rows=5 width=25) (actual time=0.076..0.077 rows=10 loops=1)
Sort Key: film.film_id
Sort Method: quicksort Memory: 25kB
-> Nested Loop Left Join (cost=4.74..29.38 rows=5 width=25) (actual time=0.031..0.066 rows=10 loops=1)
-> Index Scan using idx_title on film (cost=0.28..8.29 rows=1 width=19) (actual time=0.010..0.010 rows=1 loops=1)
Index Cond: ((title)::text = 'ACADEMY DINOSAUR'::text)
-> Nested Loop (cost=4.47..19.09 rows=200 width=8) (actual time=0.019..0.052 rows=10 loops=1)
-> Bitmap Heap Scan on film_actor (cost=4.32..18.26 rows=5 width=4) (actual time=0.013..0.024 rows=10 loops=1)
Recheck Cond: (film_id = film.film_id)
Heap Blocks: exact=10
-> Bitmap Index Scan on idx_fk_film_id (cost=0.00..4.32 rows=5 width=0) (actual time=0.007..0.007 rows=10 loops=1)
Index Cond: (film_id = film.film_id)
-> Index Scan using actor_pkey on actor (cost=0.14..0.17 rows=1 width=10) (actual time=0.002..0.002 rows=1 loops=10)
Index Cond: (actor_id = film_actor.actor_id)
Planning time: 0.440 ms
Execution time: 0.136 ms
এখানে, আমরা array_agg()
উপরের দিকে সরানো । আপনি দেখতে পাচ্ছেন, এই পরিকল্পনাটিও মূলটির চেয়ে ভাল LEFT JOIN LATERAL
।
এটি বলেছিল, আমি নিশ্চিত নই যে এই স্ব-উদ্ভাবিত নিয়মের থাম্বের নিয়মটি ( যতটা সম্ভব উচ্চ / দেরীতে সমষ্টিগত ফাংশনটি প্রয়োগ করুন ) অন্যান্য ক্ষেত্রেও সত্য।
অতিরিক্ত তথ্য
ফিডাল: https://dbfiddle.uk/?rdbms=postgres_10&fiddle=4ec4f2fffd969d9e4b949bb2ca765ffb
সংস্করণ: জিগিসি (অ্যালপাইন 6.4.0) 6.4.0, 64-বিট দ্বারা সংকলিত x86_64-pc-linux-musl এ পোস্টগ্রিএসকিউএল 10.4
পরিবেশ: Docker: docker run -e POSTGRES_PASSWORD=sakila -p 5432:5432 -d frantiseks/postgres-sakila
। দয়া করে নোট করুন যে ডকার হাবের চিত্রটি পুরানো, তাই আমি প্রথমে স্থানীয়ভাবে একটি বিল্ড তৈরি করেছি: build -t frantiseks/postgres-sakila
গিট সংগ্রহস্থলটি ক্লোন করার পরে।
সারণির সংজ্ঞা:
চলচ্চিত্র
film_id | integer | not null default nextval('film_film_id_seq'::regclass)
title | character varying(255) | not null
Indexes:
"film_pkey" PRIMARY KEY, btree (film_id)
"idx_title" btree (title)
Referenced by:
TABLE "film_actor" CONSTRAINT "film_actor_film_id_fkey" FOREIGN KEY (film_id) REFERENCES film(film_id) ON UPDATE CASCADE ON DELETE RESTRICT
অভিনেতা
actor_id | integer | not null default nextval('actor_actor_id_seq'::regclass)
first_name | character varying(45) | not null
Indexes:
"actor_pkey" PRIMARY KEY, btree (actor_id)
Referenced by:
TABLE "film_actor" CONSTRAINT "film_actor_actor_id_fkey" FOREIGN KEY (actor_id) REFERENCES actor(actor_id) ON UPDATE CASCADE ON DELETE RESTRICT
film_actor
actor_id | smallint | not null
film_id | smallint | not null
Indexes:
"film_actor_pkey" PRIMARY KEY, btree (actor_id, film_id)
"idx_fk_film_id" btree (film_id)
Foreign-key constraints:
"film_actor_actor_id_fkey" FOREIGN KEY (actor_id) REFERENCES actor(actor_id) ON UPDATE CASCADE ON DELETE RESTRICT
"film_actor_film_id_fkey" FOREIGN KEY (film_id) REFERENCES film(film_id) ON UPDATE CASCADE ON DELETE RESTRICT
ডেটা: এটি সাকিলা নমুনা ডাটাবেস থেকে। এই প্রশ্নটি বাস্তব জীবনের ঘটনা নয়, আমি এই ডাটাবেসটি বেশিরভাগই একটি শেখার নমুনা ডাটাবেস হিসাবে ব্যবহার করছি। আমি কিছু মাস আগে এসকিউএল এর সাথে পরিচয় করিয়েছি এবং আমি আমার জ্ঞান প্রসারিত করার চেষ্টা করছি। এটিতে নিম্নলিখিত বিতরণ রয়েছে:
select count(*) from film: 1000
select count(*) from actor: 200
select avg(a) from (select film_id, count(actor_id) a from film_actor group by film_id) a: 5.47