将数组传递给PL / pgSQL存储过程时出错

时间:2015-01-19 21:52:44

标签: php sql postgresql prepared-statement plpgsql

我有这个程序:

CREATE OR REPLACE FUNCTION get_saldo_conto(idUtente integer, idConto integer, categories int[], end_date date) RETURNS numeric(8,2) AS $$
DECLARE 
row_transazione transazione%ROWTYPE;
saldoIniziale numeric(8,2);
totale numeric(8,2);

BEGIN

    saldoIniziale = (SELECT saldo_iniziale FROM conto WHERE id = idConto AND id_utente = idUtente);
    totale = 0;

        FOR row_transazione IN SELECT * 
                               FROM transazione 
                               LEFT JOIN categoria ON id_categoria = categoria.id 
                               WHERE id_conto = idConto 
                               AND transazione.id_utente = idUtente 
                               AND id_categoria = ANY (categories) 
                               AND data <= end_date
        LOOP
            IF(row_transazione.tipo = 'entrata') THEN
                totale = totale + row_transazione.importo;      
            ELSE
                totale = totale - row_transazione.importo;  
            END IF;     
        END LOOP;

    RETURN (saldoIniziale + totale) AS saldo_corrente;

END;

$$ LANGUAGE 'plpgsql';

当我用

打电话时
SELECT get_saldo_conto('1','19','{1,2,4,5,6}', '20/01/2015');

给我一​​个错误

ERROR: op ANY/ALL (array) requires array on right side

我做错了传递数组吗? 我也尝试过像{1,2,4,5,6}&#39; :: int []那样没有成功。

CREATE TABLE transazione(
    id SERIAL PRIMARY KEY,
    tipo VARCHAR(7) NOT NULL CHECK(tipo IN('spesa', 'entrata')),
    importo NUMERIC(8,2) NOT NULL,
    descrizione VARCHAR(40),
    data DATE DEFAULT CURRENT_TIMESTAMP,
    id_conto INTEGER NOT NULL REFERENCES conto(id) ON UPDATE CASCADE ON DELETE CASCADE,
    id_utente INTEGER NOT NULL REFERENCES utente(id) ON UPDATE CASCADE ON DELETE CASCADE,
    id_categoria INTEGER REFERENCES categoria(id) ON UPDATE CASCADE ON DELETE SET NULL
);

1 个答案:

答案 0 :(得分:2)

调试

您定义了行变量

row_transazione transazione%ROWTYPE;

但是你分配SELECT * FROM transazione LEFT JOIN categoriato它,显然不适合这种类型。

但是,您显示的错误消息没有意义。代码中ANY / ALL的唯一情况看起来是正确的。你确定你打电话给你认为你打电话的功能吗?调查:

SELECT n.nspname, p.proname
     , pg_get_function_identity_arguments(p.oid) AS params
FROM   pg_proc p
JOIN   pg_namespace n ON n.oid = p.pronamespace
WHERE  p.proname = 'get_saldo_conto';

..查找具有给定名称的所有函数。和

SHOW search_path;

..检查search_path是否通向正确的。

审计职能

你的功能会像这样:

CREATE OR REPLACE FUNCTION get_saldo_conto(_id_utente  integer
                                         , _id_conto   integer
                                         , _categories int[]
                                         , _end_date   date)
  RETURNS numeric(8,2) AS
$func$
DECLARE 
   row_trans     transazione;
   saldoIniziale numeric(8,2) := (SELECT saldo_iniziale
                                  FROM   conto
                                  WHERE  id_utente = _id_utente
                                  AND    id = _id_conto);
   totale        numeric(8,2) := 0;
BEGIN
   FOR row_trans IN
      SELECT t.* 
      FROM   transazione t
   -- LEFT   JOIN categoria ON id_categoria = categoria.id  -- useless anyway
      WHERE  t.id_utente = _id_utente 
      AND    t.id_conto = _id_conto 
      AND    t.id_categoria = ANY (_categories) 
      AND    data <= _end_date
   LOOP
      IF row_trans.tipo = 'entrata' THEN
         totale := totale + row_trans.importo;      
      ELSE
         totale := totale - row_trans.importo;  
      END IF;     
   END LOOP;

   RETURN (saldoIniziale + totale);  -- AS saldo_corrente -- no alias here!
END    
$func$ LANGUAGE plpgsql;

但这只是为了展示语法。该功能昂贵的废话

高级简单查询

替换为简单的SELECT

SELECT COALESCE((
          SELECT saldo_iniziale
          FROM   conto
          WHERE  id_utente = _id_utente 
          AND    id = _id_conto), 0)
     + COALESCE((
          SELECT sum(CASE WHEN tipo = 'entrata' THEN importo ELSE 0 END)
               - sum(CASE WHEN tipo = 'spesa'   THEN importo ELSE 0 END)
          FROM   transazione 
          WHERE  id_conto = _id_conto
          AND    id_utente = _id_utente 
          AND    id_categoria = ANY (_categories) 
          AND    data <= _end_date), 0) AS saldo;

假设conto中的行在(id_utente,id)上是唯一的。
根据实施细节,最佳查询可能会有所不同。我选择了一个可以安全地防止丢失行和NULL值的变体。无论哪种方式,普通查询都应该比所有行上的循环快得多。

如果需要,可以将其包装到函数(SQL或plpgsql)中。

除了:

  • transazione.tipo应该是enum类型 - 甚至只是"char"booleanvarchar(7)是标签的浪费,有两个可能的值。

  • data DATE DEFAULT CURRENT_TIMESTAMP 应该是data DATE DEFAULT CURRENT_DATE