Oracle index maximum key length exceeded - oracle

I get error ORA-01450: maximum key length (6398) exceeded as I try creating below index
CREATE INDEX FORENAME_SURNAME ON CARD_HOLDER( REGEXP_REPLACE (UPPER( FORENAME ),'\\s|-|_|\\.|\\:|\\,',''), REGEXP_REPLACE (UPPER( SURNAME ),'\\s|-|_|\\.|\\:|\\,','') );
Error code is fair clear but my columns are 100Bytes each so how is possible index exceed the max??
And db_block_size = 8192 not 6398.
Below columns definition
CREATE TABLE CARD_HOLDER
( "CARD_HOLDER_ID" NUMBER NOT NULL ENABLE,
"TITLE" VARCHAR2(10 BYTE),
"FORENAME" VARCHAR2(100 BYTE),
"SURNAME" VARCHAR2(100 BYTE) NOT NULL ENABLE,
)

You can try using this with concatenation instead of two different columns. However this is only a workaround and might not be useful unless you are using the query as such with concatenation.
CREATE INDEX forename_surname ON
card_holder ( regexp_replace(upper(forename),'\\s|-|_|\\.|\\:|\\,','')
|| regexp_replace(upper(surname),'\\s|-|_|\\.|\\:|\\,','') );

The length of 6398 is the maximal length of the index key in a 8KB block size tablespace as the block must contain the key plus some overhead.
Why are you getting the error for your 100 byte columns can be easily demonstrated.
Let's define a view with your additional columns
create view ch2 as
select c.*, REGEXP_REPLACE (UPPER( FORENAME ),'\\s|-|_|\\.|\\:|\\,','') for2,
REGEXP_REPLACE (UPPER( SURNAME ),'\\s|-|_|\\.|\\:|\\,','') sur2
from CARD_HOLDER c
If you check the data types of the new columns you see
select COLUMN_NAME, DATA_TYPE, DATA_LENGTH
from user_tab_columns where table_name = 'CH2' and column_name in ('FOR2','SUR2');
COLUMN_NAME, DATA_TYPE, DATA_LENGTH
FOR2 VARCHAR2 4000
SUR2 VARCHAR2 4000
Oracle expects the result of the regexp_replace could have the maximal length of 4000, which end in total of 8000.
So what to do?
Ask Tom has a nice overview of workarounding the problem with key length.
Among others solution you could define a tablespace with a larger block size for your index.
You may also step back and use TRANSLATE (or REPLACE) to remove the unwanted characters such as
TRANSLATE(upper(FORENAME),'x'||CHR(9)||CHR(10)||CHR(11)||CHR(12)||CHR(13)||' -_.:,','x')
Here Oracle asumes the result will be only 400 charater long so there will be no problem with the index key.

Related

Oracle query looking for particular string format

Am working with an Oracle 11g db in which I need to run a query that returns all rows with values in a specific format.
I.e., I need to find all rows with a value like 'abc1234'. The actual values aren't important....what I need is to find all rows with the first 3 characters being alpha and the subsequent 4 characters all being numeric.
Any input would be much appreciated.
Might not be exact since I don't have an Oracle server handy to test but something like this should get you started in the right direction:
SELECT * FROM your_table WHERE REGEXP_LIKE(your_column, '([a-z]\3[0123456789]\4', 'i')
This will check all rows in your table where the specified column has any alphabet character A to Z three times followed by 4 numbers and return that list. The 'i' at the end just says ignore case on the alphabet part. You can choose to not have that option if you so wish.
Here are a couple links as well with more information on the regexp_like syntax:
Oracle SQL - REGEXP_LIKE contains characters other than a-z or A-Z
https://docs.oracle.com/cd/B28359_01/server.111/b28286/conditions007.htm#SQLRF00501
Try it this will work definitely:
Let's take a sample example:
For example, create a sample table
CREATE TABLE bob (
empno VARCHAR2(10) NOT NULL,
ename VARCHAR2(15) NOT NULL,
ssn VARCHAR2(10) NOT NULL);
Insert data into that table
Insert into bob ( empno,ename,ssn) values ('abC0123458','zXe5378023','0pl4783202');
Insert into bob ( empno,ename,ssn) values ('0123abcdef','a12dldfddd','Rns0101010');
Query to select the all the columns
select * from bob
where length(trim(translate(substr(empno,1,3),'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',' '))) IS NULL
AND
length(trim(translate(substr(empno,4,4),'0123456789',' '))) IS NULL;
To do the same thing on multiple columns use union operator
select * from bob
where length(trim(translate(substr(empno,1,3),'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',' '))) IS NULL
AND
length(trim(translate(substr(empno,4,4),'0123456789',' '))) IS NULL;
union
select * from bob
where length(trim(translate(substr(ename,1,3),'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',' '))) IS NULL
AND
length(trim(translate(substr(ename,4,4),'0123456789',' '))) IS NULL;

Change Character Allowance

I have a VARCHAR field that only allows 12 characters max. How do I change character allowance to 9 or 15 for example?
Google succeeds in telling me what the max number of characters in VARCHAR in any given version of Oracle database. I know, I get that. I just want to ALTER the column character allowance within that range.
alter table table_name MODIFY (column_to_change varchar(new size))
ALTER TABLE tbl_name MODIFY col_name column_definition;
So if you have:
CREATE TABLE table_name (
value VARCHAR2(12)
);
Then you can do:
ALTER TABLE table_name MODIFY value VARCHAR2(15 BYTE);
and the column will have a capacity of 15 bytes.
Or:
ALTER TABLE table_name MODIFY value VARCHAR2(9 CHAR);
and the column will have a capacity of 9 characters.

Why did SQL*Loader load 808594481 when using the INTEGER data-type?

I was loading data using SQL*Loader and when making the control file I used the table definition and accidentally left the INTEGER data type on the "version" line.
And in the "version" field (data type integer) it inserted the value 808594481.
I'm having a hard time understanding how it processed this value -- I'm assuming it took it as a literal ... but is that the sum of the ASCII representations of each letter?
NOPE!
SELECT ASCII('I')+ascii('N')+ASCII('T')+ASCII('E')+ASCII('G')+ASCII('E')+ASCII('G')+ASCII('E')+ASCII('R')
FROM SYS.DUAL
returns 666 (which, btw is hilarious).
concatenate ascii values?
SELECT ASCII('I')||ascii('N')||ASCII('T')||ASCII('E')||ASCII('G')||ASCII('E')||ASCII('G')||ASCII('E')||ASCII('R')
FROM SYS.DUAL
returns 737884697169716982
I'm hoping someone out there knows the answer.
This is the actual control file:
OPTIONS (SKIP=1)
LOAD DATA
APPEND into table THETABLE
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
(id ,
parent_id ,
record_id ,
version INTEGER,
created_at ,
updated_at ,
created_by ,
updated_by ,
species_and_cohort ,
species_and_cohort_count)
Table DDL:
create table THETABLE
(
id VARCHAR2(36),
parent_id VARCHAR2(36),
record_id VARCHAR2(36),
version INTEGER,
created_at VARCHAR2(25),
updated_at VARCHAR2(25),
created_by VARCHAR2(50),
updated_by VARCHAR2(50),
species_and_cohort VARCHAR2(150),
species_and_cohort_other VARCHAR2(150),
species_and_cohort_count NUMBER
)
Data:
id,parent_id,record_id,version,created_at,updated_at,created_by,updated_by,species_and_cohort,species_and_cohort_other,species_and_cohort_count
60D90F54-C5F2-47AF-951B-27A424EAE8E3,f9fe8a3b-3470-4caf-b0ba-3682a1c79731,f9fe8a3b-3470-4caf-b0ba-3682a1c79731,1,2014-09-23 21:02:54 UTC,2014-09-23 21:02:54 UTC,x#gmail.com,x#gmail.com,"PRCA Cherrylaurel,Sapling","",5
FC6A2120-AA0B-4238-A2F6-A6AEDD9B8202,f9fe8a3b-3470-4caf-b0ba-3682a1c79731,f9fe8a3b-3470-4caf-b0ba-3682a1c79731,1,2014-09-23 21:03:02 UTC,2014-09-23 21:03:02 UTC,x7#gmail.com,x7#gmail.com,"JUVI Eastern Redcedar,Sapling","",45
If you split 808594481 into bytes as it would be encoded in a 32 bit twos complement encoding, and treat each byte as an ascii-encoded character, you get "02,1" or "1,20" depending on byte order. You probably inserted a string that starts or ends with one of those, and some layer between your code and the database silently converted it to an integer.

Insert query showing an error

I created a table in oracle10g using following query......
CREATE TABLE "MOBILELOCATION"
("EMPLOYEEID" NUMBER NOT NULL ENABLE,
"PRESENTDATE" VARCHAR2(30) NOT NULL ENABLE,
"PRESENTTIME" VARCHAR2(30) NOT NULL ENABLE,
"LATITUDE" NUMBER(6,10) NOT NULL ENABLE,
"LONGITUDE" NUMBER(6,10) NOT NULL ENABLE)
Table was created successfully... but the problem is while iam trying to insert a row into the table it is showing the error
error ORA-01438: value larger than specified precision allowed for this column
The query i used is ,
insert into mobilelocation values(12303,'30-10-2011','09:30',16.9876,82.3426);
Where i voilated the constraints?? Please explain any one..
Your columns LATITUDE and LONGTITUDE are defined rather strangely - NUMBER(6,10) says 6 for precision (total number of digits) and 10 for scale (number of digits to the right of the decimal point).
You either change them to NUMBER(*) or to NUMBER (10, 6) or NUMBER(*,4) or similar... the correct declaration is hard to guess but all mentioned would work with the sample data you provided...
For reference see http://download.oracle.com/docs/cd/B19306_01/server.102/b14220/datatype.htm#i16209

How to generate alphanumeric id in Oracle

In my vb application I want an autogenerated id of alphanumeric characters, like prd100. How can I increment it using Oracle as backend?
Any particular reason it needs to be alphanumeric? If it can just be a number, you can use an Oracle sequence.
But if you want just a random string, you could use the dbms_random function.
select dbms_random.string('U', 20) str from dual;
So you could probably combine these 2 ideas (in the code below, the sequence is called oid_seq):
SELECT dbms_random.string('U', 20) || '_' || to_char(oid_seq.nextval) FROM dual
There are two parts to your question. The first is how to create an alphanumeric key. The second is how to get the generated value.
So the first step is to determine the source of the alpha and the numeric components. In the following example I use the USER function and an Oracle sequence, but you will have your own rules. I put the code to assemble the key in a trigger which is called whenever a row is inserted.
SQL> create table t1 (pk_col varchar2(10) not null, create_date date)
2 /
Table created.
SQL> create or replace trigger t1_bir before insert on t1 for each row
2 declare
3 n pls_integer;
4 begin
5 select my_seq.nextval
6 into n
7 from dual;
8 :new.pk_col := user||trim(to_char(n));
9 end;
10 /
Trigger created.
SQL>
The second step requires using the RETURNING INTO clause to retrieve the generated key. I am using SQL*PLus for this example. I confess to having no idea how to wire this syntax into VB. Sorry.
SQL> var new_pk varchar2(10)
SQL> insert into t1 (create_date)
2 values (sysdate)
3 returning pk_col into :new_pk
4 /
1 row created.
SQL> print new_pk
NEW_PK
--------------------------------
APC61
SQL>
Finally, a word of warning.
Alphanumeric keys are a suspicious construct. They reek of "smart keys" which are, in fact, dumb. A smart key is a value which contains multiple parts. At somepoint you will find yourself wanting to retrieving all rows where the key starts with 'PRD', which means using SUBSTR() or LIKE. Even worse someday the definition of the smart key will change and you will have to cascade a complicated update to your table and its referencing foreign keys. A better ides is to use a surrogate key (number) and have the alphanumeric "key" defined as separate columns with a UNIQUE composite constraint to enforce the business rule.
SQL> create table t1 (id number not null
2 , alpha_bit varchar2(3) not null
3 , numeric_bit number not null
4 , create_date date
5 , constraint t1_pk primary key (id)
6 , constraint t1_uk unique (alpha_bit, numeric_bit)
7 )
8 /
Table created.
SQL>

Resources