Preparar
Sus fórmulas se ven así:
d*b+(l*4+r)+(i/d)+s
Reemplazaría las variables con $n
notación para que puedan reemplazarse con valores directamente en plpgsql EXECUTE
(ver a continuación):
$1*$5+($3*4+$2)+($6/$1)+$4
Puede almacenar sus fórmulas originales adicionalmente (para el ojo humano) o generar este formulario dinámicamente con una expresión como:
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');
Solo asegúrate de que tu traducción sea sólida. Alguna explicación para las expresiones regexp :
\ m .. coincide solo al comienzo de una palabra
\ M .. coincide solo al final de una palabra
4to parámetro 'g'
... reemplazar globalmente
Función básica
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;
Llamada:
SELECT f_calc(1, 2, 3, 4.1, 5.2, 6.3, '$1*$5+($3*4+$2)+($6/$1)+$4');
Devoluciones:
29.6000000000000000
Puntos principales
La función toma 6 parámetros de valor y formula text
como séptimo. Puse la fórmula al final, para que podamos usar en $1 .. $6
lugar de $2 .. $7
. Solo por el bien de la legibilidad.
Asigné tipos de datos para los valores como mejor me parecía. Asigne los tipos adecuados (para implementar controles básicos de cordura) o simplemente haga que todos numeric
:
Pase valores para la ejecución dinámica con la USING
cláusula Esto evita el vaciado de un lado a otro y hace que todo sea más simple, seguro y rápido.
Utilizo un OUT
parámetro porque es más elegante y hace una sintaxis más clara y corta. RETURN
No se necesita un final , el valor de los parámetros OUT se devuelve automáticamente.
Considere la conferencia sobre seguridad de @Chris y el capítulo "Cómo escribir las funciones de DEFINER DE SEGURIDAD de forma segura" en el manual. En mi diseño, el único punto de inyección es la fórmula misma.
Puede usar valores predeterminados para algunos parámetros para simplificar aún más la llamada.
CREATE FUNCTION foo(text) returns text IMMUTABLE LANGUAGE SQL SECURITY DEFINER AS $$...
) o puedeALTER FUNCTION foo(text) SECURITY DEFINER
a+b
es decir, se almacena en una columna de tipo de texto en una tabla, entonces tengo una funciónfoo(a int, b int,formula text)
si obtiene la fórmula es a + b, ¿cómo puedo hacer que la función realmente haga a + b en lugar de ¿Tengo que tener una declaración de caso muy larga para todas las fórmulas posibles y repetir el código en todos los segmentos?Una alternativa a simplemente almacenar la fórmula y luego ejecutarla (que, como Chris mencionó, tiene problemas de seguridad ) sería tener una tabla separada llamada
formula_steps
que básicamente contendría las variables y operadores y la secuencia en la que se ejecutan. Esto sería un poco más de trabajo, pero sería más seguro. La tabla podría verse así:Otra opción sería utilizar alguna biblioteca / herramienta de terceros para evaluar expresiones matemáticas. Esto haría que su base de datos sea menos vulnerable a la inyección de SQL, pero ahora acaba de cambiar los posibles problemas de seguridad a su herramienta externa (que aún podría ser bastante segura).
La opción final sería escribir (o descargar) un procedimiento que evalúe expresiones matemáticas. Existen algoritmos conocidos para este problema, por lo que no debería ser difícil encontrar información en línea.
fuente