เก็บสูตรไว้ในตารางและใช้สูตรในฟังก์ชัน


10

ฉันมีฐานข้อมูล PostgreSQL 9.1 ซึ่งเป็นส่วนหนึ่งที่จัดการค่าคอมมิชชั่นของตัวแทน ตัวแทนแต่ละรายมีสูตรการคำนวณของตนเองเพื่อรับค่าคอมมิชชั่น ฉันมีฟังก์ชั่นในการสร้างจำนวนค่าคอมมิชชันที่แต่ละตัวแทนควรได้รับ แต่มันเป็นไปไม่ได้ที่จะใช้เมื่อจำนวนตัวแทนเพิ่มขึ้น ฉันถูกบังคับให้ทำใบแจ้งยอดที่ยาวและรหัสซ้ำซึ่งทำให้ฟังก์ชั่นของฉันใหญ่มาก

สูตรทั้งหมดมีตัวแปรคงที่:

d .. วันทำงานในเดือนนั้น
r .. โหนดใหม่ได้รับการรับรอง
l .. คะแนนความภักดี
s .. ค่านายหน้าย่อย
b .. อัตราฐาน
ฉัน .. รายได้ที่ได้รับ

สูตรสามารถเป็นดังนี้:

d*b+(l*4+r)+(i/d)+s

แต่ละตัวแทนเจรจาสูตรการชำระเงินกับฝ่ายทรัพยากรบุคคล ดังนั้นฉันสามารถเก็บสูตรไว้ในตารางตัวแทนได้แล้วมีฟังก์ชั่นเล็ก ๆ ที่เพิ่งได้สูตรจากตารางและแปลมันด้วยค่าและคำนวณจำนวนหรือไม่

คำตอบ:


6

เตรียมการ

สูตรของคุณมีลักษณะดังนี้:

d*b+(l*4+r)+(i/d)+s

ฉันจะแทนที่ตัวแปรด้วย$nสัญกรณ์เพื่อให้พวกเขาสามารถแทนที่ด้วยค่าโดยตรงใน plpgsql EXECUTE(ดูด้านล่าง):

$1*$5+($3*4+$2)+($6/$1)+$4

คุณสามารถจัดเก็บสูตรดั้งเดิมของคุณเพิ่มเติม (สำหรับสายตามนุษย์) หรือสร้างแบบฟอร์มนี้แบบไดนามิกด้วยนิพจน์เช่น:

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');

ตรวจสอบให้แน่ใจว่าคุณแปลเสียง คำอธิบายบางอย่างสำหรับการแสดงออก regexp :

\ m .. จับคู่เฉพาะตอนต้นของคำ
\ M .. จับคู่เฉพาะตอนท้ายของคำ

พารามิเตอร์ที่ 4 'g'.. แทนที่ทั้งหมด

ฟังก์ชั่นหลัก

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; 

โทร:

SELECT f_calc(1, 2, 3, 4.1, 5.2, 6.3, '$1*$5+($3*4+$2)+($6/$1)+$4');

ผลตอบแทน:

29.6000000000000000

ประเด็นสำคัญ

  • ฟังก์ชั่นใช้พารามิเตอร์ค่า 6 และformula textเป็น 7 ฉันใส่สูตรที่ผ่านมาดังนั้นเราสามารถใช้แทน$1 .. $6 $2 .. $7เพียงเพื่อประโยชน์ในการอ่าน
    ฉันกำหนดประเภทข้อมูลสำหรับค่าตามที่เห็นเหมาะสม กำหนดประเภทที่เหมาะสม (เพื่อใช้การตรวจสุขภาพพื้นฐาน) หรือทำให้ทั้งหมดnumeric:

  • ส่งผ่านค่าสำหรับการดำเนินการแบบไดนามิกด้วยส่วนUSINGคำสั่ง สิ่งนี้หลีกเลี่ยงการร่ายไปมาและทำให้ทุกอย่างง่ายขึ้นปลอดภัยขึ้นและเร็วขึ้น

  • ฉันใช้OUTพารามิเตอร์เพราะมันดูดีกว่าและใช้ไวยากรณ์ที่สั้นกว่า สุดท้ายRETURNไม่จำเป็นต้องค่าของพารามิเตอร์ OUT (s) จะถูกส่งกลับโดยอัตโนมัติ

  • พิจารณาการบรรยายเรื่องความปลอดภัยโดย @Chrisและบท"การเขียนฟังก์ชั่นความปลอดภัยอย่างปลอดภัย"ในคู่มือ ในการออกแบบของฉันจุดเดียวของการฉีดคือสูตรเอง

  • คุณสามารถใช้ค่าเริ่มต้นสำหรับพารามิเตอร์บางอย่างเพื่อให้การโทรง่ายขึ้น


5

โปรดอ่านสิ่งนี้อย่างละเอียดเกี่ยวกับข้อควรพิจารณาด้านความปลอดภัย เป็นหลักคุณพยายามฉีด SQL โดยพลการในฟังก์ชั่นของคุณ ดังนั้นคุณต้องมีการเรียกใช้นี้ภายใต้ผู้ใช้ที่มีสิทธิ์ จำกัด สูง

  1. สร้างผู้ใช้และเพิกถอนการอนุญาตทั้งหมดจากผู้ใช้ อย่าให้สิทธิ์แก่สาธารณะในฐานข้อมูลเดียวกับที่คุณทำ

  2. สร้างฟังก์ชันเพื่อประเมินค่านิพจน์สร้างsecurity definerและเปลี่ยนแปลงเจ้าของเป็นผู้ใช้ที่ จำกัด

  3. ประมวลผลนิพจน์แล้วส่งไปยังฟังก์ชัน eval () ที่คุณสร้างไว้ด้านบน คุณสามารถทำสิ่งนี้ในฟังก์ชั่นอื่นถ้าคุณต้องการ

โปรดทราบอีกครั้งว่านี่มีผลกระทบด้านความปลอดภัยที่ร้ายแรง

แก้ไข: รหัสตัวอย่างย่อ (ยังไม่ทดลอง แต่ควรพาคุณไปที่นั่นหากคุณติดตามเอกสาร):

CREATE OR REPLACE FUNCTION eval_numeric(text) returns numeric language plpgsql security definer immutable as
$$
declare retval numeric;
begin

execute $e$ SELECT ($1)::numeric$e$ into retval;
return retval;
end;
$$;

ALTER FUNCTION eval_numeric OWNER TO jailed_user;

CREATE OR REPLACE FUNCTION foo(expression text, a numeric, b numeric) returns numeric language sql immutable as $$
select eval(regexp_replace(regexp_replace($1, 'a', $2, 'g'), 'b', '$3', 'g'));
$$; -- can be security invoker, but eval needs to be jailed.

"ทำให้คำจำกัดความด้านความปลอดภัย" สับสนจริงๆคุณช่วยอธิบายได้ไหม
jcolebrand

1
PostgreSQL มีสองโหมดความปลอดภัยที่ฟังก์ชั่นสามารถทำงานได้ การรักษาความปลอดภัยเป็นค่าเริ่มต้น DEFINER ความปลอดภัยหมายถึง "ทำงานกับบริบทความปลอดภัยของเจ้าของฟังก์ชั่น" เรียงลำดับเช่นบิต SETUID บน * ระวัง ในการสร้างนิยามความปลอดภัยของฟังก์ชันคุณสามารถระบุสิ่งนี้ได้ในการประกาศฟังก์ชัน ( CREATE FUNCTION foo(text) returns text IMMUTABLE LANGUAGE SQL SECURITY DEFINER AS $$...) หรือคุณสามารถทำได้ALTER FUNCTION foo(text) SECURITY DEFINER
Chris Travers

โอ้นั่นคือ PG lino ที่เจาะจง Gotcha Shoulda ใช้ backticks ในคำตอบ ;-)
jcolebrand

@ChrisTravers ฉันคาดหวังว่าบางตัวอย่างรหัสเพื่อประเมินสูตรa+bคือเก็บไว้ในคอลัมน์ประเภทข้อความในตารางแล้วฉันมีฟังก์ชั่นfoo(a int, b int,formula text)ถ้ามันได้รับสูตรเป็น + b ฉันจะทำให้ฟังก์ชั่นทำจริง + b แทน ฉันต้องมีคำสั่ง case ที่ยาวมากสำหรับสูตรที่เป็นไปได้ทั้งหมดและทำซ้ำรหัสในเซ็กเมนต์ทั้งหมดหรือไม่
indago

1
@indago ฉันคิดว่าคุณต้องการแยกสิ่งนี้ออกเป็นสองเลเยอร์เนื่องจากปัญหาด้านความปลอดภัย แรกคือชั้นการแก้ไข คุณสามารถใช้ regexes ใน PostgreSQL เพื่อทำสิ่งนี้ ในระดับที่ต่ำกว่าโดยทั่วไปคุณกำลังเรียกใช้ฟังก์ชันนี้ในฟังก์ชัน SQL ที่ถูกจำคุก คุณจำเป็นต้องให้ความสำคัญกับความปลอดภัยอย่างใกล้ชิดถ้าคุณจะทำเช่นนี้และคุณต้องให้ความสนใจอย่างใกล้ชิดเพื่อส่งคืนค่า โดยที่ไม่รู้ตัวมากขึ้นมันก็ยากที่จะทำอะไรกับโค้ด samople แต่จะแก้ไขคำตอบ
Chris Travers

2

อีกทางเลือกหนึ่งในการจัดเก็บสูตรและดำเนินการ (ซึ่งตามที่Chris กล่าวถึงมีปัญหาด้านความปลอดภัย ) จะต้องมีตารางแยกต่างหากที่เรียกว่าformula_stepsโดยทั่วไปจะมีตัวแปรและตัวดำเนินการและลำดับที่จะถูกเรียกใช้ นี่จะทำงานได้อีกเล็กน้อย แต่จะปลอดภัยกว่า ตารางอาจมีลักษณะเช่นนี้:

formula_steps
-------------
  formula_step_id
  formula_id (FK อ้างอิงโดยตารางตัวแทน)
  INPUT_1
  INPUT_2
  โอเปอเรเตอร์ (อาจเป็น ID ไปยังตารางของโอเปอเรเตอร์ที่ได้รับอนุญาตหากคุณไม่ต้องการเก็บสัญลักษณ์ของโอเปอเรเตอร์โดยตรง)
  ลำดับ

อีกตัวเลือกหนึ่งคือใช้ห้องสมุด / เครื่องมือของบุคคลที่สามเพื่อประเมินนิพจน์ทางคณิตศาสตร์ สิ่งนี้จะทำให้ฐานข้อมูลของคุณมีความเสี่ยงน้อยลงต่อการฉีด SQL แต่ตอนนี้คุณเพิ่งเปลี่ยนปัญหาความปลอดภัยที่อาจเกิดขึ้นกับเครื่องมือภายนอกของคุณ (ซึ่งอาจยังปลอดภัยอยู่)


ตัวเลือกสุดท้ายคือการเขียน (หรือดาวน์โหลด) ขั้นตอนที่ประเมินนิพจน์ทางคณิตศาสตร์ มีอัลกอริทึมที่รู้จักกันดีสำหรับปัญหานี้ดังนั้นจึงไม่ยากที่จะหาข้อมูลออนไลน์


1
+1 สำหรับตัวเลือกที่สาม หากรู้ถึงอินพุตที่เป็นไปได้ทั้งหมดให้ใช้ hard code เพื่อเลือกอินพุตแต่ละรายการและแทนที่ (ถ้าจำเป็นและจำเป็น) ลงในสูตรที่เก็บไว้เป็นข้อความจากนั้นใช้ไลบรารีรูทีนเพื่อประเมินค่าทางคณิตศาสตร์ ความเสี่ยงในการฉีด SQL ลดลง
Joel Brown
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.