Oracle 11g hextoraw sql query results - sql

So, I have a table called mams_folder , where primary key is mams_folder_id.
Now, its type is raw. I am representing keys in hexadecimal string.
Following queries were run on sql developer.
Now, I run these queries :
select * from mams_folder f where f.mams_folder_id= hextoraw('EEA12100F39D100384D2080020F03012'); //Works fine
select * from mams_folder f where f.mams_folder_id= 'EEA12100F39D100384D2080020F03012';//Surprisingly works fine too. Why ?
select * from mams_folder f where f.mams_folder_id= hextoraw('5426deaf924642bb9a38dc0b5be87ed6'); //Works fine as expected
select * from mams_folder f where f.mams_folder_id= '5426deaf924642bb9a38dc0b5be87ed6'; //Returns no rows as expected
Both are valid primary keys. '5426deaf924642bb9a38dc0b5be87ed6' was newly inserted in database.
Why does db returns answer to second query but returns null for the last ?
Does it have to do something with db caching ?
Update :
Ok, I came to know that if I am using primary keys in uppercase hex string, then even without using hextoraw() , queries work fine (as we can see above). However when smaller case is used, hextoraw() becomes compulsary to use else empty result is shown. Why ?

I believe what you are seeing stems from implicit/explicit type conversion mechanics when inserting by literal or hextoraw then later predicating against a literal.
Formal Hexadecimal is 0123456789ABCDEF (uppercase), radix 16, though there is tooling (both in Oracle and elsewhere) to recognize character strings containing 0123456789abcdefABCDEF (case insensitive) as hex.
HEXTORAW is case-insensitive and accepts lower-case hex. But the returned raw value will be formal hex.
As an example, running the following:
SELECT HEXTORAW('5426deaf924642bb9a38dc0b5be87ed6') AS HEX FROM DUAL;
Gives formal upper-case hex
HEX
5426DEAF924642BB9A38DC0B5BE87ED6
And the following will of course fail, as it contains a non-hex char g
SELECT HEXTORAW('5426geaf924642bb9a38dc0b5be87ed6') FROM DUAL;
ORA-01465: invalid hex number
As will implicit conversion when inserting the above literal into a raw field. RAW requires hex.
For the queries against mams_folder table, I believe the queries with lower-case literal predicates find no matches because when predicating with a string literal, the ensuing type conversion to compare RAW and VARCHAR2 winds up attempting to match formal hex char against lower-case literal char. (It does not convert the provided literal to raw, but compares the hex to the literal)
Since HEXTORAW converts any input string to formal hex in the conversion process, it ends up with a different comparison than the plain literal would anyway, even if it were comparing char instead of raw.
So the simple predicate that matches the converted (upper-case) RAW against a lower-case literal will result in no matches. But when comparing an upper-case literal against the RAW, the formal hex used in comparison happens to match the upper-case string literal and the predicate is fulfilled.
Here's an example:
CREATE TABLE mams_folder (mams_folder_id RAW(32));
Then add the test data. One item is added as an upper-case literal, one added as a lower-case literal, relying on oracle to do any needed type conversion implicitly
INSERT INTO mams_folder VALUES ('EEA12100F39D100384D2080020F03012');
INSERT INTO mams_folder VALUES ('5426deaf924642bb9a38dc0b5be87ed6');
Then just query to see how things look initially. No hextoraw involved yet.
SELECT * FROM MAMS_FOLDER;
MAMS_FOLDER_ID
EEA12100F39D100384D2080020F03012
5426DEAF924642BB9A38DC0B5BE87ED6
Note the printed value for the inserted literal '5426deaf924642bb9a38dc0b5be87ed6'.
Now the conversion comparing raw to upper-case literal matches.
In this query:
SELECT MAMS_FOLDER_ID, 'EEA12100F39D100384D2080020F03012' AS TARGET FROM MAMS_FOLDER;
We can see that the first row matches the converted raw with the literal.
MAMS_FOLDER_ID TARGET
EEA12100F39D100384D2080020F03012 EEA12100F39D100384D2080020F03012
5426DEAF924642BB9A38DC0B5BE87ED6 EEA12100F39D100384D2080020F03012
With the implicit comparisons matching the formal hex from the RAW against the literal rather than converting the literal to a raw, UPPER would work just fine in a predicate as well.
SELECT * FROM MAMS_FOLDER F WHERE F.MAMS_FOLDER_ID = UPPER('5426DEAF924642BB9A38DC0B5BE87ED6');
MAMS_FOLDER_ID
5426DEAF924642BB9A38DC0B5BE87ED6

Related

Oracle - Why is CHAR Column is automatically adding a leading zero?

I am working with an Oracle DB 11g
I have a database table with the primary key being a CHAR(4) - Though only numbers are used for this column.
I noticed that there are some records that for example show '0018' or '0123'.
So few things I noticed odd and needed some help on
-Does a CHAR column "automatically" pad zeros to a value?
-Also I noticed when writing a SQL that if I DONT use quotes in my where clause that it returns results, but if I do use quotes it does not? So for example
DB CHAR(4) column has a key of '0018'
I use this query
SELECT * FROM TABLE_A WHERE COLUMN_1=18;
I get the row as expected.
But when I try the following
SELECT * FROM TABLE_A WHERE COLUMN_1='18';
This does NOT work but this does work again
SELECT * FROM TABLE_A WHERE COLUMN_1='0018';
So I am a bit confused how the first query can work as expected without quotes?
Does a CHAR column "automatically" pad zeros to a value?
No. From the documentation:
If you insert a value that is shorter than the column length, then Oracle blank-pads the value to column length.
So if you insert the number 18 it will be implicitly converted to the string '18 ', with two trailing spaces. You can see that in this fiddle, which also shows the comparisons.
That means something else is zero-padding your data - either your application/code before inserting, or possibly in a trigger.
Also I noticed when writing a SQL that if I DONT use quotes in my where clause that it returns results, but if I do use quotes it does not
The data type comparison and conversion rules are shown in the documentation too:
When comparing a character value with a numeric value, Oracle converts the character data to a numeric value.
When you do:
SELECT * FROM TABLE_A WHERE COLUMN_1=18;
the string '0018' is implicitly converted to the number 18 so that it can be compared with your numeric literal. The leading zeros are meaningless once it's converted, so '0018', '018 ' and 18 ' would all match.
With your zero-padded column value that matches and you do get a result: 18 ('0018' converted to a number) = 18
That means that every value in the table has to be converted before it can be compared; which also means that if you has a normal index on column_1 then it wouldn't be utilised in that comparison.
When you do:
SELECT * FROM TABLE_A WHERE COLUMN_1='18';
the column and literal are the same data type so no conversion has to be applied (so a normal index can be used). Oracle will use blank-padded comparison semantics here, because the column is char, padding the shorter literal value to the column size as '18 ', and then it will only match if the strings match exactly - so '18 ' would match but '0018' or ' 18 ' or anything else would not.
With your zero-padded column value that does not match and you don't get a result: '0018' != '18 ' ('18' padded to length 4)
When you do:
SELECT * FROM TABLE_A WHERE COLUMN_1='0018';
the column and literal are the same data type so no conversion, no padding is applied as the literal is already the same length as the column value, and again it will only match if the strings match exactly - so '0018' would match but '18 ' or ' 18 ' or anything else would not.
With your zero-padded column value that matches and you do get a result: '0018' = '0018'
Does a CHAR column "automatically" pad zeros to a value?
Not always zero's sometimes spaces. if all characters values are numeric yes it will pad zeros up to a fixed size of the character field.
So I am a bit confused how the first query can work as expected without quotes?
Because of implicit type conversions. The system is casting either the char to numeric or the numeric to char in which case it either drops the leading zeros and compares numeric values or it pads to be of the same data type and then compares. I'm pretty sure it's going character to numeric and thus the leading zeros are dropped when comparing.
See: https://docs.oracle.com/cd/B13789_01/server.101/b10759/sql_elements002.htm for more details on data type comparison and implicit casting
More:
in the case of : SELECT * FROM TABLE_A WHERE COLUMN_1='18'; I
think the 18 is already a character data so it becomes '18 ' (note 2 spaces after 18)
compared to '0018'
SELECT * FROM TABLE_A WHERE COLUMN_1=18; columN_1 gets cast to numeric so 18=18
SELECT * FROM TABLE_A WHERE COLUMN_1='0018'; column_1 is already a char(4) so '0018' = '0018'

SQL Like and Wildcard with Oracle SQL Developer

I am using SQL Developer over a backend Oracle DB. I have a record where the buyer name is Pete Hansen.
Why I try
select * from data1 where buyer = 'Pete Hansen';
I get the result, no problem. However, when I use the following, I do not get any results:
select * from data1 where buyer like 'Pete Hansen';
Also, when I try the following, I do not get any results
select * from data1 where buyer like 'Pete Hans_n';
but the following works well:
select * from data1 where buyer like 'Pete Hans_n%';
Could you please help me understand? Thanks in advance.
Your buyer column seems to be defined as char; you can see the issue reproduced in this db<>fiddle, but not when the column is varchar2.
The documentation for character comparison explains the difference between blank-padded or nonpadded comparison semantics. When you compare them with =, because both the column and the string literal are `char, blank-padded semantics are used:
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. ... 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 data type CHAR, NCHAR, text literals, or values returned by the USER function.
When the column is `varchar2 then nonpadded semantics are used:
With nonpadded semantics, ... If two values of equal length have no differing characters, then the values are considered equal. Oracle uses nonpadded comparison semantics whenever one or both values in the comparison have the data type VARCHAR2 or NVARCHAR2.
LIKE works differently. Only your final pattern with % matches, because that is allowing for the trailing spaces in the char value, while the other two patterns do not. With the varchar2 version there aren't any trailing spaces to the other two patterns also match.
It's unusual to need or want to user char columns; varchar2 is more usual. Tom Kyte opined on this many years ago.
I suspect it may have to do with trailing white spaces, which like operator is not forgiving about but = is. To test that, try
select * from data1 where trim(buyer) like 'Pete Hansen';

try to concatenate 2 strings, result ends in a lot of spaces

select CONCAT(convert(char, 123), 'sda');
Or
select convert(char, 123) + 'sda'
Or
select ltrim(convert(char, 123) + 'sda')
Output is:
How can I get the output without those spaces?
The problem here is 2 fold. Firstly that you are converting to a char, which is a fixed width datatype, and secondly that you aren't defining the length of your char, therefore the default length is used. For CAST and CONVERT that's a char(30).
So, what you have to start is convert(char, 123). This converts the int 123 to the fixed width string '123 '. Then you concatenate the varchar(3) value 'sda' to that, resulting in '123 sda'. This is working exactly as written, but clearly not as you intend.
The obvious fix would be to use a varchar and define a length, such as CONCAT(CONVERT(varchar(5),123),'sda') which would return '123sda', however, all of the CONCAT function's parameters are a string type:
string_value
A string value to concatenate to the other values. The CONCAT function requires at least two string_value arguments, and no more than 254 string_value arguments.
This means you can simply just pass the value 123 and it'll be implicitly cast to a string type: CONCAT(123,'sda').
To reiterate my comment's link too: Bad Habits to Kick : Declaring VARCHAR without (length)
You are using char while you probably want [n]varchar(...): the former pads the string with white spaces, while the latter does not:
concat(convert(varchar(10), 123), 'sda');
But simpler yet: concat() forces the conversion of its arguments to the correct datatype by default, so this should do it:
concat(123, 'sda')
First, in SQL Server, never us char or related string definitions without a length. SQL Server requires a length and the default depends on the context. If you depend on the default length your code has a bug just waiting to happen.
Second, char is almost never what you want. It is a fixed length string, with shorter strings padded with spaces.
If you want an explicit conversion use varchar, variable length strings:
select convert(varchar(255), 123) + 'sda'
Or dispense with the explicit conversion and use concat():
select concat(123, 'sda')
As the others have already pointed out the root cause of the issue, if you cannot edit the datatype, you can always use SELECT CONCAT(TRIM(CONVERT(char,123)),'sda'). Although it's highly recommended to either use varchar(n) or give exclusive length of char as it is kind of pointless to create fixed length string and then reduce the length by using TRIM. varchar(30) would perfectly fit in here as the length can still NOT exceed the 30 symbols, but would not use all the length if the string is shorter.
Lets refer to Microsoft docs:
When n isn't specified in a data definition or variable declaration statement, the default length is 1. If n isn't specified when using the CAST and CONVERT functions, the default length is 30.
Reference: https://learn.microsoft.com/en-us/sql/t-sql/data-types/char-and-varchar-transact-sql?view=sql-server-ver15#remarks
So, You have Convert(char, 123), and you did not specify the n for char, so your code is equal to Convert(char(30), 123).
Now it is clear why you have many space characters. To resolve the problem simply use variant length character datatypes such as varchar instead, however I recommend you to always use character datatypes with length. (Same as what #GordonLinoff posted: https://stackoverflow.com/a/63467483/1666800)
select convert(varchar, 123) + 'sda'

Sql Oracle query - field contains special characters or alpha

need to see if a taxid field contains letters or any special characters:. *,!,?,#,#,$,&,+,(,),/
How can I write this SQL?
Oracle has regexp_like:
select * from tablename where regexp_like(columnname, '[*!?##$&+()/]');
Here is the best way to display all the taxid's that are NOT entirely composed of digits:
select taxid
from your_table
where translate(taxid, '.0123456789', '.') is not null
TRANSLATE will "translate" (replace) each period in the input with a period in the output. Since the other characters in the second argument do not have a corresponding character in the third argument, they will simply be deleted. If the result of this operation is not null, then the taxid contains at least one character that is not a digit. If taxid is all digits, the result of the operation is null. Note that the period character used here is needed, due to an oddity in Oracle's definition of TRANSLATE: if any of its arguments is null, then so is its return value. That doesn't make a lot of sense, but we must work with the functions as Oracle defined them.

Comparing Strings in where clause

Hello I am confused according to string comparison in sql.
select * from table where column1 = 'abc';
As I understand the string 'abc' is converted to a number let us pretend (1+2+3=6) for this example.
This means that
select * from table where column1 = 'cba';
will also have the same value 6. The Strings are not the same. Please enlighten me.
Edit: Because you think this is a joke.
"The character letter King is converted to a numeric representation. Assuming a US7ASCII database character set with AMERICAN NLS settings, the literal king is converted into a sum of its ordinal character values: K+i+n+g = (75+105+110+103=393)."
This is the exact text from a book that made me confused.
you rather see it like this
a= 00000100
b= 00010000
c= 01100100
abc= 000001000001000001100100
cba= 011001000001000000000100
Thus not the same
The quote seems to be from page 31 of chapter 9 of this OCA/OCP Oracle Database 11g All-in-One Exam Guide. This appears to be incorrect (being kind), since if it worked like then abc and cba would indeed be seen as equivalent.
The 11gR2 SQL language reference says:
In binary comparison, which is the default, Oracle compares character
strings according to the concatenated value of the numeric codes of
the characters in the database character set. One character is greater
than another if it has a greater numeric value than the other in the
character set.
The key difference is phrase 'the concatenated value', i.e. closer to what #JoroenMoonen demonstrated, where the numeric codes from the character set are pieced together; and not the sum of the values as the book showed.
But it would be misleading to think of the numeric codes for each character being concatenated and the resulting (potentially very long!) string representing a number which is compared. Taking those values, abc = 000001000001000001100100 = 266340, and cba = 011001000001000000000100 = 6557700. Just comparing 6557700 with 266340 would indeed show that cba is 'greater than' abc. But cb is also 'greater than' abc - select greatest('abc', 'cb') from dual - and if you do the same conversion you get cb = 0110010000010000 = 25616, which as a number is clearly less than 266340.
I think it's actually better explained in the equivalent 10gR1 documentation:
Oracle compares two values character by character up to the first
character that differs. The value with the greater character in that
position is considered greater. If two values of different length are
identical up to the end of the shorter one, then the longer value is
considered greater. If two values of equal length have no differing
characters, then the values are considered equal.
So, assuming ASCII, c (99) is greater than a (97), so it doesn't need to look at any further characters in either string. This can never see abc and cba as equivalent.
Anyway, you're quite right to be confused by the book's explanation.