Apa yang Anda minta adalah tidak mungkin . SQL adalah bahasa yang diketik dengan ketat. Fungsi PostgreSQL perlu mendeklarasikan tipe pengembalian (RETURNS ..
) pada saat pembuatan .
Cara terbatas untuk mengatasi ini adalah dengan fungsi polimorfik. Jika Anda dapat memberikan jenis pengembalian pada waktu fungsi panggilan . Tapi itu tidak terbukti dari pertanyaan Anda.
- Memfaktorkan ulang fungsi PL/pgSQL untuk mengembalikan output dari berbagai kueri SELECT
Anda bisa mengembalikan hasil yang sepenuhnya dinamis dengan catatan anonim. Tapi kemudian Anda diminta untuk memberikan daftar definisi kolom dengan setiap panggilan. Dan bagaimana Anda tahu tentang kolom yang dikembalikan? Tangkap 22.
Ada berbagai solusi, tergantung pada apa yang Anda butuhkan atau dapat Anda kerjakan. Karena semua kolom data Anda tampaknya memiliki tipe data yang sama, saya sarankan untuk mengembalikan array :text[]
. Atau Anda dapat mengembalikan jenis dokumen seperti hstore
atau json
. Terkait:
-
Alternatif dinamis untuk pivot dengan CASE dan GROUP BY
-
Ubah kunci hstore secara dinamis menjadi kolom untuk kumpulan kunci yang tidak diketahui
Tetapi mungkin lebih mudah menggunakan dua panggilan:1:Biarkan Postgres membuat kueri. 2:Jalankan dan ambil kembali baris yang dikembalikan.
- Memilih beberapa nilai max() menggunakan satu pernyataan SQL
Saya tidak akan menggunakan fungsi dari Eric Minikel seperti yang disajikan dalam pertanyaan Anda sama sekali . Itu tidak aman terhadap injeksi SQL dengan cara pengidentifikasi cacat yang berbahaya. Gunakan format()
untuk membuat string kueri kecuali Anda menjalankan versi lama yang lebih lama dari Postgres 9.1.
Implementasi yang lebih singkat dan lebih bersih dapat terlihat seperti ini:
CREATE OR REPLACE FUNCTION xtab(_tbl regclass, _row text, _cat text
, _expr text -- still vulnerable to SQL injection!
, _type regtype)
RETURNS text AS
$func$
DECLARE
_cat_list text;
_col_list text;
BEGIN
-- generate categories for xtab param and col definition list
EXECUTE format(
$$SELECT string_agg(quote_literal(x.cat), '), (')
, string_agg(quote_ident (x.cat), %L)
FROM (SELECT DISTINCT %I AS cat FROM %s ORDER BY 1) x$$
, ' ' || _type || ', ', _cat, _tbl)
INTO _cat_list, _col_list;
-- generate query string
RETURN format(
'SELECT * FROM crosstab(
$q$SELECT %I, %I, %s
FROM %I
GROUP BY 1, 2 -- only works if the 3rd column is an aggregate expression
ORDER BY 1, 2$q$
, $c$VALUES (%5$s)$c$
) ct(%1$I text, %6$s %7$s)'
, _row, _cat, _expr -- expr must be an aggregate expression!
, _tbl, _cat_list, _col_list, _type
);
END
$func$ LANGUAGE plpgsql;
Panggilan fungsi yang sama dengan versi asli Anda. Fungsi crosstab()
disediakan oleh modul tambahan tablefunc
yang harus diinstal. Dasar-dasar:
- Kueri Tab Silang PostgreSQL
Ini menangani nama kolom dan tabel dengan aman. Perhatikan penggunaan tipe pengenal objek regclass
dan regtype
. Juga berfungsi untuk nama yang memenuhi syarat skema.
- Nama tabel sebagai parameter fungsi PostgreSQL
Namun, ini tidak sepenuhnya aman saat Anda meneruskan string untuk dieksekusi sebagai ekspresi (_expr
- cellc
dalam kueri asli Anda). Jenis input ini pada dasarnya tidak aman terhadap injeksi SQL dan tidak boleh diekspos ke publik umum.
- Injeksi SQL dalam fungsi Postgres vs kueri yang disiapkan
Memindai tabel hanya sekali untuk kedua daftar kategori dan harus sedikit lebih cepat.
Tetap tidak dapat mengembalikan jenis baris yang sepenuhnya dinamis karena itu sama sekali tidak mungkin.