SQL

2012-01-04 11 views
13

içinde WITH yan tümcesi kullanma hakkında kılavuzluk Özyinelemeli sorgular için (0) WITH yan tümcesini nasıl kullanacağımı anlıyorum, ancak genel kullanımını/gücünü anlama konusunda sorunlar yaşıyorum.SQL

update global.prospect psp 
set status=status||'*' 
where psp.psp_id=(
      select p2.psp_id 
      from global.prospect p2 
      where p2.status='new' or p2.status='reset' 
      order by p2.request_ts 
      limit 1) 
returning psp.*; 

bu WITH sarmalayıcı yerine kullanmak için iyi bir aday olurdu: Örneğin

aşağıdaki sorgusu, id zaman damgası ile ilk kaydın id dönen bir alt sorgu kullanılarak belirlenir bir kayıt günceller nispeten çirkin alt sorgu? Öyleyse neden?

+0

Dokümantasyona göre, '' [RECURSIVE] '' INSERT' ve 'UPDATE' ifadelerinin üzerinde PostgreSQL 9.1’de eklenmiştir. –

+0

@JoeyAdams - dml ile kullanma - soğanın başka bir katmanını anlamak için –

cevap

18

, yukarıda aşağıdaki sorguları yarış koşulları vardır. Düşünün:


Sizin örnek bir CTE (ortak tablo ifade) kullanabilirsiniz, ancak bu size bir alt sorgu yapamadı hiçbir şey verecektir:

WITH x AS (
    SELECT psp_id 
    FROM global.prospect 
    WHERE status IN ('new', 'reset') 
    ORDER BY request_ts 
    LIMIT 1 
    ) 
UPDATE global.prospect psp 
SET status = status || '*' 
FROM x 
WHERE psp.psp_id = x.psp_id 
RETURNING psp.*; 

BTW, döndürülen satır güncelleştirildi sürümü olacak. Madde İLE bir temel haline gelir nerede başka bir tabloya döndü satır eklemek istedik Eğer


, işte: CTE kullanarak

WITH x AS (
    SELECT psp_id 
    FROM global.prospect 
    WHERE status IN ('new', 'reset') 
    ORDER BY request_ts 
    LIMIT 1 
    ), y AS (
    UPDATE global.prospect psp 
    SET status = status || '*' 
    FROM x 
    WHERE psp.psp_id = x.psp_id 
    RETURNING psp.* 
    ) 
INSERT INTO z 
SELECT * 
FROM y 

Veri değiştirerek sorguları sonra PostgreSQL 9.1 veya ile mümkündür.
more in the excellent manual.

+0

vay - gerçekten iyi - teşekkürler. pg doc kalitesine katılıyorum, ancak günümüze kadar CTE'ler, bu konuda okurken ne denli havalı olanlardan biri olsa da, pratikte hiçbir zaman bu konuya hiç bir zaman sahip olmadı. iki örneğiniz (sanırım!) çok yardımcı olur –

9

WITH, SELECT sorgusunda kullanmak üzere "geçici tabloları" tanımlamanıza izin verir. Örneğin, son zamanlarda iki takım arasındaki değişiklikleri hesaplamak için, böyle bir sorgu yazdım:

-- Let o be the set of old things, and n be the set of new things. 
WITH o AS (SELECT * FROM things(OLD)), 
    n AS (SELECT * FROM things(NEW)) 

-- Select both the set of things whose value changed, 
-- and the set of things in the old set but not in the new set. 
SELECT o.key, n.value 
    FROM o 
    LEFT JOIN n ON o.key = n.key 
    WHERE o.value IS DISTINCT FROM n.value 

UNION ALL 

-- Select the set of things in the new set but not in the old set. 
SELECT n.key, n.value 
    FROM o 
    RIGHT JOIN n ON o.key = n.key 
    WHERE o.key IS NULL; 

üstündeki o ve n "tabloları" tanımlayarak, ben ifadeler things(OLD) ve things(NEW) tekrarlamaktan kaçınmak başardı.

Elbette, FULL JOIN kullanarak UNION ALL'u ortadan kaldırabiliriz, ancak bunu özel durumumda yapamadım.

  • durum 'reset' 'yeni' ya da global.prospect en eski satırını bulun:


    Doğru Sorgunuzla anlamak, bu bunu yapar. onun durumu

  • Dönüş (status bizim çimdik dahil) satır için yıldız işareti ekleyerek

  • Mark onu.

WITH durumunuzdaki herhangi bir şeyi basitleştireceğini sanmıyorum. Yine de bir FROM maddesini kullanmak biraz daha şık olabilir:

update global.prospect psp 
set status = status || '*' 
from (select psp_id 
     from global.prospect 
     where status = 'new' or status = 'reset' 
     order by request_ts 
     limit 1 
     ) p2 
where psp.psp_id = p2.psp_id 
returning psp.*; 

Untested. Eğer işe yararsa beni bilgilendir.

Size dışında zaten tam olarak ne hemen hemen açıklanmıştır: Bu kolayca birden çok satır güncellemek için uzatılabilir

  • . Bir alt sorgu ifadesi kullanan sürümünüzde, alt sorgu birden çok satır oluşturmak için değiştirildiyse sorgu başarısız olur.

  • Alt sorguya global.prospect takmadım, bu yüzden okunması biraz daha kolay. Bu bir FROM yan tümcesi kullandığından, güncelleştirilmekte olan tabloyu yanlışlıkla başvuruyorsanız bir hata alırsınız.

  • Sizin sürümünüzde, her bir öğe için alt sorgu ifadesi ile karşılaşılır. PostgreSQL bunu optimize etmeli ve sadece ifadeyi bir kez değerlendirse de, psp'da bir sütuna yanlışlıkla başvurursanız veya bir uçucu ifade eklediğinizde bu optimizasyon ortadan kalkar.dahil tablolara eşzamanlı yazma erişimi olabiliyorsa

+0

Birden çok satırı güncellemek için, herhangi bir alt sorgu veya CTE'ye gerek yoktur, sadece: "UPDATE global.prospect SET status = status || '*' NEREDE durumu ('yeni', 'sıfırlama') GERİ DÖNÜŞ *; ' –