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

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'

Related

Oracle 11g hextoraw sql query results

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

In db2 how to select a column in integer having aligned it to right with leading spaces?

I need to extract a report from some tables using db2 having pgm(dnatiaul).
Using a query i want to get the below output with first char to be spaces.
ex: integer(16)
54457750
49457750
o/p: char(int(16))
54457750
49457750
As i am trying to convert it to char it is aligning to left.
I tried Lpad which gives me **extra length i.e (18) + '.' also
Help me
LPAD is the right choice, but you would need to specify how long the result string needs to be. This can be done using CAST. Here I cast the result to 10 characters.
db2 "select cast(lpad(123422,10,' ') as char(10)) as testme from sysibm.sysdummy1"
TESTME
----------
123422
1 record(s) selected.
DIGITS(integer_column_name) will give you a character string of CHAR(10) with the numeric value right-justified and left-filled with zeroes. Thus, an integer column containing 543210 will become a character string containing 0000543210.
Likewise, DIGITS(small_integer_column_name) will give you a character string of CHAR(5)

Regex to split values in PostgreSQL

I have a list of values coming from a PGSQL database that looks something like this:
198
199
1S
2
20
997
998
999
C1
C10
A
I'm looking to parse this field a bit into individual components, which I assume would take two regexp_replace function uses in my SQL. Essentially, any non-numeric character that appears before numeric ones needs to be returned for one column, and the other column would show all non-numeric characters appearing AFTER numeric ones.
The above list would then be split into this layout as the result from PG:
I have created a function that strips out the non-numeric characters (the last column) and casts it as an Integer, but I can't figure out the regex to return the string values prior to the number, or those found after the number.
All I could come up with so far, with my next to non-existant regex knowledge, was this: regexp_replace(fieldname, '[^A-Z]+', '', 'g'), which just strips out anything not A-Z, but I can;t get to to work with strings before numeric values, or after them.
For extracting the characters before the digits:
regexp_replace(fieldname, '\d.*$', '')
For extracting the characters after the digits:
regexp_replace(fieldname, '^([^\d]*\d*)', '')
Note that:
if there are no digits, the first will return the original value and then second an empty string. This way you are sure that the concatenation is equal to the original value in this case also.
the concatenation of the three parts will not return the original if there are non-numerical characters surrounded by digits: those will be lost.
This also works for any non-alphanumeric characters like #, [, ! ...etc.
Final SQL
select
fieldname as original,
regexp_replace(fieldname, '\d.*$', '') as before_s,
regexp_replace(fieldname, '^([^\d]*\d*)', '') as after_s,
cast(nullif(regexp_replace(fieldname, '[^\d]', '', 'g'), '') as integer) as number
from mytable;
See fiddle.
This answer relies on information you delivered, which is
Essentially, any non-numeric character that appears before numeric
ones needs to be returned for one column, and the other column would
show all non-numeric characters appearing AFTER numeric ones.
Everything non-numeric before a numeric value into 1 column
Everything non-numeric after a numeric value into 2 column
So there's assumption that you have a value that has a numeric value in it.
select
val,
regexp_matches(val,'([a-zA-Z]*)\d+') AS before_numeric,
regexp_matches(val,'\d+([a-zA-Z]*)') AS after_numeric
from
val;
Attached SQLFiddle for a preview.

zero padding in teradata sql

Table A
Id varchar(30)
I'm trying to re-create a logic where I have to use 9 digit Ids irrespective of the actual length of the Value of the Id field.
So for instance, if the Id is of length 6, I'll need to left pad with 3 leading zeros. The actual length can be anything ranging from 1 to 9.
Any ideas how to implement this in Teradata SQL?
If the actual length is 1 to 9 characters why is the column defined as VarCar(30)?
If it was a numeric column it would be easy:
CAST(CAST(numeric_col AS FORMAT '9(9)') AS CHAR(9))
For strings there's no FORMAT like that, but depending on your release you might have an LPAD function:
LPAD(string_col, 9, '0')
Otherwise it's:
SUBSTRING('000000000' FROM CHAR_LENGTH(string_col)+1) || string_col,
If there are more than nine characters all previous calculations will return them.
If you want to truncate (or a CHAR instead of a VARCHAR result) you have to add a final CAST AS CHAR(9)
And finally, if there are leading or trailing blanks you might want to use TRIM(string_col)

SQL Select by condition on a integer field

I have an integer column in my table. It is product id and has values like
112233001
112233002
113311001
225577001
This numbering (AABBCCDDD) is formed of 4 parts:
AA : first level category
BB : second level category
CC : third level category
DDD : counter
I want to check condition in my SELECT statement to select rows that for example have BB = 33 and AA = 11
Please help
Would this suffice:
select x from table where field >= 113300000 and field < 113400000
SELECT * FROM YOURTABLE
WHERE
substr(PRODUCT_ID, 3, 2)='33'
AND
substr(PRODUCT_ID, 1, 2)='11'
OR
SELECT * FROM YOURTABLE
WHERE
PRODUCT_ID LIKE '11%33%'
and yes in short you have to convert to string
reference of substr
Purpose
The SUBSTR functions return a portion of char, beginning at character position, substring_length characters long. SUBSTR calculates lengths using characters as defined by the input character set. SUBSTRB uses bytes instead of characters. SUBSTRC uses Unicode complete characters. SUBSTR2 uses UCS2 code points. SUBSTR4 uses UCS4 code points.
If position is 0, then it is treated as 1.
If position is positive, then Oracle Database counts from the beginning of char to find the first character.
If position is negative, then Oracle counts backward from the end of char.
If substring_length is omitted, then Oracle returns all characters to the end of char. If substring_length is less than 1, then Oracle returns null.
char can be any of the datatypes CHAR, VARCHAR2, NCHAR, NVARCHAR2, CLOB, or NCLOB. Both position and substring_length must be of datatype NUMBER, or any datatype that can be implicitly converted to NUMBER, and must resolve to an integer. The return value is the same datatype as char. Floating-point numbers passed as arguments to SUBSTR are automatically converted to integers.
Select field from table where substr(field,,) = value
This seems like it could work. Otherwise you may have to cast them as strings and parse the values out that you need which would make your queries much slower.
SELECT *
FROM table t
WHERE t.field >= 113300000
AND t.field < 113400000
u need to use _ wildcard char -
SELECT *
FROM TABLE
WHERE
FIELD LIKE '1133_____'
here, each _ is for one char. So you need to put the same number of _ to keep the length same