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?
CodePudding user response:
Does a CHAR column "automatically" pad zeros to a value?
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'
CodePudding user response:
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'