Numeric Field Overflow when Subtracting from Numeric value in Postgresql - postgresql

Relatively new to Postgres, and been having trouble subtracting a value from a NUMERIC(4,2) value type in an update statement. The following code:
UPDATE Tickets
SET ticketPrice = ticketPrice-3
FROM Showings
WHERE Showings.priceCode = modTicket
Elicits the following error:
ERROR: numeric field overflow
Detail: A field with precision 4, scale 2 must round to an absolute value less than 10^2.
ticketPrice has the value type of NUMERIC(4,2). How do I make this subtraction? There are no possible values that this subtraction would cause to extend past two decimal points, or in the negatives at all. The only values that this subtraction applies to are 5.00, 3.50, and 8.00.

You could try to find out the error source like this:
do $$
declare r record;
foo numeric(4,2);
begin
for r in (select t.* from Tickets as t, Showings as s where s.priceCode = t.modTicket) loop
foo := r.ticketPrice - 3;
end loop;
exception
when others then raise notice '%', r;
raise;
end $$;
Example:
do $$
declare r record;
foo numeric(1);
begin
for r in (with t(x,y) as (values(1,2),(3,4)) select * from t) loop
foo := r.y + 7;
end loop;
exception
when others then raise notice '%', r;
raise;
end $$;
Output:
NOTICE: (3,4)
ERROR: numeric field overflow
DETAIL: A field with precision 1, scale 0 must round to an absolute value less than 10^1.
CONTEXT: PL/pgSQL function inline_code_block line 1 at assignment

UPDATE Tickets as t
SET ticketPrice = ticketPrice-3
FROM Showings as s
WHERE s.priceCode = t.modTicket
Documentation of UPDATE Query

Related

PostgreSQL: SQLstate: 22003 numeric overflow

I have a table in PostgreSQL (mixbest) with 73018 rows. The fields that I want to select are:
sample integer
m integer,
pciv double precision,
aggrec double precision
soil character(1)
I'm trying a SELECT but I get the following error SQLstate: 22003 numeric overflow. My select:
SELECT sample, m, 1-(EXP(SUM(LN(1-pciv)))) pciv, avg (aggrec) aggrec_avg, soil
FROM mixbest
GROUP BY sample, m, soil;
I know the problem is the EXP() due to I've tried the same select with the expression (SUM(LN(1-pciv)))) and I don't get the same error.
I tried to execute the select only in a few lines, and it works:
SELECT sample, m, 1-(EXP(SUM(LN(1-pciv)))) pciv, avg (aggrec) aggrec_avg, soil
FROM mixbest
WHERE sample< 4492 GROUP BY sample, m, soil;
Do you have any suggestion?
Something like this, I guess:
create or replace function mixbest_avg(out sample int, out m int, out pciv double precision, out aggrec_avg double precision, out soil character(1))
returns setof record as
$$
declare
rec record;
begin
for rec in
SELECT t.sample _sample, t.m _m, SUM(LN(1-t.pciv)) _pciv, avg(t.aggrec) _aggrec, t.soil _soil
FROM mixbest t
GROUP BY t.sample, t.m, t.soil
loop
begin
rec._pciv = 1 - exp(rec._pciv);
exception
when numeric_value_out_of_range then -- here we catch an exception
rec._pciv = 0; -- or other default value
end;
select rec._sample, rec._m, rec._pciv, rec._aggrec, rec._soil into sample, m, pciv, aggrec_avg, soil;
return next;
end loop;
end
$$ language plpgsql;

Syntax error in declaration of PL/pgSQL function

Can anyone help me with this procedure? It's a pretty simple one, just want to insert some data into a table, but pgAdmin is giving me some errors.
This is the procedure code:
CREATE OR REPLACE FUNCTION FILL_INVOICE2(IN_NUM integer)
RETURNS void AS
DECLARE
counter numeric := 0;
BEGIN
IF in_num > 1 THEN
WHILE counter < 10
LOOP
INSERT INTO INVOICE(ID,INVOICE_ID,SUBSCRIBER_ID,AMOUNT,INVOICE_DATE,RECORD_DATE,INVOICE_TYPE,REST_TO_PAY,DESCRIPTION,INVOICE_REFERENCE)
VALUES(counter,counter,counter,100,current_date,current_date,1,100,'Telco services',1111);
counter := counter + 1;
RAISE NOTICE 'The counter is %', counter;
END LOOP;
END IF;
RETURN;
END;
Error is:
ERROR: syntax error at or near "DECLARE counter numeric"
LINE 3: DECLARE
^
********** Error **********
ERROR: syntax error at or near "DECLARE counter numeric"
SQL state: 42601
Character: 75"
This would work:
CREATE OR REPLACE FUNCTION fill_invoice2(in_num integer)
RETURNS void AS
$func$
DECLARE
counter numeric := 0;
BEGIN
IF in_num > 1 THEN
WHILE counter < 10
LOOP
INSERT INTO invoice(ID,INVOICE_ID,SUBSCRIBER_ID,AMOUNT,INVOICE_DATE,RECORD_DATE
,INVOICE_TYPE,REST_TO_PAY,DESCRIPTION,INVOICE_REFERENCE)
VALUES(counter,counter,counter,100,current_date,current_date
,1,100,'Telco services',1111);
counter := counter + 1;
RAISE NOTICE 'The counter is %', counter;
END LOOP;
END IF;
END
$func$ LANGUAGE plpgsql;
Major points
Missing language declaration.
Missing quotes around function body. Preferrably, use dollar-quoting - like #Eelke advised. Details:
Insert text with single quotes in PostgreSQL
But the whole function looks needlessly expensive.
Use a single INSERT based on generate_series() to replace the expensive loop with inserts per row. Optionally, you can wrap it in a function. Example with simple SQL function:
CREATE OR REPLACE FUNCTION fill_invoice2(in_num integer)
RETURNS void AS
$func$
INSERT INTO invoice(ID,INVOICE_ID,SUBSCRIBER_ID,AMOUNT,INVOICE_DATE,RECORD_DATE
,INVOICE_TYPE,REST_TO_PAY,DESCRIPTION,INVOICE_REFERENCE)
SELECT g,g,g,100,current_date,current_date,1,100,'Telco services',1111
FROM generate_series(0,10) g
WHERE $1 > 1;
$func$ LANGUAGE sql;
Does the same as your original.
I would also consider column defaults for some of your columns. For instance:
ALTER TABLE invoice
ALTER COLUMN invoice_date SET DEFAULT current_date
, ALTER COLUMN record_date SET DEFAULT current_date;
Details:
converting mysql scripts to postgresql script
Then just don't mention those column in the INSERT statement and defaults are filled in automatically.
The body should be passed as a string
CREATE OR REPLACE FUNCTION FILL_INVOICE2(IN_NUM integer) RETURNS void AS
$$
DECLARE
counter numeric := 0;
BEGIN
IF in_num > 1 THEN
WHILE counter < 10 LOOP
INSERT INTOI NVOICE(ID,INVOICE_ID,SUBSCRIBER_ID,AMOUNT,INVOICE_DATE,
RECORD_DATE,INVOICE_TYPE,REST_TO_PAY,DESCRIPTION,INVOICE_REFERENCE)
VALUES(counter,counter,counter,100,current_date,current_date,1,100,
'Telco services',1111);
counter := counter + 1;
RAISE NOTICE 'The counter is %', counter;
END LOOP;
END IF;
RETURN;
END;
$$
You can use $$ to mark the beginning en end of a multiline string.

Syntax Issue with Postgres Function

I am having trouble debugging some syntax issues with my postgresql function. The error doesn't indicate a line that the issue is on and I am too new to Postgres to recognize what's wrong. I am using SQLFiddle for testing. SQLFiddle link is http://sqlfiddle.com/#!15/266ef
Function I am using
CREATE OR REPLACE FUNCTION updateSalary() RETURNS VOID AS
$BODY$
DECLARE
sum INTEGER := 0;
dep CURSOR FOR SELECT Dno FROM Department;
dep_row Department%ROWTYPE;
emp CURSOR(dept_Dno) CURSOR FOR
SELECT Dno, Salary FROM Employee WHERE Dno = dept_Dno;
emp_row Employee%ROWTYPE;
BEGIN
open dep;
LOOP
FETCH dep into dep_row;
exit when NOT FOUND;
open emp(dep_row.Dno);
LOOP
FETCH emp into emp_row;
exit when NOT FOUND;
SET sum := sum + emp_row.salary;
END LOOP;
UPDATE department SET total_sal = sum WHERE department.dno = emp_row.dno;
close emp;
SET sum := 0;
END LOOP;
close dep;
END;
$BODY$
LANGUAGE plpgsql;
Error I am receiving
Schema Creation Failed: ERROR: missing data type declaration at or near ")":
Cursor argument must have a type declared (and remove second word 'cursor'):
...
emp CURSOR(dept_Dno integer) FOR
SELECT Dno, Salary FROM Employee WHERE Dno = dept_Dno;
...
Assignments without keyword 'set':
sum := sum + emp_row.salary;

Error: INTO specified more than once at or near "INTO"

Inside a postgresql function I'm trying to get two values selected from a table into two variables, but I get this error:
INTO specified more than once at or near "INTO"
This is the (pseudo)code:
CREATE OR REPLACE FUNCTION func() RETURNS TRIGGER AS
$$
DECLARE
a numeric;
b varchar(20);
c numeric;
BEGIN
IF ... THEN
...
SELECT x INTO a FROM t1 WHERE y = 1
IF a > 5 THEN
SELECT m, n INTO b, c FROM t2 WHERE ...;
...
END IF;
END IF;
END
$$ LANGUAGE plpgsql;
The problem is just (as always) a missing semicolon.
Just add the missing semicolon on the first SELECT statement
[...]
SELECT x INTO a FROM t1 WHERE y = 1; #missing semicolon
IF a > 5 THEN
SELECT m, n INTO b ...;
[...]
The INTO specified more than once error is generated from the second SELECT statement (when it finds a second INTO) and it doesn't suggest much about where to find the problem.

How to access plpgsql composite type array components

Let's say I've created a composite type in Postgresql:
CREATE TYPE custom_type AS
(x integer
y integer);
I need to use it in a function as an array:
...
DECLARE
customVar custom_type[];
BEGIN
....
My question is: how do I access custom_type's specific components?
For example, I want to (re)assign 'x' for the third element in custom_type array...
postgres=> create type pt as (x int, y int);
CREATE TYPE
postgres=> create or replace function fx()
returns void as $$
declare a pt[] = ARRAY[(10,20),(30,40)]; declare xx pt;
begin
for i in array_lower(a, 1) .. array_upper(a,1)
loop
xx = a[i]; xx.x := xx.x + 1; a[i] := xx; raise notice '%', a[i].x;
end loop;
end;
$$ language plpgsql;
CREATE FUNCTION
postgres=> select fx();
NOTICE: 11
NOTICE: 31
fx
────
(1 row)
Significant limit for target of assign statement is possibility to refer only one level nested properties. This limit can be bypassed by auxiliary variables - it is not too friendly - and internal implementation is too simple, but it is fast and enough for typical stored procedure usage although it is not strong in comparison with generic programming languages.
Given:
SELECT ARRAY[(1,2),(3,4)]::custom_type[];
Use an array subscript and then refer to the field by name.
regress=> SELECT (ARRAY[(1,2),(3,4)]::custom_type[])[1].x;
x
---
1
(1 row)