@ Alci'nin cevabını açıklamak için:
PostgreSQL hangi sırayla şeyler yazdığınızla ilgilenmez
PostgreSQL, bir WHERE
maddedeki girdilerin sırasını hiç umursamıyor ve sadece maliyet ve seçicilik tahminine dayanarak indeksleri ve yürütme sırasını seçiyor.
Birleştirmelerin yazıldığı sıra da yapılandırılana kadar yok sayılır join_collapse_limit
; bundan daha fazla birleşme varsa, bunları yazıldığı sırayla uygular.
Alt sorgular, bunları içeren sorgudan önce veya sonra, en hızlı olana bağlı olarak, dış sorgu gerçekten bilgiye ihtiyaç duymadan önce alt sorgu çalıştırıldığı sürece yürütülebilir. Çoğunlukla gerçekte alt sorgu, ortada bir tür yürütülür veya dış sorgu ile birleştirilir.
PostgreSQL'in aslında sorgunun bölümlerini yürütmesinin garantisi yoktur. Tamamen optimize edilebilirler. Yan etkileri olan fonksiyonları çağırıyorsanız, bu önemlidir.
PostgreSQL sorgunuzu dönüştürecek
PostgreSQL, sonuçları değiştirmeden daha hızlı çalışabilmeleri için aynı etkileri korurken sorguları büyük ölçüde dönüştürecektir.
Alt sorgunun dışındaki terimler , alt sorgunun içine itilebilir , böylece dış sorguda yazdığınız yere değil, alt sorgunun bir parçası olarak yürütülürler.
Alt sorgudaki terimler dış sorguya çekilebilir , bu nedenle bunların çalıştırılması alt sorguda yazdığınız yere değil dış sorgunun bir parçası olarak yapılır.
Alt sorgu , dış masadaki bir bağlantıya düzleştirilebilir ve sık sık düzleştirilir . Aynı şey gibi şeyler EXISTS
ve NOT EXISTS
sorgular için de geçerlidir.
Görünümler, görünümü kullanan sorguda düzleşir
SQL fonksiyonları genellikle çağıran sorguya dahil edilir
... ve sürekli ifade ön değerlendirme, bazı alt sorguların korelasyonu ve her türlü diğer planlayıcı / optimizer hileleri gibi sorgulara yapılan birçok dönüşüm var.
Genel olarak PostgreSQL, sorgunuzu büyük ölçüde bu sorguların her birinin sorduğu noktaya dönüştürebilir ve yeniden yazabilir:
select my_table.*
from my_table
left join other_table on (my_table.id = other_table.my_table_id)
where other_table.id is null;
select *
from my_table
where not exists (
select 1
from other_table
where other_table.my_table_id = my_table.id
);
select *
from my_table
where my_table.id not in (
select my_table_id
from other_table
where my_table_id is not null
);
genellikle hepsi tam olarak aynı sorgu planını üretecektir. (Zaten yukarıdaki herhangi bir aptal hata yapmadım varsayarak).
Yalnızca sorgu planlayıcısının denediğiniz ve bunları otomatik olarak uyguladığınız püf noktaları bulmuş olduğunu bulmak için bir sorguyu optimize etmeyi denemek alışılmadık bir durum değildir, bu nedenle el ile optimize edilmiş sürüm orijinalden daha iyi değildir.
Sınırlamalar
Planlayıcı / optimize edici çok az bulunur ve sorgunun etkilerini, karar verecek mevcut verileri, uygulanan kuralları ve CPU zamanını değiştiremeyeceğinden kesinlikle emin olmak şartıyla sınırlıdır. optimizasyonları düşünmek için harcama yapabilir. Örneğin:
Planlayıcı, tutulan istatistiklere dayanır ANALYZE
(genellikle autovacuum aracılığıyla). Bunlar modası geçmişse, plan seçimi kötü olabilir.
İstatistikler yalnızca bir örnektir, bu nedenle örnekleme etkileri nedeniyle, özellikle de çok küçük bir örnek alındığında yanıltıcı olabilirler. Kötü plan seçimleri sonuçlanabilir.
İstatistikler, sütunlar arasındaki korelasyonlar gibi tabloyla ilgili bazı veri türlerini takip etmez. Bu, planlamacının, olayların olmadıklarında bağımsız olduklarını varsaydığı zaman kötü kararlar vermesine neden olabilir.
Planlayıcı, random_page_cost
kurulu olduğu sistemdeki çeşitli işlemlerin göreceli hızını söylemek gibi maliyet parametrelerine güvenir . Bunlar sadece rehber. Kötü yanılıyorlarsa, kötü plan seçimlerine yol açabilirler.
Bir ile herhangi alt sorgu LIMIT
veya OFFSET
düzleştirilmiş veya pullup / Aşağı iten tabi edilemez. Bu o idam ettirmekle olsa, hatta o, dış sorgunun tüm bölümlerine önce idam ettirmekle anlamına gelmez hiç .
CTE terimleri (bir WITH
sorgudaki cümlecikler ), eğer hiç çalıştırılmamışlarsa, daima bütünüyle uygulanır. Düzleştirilemezler ve terimler CTE terim engeli boyunca yukarı itilemez veya aşağı çekilemez. CTE terimleri her zaman son sorgudan önce yürütülür. Bu, SQL standardı olmayan bir davranıştır, ancak PostgreSQL'in işleri yaptığı şekilde belgelenmiştir.
PostgreSQL'in yabancı tablolar, security_barrier
görüşler ve diğer bazı özel ilişki türleri hakkındaki sorgular arasında optimize etme yeteneği sınırlıdır .
PostgreSQL, düz SQL dışında hiçbir şeyle yazılan bir işlevi satır içi yapmaz, ne de dış sorgu ile pullup / pushdown yapmaz.
Planlayıcı / optimize edici, ifade dizinlerini seçme ve dizin ile ifade arasındaki önemsiz veri türü farkları hakkında gerçekten aptaldır.
Çok daha fazla ton.
Sorgunuz
Sorgunuz durumunda:
select 1
from workdays day
where day.date_day >= '2014-10-01'
and day.date_day <= '2015-09-30'
and day.offer_id in (
select offer.offer_day
from offer
inner join province on offer.id_province = province.id_province
inner join center cr on cr.id_cr = province.id_cr
where upper(offer.code_status) <> 'A'
and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557')
and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
)
hiçbir şey, fazladan birleştirme kümesiyle daha basit bir sorguya düzleştirilmesini engellemez ve çok büyük olasılıkla olacaktır.
Muhtemelen şunun gibi bir şeyi ortaya çıkarır (denenmemiş, açıkça):
select 1
from workdays day
inner join offer on day.offer_id = offer.offer_day
inner join province on offer.id_province = province.id_province
inner join center cr on cr.id_cr = province.id_cr
where upper(offer.code_status) <> 'A'
and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557')
and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
and day.date_day >= '2014-10-01'
and day.date_day <= '2015-09-30';
PostgreSQL daha sonra birleştirme sırasını optimize eder ve seçiciliği ve satır sayısı tahminlerini ve kullanılabilir dizinleri temel alan yöntemleri birleştirir. Bunlar makul bir şekilde gerçeği yansıtıyorsa, o zaman birleşimleri yapacaklar ve nerede cümle girişlerini en iyi olan her hangi bir sırayla çalıştıracaklar - sık sık biraraya getirecekler, bu yüzden bunu biraz, sonra biraz daha sonra ilk bölüme geri dönecektir. , vb.
İyileştiricinin ne yaptığını nasıl görebilirsiniz?
PostgreSQL'in sorgunuzu optimize ettiği SQL'i göremezsiniz, çünkü SQL'i bir iç sorgu ağacı temsiline dönüştürür ve sonra onu değiştirir. Sen edebilirsiniz sorgu planını dökümü ve diğer sorguları ile karşılaştırın.
Bu sorgu planını veya iç plan ağacını tekrar SQL'e göndermenin yolu yok.
http://explain.depesz.com/ nezih bir sorgu planı yardımcısına sahiptir. Planları vb. Sorgulamak için tamamen yeniyseniz (bu durumda bu yazıdan bu kadar uzağa gitmenize şaşırdım) PgAdmin'in çok daha az bilgi sağlayan ancak daha basit bir grafiksel sorgu planı görüntüleyicisi vardır.
İlgili okuma:
İtme / çekme ve düzleştirme yetenekleri her sürümde gelişmeye devam eder . PostgreSQL genellikle pull-up / push-down / flatening kararları konusunda haklıdır, ancak her zaman değil, bu nedenle bazen (ab) bir CTE veya OFFSET 0
hack kullanmanız gerekir. Böyle bir durum bulursanız, bir sorgu planlayıcısı hatası bildirin.
Gerçekten, gerçekten meraklıysanız debug_print_plans
, ham sorgu planını görme seçeneğini de kullanabilirsiniz , ancak bunu okumak istemediğinize söz veriyorum. Gerçekten mi.