更新动态查询的PostgreSQL函数出错

q3qa4bjr  于 2023-06-22  发布在  PostgreSQL
关注(0)|答案(1)|浏览(164)

这是错误消息:

ERROR:  error de sintaxis en o cerca de «;»
LINE 35: END;

我正在尝试创建一个动态执行查询,以更新单个产品、供应商或实验室的药品价格。这是我在Postgres中的函数:

CREATE OR REPLACE FUNCTION mod_medicamento(char,text,char,float)RETURNS void as $$
Declare
/*
$1 for table (L=laboratorio,P=Proovedor(supplier),M=medicamento
$2 for name of lab,supplier or product
$3 paramater for tyoe of operation (A for increment price D for decrement price)
$4 for porcentage(0.01 to 0.99)
*/

tabla ALIAS FOR $1;
nombre ALIAS FOR $2;
operacion ALIAS FOR $3;
porcentaje ALIAS FOR $4;
nombre_tabla varchar(30);
nombre_campo varchar(30);
calculo varchar(20);
BEGIN

    if tabla='L'then
        nombre_tabla:='laboratorio';
        nombre_campo:='laboratorio';
    else if tabla='P'then
        nombre_tabla:='proovedor';
        nombre_campo:='proovedor';
    else if tabla='M' then
        nombre_tabla:='medicamento';
        nombre_campo:='nombre';
    else
        RAISE EXCEPTION 'selecciona una opcion valida (L/P/M)';
    END IF; 

    if operacion='A' then
        calculo:='precio+precio*';
     else if operacion='D' then
        calculo:='precio-precio*';
    else    
        RAISE EXCEPTION 'selecciona una opcion valida de operacion(A/D)';   
    end if;
    
    Execute format('Update medicamento set precio=%L%L where id_%L = (Select id_%L from %I
            where %I like %L)',calculo,porcentaje,nombre_tabla,nombre_tabla,nombre_tabla,nombre_campo,
                  nombre);
END;
$$ LANGUAGE plpgsql;
taor4pac

taor4pac1#

Adrian已经指出了即时语法错误。
但还有更多。考虑这个重写:

CREATE OR REPLACE FUNCTION mod_medicamento(_tabla char, _nombre text, _operacion char, _porcentaje float) -- ①
  RETURNS void
  LANGUAGE plpgsql AS
$func$
DECLARE
/*
$1 table (L=laboratorio, P=proovedor(supplier), M=medicamento
$2 name of lab, supplier, or product
$3 paramater for type of operation (A for increment price, D for decrement price)
$4 for percentage(0.01 to 0.99)
*/
   _nombre_tabla text;
   _nombre_campo text;
   _calculo text;
BEGIN
   -- enforce: $4 for percentage(0.01 to 0.99)  -- ②
   IF _porcentaje > 0 AND _porcentaje < 1 THEN  -- actually in range (0, 1)
      -- all good
   ELSE -- also catches null
      RAISE EXCEPTION '_porcentaje must be in range [0, 1). Was: %', _porcentaje;
   END IF;
   
   CASE _tabla  -- ③
   WHEN 'L' THEN
      _nombre_tabla := 'laboratorio';
      _nombre_campo := 'laboratorio';
   WHEN 'P' THEN
      _nombre_tabla := 'proovedor';
      _nombre_campo := 'proovedor';
   WHEN 'M' THEN
      _nombre_tabla := 'medicamento';
      _nombre_campo := 'nombre';
   ELSE
     RAISE EXCEPTION 'selecciona una opcion valida (L/P/M)';
   END CASE;

   CASE _operacion
   WHEN 'A' THEN
      _calculo := '+';
   WHEN 'D' THEN
      _calculo := '-';
   ELSE
     RAISE EXCEPTION 'selecciona una opcion valida de operacion(A/D)';
   END CASE;

   EXECUTE format(
-- RAISE NOTICE '%'  , format(  -- use instead of EXECUTE for debugging!
$q$
UPDATE medicamento m
SET    precio = m.precio * (1 %1$s %2$s)  -- ④, ⑤
FROM   %3$I t                             -- ⑥
WHERE  t.id_%4$s LIKE %5$L
AND    m.id_%3$s = t.id_%3$s
$q$
    , _calculo, _porcentaje, _nombre_tabla, _nombre_campo, _nombre);
END
$func$;

生成并执行以下形式的查询:

UPDATE medicamento m
SET    precio = m.precio * (1 + 0.123)
FROM   proovedor t
WHERE  t.id_proovedor LIKE 'nombre'
AND    m.id_proovedor = t.id_proovedor

①不鼓励使用ALIAS。改用命名参数。
我喜欢在参数和变量名前面加上下划线(_),以避免与列名发生命名冲突,但这是可选的。
②实际执行_porcentaje的允许范围。(也不允许0,这不会改变任何东西。
③“切换”CASE对于用例来说更高效和优雅。示例:

  • 在几列之间显示具有最大值的列名

④简化计算。precio + precio * 0.123--> precio * (1 + 0.123)
⑤你的原来的连接根本不起作用,因为format()中的格式说明符%L添加了破坏语法的单引号。
⑥在UPDATE中使用FROM clause而不是相关子查询。如果有多个行匹配,则原始查询将引发异常。也就是:更快。
由于您不返回任何内容,因此您可能需要PROCEDURE而不是FUNCTION。但两个都行。参见:

像这样的动态SQL可能对SQL注入开放。但是,虽然您不直接连接用户输入,但您是安全的。

相关问题