WHERE cümleleri yazıldığı sırayla uygulanmış mı?


36

Büyük bir tabloya (37 milyon satır) bakan bir sorguyu optimize etmeye çalışıyorum ve işlemlerin bir sorguda hangi sırayla yürütüldüğü hakkında bir sorum var.

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')
    )

Are WHEREtarih aralığındaki hükümler alt sorgu önce idam? Daha hızlı bir işlem yapmak için diğer maddeler için büyük döngülerden kaçınmak için en kısıtlayıcı maddeleri ilk sıraya koymak iyi bir yöntem midir?

Şimdi sorguların yürütülmesi çok zaman alıyor.

Yanıtlar:


68

@ Alci'nin cevabını açıklamak için:

PostgreSQL hangi sırayla şeyler yazdığınızla ilgilenmez

  • PostgreSQL, bir WHEREmaddedeki 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 EXISTSve NOT EXISTSsorgular 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_costkurulu 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 LIMITveya OFFSETdü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 WITHsorgudaki 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_barriergö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 0hack 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.


Vay ... oldukça eksiksiz cevap :-) Postgresql (yanı sıra Oracle gibi iyi bilinen diğer DB motorları gibi) ile planlarımın yavaş olduğu durumlardan biri de, sütunlar veya çok sayıda ilişkili birleşme arasındaki korelasyonlardır. Genellikle, binlerce insan olduğunda, planın bu noktasında sadece birkaç satır olduğunu düşünerek iç içe döngüler yapmakla sonuçlanır. Bu tür sorguları optimize etmenin bir yolu 'enable_nestloop = off;' sorgu süresi için.
alci

V9.5.5 'in TO_DATE başvurusunda bulunup bulunmadığını kontrol etmeden önce basit bir 7 cümlede sorulan basit bir cümlede sordum. Sipariş önemlidir.
user1133275

@ user1133275 Bu durumda, sadece sizin için şans eseri çalıştı, çünkü hesaplama maliyet tahminleri aynıydı. PostgreSQL to_date, daha sonraki sürümlerde kontrol edilmeden önce veya bazı optimizer istatistiklerinde değişiklik yapılması nedeniyle çalışmaya karar verebilir . İçin güvenilir tek kontrolden sonra çalışmalıdır bir fonksiyonu önce kontrollerine, bir kullanma CASEdeyimi.
Craig Ringer

SO'da gördüğüm en büyük cevaplardan biri ! Yaşasın dostum!
17'de 62mkv

Basit sorgulama ekleme işlemi order byiçin sorgu çalıştırma işleminin hiç olmadığı kadar hızlı olduğu durumlar yaşadım order by. Sorgularımı bir araya getirerek istemişlerim gibi uygulamamın sebeplerinden biri de budur - harika bir optimize ediciye sahip olmak güzel ama kaderinize tamamen güveneceğinizin ve bunun sonucunu düşünmeden sorguları yazmanın akıllıca olmadığını düşünüyorum. o may beidam ... Büyük bir cevap !!
Greg0ry 10:18

17

SQL, bildirimsel bir dildir: Ne yapmak istediğinizi söylüyorsunuz, nasıl yapacağınızı değil. RDBMS, yürütme planı olarak adlandırılan sorguyu yürütme şeklini seçecektir.

Bir zamanlar (5-10 yıl önce), bir sorgunun yazılma şeklinin yürütme planı üzerinde doğrudan bir etkisi vardı, ancak günümüzde, çoğu SQL veritabanı motoru planlama için Maliyete Dayalı Bir Optimize Edici kullanıyor. Yani, sorguyu yürütmek için veritabanı nesneleri üzerindeki istatistiklerine dayanarak farklı stratejileri değerlendirecek ve en iyisini seçecektir.

Çoğu zaman, gerçekten en iyisidir, ancak bazen DB motoru kötü seçimler yapar ve bu da çok yavaş sorgular sağlar.


Bazı RDBMS'lerde sorgu sırasının hala önemli olduğu, ancak daha ileri olanlar için söylediğiniz her şeyin teoride olduğu kadar pratikte de doğru olduğu belirtilmelidir. Sorgu planlayıcısı kötü yürütme sırası seçimleri seçerken, genellikle daha etkin bir yöne itmek için kullanılabilir sorgu ipuçları vardır (örneğin, WITH(INDEX(<index>))belirli bir birleşim için dizin seçimini zorlamak için MSSQL'de olduğu gibi ).
David Spillett

Asıl soru, eğer üzerinde biraz endeks date_dayvarsa. Yok ise, o zaman optimize edicinin karşılaştırması gereken çok fazla planı yoktur.
jkavalik
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.