Checking whether a string contains any numeric values - sql

I have the following table:
create table students
(
stuName varchar2(100),
cgpa number
);
My goal is to create a PL/SQL trigger that would fire if anyone tries to enter a name that contains any numeric values. My attempt:
create or replace trigger invalid_name
before insert
on students
for each row
declare
vName varchar2(100);
begin
vName := :new.stuName;
if upper(vName) like upper(vName) then
vName := initcap(vName);
end if;
exception
when value_error then
dbms_output.put_line('ERROR: Name contains numeric value(s).');
end;
I thought if the upper function were to act on a string containing any numeric value in it, it would throw an exception. But that's not happening and insert action is being executed.

I'd suggest using a constraint rather than a trigger.
create table foo (
name varchar2(100) NOT NULL
constraint name_non_numeric check ( not regexp_like( name, '[0-9]' ) )
);
Table created.
insert into foo ( name ) values ( 'Andy' );
1 row created.
> insert into foo ( name ) values ( 'Logan 5' );
insert into foo ( name ) values ( 'Logan 5' )
*
ERROR at line 1:
ORA-02290: check constraint (NAMESPACE.NAME_NON_NUMERIC) violated

If you don't want to replace, but check and raise an error, you can use this trick and raise an error if the result is not null:
SELECT LENGTH(TRIM(TRANSLATE('123b', ' +-.0123456789',' '))) FROM dual;
Result: 1
SELECT LENGTH(TRIM(TRANSLATE('a123b', ' +-.0123456789',' '))) FROM dual;
Result: 2
SELECT LENGTH(TRIM(TRANSLATE('1256.54', ' +-.0123456789',' '))) FROM dual;
Result: null
SELECT LENGTH(TRIM(TRANSLATE ('-56', ' +-.0123456789',' '))) FROM dual;
Result: null

Related

INSERT ALL fails with trigger setting timestamp

Let's say we have a table like this:
CREATE TABLE test_table
(
text VARCHAR2(200) NOT NULL,
text2 VARCHAR2(200) NOT NULL,
ts TIMESTAMP
);
And we want to insert some data using INSERT ALL:
INSERT ALL
INTO test_table ( text, text2 ) VALUES ( 'test', 'test2' )
SELECT * FROM dual;
The result is
1 row inserted.
But, when we want to add trigger, to fill ts column with SYSTIMESTAMP
CREATE OR REPLACE TRIGGER test_trigger
BEFORE INSERT ON test_table
FOR EACH ROW
BEGIN
DBMS_OUTPUT.put_line('text=' || :new.text);
DBMS_OUTPUT.put_line('text2=' || :new.text2);
DBMS_OUTPUT.put_line('ts=' || :new.ts);
:new.ts := SYSTIMESTAMP;
END;
/
Running the same script
SET SERVEROUT ON;
INSERT ALL
INTO test_table ( text, text2 ) VALUES ( 'test', 'test2' )
SELECT * FROM dual;
The result is:
text=test
text2=
ts=
INSERT ALL
INTO test_table ( text, text2 ) VALUES ( 'test', 'test2' )
SELECT * FROM dual
Error report -
ORA-01400: cannot insert NULL into ("TEST"."TEST_TABLE"."TEXT2")
Using INSERT works fine
SET SERVEROUT ON;
INSERT INTO test_table ( text, text2 ) VALUES ( 'test', 'test2' )
The result is
text=test
text2=test2
ts=
1 row inserted.
Also this works:
INSERT ALL
INTO test_table ( text, text2, ts) VALUES ( 'test', 'test2', null )
SELECT * FROM dual
When I change ts column type to DATE works fine with this kind of trigger.
I'm using Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production, I've also checked on Oracle 12c but there is no error, so maybe it's some kind of bug in 11g release?
Nothing seems wrong with your code, it may be a bug with version you are using.
That said, what you are trying to achieve is more usually done by following create table statement
CREATE TABLE test_table (
text VARCHAR2(200) NOT NULL,
text2 VARCHAR2(200) NOT NULL,
ts TIMESTAMP not null default systimestamp
);
You will not need trigger for this at all.

PL/SQL: ORA-12704: character set mismatch when insert varchar2 to nvarchar2

I try to populate a table TBL in a loop. And get ORA-12704: character set mismatch on a subquery.
Here is a query I use:
BEGIN
FOR i IN (SELECT t.Stuff FROM STUFF_TABLE t ORDER BY t.Name ASC)
LOOP
INSERT INTO TBL(StuffId, StuffName)
VALUES(
i.Stuff,
(SELECT TempStuffName FROM
(SELECT COALESCE(st.StuffName, i.Stuff) as TempStuffName FROM STUFFDEFINITION st WHERE st.Stuff = i.Stuff ORDER BY st.Version DESC)
WHERE ROWNUM = 1)
);
END LOOP;
END;
The columns types are the following:
STUFF_TABLE.Stuff nvarchar2(30)
TBL.StuffId nvarchar2(30)
TBL.StuffName nvarchar2(50)
STUFFDEFINITION.Stuff varchar2(255)
STUFFDEFINITION.StuffName varchar2(255)
The issue, as I understand it, is in type casting namely from varchar2(255) to nvarchar2(50).
I tried to use CAST, Translate functions, but it didn't help. The ORA-12704: character set mismatch still occurs.
Is there a way to populate TBL in a loop as I try it to do?
Below is the test data to reproduce issue I talk about:
CREATE TABLE STUFF_TABLE
(
Stuff nvarchar2(30),
Name nvarchar2(50)
);
CREATE TABLE TBL
(
StuffId nvarchar2(30),
StuffName nvarchar2(50)
);
CREATE TABLE STUFFDEFINITION
(
Stuff varchar2(255),
StuffName varchar2(255),
Version number(19)
)
INSERT INTO STUFF_TABLE(Stuff, Name) VALUES('First', 'Name1');
INSERT INTO STUFF_TABLE(Stuff, Name) VALUES('Second', 'Name2');
INSERT INTO STUFF_TABLE(Stuff, Name) VALUES('Third', 'Name3');
INSERT INTO STUFFDEFINITION(Stuff, StuffName, Version) VALUES('First', 'First Stuff', 1);
INSERT INTO STUFFDEFINITION(Stuff, StuffName, Version) VALUES('First', 'First Stuff', 2);
INSERT INTO STUFFDEFINITION(Stuff, StuffName, Version) VALUES('Second', 'Second Stuff', 1);
INSERT INTO STUFFDEFINITION(Stuff, StuffName, Version) VALUES('Third', 'Third Stuff', 1);
From your statement its clear that you are joining a varchar2 column with nvarchar2 and inserting a varchar2 column to a nvarchar2 column. You need to do a conversion first. Try this:
BEGIN
FOR i IN (SELECT t.Stuff FROM STUFF_TABLE t ORDER BY t.Name ASC)
LOOP
INSERT INTO TBL(StuffId, StuffName)
VALUES(
i.Stuff,
(SELECT to_nchar(TempStuffName)
FROM
(SELECT COALESCE(to_nchar(st.StuffName), i.Stuff) as TempStuffName
FROM STUFFDEFINITION st
WHERE to_nchar(st.Stuff) = i.Stuff
ORDER BY st.Version DESC)
WHERE ROWNUM = 1)
);
END LOOP;
END;
You shouldn't need to use PL/SQL:
INSERT INTO TBL ( StuffId, StuffName )
SELECT TO_NCHAR( t.stuff ),
TO_NCHAR(
COALESCE(
MAX( d.StuffName ) KEEP ( DENSE_RANK LAST ORDER BY d.version ),
t.stuff
)
)
FROM StuffTable t
LEFT OUTER JOIN StuffDefinition d
ON ( t.stuff = TO_NCHAR( d.stuff ) )
GROUP BY t.stuff;
Oracle says this about that error: 'A string expression in the VALUES clause of an INSERT statement does not have the same character set as the column into which the value would be inserted.'
Please select from nls_database_parameters and tell me what the values of these are:
NLS_CHARACTERSET
NLS_NCHAR_CHARACTERSET
NLS_RDBMS_VERSION
NLS_LENGTH_SEMANTICS

Removing new line and tab in a oracle database table column

This is the post regarding some problem with new line and tab in a oracle database table column
as the problem is that I have to create a column which
takes only keyboard symbols and alphanumerical values and i am able to get the
null value in the column but not able to get the new line ( ex chr(13),chr(9),chr(10), if i get one of this also, its difficult to get the combination of both)
I have used reg_exp (like and replace ) and also tanslate and repalce dint find any proper solution.
ex
create table Table1(someField varchar2(100) );
insert into Table1 values ( 'hi'|| CHR(10)||'how r u');
insert into Table1 values ( 'hello'|| CHR(13)||'&');
error must not come because we have a value that is
1) insert "hi
how r u"- successful .
2) insert "hello &"- successful .
but while inserting only space (chr(10)/chr(13)/chr(9) or space or tab)
error must come under below insert condition
insert into someTable values ( CHR(10));
insert into someTable values ( ' '|| CHR(10));
insert into someTable values ( '
'|| CHR(10)||' ');
One way could be by adding a check constraint to your table; for example:
alter table table1 add constraint table1Check check (
regexp_replace(somefield, '[' || chr(10) || chr(9) ||chr(13) || ' ]', '') is not null
)
What you have:
SQL> insert into Table1 values ( 'hi'|| CHR(10)||'how r u');
1 row created.
SQL> insert into Table1 values ( 'hello'|| CHR(13)||'&');
1 row created.
SQL> insert into Table1 values ( CHR(10));
insert into Table1 values ( CHR(10))
*
ERROR at line 1:
ORA-02290: check constraint (ALEK.TABLE1CHECK) violated
SQL> insert into Table1 values ( ' '|| CHR(10));
insert into Table1 values ( ' '|| CHR(10))
*
ERROR at line 1:
ORA-02290: check constraint (ALEK.TABLE1CHECK) violated
SQL> insert into Table1 values ( ' '|| CHR(10)||' ');
insert into Table1 values ( ' '|| CHR(10)||' ')
*
ERROR at line 1:
ORA-02290: check constraint (ALEK.TABLE1CHECK) violated
This way you will not be able to insert a value not matching your condition.

Oracle SQL random value from string set

In Oracle SQL 11g I am trying to fill a table with procedure. For some columns I need to take data randomly from predefined set of strings. How do I define such set and take data from it by random order?
You could use a cte and dbms_random.value. Something like:
with strings as (
select 'string1' as s from dual union all
select 'string2' as s from dual union all
select 'string3' as s from dual union all
select 'string4' as s from dual
)
select <col1>,
(select s
from (select s from strings order by dbms_random.value) s
where rownum = 1
) as RandomString
from dual;
Can you give this a try,it is working.
1.Insert the list of strings in a table (strings).
2.Create a function(RANDOM) to generate random number.
3.Create a procedure(PROC_STRING) to pick a string name from (STRINGS) table using the random number generated from function(RANDOM) and then insert into (NEW_TABLE)
PROGRAM:
--Table with list of string names
Create table strings (string_id number,string_name varchar2(2000) );
--Table to store new string names in random order
Create table new_table (string_id number,string_name varchar2(2000) );
--Function to generate random numbers
create or replace function random(p_number in number)
return number
is
a number;
begin
select dbms_random.value(1,10) into a
from dual;
a := floor(a);
return a;
end;
/
delete from strings;
delete from new_table;
insert into strings values(1,'abc');
insert into strings values(2,'def');
insert into strings values(3,'ghi');
insert into strings values(4,'abc 1');
insert into strings values(5,'def 1');
insert into strings values(6,'ghi 1');
insert into strings values(7,'abc 2');
insert into strings values(8,'def 2');
insert into strings values(9,'ghi 2');
insert into strings values(10,'xyz 3');
--Procedure to pick string names randomly from strings table and insert into new_table
create or replace procedure proc_string(p_no in number)
as
s_id number;
s_name varchar2(2000);
begin
select random(1) into s_id from dual;
select string_name into s_name from strings where string_id = s_id;
insert into New_table values(s_id,s_name);
dbms_output.put_line('insert successfully completed');
commit;
Exception when others
then dbms_output.put_line('ERROR:' || SQLCODE || ' ' || SQLERRM);
end;
/
commit;
EXECUTION:
--After executing the procedure for 3 times
SQL> exec proc_string(1);
insert successfully completed
PL/SQL procedure successfully completed.
-- Random string names got inserted into newtable
SQL> select * from new_table;
STRING_ID STRING_NAME
5 def 1
3 ghi
1 abc
Let me know if you questions.

Can I call a user-defined function from a column CHECK constraint?

I have a user-defined SQL function that returns 1 or 0 and I want to call it from a column CHECK constraint.
Yes. SQL Anywhere doesn't have a boolean data type so you have to code a predicate that yields TRUE, FALSE or UNKNOWN. In other words, if your function returns 1 or 0 for pass or fail, you have to code the constraint as CHECK ( f() = 1 ).
Note that TRUE and UNKNOWN both result in a "pass"; only a FALSE result causes the check to fail.
The following sample shows how to ALTER a table that already contains data, to add a column with such a CHECK constraint.
Breck
CREATE TABLE t (
pkey INTEGER NOT NULL PRIMARY KEY );
INSERT t VALUES ( 1 );
COMMIT;
CREATE FUNCTION is_filled_in (
IN #value VARCHAR ( 100 ) )
RETURNS TINYINT
BEGIN
IF COALESCE ( #value, '' ) <> '' THEN
RETURN 1;
ELSE
RETURN 0;
END IF;
END;
ALTER TABLE t ADD c VARCHAR ( 3 ) DEFAULT 'xxx'
CHECK ( is_filled_in ( c ) = 1 );
-- Works ok...
INSERT t VALUES ( 2, 'yyy' );
COMMIT;
-- Throws SQLCODE -209 Invalid value for column 'c' in table 't'...
INSERT t VALUES ( 3, '' );
COMMIT;
SELECT * FROM t;