Suitable Primary key type in oracle - oracle

What is the difference if we make primary key as
varchar2
byte[] <-18
How it will impact the searching in a table.

If you mean what is the difference between varchar2(18 char) or varchar2(18 byte), it won't mean anything from the perspective of the primary key.
If you define the field as VARCHAR2(18 BYTE), Oracle can use up to 18 bytes for storage, but you may not actually be able to store 18 characters in the field, because some of them take more than one byte to store, e.g. non-English characters, ascii extended characters.
In the primary key, an automatic unique index is associated to it. So, with a character set as UTF-8, an index might occupy more space when using char than byte. But, normally speaking, it will have no relation whatsoever with either using char or byte.
Keep in mind that if you use multibyte charactersets as UTF-8, then the space consumed by the data types will be greater when you use CHAR instead of BYTE as NLS_SEMANTICS.
So, the greater the storage, the bigger the index, then more time/cpu consuming when you access to it.
Hope it helps.
Regards

Related

How to define prefix index as primary key in liquibase?

I have a table in which the primary key value can be long.
When I try to redefine the primary column length, I get the following error
liquibase.exception.DatabaseException: Specified key was too long; max key length is 3072 bytes
I'm guessing the above error is due to MySQL's default max index size, so it seems like my next best option atm is to define this primary key with a prefix index (or maybe expand the max index length here somehow).
Is there any way to do that with liquibase?
I don't know how to get Liquibase to define a primary key with an index prefix, but it won't help to use an index prefix anyway.
https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-keys.html says:
The object key name is a sequence of Unicode characters with UTF-8 encoding of up to 1,024 bytes long.
Thus it's between 255 and 1024 characters, depending on how many of the characters are multibyte.
But MySQL index prefix syntax measures length in characters, not bytes.
So you would need to define an index prefix length of 1024 to handle S3 keys, but that's too long for a MySQL index definition.
You must define a different column as the primary key in this table. For example, you could use an MD5() hash of the S3 object key name, and store that in a CHAR(32) or BINARY(16).

Max character data which can store CLOB and NCLOB type

Quote from documentation
The LOB datatypes for character data are CLOB and NCLOB. They can store up
to 8 terabytes of character data (CLOB) or national character set data (NCLOB).
and this is another quote from same page:
The CLOB and NCLOB datatypes store up to 128 terabytes of character data in the database. CLOBs store database character set data, and NCLOBs store Unicode national character set data.`
I am little confused, there is some misunderstanding in documentation or i miss something?
The difference stems from the fact that you can define LOBs with different "chunk" sizes. Plus their maximum size is limited by the number of database blocks used for them. If you create a database (or tablespace) with a larger blocksize this means a LOB can contain more data.
From the manual:
CLOB objects can store up to (4 gigabytes -1) * (the value of the CHUNK parameter of LOB storage) of character data
And the next sentence describes the relation to the blocksize:
If the tablespaces in your database are of standard block size, and if you have used the default value of the CHUNK parameter of LOB storage when creating a LOB column, then this is equivalent to (4 gigabytes - 1) * (database block size).

Oracle LOB Not getting stored inline

Setup
I have an oracle table that has couple of attributes and a CLOB datatype. The table below I have create with two ways ,each of which should give the same behavior.
CREATE TABLE DEMO(
a number (10, 2),
data CLOB
)
CREATE TABLE DEMO(
a number (10, 2),
data CLOB
) LOB (data) Stored AS (STORAGE IN ROW ENABLED)
Scenario
As per the oracle documentation when the CLOB is greater the 4000 bytes it will be stored outline else inline.
When I store the data in this table for a clob value say "Hello" and then I see the segment information for the "Demo table" and "Demo table LOB segment" , it shows that all the data is going to table and no new blocks are being consumed in the Lob Segment.
When I store a bigger data with total character less than 1500 , then also I get the same behavior as above.
But when I store a data with total character > 2000 and < 3000 , then the LOB data is going to the LOB segment even though total character are less than 3000.
Question
Why is the data smaller than 3000 characters is going to the LOB Segment ? . Is that each character takes 2 bytes , which justifies that data till 1500 is going to the data instead of Log Segment.
Problem
Lots of disk space is getting wasted because of the LOB Table , since the CHUNK size is 8kb and the data per block will always be around 3 - 4K character and in some cases exceeding that. So essential for each row 4Kb space is wasted and in out case of 20 mn rows , its running in 50's of GBs
This may explain the above behaviour..
"The CLOB and NCLOB datatypes store up to 4 gigabytes of character data in the database. CLOBs store database character set data and NCLOBs store Unicode national character set data. For varying-width database character sets, the CLOB value is stored in the database using the two-byte Unicode character set, which has a fixed width. Oracle translates the stored Unicode value to the character set requested on the client or on the server, which can be fixed-width or varying width. When you insert data into a CLOB column using a varying-width character set, Oracle converts the data into Unicode before storing it in the database."
http://docs.oracle.com/cd/B10500_01/server.920/a96524/c13datyp.htm#3234

How to store unlimited characters in Oracle 11g?

We have a table in Oracle 11g with a varchar2 column. We use a proprietary programming language where this column is defined as string. Maximum we can store 2000 characters (4000 bytes) in this column. Now the requirement is such that the column needs to store more than 2000 characters (in fact unlimited characters). The DBAs don't like BLOB or LONG datatypes for maintenance reasons.
The solution that I can think of is to remove this column from the original table and have a separate table for this column and then store each character in a row, in order to get unlimited characters. This tble will be joined with the original table for queries.
Is there any better solution to this problem?
UPDATE: The proprietary programming language allows to define variables of type string and blob, there is no option of CLOB. I understand the responses given, but I cannot take on the DBAs. I understand that deviating from BLOB or LONG will be developers' nightmare, but still cannot help it.
UPDATE 2: If maximum I need is 8000 characters, can I just add 3 more columns so that I will have 4 columns with 2000 char each to get 8000 chars. So when the first column is full, values would be spilled over to the next column and so on. Will this design have any bad side effects? Please suggest.
If a blob is what you need convince your dba it's what you need. Those data types are there for a reason and any roll your own implementation will be worse than the built in type.
Also you might want to look at the CLOB type as it will meet your needs quite well.
You could follow the way Oracle stored their stored procedures in the information schema. Define a table called text columns:
CREATE TABLE MY_TEXT (
IDENTIFIER INT,
LINE INT,
TEXT VARCHAR2 (4000),
PRIMARY KEY (INDENTIFIER, LINE));
The identifier column is the foreign key to the original table. The Line is a simple integer (not a sequence) to keep the text fields in order. This allows keeping larger chunks of data
Yes this is not as efficient as a blob, clob, or LONG (I would avoid LONG fields if at all possible). Yes, this requires more mainenance, buf if your DBAs are dead set against managing CLOB fields in the database, this is option two.
EDIT:
My_Table below is where you currently have the VARCHAR column you are looking to expand. I would keep it in the table for the short text fields.
CREATE TABLE MY_TABLE (
INDENTIFER INT,
OTHER_FIELD VARCHAR2(10),
REQUIRED_TEXT VARCHAR(4000),
PRIMERY KEY (IDENTFIER));
Then write the query to pull the data join the two tables, ordering by LINE in the MY_TEXT field. Your application will need to split the string into 2000 character chunks and insert them in line order.
I would do this in a PL/SQL procedure. Both insert and select. PL/SQL VARCHAR strings can be up to 32K characters. Which may or may not be large enough for your needs.
But like every other person answering this question, I would strongly suggest making a case to the DBA to make the column a CLOB. From the program perspective this will be a BLOB and therefore simple to manage.
You said no BLOB or LONG... but what about CLOB? 4GB character data.
BLOB is the best solution. Anything else will be less convenient and a bigger maintenance annoyance.
Is BFILE a viable alternative datatype for your DBAs?
I don't get it. A CLOB is the appropriate database datatype. If your weird programming language will deal with strings of 8000 (or whatever) characters, what stops it writing those to a CLOB.
More specifically, what error do you get (from Oracle or your programming language) when you try to insert an 8000 character string into a column defined as a CLOB.

Why does Char(1) change to Char(3) when copying over an Oracle DBLINK?

I have 2 databases, and I want to transport an existing table containing a CHAR column from database A to database B.
Database A is Oracle 9i, has encoding WE8ISO8859P1, and contains a table "foo" with at least 1 column of type CHAR(1 char). I can not change the table on database A because it is part of a third party setup.
Database B is my own Oracle 10g database, using encoding AL32UTF8 for all kinds of reasons, and I want to copy foo into this database.
I setup a database link from database B to database A. Then I issue the following command:
*create table bar as select * from #link#.foo;*
The data gets copied over nicely, but when I check the types of the columns, I notice that CHAR(1 char) has been converted into CHAR(3 char), and when querying the data in database B, it is all padded with spaces.
I think somewhere underwater, Oracle confuses it's own bytes and chars. CHAR(1 byte) is different from CHAR(1 char) etc. I've read about all that.
Why does the datatype change into a padded CHAR(3 char) and how do I stop Oracle from doing this?
Edit: It seems to have to do with transfering CHAR's between two specific patchlevels of Oracle 9 and 10. It looks like it is really a bug. as soon as I find out I'll post an update. Meanwhile: don't try to move CHAR's between databases like I described. VARCHAR2 works fine (tested).
Edit 2: I found the answer and posted it here: Why does Char(1) change to Char(3) when copying over an Oracle DBLINK?
Too bad I can not accept my own answer, because my problem is solved.
This problem is caused by the way Oracle (mis)handles character conversions between different character sets based on the original column length definition. When you define the size of a character type column in bytes, Oracle does not know how to do a conversion and bodges it. The solution is to always define the length of a character type in characters.
For a more in-depth explanation of the problem and how I figured this out have a look at
http://www.rolfje.com/2008/11/04/transporting-oracle-chars-over-a-dblink/
YOu need to learn the difference between the WE8ISO8859P1 NLS (which stores characters in one byte) and the AL32UTF8 which stores characters in up to four bytes. You will need to spend some quality time with the Oracle National Language Support (NLS) Documentation. Oracle automatically does the conversion through the database link, in an attempt to be helpful.
Try the following from your SQL prompt:
ALTER SESSION NLS_NCHAR WE8ISO8859P1
create table bar as select * from #link#.foo;
The first thing I would try is Creating the table NOT as a CTAS but with a list of column definitions and try to perform an insert of the first few thousand rows. If that didn't succeed then it would be very clear why... and you'd have quick confirmation that Thomas Low is dead on accurate.

Resources