Number to character conversion error in trigger. [ORACLE 11G ] - sql

I'm trying to make a trigger that returns an error with a specific message when the user tries to insert a new record to Addresses table and provides the wrong post code. Two IF statements check if the number of characters in the given post-code is right (it should be 6: 2 numbers, 1 dash and 3 numbers). Another IF statement checks if the position of the dash is 3. When I try to insert a post-code with too few characters (for example '12932') it works fine and returns the right message. The problem begins when I provide the post-code in which the dash position is incorrect (for example '1-9000'). In such case it returns an error (PL/SQL: numeric or value error: character to number conversion error, line 15). What could be the reason? Because personally I can't find any char to number conversions. Thanks.
Code:
CREATE OR REPLACE TRIGGER post_code_trigger
BEFORE INSERT OR UPDATE ON Addresses
FOR EACH ROW
DECLARE
code_length NUMBER;
index_of_dash NUMBER;
code_tmp VARCHAR(6);
BEGIN
code_tmp := :NEW.Post_code;
code_length := LENGTH(code_tmp);
index_of_dash := INSTR(code_tmp, '-');
IF(code_length <> 6) THEN
RAISE_APPLICATION_ERROR(-20000, 'Post code contains the wrong number of characters.');
ELSE
IF (index_of_dash <> 3) THEN
RAISE_APPLICATION_ERROR(-20000, 'Post code doesnt contain '-' character or it is in the wrong posiition. Sample post code "26-500" ');
END IF;
END IF;
END;
/
INSERT INTO Addresses (address_ID, city, post_code, province, street_name, house_number) VALUES (2, 'Warsaw','1-9000','mazowieckie','Sylvester Stallone Street', 67);

In the body of the code, indekx_of_dash does not match index_of_dash in the declaration. Once you fix that typo, it works.
It can be simplified to:
CREATE OR REPLACE TRIGGER post_code_trigger
BEFORE INSERT OR UPDATE ON Addresses
FOR EACH ROW
DECLARE
BEGIN
IF LENGTH(:NEW.Post_code) <> 6 THEN
RAISE_APPLICATION_ERROR(-20000, 'Post code contains the wrong number of characters.');
ELSIF INSTR(:NEW.Post_code, '-') <> 3 THEN
RAISE_APPLICATION_ERROR(-20000, 'Post code doesnt contain "-" character or it is in the wrong posiition. Sample post code "26-500" ');
END IF;
END;
/
But you probably don't want to use a trigger and just want a CHECK constraint.
ALTER TABLE addresses ADD CONSTRAINT addresses_postcode_format
CHECK (post_code LIKE '??-???');
db<>fiddle here

Ok, I found a solution. Firstly I simplified my code according to the MT0's answer. Then I deleted the '-' part from the error message. Now everything works as it should. Thank you.
IF INSTR(:NEW.Post_code, '-') <> 3 THEN
RAISE_APPLICATION_ERROR(-20000, 'Post code doesnt contain a dash character or it is in the wrong posiition. Sample post code "26-500" ');
instead of
ELSIF INSTR(:NEW.Post_code, '-') <> 3 THEN
RAISE_APPLICATION_ERROR(-20000, 'Post code doesnt contain '-' character or it is in the wrong posiition. Sample post code "26-500" ');

When you originally posted your question you had this line:
RAISE_APPLICATION_ERROR(-20000, 'Post code doesnt contain "-" character or it is in the wrong posiition. Sample post code "26-500" ');
Your real code actually has (but with Polish text):
RAISE_APPLICATION_ERROR(-20000, 'Post code doesnt contain '-' character or it is in the wrong posiition. Sample post code "26-500" ');
The problem then is the '-' in the middle of that. The single quotes around the dash end one string, and start another; and the dash is now outside the string literal(s), so Oracle interprets it as a minus sign. That causes it to implicitly convert the two shorter string literals either side to numbers, so they can be subtracted from each other. So you're really doing:
to_number('Post code doesnt contain ')
-
to_number(' character or it is in the wrong posiition. Sample post code "26-500" ')
The error is occurring when it tries to convert those strings, because they clearly do not represent numbers.
With the dash enclosed in double-quotes instead the whole thing is one string literal again, so there is no confusion or attempt to perform a calculation.
Incidentally, you seem to have avoided an issue with quotes with doesnt instead of doesn't, though presumably only in the translated version. But if you wanted to use single quotes within a string, you can either escape them:
'Post code doesn''t contain ''-'' character or it is in the wrong position. Sample post code ''26-500'''
or use the alternative quoting mechanism:
q'[Post code doesn't contain '-' character or it is in the wrong position. Sample post code '26-500']'
db<>fiddle demo

Related

Character/String passed from java to stored procedure does not work

Hi I have a stored procedure which accepts parameter "Q_RES" from java. Its value is either "Y" or "N" . In my stored procedure I have a condition
If Q_RES = 'Y' THEN
sp_stat = 'TABLENAME.RES IN (' || Q_RES || ')';
END IF
nut I get error has ORA-00904: "Y" invalid Identifier
How can I fix this?
In PL/SQL assignment is Pascal style :=', not=`. Aside from that, I don't see any problem with the string concatenation shown here.
I suspect that sp_stat is being used later, in composing dynamic SQL. I would take a close look at the SQL being generated. It's probably goign to look like this:
... TABLENAME.RES IN (Y) ...
And if that is interpreted as SQL text, Oracle is going to see that bareword Y as an identifier, such as a column name, and attempt to resolve that. (If a table referenced in the query has a column named Y, the query wouldn't throw an error about an invalid identifier.
Just guessing here, but I suspect that we want Y to be a string literal. A string literal is enclosed in single quotes, like this:
... TABLENAME.RES IN ('Y') ...
To get a single quote inside a string literal, we can use two single quotes, e.g.
'I''m happy this is working'
Since we already know Q_RES is equal to 'Y', why not just do this:
sp_stat := 'TABLENAME.RES IN (''Y'')';
^^ ^^
or, if we want to reference Q_RES, enclose it in single quotes, like this:
sp_stat := 'TABLENAME.RES IN (''' || Q_RES || ''')';
^^ ^^

Trigger Oracle operator LIKE to respect a string format

I'm trying to learn how triggers work and how to write them properly with a given exercise. I'm using SQL Developper with the version 4.2 of Oracle.
I have a table PERSON with these fields : 'matricule', 'pmatricule' and 'typpers'.
'typpers' is a letter to show if the person is a student (E), a professor (P) or other (X). 'matricule' is a chain of 3 to 7 numbers and 'matricule' is a chain with 'p' followed by a chain of 3 to 6 numbers. Example : 'p145', 'p123456'.
I want my trigger to check if the format of 'matricule' or 'pmatricule' is respected when someone wants to insert a row in the table PERSON.
CREATE OR REPLACE TRIGGER new_matricule
BEFORE INSERT ON PERSONNE
FOR EACH ROW
DECLARE
new_pmatricule PERSONNE.pmatricule%type;
BEGIN
IF((:NEW.TYPPERS = 'P' OR :NEW.TYPPERS = 'X') AND :NEW.pmatricule NOT LIKE 'p%______') THEN
RAISE_APPLICATION_ERROR(-20100, 'pmatricule format is not good');
END IF;
END;
My trigger works if I'm trying to insert a 'pmatricule' format like '52p' but it also activates when I'm trying to insert such as 'p145'.
I assume that my syntax is not good enough but I'm not finding the proper syntax to do that.
Has anyone an idea ?
First, simplify the syntax for the first part of the expression using in. Then, if you want to count the number of characters, leave out the '%'. Something like this:
IF((:NEW.TYPPERS IN ('P', 'X') AND :NEW.pmatricule NOT LIKE 'p______') THEN
RAISE_APPLICATION_ERROR(-20100, 'pmatricule format is not good');
END IF;
This says that the "p" needs to be followed by six characters. Your version says "at least 6 characters".
If you want two characters followed by a p, it would be:
:NEW.pmatricule NOT LIKE '__p'
Any number followed by a "p" would be:
:NEW.pmatricule NOT LIKE '%p'
If you want more advanced pattern matching, you probably want to use regexp_like(). It is much more versatile.
EDIT:
Based on your comment, you want:
where regexp_like(:NEW.pmatricule, '^p[0-9]{3,6}$')
This is probably the easiest way to write the condition.
If you mean 3-6 characters, you can use the cumbersome:
where :NEW.pmatricule like 'p___' or
:NEW.pmatricule like 'p____' or
:NEW.pmatricule like 'p_____' or
:NEW.pmatricule like 'p______'

Replace function doesn't work as expected

I'm having trouble figuring out why REPLACE() doesn't work correctly.
I'm getting a string formatted as:
RISHON_LEZION-CMTSDV4,Cable7/0/4/U1;RISHON_LEZION-CMTSDV4,Cable7/0/4/U2;RISHON_LEZION-CMTSDV4,Cable7/0/5/U0;.....
Up to 4000 characters .
Each spot of ; represent a new string(can be up to about 15 in one string). I'm splitting it by using REPLACE() - each occurence of ; replace with $ + go down a line + concat the entire string again (I have another part that is splitting down the string)
I think the length of the string is some how effecting the result, though I never heard replace has some kind of limitation about the length of the string.
SELECT REPLACE(HOT_ALERTKEY_PK, ';', '$' || CHR(13) || CHR(10) || HOT_ALERTKEY_PK || '$')
from (SELECT 'RISHON_LEZION-CMTSDV4,Cable7/0/3/U0;RISHON_LEZION-CMTSDV4,Cable7/0/3/U1;RISHON_LEZION-CMTSDV4,Cable7/0/3/U2;RISHON_LEZION-CMTSDV4,Cable7/0/4/U0;RISHON_LEZION-CMTSDV4,Cable7/0/4/U1;RISHON_LEZION-CMTSDV4,Cable7/0/4/U2;RISHON_LEZION-CMTSDV4,Cable7/0/5/U0;RISHON_LEZION-CMTSDV4,Cable7/0/5/U1;RISHON_LEZION-CMTSDV4,Cable7/0/5/U2;RISHON_LEZION-CMTSDV4,Cable7/0/7/U0;RISHON_LEZION-CMTSDV4,Cable7/0/7/U1;RISHON_LEZION-CMTSDV4,Cable7/0/7/U2;RISHON_LEZION-CMTSDV4,Cable7/0/9/U0;RISHON_LEZION-CMTSDV4,Cable7/0/9/U1;RISHON_LEZION-CMTSDV4,Cable7/0/9/U2' as hot_alertkey_pk
FROM dual)
This for some reason result in splitting the string correctly, up to cable7/0/5/U0; , and stops. If I remove one or more parts from the start of the string (up to the semicolumn is each part) then I'm getting it up to the next cables, according to how many I remove from the beggining.
Why is this happening ?
Thanks in advance.
If you wrap your sample input string within to_clob() in the inner query, and you wrap the resulting string within length() in the outer query, you will find that the result is 8127 characters. This answers your question, but only partially.
I am not sure why replace doesn't throw an error, or perhaps just truncate the result at 4000 characters. I got exactly the same result as you did in Oracle 11.2, with the result chopped off after 3503 characters. I just looked quickly at the Oracle documentation for replace() and it doesn't say what the behavior should be if the input is VARCHAR2 but the output is more than 4000 characters. It looks as though it performed as many substitutions as it could and then it stopped (the next substitution would have gone above 4000 characters).

Oracle: remove first 4 characters from a string

So I want to remove the first 4 characters from a string in oracle. Those characters can be different every time.
In my case I need to take away the first 4 characters of an IBAN and put them at the end of the string. I got the part of putting them to the end of the string but I can't get the first 4 characters to be removed. Every solution I find on the internet removes specified characters, not characters from a certain position in the string (1 to 4).
I used the code below to get the first 4 characters to the end of the string and wanted to try something similar for removing them at the front but without success.
SELECT SUBSTR(iban_nummer, 1, 4) INTO iban_substring FROM dual;
iban_nummer := iban_nummer || iban_substring;
See the docs:
substring_length ...
When you do not specify a value for this argument, then the function returns all characters to the end of string. When you specify
a value that is less than 1, the function returns NA.
So iban_nummer := substr(iban_nummer, 5) || substr(iban_nummer, 1,4) should work. The first part selects all characters beginning from the 5th, the second character numbers 1..4.
update table_name set col_name=substr(col_name,5);
try regexp, like:
SELECT regexp_replace(t.iban_nummer,'(.{4})(.*)','\2\1') FROM t;
Alternative way using regexp :
SELECT regexp_replace(t.iban_nummer,'^.{4}(.*)','\2\1') FROM dual;

How do I parse a string in Oracle?

How can I parse the value of "request" in the following string in Oracle?
<!-- accountId="123" activity="add" request="add user" -->
The size and the position of the request is random.
You can use regular expressions to find this:
regexp_replace(str, '.*request="([^"]*)".*', '\1')
Use INSTR(givenstring, stringchartosearch,start_position) to find the position of 'request="' and to find the position of the closing '"'.
Then use substr(string, starting_position, length).
You'd use a combination of instr and substr
THIS EXAMPLE IS FOR EXAMPLE PURPOSES ONLY. DO NOT USE IT IN PRODUCTION CODE AS IT IS NOT VERY CLEAN.
substr(my_str,
-- find request=" then get index of next char.
instr(my_str, 'request="') + 9,
-- This is the second " after request. It does not allow for escapes
instr(substr(my_str,instr(my_str, 'request="')), 2))
Below is my tested variations from cwallenpoole and Craig. For the regexp - note that if "request=" does not exist, the result will be the entire string. user349433 was partly there too, a space before "request=" in the search works just as well:
SET serveroutput ON
DECLARE
l_string VARCHAR2(100) := '<!-- accountId="123" activity="add" request="add user" -->';
l_result_from_substr VARCHAR2(50);
l_result_from_regexp VARCHAR2(50);
BEGIN
SELECT SUBSTR(l_string, instr(l_string, 'request="') + 9, instr(SUBSTR(l_string,instr(l_string, 'request="')), '"', 2)-1),
regexp_replace(l_string, '.* request="([^"]*)".*', '\1')
INTO l_result_from_substr,
l_result_from_regexp
FROM dual;
dbms_output.put_line('Result from substr: '||l_result_from_substr);
dbms_output.put_line('Result from regexp: '||l_result_from_regexp);
END;
/
Please note the equal sign "=" does not necessarily have to come immediately after the request variable in the assignment. As such, it is not entirely correct to search for "request=". You should create a basic finite state machine using INSTR to first find "request", then find "=", ...