मेरे पास निम्न तालिकाएँ हैं (सकीला डेटाबेस से ली गई हैं):
- फ़िल्म: film_id पेक है
- अभिनेता: अभिनेता_निधि है
- film_actor: film_id और actor_id फिल्म / अभिनेता के लिए fkeys हैं
मैं एक विशेष फिल्म का चयन कर रहा हूं। इस फिल्म के लिए, मैं भी चाहता हूं कि सभी कलाकार उस फिल्म में भाग लें। मेरे पास इसके लिए दो प्रश्न हैं: एक के साथ 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
संस्करण: PostgreSQL 10.4 x86_64-pc-linux-musl पर, gcc द्वारा संकलित (अल्पाइन 6.4.0) 6.4.0, 64-बिट
पर्यावरण: डॉकर 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