Oracle error: character string buffer too small - sql

I am running a basic query that retrieves rows based on basic conditional clauses, nothing complex. This works fine:
<cfquery name="courses" datasource="banner">
SELECT *
FROM tjucatalog
WHERE (course_status = 'Active')
AND CONCAT(subject,course_no) IN (#PreserveSingleQuotes(courselist)#)
AND term IN ('Fall 2012')
AND ((end_date > #now()#) OR (course_meeting_info IS NOT NULL))
ORDER BY TYear, TSort, DayNum, start_date, time, title
</cfquery>
However, when I remove the "AND term IN" line from the query, it fails.
<cfquery name="courses" datasource="banner">
SELECT *
FROM tjucatalog
WHERE (course_status = 'Active')
AND CONCAT(subject,course_no) IN (#PreserveSingleQuotes(courselist)#)
AND ((end_date > #now()#) OR (course_meeting_info IS NOT NULL))
ORDER BY TYear, TSort, DayNum, start_date, time, title
</cfquery>
The error I get is: ORA-06502: PL/SQL: numeric or value error: character string buffer too small ORA-06512: at "BANINST1.TJUCATALOG_PACK", line 519
Is this maybe a view that requires the field 'term' to be included, or is there something else at play here I'm entirely unaware of?

This appears to be an error in a package that's being called under the hood, possibly from a view. You're querying against tjucatalog, and it seems plausible that is a view where one of the returned columns is actually a functional call.
It isn't necessarily the term column that's the problem. By removing that condition, more rows will be returned, and the function is being called against a column value from a row that isn't there when the condition is in place. But it could be any column in one of those now-visible rows.
As a simple and contrived example of what might be happening:
create table t42 (id number, foo varchar2(20));
insert into t42 (id, foo) values (1, 'Short');
insert into t42 (id, foo) values (2, 'More than 10');
create package p42 as
function func(p_id in number) return varchar2;
end p42;
/
create package body p42 as
function func(p_id in number) return varchar2 is
l_bar varchar2(10);
begin
select foo into l_bar from t42 where id = p_id;
return l_bar;
end func;
end p42;
/
create view v42 as select id, p42.func(id) as bar from t42;
So we have a table with two rows, one with a foo less than 10 characters, the other more than 10 characters. We have a (silly) package function that takes an id value, looks up foo, and returns it. And a view that uses that function.
This works:
select * from v42 where id = 1;
ID BAR
---------- --------------------
1 Short
But removing the condition causes it to fail:
select * from v42;
ERROR:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at "SCOTT.P42", line 5
Line 5 of my package body is select foo into l_bar from t42 where id = p_id; and the problem is that I've declare l_bar as varchar2(10), which is too small for the foo value for id=2. I should have declared it as varchar2(20), or even better t42.foo%TYPE.
To see what your problematic function is doing, look at the source code, which you can get from the database (if it isn't wrapped) if you don't have it available:
select line, text from all_source
where owner = 'BANINST1'
and name = 'TJUCATALOG_PACK'
and type = 'PACKAGE BODY'
order by line;

Our Oracle gurus returned and told us they had to change a field type from varchar2 (4000) to CLOB. The lack of the term field as a filter clause was a red herring error. I don't know which field specifically in the query needed to be increased for allowed length, but it works so I'm happy.

Related

ORA-01722: invalid number while select numeric column and same numeric column reference in where

select id
from abc
where id = 1001;
return invalid number.
abc is a view
datatype of id is number
In create view cast is used
CREATE OR REPLACE VIEW abc AS
SELECT CAST (SUBSTR(T_ID,3,100) AS NUMBER) AS ID
from TEST
id column has all numeric values only
SELECT * FROM ABC WORKS FINE AND SO insert query works
tried below code which returns nothing in dbms output
declare
l_dummy number;
begin
for cur in (select ID from abc)
loop
begin
l_dummy := to_number(cur.ID);
exception
when others then dbms_output.put_line(cur.ID);
end;
end loop;
end;
column datatype nullable
ID NUMBER Yes 1 NO NO NO
SELECT *
FROM abc
WHERE
ID = 1001
returns:
ORA-01722: invalid number
01722. 00000 - "invalid number"
*Cause: The specified number was invalid.
*Action: Specify a valid number.
There are two problems:
SUBSTR(T_ID,3,100) will not return number always -- We need to consider the only numeric query
If we apply anything in WHERE condition of the view, Order of the execution of the WHERE condition is the call of Oracle optimizer.
Please see below code:
-- Data preparation
create table TEST (T_ID varchar2(20));
INSERT INTO TEST VALUES('AB1000'); -- Good data
INSERT INTO TEST VALUES('AB1001'); -- Good data
INSERT INTO TEST VALUES('CD1001'); -- Good data
INSERT INTO TEST VALUES('XY1004'); -- Good data
INSERT INTO TEST VALUES('XYZ1004'); -- Bad data
--
-- Data in the table
SELECT * FROM TEST;
Output
-- You need to create your view as following
-- rownum is used so that WHERE clause of view is executed first
-- and then any external WHERE clause on the view is executed
CREATE OR REPLACE VIEW ABC_NEWVIEW AS
SELECT
ID
FROM
(
SELECT
CAST(SUBSTR(T_ID, 3, 100) AS NUMBER) AS ID,
ROWNUM RN
FROM
TEST
WHERE
CASE
WHEN TRIM(TRANSLATE(SUBSTR(T_ID, 3, 100), '0123456789-,.', ' ')) IS NULL THEN 'numeric'
ELSE 'alpha'
END = 'numeric'
)
--
-- View
SELECT * FROM ABC_NEWVIEW
Output
-- Query using WHERE condition
SELECT *
FROM ABC_NEWVIEW
WHERE
ID = 1001
Output
You can find the demo in the following link:
DB Fiddle demo
You can see the demo of VIEW without ROWNUM:
Reproduced your issue -- So the issue is the order of the execution.
Cheers!!

Can't save comma separated number string in varchar2()

I've got a list of items I want to add in a single click, for this purpose I created a table with a column with a type varchar2(4000), in this column I want to list id's that refer to the other table so I can paste the value of this column as a parameter. ex. select t.* from table_name t where t.point_id in (varchar2 string of comma seprated point_ids).
The problem I've got is that when I put more than 1 id in the varchar2 field I get ORA-06502: PL/SQL: numeric or value error: character to number conversion error
How can I avoid this error? My field is varchar2, not number and I don't want it to be converted. I need the value I'm parsing to be saved. ex. (11, 12)
Picture of my Table:
EDIT: Note - My select is working okay, the problem I'm having is with saving the information.
My Insert :
procedure lab_water_pointsgroup (v_group_id lab_water_pointsgroups.group_name%type,
v_group_name lab_water_pointsgroups.group_code%type,
v_group_code lab_water_pointsgroups.lab_points_ids%type,
v_lab_points_ids lab_water_pointsgroups.group_id%type) as
begin
update lab_water_pointsgroups
set group_name = v_group_name,
group_code = v_group_code,
lab_points_ids = v_lab_points_ids
where group_id = v_group_id;
if ( SQL%RowCount = 0 ) then
insert into lab_water_pointsgroups
(group_id, group_name, group_code, lab_points_ids)
values
(v_group_id, v_group_name, v_group_code, v_lab_points_ids);
end if;
end;
Not sure how exactly I can help you here as you gave no example. Have a look at the below demo, maybe the contruct with xmltable solves your problem. HTH KR
create table testtab (id number);
insert into testtab values (1);
select * from testtab where id in ('1'); -- works
select * from testtab where id in (1); -- works
select * from testtab where id in (1,2); -- works
select * from testtab where id in ('1,2'); -- ORA-01722: invalid number
select * from testtab where id in (select to_number(xt.column_value) from xmltable('1,2') xt); -- works
Here is how you defined parameters for your procedure:
v_group_id lab_water_pointsgroups.group_name%type,
v_group_name lab_water_pointsgroups.group_code%type,
v_group_code lab_water_pointsgroups.lab_points_ids%type,
v_lab_points_ids lab_water_pointsgroups.group_id%type
I suspect that you made mistake with types, because id has name type, name has code type etc. So it should be:
v_group_id lab_water_pointsgroups.group_id%type,
v_group_name lab_water_pointsgroups.group_name%type,
v_group_code lab_water_pointsgroups.group_code%type,
v_lab_points_ids lab_water_pointsgroups.lab_points_ids%type
And I suggest to use merge instead of this update / insert, but it's not what you asked for :)
Your error is in that you don't make difference between variable containing comma separated numbers and actual enumeration in the 'in' operator. After your code analize and preparation to execution your statement will be like .. id in ('1,2,3') instead of ..id in (1,2,3), did you notice differnce ? So you need to transform comma separated values into array or in this case into collection. Your code should be like this:
select t.*
from table_name t
where t.point_id in
(select regexp_substr(YOUR_VARCHAR2_COLUMN_VALUE, '[^,]+', 1, level)
from dual
connect by regexp_substr(YOUR_VARCHAR2_COLUMN_VALUE, '[^,]+', 1, level) is not null)

Why isn't Oracle converting characters to numbers?

Oracle throws ORA-01722: invalid number in my SQL query and it is unclear why.
I have a table called "LIGHTS" and I want to get the lights with a WATTAGE <= 3. WATTAGE is stored as a VARCHAR2(40) for some reason, but each character does seem to be an integer or float. When I convert WATTAGE to a number using the query:
SELECT TO_NUMBER(WATTAGE) FROM LIGHTS
There's no problem. I get a result like this:
TO_NUMBER(WATTAGE)
1
7
-1
0
15
17.5
However, when I add a WHERE condition to filter the numbers for those less than 3, I get the ORA-01722: invalid number error:
SELECT WATTAGE FROM LIGHTS
WHERE TO_NUMBER(WATTAGE) <= 3
What could be going wrong?
ORA-01722: invalid number comes from the TO_NUMBER(), not from the conditional. I.e., try this and you'll get the same error:
SELECT TO_NUMBER('test') FROM dual;
This would indicate that at least one of your values is not numeric.
Alas Oracle doesn't have a simple way to check whether a string is in fact representing a number. (One of the many reasons to use the correct data type in the first place!)
However, you can write your own. Here is just a brief demo of this concept. I create a table with a column of VARCHAR2 data type, and populate it with a few strings, one of which is not a number.
create table tbl (nbr varchar2(100));
insert into tbl
select '103' from dual union all
select '-1.3' from dual union all
select 'abc' from dual
;
Then I create a small function with a nested block that should error out if TO_NUMBER fails. The error handler will "do something" specific to errors and then return control to the main function. Then I can use this in a WHERE clause. Here are the function and then how it can be used to find the offending values:
create or replace function not_a_number(str varchar2)
return varchar2
as
x number;
r varchar2(100);
begin
begin
x := to_number(str);
exception
when others then
r := str;
end;
return r;
end;
/
select nbr
from tbl
where not_a_number(nbr) is not null;
NBR
-------
abc

Selecting and passing a record as a function argument

It may look like a duplicate of existing questions (e.g. This one) but they only deal with passing "new" arguments, not selecting rows from the database.
I have a table, for example:
CREATE TABLE my_table (
id bigserial NOT NULL,
name text,
CONSTRAINT my_table_pkey PRIMARY KEY (id)
);
And a function:
CREATE FUNCTION do_something(row_in my_table) RETURNS void AS
$$
BEGIN
-- does something
END;
$$
LANGUAGE plpgsql;
I would like to run it on data already existing in the database. It's no problem if I would like to use it from another PL/pgSQL stored procedure, for example:
-- ...
SELECT * INTO row_var FROM my_table WHERE id = 123; -- row_var is of type my_table%rowtype
PERFORM do_something(row_var);
-- ...
However, I have no idea how to do it using an "ordinary" query, e.g.
SELECT do_something(SELECT * FROM my_table WHERE id = 123);
ERROR: syntax error at or near "SELECT"
LINE 1: SELECT FROM do_something(SELECT * FROM my_table ...
Is there a way to execute such query?
You need to pass a scalar record to that function, this requires to enclose the actual select in another pair of parentheses:
SELECT do_something( (SELECT * FROM my_table WHERE id = 123) );
However the above will NOT work, because the function only expects a single column (a record of type my_table) whereas select * returns multiple columns (which is something different than a single record with multiple fields).
In order to return a record from the select you need to use the following syntax:
SELECT do_something( (SELECT my_table FROM my_table WHERE id = 123) );
Note that this might still fail if you don't make sure the select returns exactly one row.
If you want to apply the function to more than one row, you can do that like this:
select do_something(my_table)
from my_table;

Error in PostgreSQL stored procedure that returns a table

I'm trying to write a stored procedure that returns a table. The procedure is syntactically correct and psql accepts it, but it throws runtime errors.
What I have so far:
CREATE OR REPLACE FUNCTION get_todays_appointments()
RETURNS TABLE
(
fname VARCHAR(32),
lname VARCHAR(32),
phoneno CHAR(10),
datetime TIMESTAMP WITHOUT TIME ZONE,
duration INTERVAL,
caseid INTEGER
) AS $$
BEGIN
RETURN QUERY
SELECT
(
client.fname,
client.lname,
client.phoneno,
appointment.datetime,
appointment.duration,
photocase.caseid
)
FROM (appointment NATURAL JOIN photocase NATURAL JOIN client)
WHERE
(
appointment.datetime >= current_date
AND appointment.datetime < current_date + 1
);
END;
$$
LANGUAGE plpgsql;
If I execute the query manually, it works exactly as indended, but using the SP I always run into the following error:
ERROR: structure of query does not match function result type
DETAIL: Returned type record does not match expected type character varying in column 1.
CONTEXT: PL/pgSQL function "get_todays_appointments" line 3 at RETURN QUERY
I've double checked the table schema about 15 times and theyre definitely correct.
The weird part is that the function works fine if I prune attributes so it returns only one at a time. As soon as I try to return more than one attribute, it throws the error.
I've googled and found some examples but nothing that actually works. I've also seen the use of SETOF, but there is no table with this signature so it doesn't really help me.
I'm using postgresql v9.1.7.
I don't have a convenient way to test this right now, but I think you're going to have to lose some parens.
CREATE OR REPLACE FUNCTION get_todays_appointments()
RETURNS TABLE
(
fname VARCHAR(32),
lname VARCHAR(32),
phoneno CHAR(10),
datetime TIMESTAMP WITHOUT TIME ZONE,
duration INTERVAL,
caseid INTEGER
) AS $$
BEGIN
RETURN QUERY
SELECT
client.fname,
client.lname,
client.phoneno,
appointment.datetime,
appointment.duration,
photocase.caseid
FROM (appointment NATURAL JOIN photocase NATURAL JOIN client)
WHERE
(
appointment.datetime >= current_date
AND appointment.datetime < current_date + 1
);
END;
$$
LANGUAGE plpgsql;
PostgreSQL's error messages are usually pretty good. This one is literally true.
ERROR: structure of query does not match function result type DETAIL:
Returned type record does not match expected type character varying in
column 1. CONTEXT: PL/pgSQL function "get_todays_appointments" line 3
at RETURN QUERY
In this case, RETURN QUERY returns a value of type "record". That's because a row constructor looks like this, SELECT ROW(value1, column1, column2). And in a SELECT statement, the keyword "ROW" is optional, so a row constructor looks like this: SELECT (value1, column1, column2).
So this skeleton syntax
select (column1, column2) from whatever
is equivalent to this.
select row(column1, column2) from whatever
But you don't want that. You want something equivalent to this.
select column1, column2 from whatever
So lose those parens around the column list.