Penggunaan 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;
Panggilan:
SELECT * FROM word_frequency(123);
Penjelasan:
Hal ini jauh lebih praktis secara eksplisit menentukan jenis kembali dari sekedar menyatakan sebagai rekor. Dengan cara ini Anda tidak perlu menyediakan daftar definisi kolom dengan setiap panggilan fungsi. RETURNS TABLEadalah salah satu cara untuk melakukannya. Ada yang lainnya. Tipe data OUTparameter harus sama persis dengan apa yang dikembalikan oleh kueri.
Pilih nama untuk OUTparameter dengan hati-hati. Mereka terlihat di tubuh fungsi hampir di mana saja. Kolom kualifikasi tabel dengan nama yang sama untuk menghindari konflik atau hasil yang tidak diharapkan. Saya melakukan itu untuk semua kolom dalam contoh saya.
Tapi perhatikan konflik penamaan potensial antara OUTparameter cntdan kolom alias dari nama yang sama. Dalam kasus khusus ini ( RETURN QUERY SELECT ...) Postgres menggunakan alias kolom di atas OUTparameter dengan cara apa pun. Ini bisa jadi ambigu dalam konteks lain. Ada berbagai cara untuk menghindari kebingungan:
- Gunakan posisi ordinal dari item dalam daftar SELECT:
ORDER BY 2 DESC. Contoh:
- Ulangi ekspresi tersebut
ORDER BY count(*).
- (Tidak berlaku di sini.) Setel parameter konfigurasi
plpgsql.variable_conflictatau gunakan perintah khusus #variable_conflict error | use_variable | use_columndalam fungsi. Lihat:
Jangan gunakan "teks" atau "hitung" sebagai nama kolom. Keduanya legal untuk digunakan di Postgres, tetapi "count" adalah kata yang dicadangkan dalam SQL standar dan nama fungsi dasar dan "teks" adalah tipe data dasar. Dapat menyebabkan kesalahan yang membingungkan. Saya menggunakan txtdan cntdalam contoh saya.
Menambahkan ;kesalahan sintaks yang hilang dan mengoreksi di header. (_max_tokens int), bukan (int maxTokens)- ketik setelah nama .
Saat bekerja dengan pembagian bilangan bulat, lebih baik mengalikannya terlebih dahulu dan membagi nanti, untuk meminimalkan kesalahan pembulatan. Lebih baik lagi: bekerja dengan numeric(atau tipe floating point). Lihat di bawah.
Alternatif
Menurut saya, seperti inilah tampilan kueri Anda (menghitung bagian relatif per token ):
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;
Ekspresi tersebut sum(t.cnt) OVER ()adalah fungsi jendela . Anda dapat menggunakan CTE daripada subkueri - cantik, tetapi subkueri biasanya lebih murah dalam kasus sederhana seperti ini.
Pernyataan eksplisitRETURN akhir tidak diperlukan (tetapi diizinkan) saat bekerja dengan OUTparameter atau RETURNS TABLE(yang membuat penggunaan OUTparameter secara implisit ).
round()dengan dua parameter hanya berfungsi untuk numerictipe. count()di subquery menghasilkan biginthasil dan sum()over ini bigintmenghasilkan numerichasil, sehingga kita menangani numericnomor secara otomatis dan semuanya jatuh ke tempatnya.