Gunakan 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)
LANGUAGE plpgsql 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$;
Telepon:
SELECT * FROM word_frequency(123);
Mendefinisikan tipe pengembalian secara eksplisit adalah banyak lebih praktis daripada mengembalikan record
umum . Dengan cara ini Anda tidak perlu memberikan daftar definisi kolom dengan setiap panggilan fungsi. RETURNS TABLE
adalah salah satu cara untuk melakukannya. Ada yang lain. 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 melakukannya untuk semua kolom dalam contoh saya.
Namun perhatikan potensi konflik penamaan antara OUT
parameter cnt
dan alias kolom dengan nama yang sama. Dalam kasus khusus ini (RETURN QUERY SELECT ...
) Postgres menggunakan alias kolom di atas OUT
parameter dengan cara apa pun. Ini bisa menjadi ambigu dalam konteks lain, meskipun. Ada berbagai cara untuk menghindari kebingungan:
- Gunakan posisi ordinal item dalam daftar SELECT:
ORDER BY 2 DESC
. Contoh:- Pilih baris pertama di setiap grup GROUP BY?
- Ulangi ekspresi
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:- Konflik penamaan antara parameter fungsi dan hasil JOIN dengan klausa USING
Jangan gunakan "teks" atau "hitung" sebagai nama kolom. Keduanya legal untuk digunakan di Postgres, tetapi "hitungan" adalah kata khusus 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, Anda mungkin menginginkan nama yang lebih eksplisit.
Menambahkan ;
. yang hilang dan memperbaiki kesalahan sintaks di header. (_max_tokens int)
, bukan (int maxTokens)
- ketik setelah nama .
Saat bekerja dengan pembagian bilangan bulat, lebih baik untuk mengalikan terlebih dahulu dan membagi kemudian, untuk meminimalkan kesalahan pembulatan. Atau bekerja dengan numeric
atau tipe floating point. Lihat di bawah.
Alternatif
Ini yang pikirkan kueri Anda seharusnya terlihat seperti (menghitung pembagian relatif per token ):
CREATE OR REPLACE FUNCTION word_frequency(_max_tokens int)
RETURNS TABLE (txt text
, abs_cnt bigint
, relative_share numeric)
LANGUAGE plpgsql 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$;
Ekspresi sum(t.cnt) OVER ()
adalah fungsi jendela. Anda bisa gunakan CTE alih-alih subquery. Bagus, tapi subquery biasanya lebih murah dalam kasus sederhana seperti ini (kebanyakan sebelum Postgres 12).
RETURN
terakhir eksplisit pernyataan tidak diperlukan (tetapi diizinkan) saat bekerja dengan OUT
parameter atau RETURNS TABLE
(yang secara implisit menggunakan OUT
parameter).
round()
dengan dua parameter hanya berfungsi untuk numeric
jenis. count()
di subquery menghasilkan bigint
hasil dan sum()
di atas bigint
ini menghasilkan numeric
hasilnya, jadi kita berurusan dengan numeric
nomor secara otomatis dan semuanya masuk ke tempatnya.