将数组传递给 PL/pgSQL 存储过程时出错
Error passing an array to PL/pgSQL stored procedure
我有这个程序:
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}'::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
);
调试
您定义了行变量
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"
或 boolean
。 varchar(7)
对于具有两个可能值的标签来说是一种浪费。
而data DATE DEFAULT CURRENT_TIMESTAMP
其实应该是data DATE DEFAULT CURRENT_DATE
.
我有这个程序:
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}'::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
);
调试
您定义了行变量
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"
或boolean
。varchar(7)
对于具有两个可能值的标签来说是一种浪费。而
其实应该是data DATE DEFAULT CURRENT_TIMESTAMP
data DATE DEFAULT CURRENT_DATE
.