Mempersiapkan
Rumus Anda terlihat seperti ini:
d*b+(l*4+r)+(i/d)+s
Saya akan mengganti variabel dengan $n
notasi sehingga mereka dapat diganti dengan nilai langsung di plpgsql EXECUTE
(lihat di bawah):
$1*$5+($3*4+$2)+($6/$1)+$4
Anda dapat menyimpan formula asli Anda tambahan (untuk mata manusia) atau menghasilkan formulir ini secara dinamis dengan ekspresi seperti:
SELECT regexp_replace(regexp_replace(regexp_replace(
regexp_replace(regexp_replace(regexp_replace(
'd*b+(l*4+r)+(i/d)+s'
, '\md\M', '$1', 'g')
, '\mr\M', '$2', 'g')
, '\ml\M', '$3', 'g')
, '\ms\M', '$4', 'g')
, '\mb\M', '$5', 'g')
, '\mi\M', '$6', 'g');
Pastikan saja, terjemahan Anda baik-baik saja. Beberapa penjelasan untuk ekspresi regexp :
\ m .. hanya cocok di awal kata
\ M .. hanya cocok di akhir kata
Parameter 4 'g'
.. ganti secara global
Fungsi inti
CREATE OR REPLACE FUNCTION f_calc(
d int -- days worked that month
,r int -- new nodes accuired
,l int -- loyalty score
,s numeric -- subagent commission
,b numeric -- base rate
,i numeric -- revenue gained
,formula text
,OUT result numeric
) RETURNS numeric AS
$func$
BEGIN
EXECUTE 'SELECT '|| formula
INTO result
USING $1, $2, $3, $4, $5, $6;
END
$func$ LANGUAGE plpgsql SECURITY DEFINER IMMUTABLE;
Panggilan:
SELECT f_calc(1, 2, 3, 4.1, 5.2, 6.3, '$1*$5+($3*4+$2)+($6/$1)+$4');
Pengembalian:
29.6000000000000000
Poin utama
Fungsi ini mengambil 6 parameter nilai dan formula text
sebagai 7. Saya menempatkan formula terakhir, jadi kita bisa menggunakan $1 .. $6
bukan $2 .. $7
. Hanya demi keterbacaan.
Saya menetapkan tipe data untuk nilai-nilai yang saya lihat cocok. Tetapkan jenis yang tepat (untuk menerapkan pemeriksaan kewarasan dasar) atau buat semuanya numeric
:
Masukkan nilai untuk eksekusi dinamis dengan USING
klausa. Ini menghindari bolak-balik dan membuat segalanya lebih sederhana, lebih aman dan lebih cepat.
Saya menggunakan OUT
parameter karena itu lebih elegan dan membuat sintaks yang lebih jelas lebih pendek. Final RETURN
tidak diperlukan, nilai parameter OUT dikembalikan secara otomatis.
Pertimbangkan kuliah tentang keamanan oleh @Chris dan bab "Menulis Fungsi Penjamin Keamanan dengan Aman" dalam manual. Dalam desain saya, satu-satunya titik injeksi adalah formula itu sendiri.
Anda dapat menggunakan default untuk beberapa parameter untuk lebih menyederhanakan panggilan.
CREATE FUNCTION foo(text) returns text IMMUTABLE LANGUAGE SQL SECURITY DEFINER AS $$...
) atau Anda dapatALTER FUNCTION foo(text) SECURITY DEFINER
a+b
disimpan dalam kolom jenis teks dalam sebuah tabel maka saya memiliki fungsifoo(a int, b int,formula text)
jika mendapat rumus adalah + b bagaimana saya bisa membuat fungsi benar-benar melakukan + b bukannya saya harus memiliki pernyataan kasus yang sangat panjang untuk semua formula yang mungkin dan mengulang kode di semua segmen?Alternatif untuk hanya menyimpan formula dan kemudian mengeksekusinya (yang seperti yang disebutkan Chris, memiliki masalah keamanan ) adalah dengan memiliki tabel terpisah yang disebut
formula_steps
yang pada dasarnya akan berisi variabel dan operator dan urutan di mana mereka dieksekusi. Ini akan menjadi pekerjaan yang sedikit lebih, tetapi akan lebih aman. Tabelnya mungkin terlihat seperti ini:Pilihan lain adalah menggunakan beberapa pustaka / alat pihak ketiga untuk mengevaluasi ekspresi matematika. Ini akan membuat basis data Anda kurang rentan terhadap injeksi SQL, tetapi sekarang Anda baru saja memindahkan kemungkinan masalah keamanan ke alat eksternal Anda (yang mungkin masih cukup aman).
Opsi terakhir adalah menulis (atau mengunduh) prosedur yang mengevaluasi ekspresi matematika. Ada algoritma yang diketahui untuk masalah ini, jadi tidak sulit untuk menemukan info online.
sumber