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 TABLE
adalah salah satu cara untuk melakukannya. Ada yang lainnya. Tipe data OUT
parameter harus sama persis dengan apa yang dikembalikan oleh kueri.
Pilih nama untuk OUT
parameter 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 OUT
parameter cnt
dan kolom alias dari nama yang sama. Dalam kasus khusus ini ( RETURN QUERY SELECT ...
) Postgres menggunakan alias kolom di atas OUT
parameter 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_conflict
atau gunakan perintah khusus #variable_conflict error | use_variable | use_column
dalam 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 txt
dan cnt
dalam 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 OUT
parameter atau RETURNS TABLE
(yang membuat penggunaan OUT
parameter secara implisit ).
round()
dengan dua parameter hanya berfungsi untuk numeric
tipe. count()
di subquery menghasilkan bigint
hasil dan sum()
over ini bigint
menghasilkan numeric
hasil, sehingga kita menangani numeric
nomor secara otomatis dan semuanya jatuh ke tempatnya.