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

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)

Related

ORA-01460 unimplemented or unreasonable conversion requested

I am passing ID's in oracle proc. ID's can be in 1000's. currently its able to process around 600 ID's, if I pass more than 600 ID's - I am getting ORA-01460 unimplemented or unreasonable conversion requested. ID is varchar2 datatype, how can I process 1000's of Id's in varchar2 or what will be the best strategy to handle this kind of issue. Any guidance/suggestion will be highly appreciated. Can this be solved using CLOB datatype?
//this is how I am processing Id's
create or replace procedure Emp(
emp_id in varchar2
)
//passing those id's in CTE before passing to subquery
WITH
EMP_LIST AS(
select regexp_substr(emp_id,'[^,]+', 1, level) from dual
connect by level <= LENGTH(regexp_substr(emp_id, '[^,]+'))+1
)
Pass a collection or VARRAY rather than passing a comma-delimited string:
CREATE TYPE number_list IS TABLE OF NUMBER(10,0);
Then you can use it something like:
CREATE PROCEDURE emp(
emp_ids IN number_list
)
IS
BEGIN
-- Do something with the ids like inserting them into a table
INSERT INTO employees ( id )
SELECT COLUMN_VALUE
FROM TABLE( emp_ids );
-- Or something like this:
SELECT something
INTO some_variable -- you need to define this variable first
FROM some_table
WHERE emp_id MEMBER OF emp_ids;
END;
/
Update
If you can't create anything then you can use a built-in collection like SYS.ODCINUMBERLIST:
CREATE PROCEDURE emp(
emp_ids IN SYS.ODCINUMBERLIST
)
IS
BEGIN
-- Do something with the ids like inserting them into a table
INSERT INTO employees ( id )
SELECT COLUMN_VALUE
FROM TABLE( emp_ids );
-- Or something like this:
SELECT something
INTO some_variable -- you need to define this variable first
FROM some_table
WHERE emp_id IN ( SELECT COLUMN_VALUE FROM TABLE( emp_ids ) );
END;
/
(Note: SYS.ODCI*LIST types are VARRAY data types and do not support the MEMBER OF operator like collections do; instead you can get the values from the VARRAY using a nested SELECT statement with a TABLE() collection expression.)
However, if you really can't CREATE anything then you won't be able to CREATE PROCEDURE .... not sure there is any solution to that apart from talking to your DBA.

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!!

How to fill some column with constant string followed by variable number

I need to fill a table column (in Oracle database) with string values that have variable part, e. g. AB0001, AB0002,...,AB0112...,AB9999, where AB is constant string part, 0001 -9999 is variable number part. i've tried the following solution in SQL for a table with 2 columns:
create table tbl1
(seq1 number(8),
string1 varchar(32));
declare
tst number(8) :=0;
begin
for cntr in 1..100
loop
tst := cntr;
insert into TBL1 values (someseq.nextval, concat('AB',tst));
end loop;
end;
But in this case I get STRING1 filled with values AB1,AB2,...,AB10,.. which is not exactly what I need.
How should I modify my script to insert values like AB0001,...,AB0010?
Either pad the number with zeros, or format it with leading zeros:
insert into TBL1
values (someseq.nextval, concat('AB', to_char(tst, 'FM0000'));
The 'FM' format modifier prevents a space being added (to allow for a minus sign).
For your specific example you don't need a PL/SQL block; you could use a hierarchical query to generate the data for the rows:
insert into tbl1(seq1, string1)
select someseq.nextval, concat('AB', to_char(level, 'FM0000'))
from dual
connect by level <= 100;
use the lpad function
select lpad(1, 4, '0') from dual
--> '0001'
try this one
INSER INTO table_name(code)
VALUES(CONCAT('AB', LPAD('99', 4, '0'));
or You can Update on the basis of PK after insertion
UPDATE table_name SET code = CONCAT('AB', LPAD(PK_Column_Name, 4, '0') ;
or You Can Use Triggers
CREATE TRIGGER trgI_Terms_UpdateTermOrder
ON DB.table_name
AFTER INSERT
AS
BEGIN
UPDATE t
SET code = CONCAT('AB', LPAD(Id, 4, '0')
FROM DB.table_name t INNER JOIN inserted i ON t.Id = I.Id
END;
GO

Oracle error: character string buffer too small

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.

Regarding sql substitution

When i ran the below queries it's failing in the second query becuase prev_test_ref1 variable is not defined. If i remove the insert statement in the first query ,run again then it's working and using the prev_test_ref1 value from the first sql query in second query. Is it because of variable scope? How can i resolve this with the insert statement.
QUERY1
column prev_test_ref1 new_value prev_test_ref1 ;
insert into testing.test_ref_details(TEST_TYPE,TEST_REF_NO)
select '1',max(test_ref_no) as prev_test_ref1
from testing.test_runs_status
where test_type = 1
and run_status = 1
and test_end_dt = (select last_day(add_months(trunc(sysdate),-6))+2 from dual)
group by test_end_dt
;
QUERY2
column last_test_end_dt new_value last_test_end_dt;
select to_char(test_completion_dt,'DD-MON-YYYY HH24:MI:SS') as last_test_end_dt
from testing.test_runs_status
where test_ref_no = '&prev_test_ref1';
In SQLPlus substitution variables will only be defined with SELECT statements. Your first insert doesn't return rows so it won't work (think about it: it only returns 1 row inserted., SQLPlus has no way to know the value inserted.)
I suggest you add a step to save the value into the variable (or use a PL/SQL block):
column prev_test_ref1 new_value prev_test_ref1 ;
SELECT MAX(test_ref_no) AS prev_test_ref1
FROM testing.test_runs_status
WHERE test_type = 1
AND run_status = 1
AND test_end_dt = (SELECT last_day(add_months(trunc(SYSDATE), -6)) + 2
FROM dual)
GROUP BY test_end_dt;
INSERT INTO testing.test_ref_details(TEST_TYPE,TEST_REF_NO)
VALUES ('1', &prev_test_ref1);
SELECT ...
declare
prev_test_ref1 number(10);
begin
insert into ...select ...;
select ... into prev_test_ref1 from ...;
end;
/
The INSERT statement has a RETURNING clause. We can use this to get access to "unknown" values from the table. The following examples uses RETURNING to get the assigned nextval from a sequence, but we could return any column from the row:
SQL> var prev_id number
SQL> insert into t23 (id, name) values (my_seq.nextval, 'MAISIE')
2 returning id into :prev_id
3 /
1 row created.
SQL> select * from t23
2 where id = :prev_id
3 /
NAME ID
---------- ----------
MAISIE 122
SQL>
Unfortunately the RETURNING clause only works with single-row SQL.
It isn't really clear what the purpose of the whole script is, especially in light of the comment "i have similar sql query which returns multiple rows. In that case i cant have separate insert statement."
If you want to use the results of a select, see if Multi-Table Inserts fit the bill. Your select statement can insert into both the primary table and also a second table (eg a global temporary table). You can then query the global temporary table to see what rows were inserted.