PostgreSQL'de bir işlevin içindeki bir SELECT sonucunu nasıl döndürürüm?


106

PostgreSQL'de bu işlevi kullanıyorum, ancak sorgunun sonucunu nasıl döndüreceğimi bilmiyorum:

CREATE OR REPLACE FUNCTION wordFrequency(maxTokens INTEGER)
  RETURNS SETOF RECORD AS
$$
BEGIN
    SELECT text, count(*), 100 / maxTokens * count(*)
    FROM (
        SELECT text
    FROM token
    WHERE chartype = 'ALPHABETIC'
    LIMIT maxTokens
    ) as tokens
    GROUP BY text
    ORDER BY count DESC
END
$$
LANGUAGE plpgsql;

Ancak PostgreSQL işlevi içinde sorgunun sonucunu nasıl döndüreceğimi bilmiyorum.

Dönüş türünün olması gerektiğini buldum SETOF RECORD, değil mi? Ancak dönüş komutu doğru değil.

Bunu yapmanın doğru yolu nedir?


Neden onları sayıyorsun; TABLE jetonunuzda yinelenen jetonlarınız var mı? Ayrıca: lütfen tablo tanımını sorunuza ekleyin.
wildplasser

1
Bütün işleviniz bu mu? Fonksiyonda başka herhangi bir ifadeniz yoksa, bunu yapmalısınız LANGUAGE SQL.
jpmc26

Yanıtlar:


135

Kullanım RETURN QUERY:

CREATE OR REPLACE FUNCTION word_frequency(_max_tokens int)
  RETURNS TABLE (txt   text   -- also visible as OUT parameter inside function
               , cnt   bigint
               , ratio bigint) AS
$func$
BEGIN
   RETURN QUERY
   SELECT t.txt
        , count(*) AS cnt                 -- column alias only visible inside
        , (count(*) * 100) / _max_tokens  -- I added brackets
   FROM  (
      SELECT t.txt
      FROM   token t
      WHERE  t.chartype = 'ALPHABETIC'
      LIMIT  _max_tokens
      ) t
   GROUP  BY t.txt
   ORDER  BY cnt DESC;                    -- potential ambiguity 
END
$func$  LANGUAGE plpgsql;

Aramak:

SELECT * FROM word_frequency(123);

Açıklama:

  • Öyle çok açıkça basitçe kayıt olarak deklare daha dönüş türünü tanımlamak için daha pratik. Bu şekilde, her işlev çağrısıyla bir sütun tanımı listesi sağlamanız gerekmez. RETURNS TABLEbunu yapmanın bir yolu. Başkaları da var. OUTParametrelerin veri türleri , sorgu tarafından döndürülenle tam olarak eşleşmelidir.

  • OUTParametreler için isimleri dikkatlice seçin . Fonksiyon gövdesinde hemen hemen her yerde görülebilirler. Çakışmaları veya beklenmedik sonuçları önlemek için aynı ada sahip tablo niteliğinde sütunlar. Bunu örneğimdeki tüm sütunlar için yaptım.

    Ancak parametre ile aynı adın sütun diğer adı arasındaki olası adlandırma çakışmasına dikkat edin . Bu özel durumda ( ) Postgres, her iki şekilde de parametre üzerinde sütun diğer adını kullanır . Yine de bu, diğer bağlamlarda belirsiz olabilir. Herhangi bir karışıklığı önlemenin çeşitli yolları vardır:OUTcntRETURN QUERY SELECT ...OUT

    1. SEÇ listedeki öğenin sıralı konumunu kullanın: ORDER BY 2 DESC. Misal:
    2. İfadeyi tekrarlayın ORDER BY count(*).
    3. (Burada uygulanamaz.) Yapılandırma parametresini ayarlayın plpgsql.variable_conflictveya #variable_conflict error | use_variable | use_columnişlevdeki özel komutu kullanın . Görmek:
  • Sütun adları olarak "metin" veya "sayma" kullanmayın. Her ikisinin de Postgres'te kullanımı yasaldır, ancak "sayı", standart SQL'de ayrılmış bir sözcüktür ve temel bir işlev adı ve "metin", temel bir veri türüdür. Kafa karıştırıcı hatalara yol açabilir. Örneklerimde txtve kullanıyorum cnt.

  • Başlıkta eksik ;ve düzeltilmiş bir sözdizimi hatası eklendi . (_max_tokens int)Değil (int maxTokens)- yazın sonra isim .

  • Tamsayı bölme ile çalışırken, yuvarlama hatasını en aza indirmek için önce çarpıp sonra bölmek daha iyidir. Daha da iyisi: ile çalışın numeric(veya kayan nokta türüyle). Aşağıya bakınız.

Alternatif

Bu benim ne düşünüyorum sorgu aslında (bir hesaplama gibi görünmelidir simge başına nispi pay ):

CREATE OR REPLACE FUNCTION word_frequency(_max_tokens int)
  RETURNS TABLE (txt            text
               , abs_cnt        bigint
               , relative_share numeric) AS
$func$
BEGIN
   RETURN QUERY
   SELECT t.txt, t.cnt
        , round((t.cnt * 100) / (sum(t.cnt) OVER ()), 2)  -- AS relative_share
   FROM  (
      SELECT t.txt, count(*) AS cnt
      FROM   token t
      WHERE  t.chartype = 'ALPHABETIC'
      GROUP  BY t.txt
      ORDER  BY cnt DESC
      LIMIT  _max_tokens
      ) t
   ORDER  BY t.cnt DESC;
END
$func$  LANGUAGE plpgsql;

İfade sum(t.cnt) OVER ()bir pencere işlevidir . Sen olabilir bir kullanmak CTE oldukça, ancak bir alt sorgu bunun gibi basit vakalarda tipik ucuzdur - alt sorgunun yerine.

Parametrelerle çalışırken veya ( parametrelerin örtük kullanımını sağlayan ) son bir açık RETURNifade gerekli değildir (ancak buna izin verilir ).OUTRETURNS TABLEOUT

round()iki parametre ile yalnızca numerictürler için çalışır . count()alt sorguda bir bigintsonuç üretir ve bir sum()over bu bigintbir numericsonuç üretir , böylece bir numericsayıyla otomatik olarak ilgileniriz ve her şey yerine oturur.


Cevabınız ve düzeltmeleriniz için çok teşekkürler. Şimdi iyi çalışıyor (oran türünü yalnızca sayısal olarak değiştirdim).
Renato Dinhani

@ RenatoDinhaniConceição Cool! Gerçekte sormadığınız ek bir soruyu yanıtlayabilecek veya yanıtlamayacak bir sürüm ekledim. ;)
Erwin Brandstetter

Güzel, bence RETURN;ondan önce ihtiyacın olan tek şey END;, en azından yaptım - ama bir BİRLİK yapıyorum, bu yüzden bunu farklı kılacak mı emin değilim.
yekta

@yekta: Rolüyle ilgili bazı bilgiler ekledim RETURN. İlgisiz bir hata düzeltildi ve bu sırada bazı iyileştirmeler eklendi.
Erwin Brandstetter

1
Return TABLE () 'de olanı sınırlamak istemediğinizde bunu yapmanın yolu nedir? IE GERİ DÖNÜŞ TABLOSU (*)?
Nick

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.