I have a trigger in Oracle SQL.
CREATE OR REPLACE TRIGGER test
BEFORE INSERT ON SomeTable
FOR EACH ROW
DECLARE str1 VARCHAR(30);
str2 VARCHAR(30);
BEGIN
-- some code
IF ( str1 <> str 2 ) THEN
DBMS_OUTPUT.PUT_LINE( ' if ' );
ELSE
DBMS_OUTPUT.PUT_LINE( ' else ' );
END IF;
END;
Now, this always goes to the else statement, even when the strings are definitely not equal. I tried to use != instead of <> with the same result. However, it works, in reverse, if I just use
IF ( str1 = str2 ) THEN ... ELSE ... END If;
So what is the right way to test for two strings not being equal to each other (in Oracle)?
Can you show us the actual values being used?
It is possible that the reason for the above behavior is becuase one of the values is null?
If it is possible for str1 and str2 to have null values, your if's should be like..
IF (str1 is null and str2 is null) then
<statments depending on whether you want to treat nulls as equal>
else if (
(str1 is null and str2 is not null) or
(str2 is null and str1 is not null) or
(str1 <> str2)) then
<statements when str1 and str2 are not equal>
else
<statements when str1 and str2 are equal?
end if;
This should determine if one character string exists inside another character string:
IF instr(str1,str2)<>0 THEN
Sometimes it is easier to negate the equality condition. E.g. if not equal(val1, val2);
function equals(
val1 varchar2,
val2 varchar2
) return boolean is
begin
if val1 is null then
return val2 is null;
elsif val2 is null then
return false;
end if;
return val1 = val2;
end;
And the in your code you would have:
BEGIN
-- some code
IF NOT equals(str1, str2) THEN
DBMS_OUTPUT.PUT_LINE( ' if ' );
ELSE
DBMS_OUTPUT.PUT_LINE( ' else ' );
END IF;
END;
Related
TYPE arr is VARRAY(10) of VARCHAR(32);
ar arr := arr('1', '1', '1');
idx INTEGER(100);
tmp INTEGER(100);
val VARCHAR(32);
PROCEDURE APPENDARR (arr IN OUT arr, idx IN, val IN) IS
BEGIN
if(idx > arr.LIMIT) then
tmp := (idx - arr.LIMIT);
arr.extend(tmp);
FOR i in REVERSE idx..arr.LAST LOOP
arr(i) := arr(i - 1);
if(arr(i) = arr(idx)) THEN
arr(i) := val;
END IF;
END LOOP;
ELSE
arr.extend();
arr(idx) := val;
END IF;
END;
error code:
ORA-06550: line 10, column 48:
PLS-00103: Encountered the symbol "," when expecting one of the following:
out
long double ref char time timestamp interval date binary
national character nchar
anyone who can help me with my error? sorry english is not my native language but i try my best :)!
I'm guessing this is part of a package, as otherwise you would need the create or replace syntax. For demo purposes, I put it in an anonymous block just to test compilation.
Two issues:
integer doesn't take a precision, it's just integer.
The idx and val parameters are missing datatypes.
The following compiles (I haven't checked what it does or whether there is any better implementation):
declare
type arr is varray(10) of varchar(32);
ar arr := arr('1', '1', '1');
idx integer;
tmp integer;
val varchar(32);
procedure appendarr
( arr in out arr
, idx in integer
, val in varchar2 )
is
begin
if idx > arr.limit then
tmp := idx - arr.limit;
arr.extend(tmp);
for i in reverse idx .. arr.last loop
arr(i) := arr(i - 1);
if arr(i) = arr(idx) then
arr(i) := val;
end if;
end loop;
else
arr.extend();
arr(idx) := val;
end if;
end;
begin
null;
end;
PL/SQL if conditions are terminated by the then keyword so they don't need brackets as in other languages, so I removed them.
Also, you have variables idx, tmp and val declared at the top which are not used anywhere, and which also have the same names as local variables within the procedure, which could be confusing. If there isn't some more code you haven't shown that uses them, maybe you can get rid of them.
Edit: As requested, here is what it would look like as a standalone procedure:
create or replace type arr as varray(10) of varchar(32)
/
create or replace procedure appendarr
( arr in out arr
, idx in integer
, val in varchar2 )
as
tmp integer;
begin
if idx > arr.limit then
tmp := idx - arr.limit;
arr.extend(tmp);
for i in reverse idx .. arr.last loop
arr(i) := arr(i - 1);
if arr(i) = arr(idx) then
arr(i) := val;
end if;
end loop;
else
arr.extend();
arr(idx) := val;
end if;
end appendarr;
/
(The / character is required by some development tools to separate blocks, but is not part of the PL/SQL language itself.)
It seems the aim is to insert value val into element idx of array arr, shifting existing values up one place. However, it doesn't quite work:
declare
ar arr := arr('First', 'Second');
begin
appendarr
( arr => ar
, idx => 2
, val => 'New value' );
for i in ar.first..ar.last loop
dbms_output.put_line(i||' '||ar(i));
end loop;
end;
1 First
2 New value
3
Fixed version:
create or replace type varchar2_tt as table of varchar2(50)
/
create or replace procedure varchar2_tt_append
( arr in out varchar2_tt
, pos in pls_integer default null
, val in varchar2 )
as
idx pls_integer := nvl(pos,arr.last +1);
begin
if idx > arr.last then
arr.extend(idx - arr.last);
else
arr.extend();
for i in reverse greatest(idx,2)..arr.last loop
arr(i) := arr(i - 1);
end loop;
end if;
arr(idx) := val;
end varchar2_tt_append;
/
Test:
declare
ar varchar2_tt := varchar2_tt('First', 'Second', 'Third', 'Fourth');
begin
varchar2_tt_append
( arr => ar
, pos => 1
, val => 'New value' );
for i in ar.first..ar.last loop
dbms_output.put_line(i||' '||ar(i));
end loop;
end;
1 New value
2 First
3 Second
4 Third
5 Fourth
I am getting reference to uninitialized collection exception when trying to add element to an array. Please help.Is that correct way to initialize the array as mentioned in my calling code?
CREATE OR REPLACE TYPE SCHEMA.STRARRAY AS TABLE OF VARCHAR2 (255);
CREATE OR REPLACE PROCEDURE SCHEMA.PR_VALIDATE
(
FILEARRAY IN STRARRAY,
DUPARRAY OUT STRARRAY)
IS
dupCount NUMBER;
fileName VARCHAR2 (50);
fileId NUMBER;
dupfileName VARCHAR2(50);
BEGIN
for i in 1 .. FILEARRAY.count
loop
fileName := FILEARRAY(i);
SELECT COUNT (T.FILEID), T.FILEID INTO dupCount,fileId FROM TB_COMPANY T, TB_COMPANY S
WHERE T.FILEID=S.FILEID
AND T.RPDT_ORI_FLE_NM = fileName AND T.RPDT_STA_CD IN ('PASS', 'FAIL')
GROUP BY T.FILEID;
IF dupCount>1
THEN
SELECT RPDT_ORI_FLE_NM INTO dupfileName FROM TB_RDTE_COMPANY_HDR_DT
WHERE RPDT_STA_CD IN ('PR15','PR16') AND RPDT_FLE_ID=fileId
AND RPDT_ORI_FLE_NM != fileName;
DBMS_OUTPUT.PUT_LINE(dupfileName);
DUPARRAY(DUPARRAY.LAST +1 ) :=dupfileName; --Here is the exception.
END IF;
end loop;
EXCEPTION
WHEN OTHERS THEN
PR_RDTE_ERRORS('PR_VALIDATE', SQLERRM);
ROLLBACK;
END;
/
Calling code:
DECLARE
DUPARRAY STRARRAY:=STRARRAY();
BEGIN
PR_VALIDATE (STRARRAY('abc.txt'),DUPARRAY);
END;
DUPARRAY.LAST will get the index of the last element of the array (and not the index of the last non-NULL value of the array) - so if you use DUPARRAY.LAST + 1 you will always exceed the array bounds.
Also, you have not extended the array - which you need to do to add an extra element.
You need to do:
DUPARRAY.EXTEND;
DUPARRAY(DUPARRAY.LAST) :=dupfileName;
You also need to initialise the DUPARRRY inside the procedure (instead of in the calling code):
So something like this:
CREATE OR REPLACE PROCEDURE PR_VALIDATE
(
FILEARRAY IN STRARRAY,
DUPARRAY OUT STRARRAY
)
IS
dupCount NUMBER;
fileName VARCHAR2 (50);
dupfileName VARCHAR2(50);
fileId NUMBER;
BEGIN
DUPARRAY := STRARRAY();
for i in 1 .. FILEARRAY.count loop
fileName := FILEARRAY(i);
SELECT COUNT (T.FILEID), T.FILEID
INTO dupCount,fileId
FROM TB_COMPANY T
INNER JOIN TB_COMPANY S
ON T.FILEID=S.FILEID
WHERE T.RPDT_ORI_FLE_NM = fileName
AND T.RPDT_STA_CD IN ('PASS', 'FAIL')
GROUP BY T.FILEID;
IF dupCount>1 THEN
SELECT RPDT_ORI_FLE_NM
INTO dupfileName
FROM TB_RDTE_COMPANY_HDR_DT
WHERE RPDT_STA_CD IN ('PR15','PR16')
AND RPDT_FLE_ID=fileId
AND RPDT_ORI_FLE_NM != fileName;
DBMS_OUTPUT.PUT_LINE(dupfileName);
DUPARRAY.EXTEND;
DUPARRAY(DUPARRAY.LAST) :=dupfileName;
END IF;
end loop;
END;
/
Then call it:
DECLARE
DUPARRAY STRARRAY;
BEGIN
PR_VALIDATE(STRARRAY('abc.txt'),DUPARRAY);
END;
/
Is that expected behavior for oracle 11g. Can someone explain why last query does not include null value?
table
statuscode
13
null
---------------------------------------------------------
select count(*) from table -- returns 2
select count(*) from table where statuscode = 13 --returns 1
select count(*) from table where statuscode <> 13 --returns 0
Think of NULL as an unknown value and testing if something equals (or does not equal) an unknown will result in an unknown (NULL) as the answer. The SQL query will display results when the boolean filter is TRUE and this will not be the case if one value is NULL.
You can test the logic in PL/SQL (since it has an accessible BOOLEAN type):
SET SERVEROUTPUT ON;
DECLARE
FUNCTION bool_to_string( bool BOOLEAN ) RETURN VARCHAR2
AS
BEGIN
RETURN CASE WHEN bool IS NULL THEN 'NULL'
WHEN bool = TRUE THEN 'TRUE'
WHEN bool = FALSE THEN 'FALSE'
ELSE 'ERROR' END;
END;
BEGIN
DBMS_OUTPUT.PUT_LINE( 'A = A => ' || bool_to_string( 'A' = 'A' ) );
DBMS_OUTPUT.PUT_LINE( 'A <> A => ' || bool_to_string( 'A' <> 'A' ) );
DBMS_OUTPUT.PUT_LINE( 'A = NULL => ' || bool_to_string( 'A' = NULL ) );
DBMS_OUTPUT.PUT_LINE( 'A <> NULL => ' || bool_to_string( 'A' <> NULL ) );
END;
/
Which outputs:
A = A => TRUE
A <> A => FALSE
A = NULL => NULL
A <> NULL => NULL
Note that the last two tests do not return FALSE but return NULL.
If you want to count including NULLs then you can do:
select count(*) from table where statuscode <> 13 OR statuscode IS NULL
I have a problem because in this code I don't know how I can to test if a string is in a list which isn't initialized .
The code is this :
TYPE t1 IS TABLE OF VARCHAR2(32767) index by PLS_INTEGER;
v_t1 t1;
WOUT varchar2(80) :='bbbb';
v_t1(1):='bbbb';
if (WOUT member of v_t1 ) then
....
end if;
I don't know how i can write in plsql the condition that wout is inside v_t1 because member is accepted only with lists initialized.
Thanks for the help
With the collection declared as you have it there's no simple way to do what you're trying to do except to iterate through the collection to find the element you want:
declare
TYPE t1 IS TABLE OF VARCHAR2(32767)
index by PLS_INTEGER;
v_t1 t1;
WOUT VARCHAR2(80) := 'zzzz';
i NUMBER;
bFound BOOLEAN := FALSE;
BEGIN
v_t1(0) := 'aaaa';
v_t1(1) := 'bbbb';
v_t1(2) := 'cccc';
v_t1(26) := 'zzzz';
i := v_t1.FIRST;
LOOP
DBMS_OUTPUT.PUT_LINE('i=' || i);
IF v_t1(i) = WOUT THEN
bFound := TRUE;
EXIT;
END IF;
i := v_t1.NEXT(i);
IF i IS NULL THEN
EXIT;
END IF;
END LOOP;
DBMS_OUTPUT.PUT_LINE('bFound=' || CASE WHEN bFOUND THEN 'TRUE' ELSE 'FALSE' END);
end;
HOWEVER - if you change the collection so that it's indexed by the string it contains you can accomplish this task a bit easier by using the EXISTS collection method:
declare
TYPE T2 IS TABLE OF VARCHAR2(32767)
INDEX BY VARCHAR2(32767);
v_t2 t2;
WOUT VARCHAR2(80) := 'zzzz';
i NUMBER;
bFound BOOLEAN;
BEGIN
v_t2('aaaa') := 'aaaa';
v_t2('bbbb') := 'bbbb';
v_t2('cccc') := 'cccc';
v_t2('zzzz') := 'zzzz';
bFound := v_t2.EXISTS(WOUT);
DBMS_OUTPUT.PUT_LINE('bFound=' || CASE WHEN bFOUND THEN 'TRUE' ELSE 'FALSE' END);
END;
The documentation on collection methods can be found here.
Share and enjoy.
You can create special function that checks if such value is inside collection. Something like that:
FUNCTION exist_in_collection( p_table t1, p_text VARCHAR2) RETURN boolean
IS
BEGIN
IF p_table.LAST IS NULL THEN
RETURN FALSE;
ELSE
FOR i IN p_table.FIRST.. p_table.LAST
LOOP
IF p_table(i) = p_text THEN
RETURN TRUE;
END IF;
END LOOP;
RETURN FALSE;
END IF;
END exist_in_collection;
if p_table is not initialized LAST is NULL you will get FALSE. And then you can use this function to check whether the string is inside collection:
IF exist_in_collection(p_table, WOUT) THEN ... END IF;
I have a stored function which is supposed to compare three text values for equality. Some of these text values may be null, and if they are, then the comparison should return a false value.
CREATE OR REPLACE FUNCTION "subject_check_if_subjectName_exists"(name1IN text, name2IN text, name3IN text, name4IN text)
returns boolean as
$$
declare
results boolean;
subjectList record;
begin
results = false;
for subjectList in select name1, name2, name3, name4 from subject loop
if (name1In = subjectList.name1) and (name2In = subjectList.name2) and (name3In = subjectList.name3) and (name4In = subjectList.name4)
then
results = true;
EXIT; -- exit out of loop
end if;
end loop;
return results;
end;
$$ language 'plpgsql';
Of both name4IN and subjectList.name4 are null, and all the other values are equal, the function doesn't return a true value - which it should. How can I compare these text values even if they are null (null = null should return true)?
I think you want to use is not distinct from:
For non-null inputs, IS DISTINCT FROM is the same as the <> operator. However, if both inputs are null it returns false, and if only one input is null it returns true. Similarly, IS NOT DISTINCT FROM is identical to = for non-null inputs, but it returns true when both inputs are null, and false when only one input is null.
Essentially, A is not distinct from B is like A = B but it treats NULLs as "equal" (i.e. it behaves like most SQL newcomers think = should). For example, consider a simple function like this:
create function f(text,text) returns text as $$
begin
if $1 is distinct from $2 then
return '!=';
end if;
return '==';
end $$
language plpgsql;
That will give you results like this:
=> select f(null, null) as "1"
f(null, '') as "2",
f('', '') as "3",
f('pancakes','pancakes') as "4",
f('pancakes', null) as "5",
f('pancakes', 'house') as "6";
1 | 2 | 3 | 4 | 5 | 6
----+----+----+----+----+----
== | != | == | == | != | !=
So something like this is what you're looking for:
if (name1In is not distinct from subjectList.name1) and ...
Essentially, this is what I want to do. I figured there must be a quicker way to compare values including null ones. This does what I want it to do:
-- Function to check subject names (name1,2,3 and 4) already exists
CREATE OR REPLACE FUNCTION "subject_check_if_subjectName_exists"(name1IN text, name2IN text, name3IN text, name4IN text)
returns boolean as
$$
declare
results boolean;
subjectList record;
begin
results = false;
for subjectList in select name1, name2, name3, name4 from subject loop
if ((subjectList.name4 is null) and (name4IN is null)) then
if ((subjectList.name3 is null) and (name3IN is null)) then
if ((subjectList.name2 is null) and (name2IN is null)) then
if (name1IN = subjectList.name1) then
results = true;
EXIT;
end if;
else
if ((name1IN = subjectList.name1) and (name2IN = subjectList.name2)) then
results = true;
EXIT;
end if;
end if;
else
if ((name1IN = subjectList.name1) and (name2IN = subjectList.name2) and (name3IN = subjectList.name3)) then
results = true;
EXIT;
end if;
end if;
else
if ((name1IN = subjectList.name1) and (name2IN = subjectList.name2) and (name3IN = subjectList.name3) and (name4IN = subjectList.name4)) then
results = true;
EXIT;
end if;
end if;
end loop;
return results;
end;
$$ language 'plpgsql';
Will try IS NOT DISTINCT FROM now.
Thank you for your help. Much appreciated!
Use coalesce:
if (coalesce(name1In, '') = coalesce(subjectList.name1, '') ....