Blank character ignored in where clause - oracle

I have done the following -
create table test (col char(10));
insert into test values ('hello');
select * from test where col = 'hello'
I have been suggested that the above should not return any result as 'col' is 10 chars, it will be right padded with blanks, so comparing with 'hello' will not return result. But I am getting the result. Can anyone please explain this? I am using 11gR2

Looking at the Oracle Documentation on literals:
Text literals have properties of both the CHAR and VARCHAR2 datatypes:
Within expressions and conditions, Oracle treats text literals as though they have the datatype CHAR by comparing them using blank-padded comparison semantics.
and the documentation of blank-padded comparison semantics states:
With blank-padded semantics, if the two values have different lengths, then Oracle first adds blanks to the end of the shorter one so their lengths are equal. Oracle then compares the values character by character up to the first character that differs. The value with the greater character in the first differing position is considered greater. If two values have no differing characters, then they are considered equal. This rule means that two values are equal if they differ only in the number of trailing blanks. Oracle uses blank-padded comparison semantics only when both values in the comparison are either expressions of datatype CHAR, NCHAR, text literals, or values returned by the USER function.
Since the left-hand side of the comparison is a CHAR(10) and the right-hand side is a text literal then blank-padded comparison semantics are used and 'hello ' = 'hello' is true.
You can see this in the simple example:
SELECT * FROM DUAL WHERE 'hello ' = 'hello';
Update:
[TL;DR] This behaviour has appeared in all versions of Oracle since at least Oracle 7 (released in 1992). I stopped searching for the documentation on releases over two decades old but I expect that you will find that this has been the behaviour in most (all?) versions.
Here is the documentation for the various versions:
Oracle 12c Text Literals & blank-padded semantics
Oracle 11g Text Literals & blank-padded semantics
Oracle 10gR2 Text Literals & blank-padded semantics
Oracle 9 Text Literals & blank-padded semantics
Oracle 8 Text Literals & blank-padded semantics
Oracle 7 Text Literals

You got the correct answer already. Now, you may wonder "how can I force a comparison where the comparison string 'hello' is interpreted as VARCHAR2, so that the query will return no rows?"
The answer is, you must force 'hello' to be seen as VARCHAR2. Like so:
... where col = cast ('hello' as VARCHAR2(10))
(note that the syntax for the cast function requires you to specify the length).
Even though you specify the length of 10, since the cast is as VARCHAR2, the result is really just 5 characters - there is no blank padding of VARCHAR2 values.

Oracle uses "blank padded" comparison semantics in this example because one of the expressions is datatype CHAR and the other expression is a string literal.
There are rules about when Oracle uses blank padded comparison and non-padded comparison semantics. It's covered in the Oracle documentation somewhere.
The suggestion were given (that Oracle would not return a result because the CHAR column would be right padded with spaces) was erroneous.

Related

Different matches when using prepared statements on CHAR(3) column

I had to make a CHAR(1 CHAR) column wider and I forgot to change the column type to VARCHAR2:
DUPLICADO CHAR(3 CHAR)
I noticed the error when my PHP app would no longer find exact matches, e.g.:
SELECT *
FROM NUMEROS
WHERE DUPLICADO = :foo
... with :foo being #4 didn't find the 3-char padded #4 value. However, I initially hit a red herring while debugging the query in SQL Developer because injecting raw values into the query would find matches!
SELECT *
FROM NUMEROS
WHERE DUPLICADO = '#4'
Why do I get matches with the second query? Why do prepared statements make a difference?
To expand a little on my comments, I found a bit in the documentation that explains difference between blankpadded and nonpadded comparison:
http://docs.oracle.com/database/121/SQLRF/sql_elements002.htm#BABJBDGB
If both values in your comparison (the two sides of the equal sign) have datatype CHAR or NCHAR or are literal strings, then Oracle chooses blankpadded comparison. That means that if the lengths are different, then it pads the short one with blanks until they are the same length.
With the column DUPLICADO being a CHAR(3), the value '#4' is stored in the column as three characters '#4 ' (note the blank as third character.) When you do DUPLICADO = '#4' the rule states Oracle will use blankpadded comparison and therefore blankpad the literal '#4' until it has the same length as the column. So it actually becomes DUPLICADO = '#4 '.
But when you do DUPLICADO = :foo, it will depend on the datatype of the bind variable. If the datatype is CHAR, it will also perform blankpadded comparison. But if the datatype is VARCHAR2, then Oracle will use non-padded comparison and then it will be up to you to ensure to do blankpadding where necessary.
Depending on client or client language you may be able to specify the datatype of the bind variable and thereby get blankpadded or nonpadded comparison as needed.
SQL Developer may be a special case that might not allow you to specify datatype - it just possibly might default to bind variables always being datatype VARCHAR2. I don't know sufficient about SQL Developer to be certain about that ;-)

Why Oracle ascii function return >255 code?

When using Oracle ascii function:
select ascii('A') from dual;
It return 65 is right.
But,when i using:
select ascii('周') from dual;
The return is 55004.The ascii can represent>255???
How to explain?
Help!!!!
My oracle version:Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 - Production
my Characterset:6 NLS_CHARACTERSET ZHS16GBK
ASCII in the name is a holdover from when Oracle only supported ASCII. It does not mean it only returns ASCII values.
From the docs:
ASCII returns the decimal representation in the database character set of the first character of char.
http://docs.oracle.com/cd/E11882_01/server.112/e41084/functions013.htm#sthref933
So the result depends on the database character set, which can be greater than 255.
This may vary with your version of Oracle, but it is probably trying to do you the favor of gracefully handling the non-7bit ASCII value that you are passing (but should not be). The doc in at least one version discusses some handling of non-ASCII inputs (http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions007.htm) though if you are using a different version of oracle you may want to refer to the appropriate docs.
If your docs don't say anything more about how it handles non-7bit characters then the answer is probably not well defined (ie no guarantee from Oracle on behavior) and you may want to consider cleansing your input so you only try calling the ASCII function on values that you know to be in the proper input set.

How to do simple Substring in Oracle

How do I do this in Oracle Query? this is saying that we want the records which has '1' inleft most position in that column.
AND substring(convert(varchar,TR.MENU_ID_VALUE), 1, 1) = '1'
The convert function function transforms a string from one character set into another. The syntax is convert(<string>, <to character set>, <from character set>)
Also, substring isn't an Oracle function, it's substr.
If you don't need to convert the string then and substr(tr.menu_id_value,1,1) will get you the leftmost character.
If you do want to convert the character set then the full list is available in the documentation, alternatively you can query v$nls_valid_values. For instance if you're converting from WE8MSWIN1252 (Windows code-page 1252) to AL32UTF8 (UTF-8) then your condition would become:
and substr(convert(tr.menu_id_value,'AL32UTF8','WE8MSWIN1252'),1,1) = '1'
From your comment I'm guessing that convert() is the SQL-Server function to convert between data-types. The Oracle equivalent, in this case, would be to_char. Though Oracle will implicitly do the conversion it is always best to use to_char and convert explicitly; giving you:
and substr(to_char(tr.menu_id_value),1,1) = '1'
Incidentally the exact Oracle equivalent is cast(). Strictly speaking, to_char() should be used if you want to use a format model, thus substr(cast tr.menu_id_value as varchar2(10)),1,1) would also give you what you're after assuming the length of tr.menu_id_value was less than or equal to 10.
Use SUBSTR
AND substr(tr.menu_id_value,1,1) = '1'

Oracle to_number function parameters

I'm having trouble with TO_NUMBER function second and third parameters. Does one of them depend on the other one? How does nls_params parameter work? I can't understand how the the result of the query
SELECT TO_NUMBER('17.000,23',
'999G999D99',
'nls_numeric_characters='',.'' ')
REFORMATTED_NUMBER
FROM DUAL;
can be 17000.23. Could somebody please explain the process of the above conversion.
P.S. The above query is taken from an Oracle Database SQL Expert Certificate preparation book.
you are telling the TO_NUMBER function that,
the two characters ,. in nls_numeric_characters represent the decimal and thousand seperator
G (thousands seperator) = .
D (decimal seperator) = ,
so it sees the number as seventeen thousand point twenty three.
see: http://download.oracle.com/docs/cd/B13789_01/olap.101/b10339/x_stddev022.htm#i78653
Now, I'll answer my own question. While using TO_NUMBER function I missed the important point that, whatever I get from TO_NUMBER function is going to be a number. And a number does not include anything else than decimal point and E scientific notation. So 17,788.99 is not actually a number but is rather the string representation of 17788.99.
If we try to subtract 500 from 17,788.99 we'll fail.(Well, Oracle implicitly converts numeric strings to numbers and vice-versa, but principally we can't perform arithmetic operations between strings and numbers). I'm sure that TO_NUMBER function is almost never used to select a column value. It's rather used to be able to make arithmetic operations. Instead, we use TO_CHAR to show a column value or any numeric expression in a neat, easy to read format. The fomat models and nls_params are not only for TO_NUMBER function, but for TO_CHAR as well.

Oracle10 and JDBC: how to make CHAR ignore trailing spaces at comparision?

I have a query that has
... WHERE PRT_STATUS='ONT' ...
The prt_status field is defined as CHAR(5) though. So it's always padded with spaces. The query matches nothing as the result. To make this query work I have to do
... WHERE rtrim(PRT_STATUS)='ONT'
which does work.
That's annoying.
At the same time, a couple of pure-java DBMS clients (Oracle SQLDeveloper and AquaStudio) I have do NOT have a problem with the first query, they return the correct result. TOAD has no problem either.
I presume they simply put the connection into some compatibility mode (e.g. ANSI), so the Oracle knows that CHAR(5) expected to be compared with no respect to trailing characters.
How can I do it with Connection objects I get in my application?
UPDATE I cannot change the database schema.
SOLUTION It was indeed the way Oracle compares fields with passed in parameters.
When bind is done, the string is passed via PreparedStatement.setString(), which sets type to VARCHAR, and thus Oracle uses unpadded comparision -- and fails.
I tried to use setObject(n,str,Types.CHAR). Fails. Decompilation shows that Oracle ignores CHAR and passes it in as a VARCHAR again.
The variant that finally works is
setObject(n,str,OracleTypes.FIXED_CHAR);
It makes the code not portable though.
The UI clients succeed for a different reason -- they use character literals, not binding. When I type PRT_STATUS='ONT', 'ONT' is a literal, and as such compared using padded way.
Note that Oracle compares CHAR values using blank-padded comparison semantics.
From Datatype Comparison Rules,
Oracle uses blank-padded comparison
semantics only when both values in the
comparison are either expressions of
datatype CHAR, NCHAR, text literals,
or values returned by the USER
function.
In your example, is 'ONT' passed as a bind parameter, or is it built into the query textually, as you illustrated? If a bind parameter, then make sure that it is bound as type CHAR. Otherwise, verify the client library version used, as really old versions of Oracle (e.g. v6) will have different comparison semantics for CHAR.
If you cannot change your database table, you can modify your query.
Some alternatives for RTRIM:
.. WHERE PRT_STATUS like 'ONT%' ...
.. WHERE PRT_STATUS = 'ONT ' ... -- 2 white spaces behind T
.. WHERE PRT_STATUS = rpad('ONT',5,' ') ...
I would change CHAR(5) column into varchar2(5) in db.
You can use cast to char operation in your query:
... WHERE PRT_STATUS=cast('ONT' as char(5))
Or in more generic JDBC way:
... WHERE PRT_STATUS=cast(? as char(5))
And then in your JDBC code do use statement.setString(1, "ONT");

Resources