Do I need a primary key with an Oracle identity column? - oracle

I am using Oracle 12c and I have an IDENTITY column set as GENERATED ALWAYS.
CREATE TABLE Customers
(
id NUMBER GENERATED ALWAYS AS IDENTITY,
customerName VARCHAR2(30) NULL,
CONSTRAINT "CUSTOMER_ID_PK" PRIMARY KEY ("ID")
);
Since the ID is automatically from a sequence it will be always unique.
Do I need a PK on the ID column, and if yes, will it impact the performance?
Would an index produce the same result with a better performance on INSERT?

No, you don't need a primary key necessarily, but you should always provide the optimiser as much information about your data as possible - including a unique constraint whenever possible.
In the case of a surrogate key (like your ID), it's almost always appropriate to declare it as a Primary Key, since it's the most likely candidate for referential constraints.
You could use an ordinary index on ID, and performance of lookups will be comparable depending on data volume - but there is virtually no good reason in this case to use a non-unique index instead of a unique index - and there is no good reason in this case to avoid the constraint which will require an index anyway.

Yes, you always should have a Uniqueness constraint (with rare exceptions) on an ID column if indeed it is (and should be) unique, regardless of the method by which it is populated - whether the value is provided by your application code or via an IDENTITY column.

Related

Is it bad practice to update part of primary key?

I have the pretty bad headache about primary key based on the three rows:
ID DATE_START DATE_END
They describes the version of object. Version of object is the combination of ID, D_S, D_E and it has no any breaks inside of timeline.
There is a few states of object:
State 1 (latest version):
ID DATE_START DATE_END
1 01-01-2022 00:00:00 31-12-9999 00:00:00
State 2 (insert new version)
ID DATE_START DATE_END
1 01-01-2022 00:00:00 01-02-2022 23:59:59
1 02-02-2022 00:00:00 31-12-9999 00:00:00
So you can see each time user creates new version, we should update DATE_END on previous, like this:
DATE_END = DATE_START - 1 Second
I'm almost sure that updating part of PK is the bad practice. I'm on to drop DATE_END from PK at all, in case of there's no any breaks inside timeline.
But my colleagues try to convince me that we should have DATE_END - it's pretty slow to search version on some date cuz we should compare DATE_START of each row against to comparing DATE_START and DATE_END in one row.
Could someone explain me who's wrong and what is the best solution in that case?
You could only store the start date and calculate the end-date; or you could store the end date and have to UPDATE the last record.
The first option incurs greater relative cost when you SELECT the output and a lower cost on INSERT;
The second option incurs a greater relative cost when you INSERT (and UPDATE) and a lower cost on SELECT.
So if all you are doing is INSERTing and SELECTing then you can look at which option you do more and prioritise the lower-cost option for that.
However, if the primary key is also the target of referential constraints then your costs are exponentially higher for having to modify an existing primary key are you will also have to modify all the columns referenced through the constraints and that will quickly become a nightmare.
What you should do is divorce the primary key used in referential constraints from the date columns that are going to be updated and use a surrogate primary key:
CREATE TABLE table_name (
key NUMBER
GENERATED ALWAYS AS IDENTITY
PRIMARY KEY,
id NUMBER
NOT NULL,
start_date DATE
NOT NULL,
end_date DATE
NOT NULL,
CONSTRAINT table_name__id__sd__ed__unique UNIQUE (id, start_date, end_date)
);
or:
CREATE TABLE table_name (
key NUMBER
GENERATED ALWAYS AS IDENTITY
PRIMARY KEY,
id NUMBER
NOT NULL,
start_date DATE
NOT NULL,
CONSTRAINT table_name__id__sd__ed__unique UNIQUE (id, start_date)
);
Then you do not need to modify any referential constraints and you can evaluate whether you want to have greater costs during INSERTion or SELECTion.
Whichever of those two you prioritise, that decision is a business decision and there is no "best" choice as what might work in one situation will not work for another situation.
I'd say that modifying primary key really is bad practice. Mind you, primary keys often are referenced by foreign keys; even if you tried to update such a primary key (referenced by other foreign key(s)), you won't be able to do it because Oracle will complain that - most probably - parent key doesn't exist. What will you do then? Disable foreign key constraint, update primary key value, update foreign key(s), enable foreign key constraints and hope that nobody/nothing did something that will violate referential integrity during that time (with disabled foreign keys).
So, why wouldn't you rather use a surrogate primary key? In modern Oracle database versions, use an identity column. If your Oracle database doesn't support it, use a sequence. Never modify its value, reference it from other tables and maintain referential integrity. Then, if you want, update those end dates, if you must, as it won't break anything.

Is primary key by default indexed in oracle

I have a table with a long value as primary key.
Now i think that oracle by default will create a index on it.And i dont need to
create a index explicityly.
The question is :Is primary key by default indexed by oracle in this case?
Yes, a primary key (or any unique column constraint) will create an index, if there is not already one present.
This is the case for almost all databases. Otherwise the uniqueness constraint cannot be efficiently enforced.

How to generate PHYSICALLY sorted GUID as primary key in Oracle?

I'm trying to generate a sorted GUID as a primary key in Oracle. In SQL Server I could use one of the following to sort the rows physically
By clustered primary key as a unique identifier.
By NEWSEQUENTIALID.
I have searched for an Oracle equivalent but failed to find a solution. I know about Is there a way to create an auto-incrementing Guid Primary Key in an Oracle database?, but there's no indication whether SYS_GUID() is sorted.
How could I create a sequential primary key in Oracle?
If you want to create a GUID then SYS_GUID() is what you should be using, you can create this in a table as per the linked question. It's unclear from the documentation whether SYS_GUID() is incrementing. It might be but that's not really a statement that imparts trust.
The next part of your question (and some comments) keeps asking about clustered primary keys. This concept does not exist in the same way in Oracle as it does in SQL Server and Sybase. Oracle does have indexed organized tables (IOT) ...
... a table stored in a variation of a B-tree index structure... rows
are stored in an index defined on the primary key for the table. Each
index entry in the B-tree also stores the non-key column values. Thus,
the index is the data, and the data is the index.
There are plenty of uses for IOTs but it's worth bearing in mind you're altering the physical structure of the database on the disk for "performance reasons". You're doing the ultimate of all premature optimizations using something that has both negative and positive aspects. Read the documentation and be sure that this is what you want to do.
I would generally use an IOT only when you don't care about DML performance but when you do a lot of range scans, or you need to order by the primary key. You create an IOT in the same way as you would an ordinary table, but because everything you want is now part of the table everything goes in your table definition:
create table test_table (
id raw(32) default sys_guid()
, a_col varchar2(50)
, constraint pk_my_iot primary key (id)
) organization index;
It's worth noting that even with an IOT you must use an explicit ORDER BY in order to guarantee returned order. However, because of the way this is stored Oracle can table a few short cuts:
select *
from ( select *
from test_table
order by id )
where rownum < 2
SQL Fiddle.
As with everything, test, don't assume that this is the structure you want.
Oracle has as SYS_GUID() function, which generates a 16-byte RAW datatype. But, I'm not sure what you mean by "sorted GUID". Can you elaborate?
Do you mean you need each generated GUID to sort "after" the previously generated GUID? I looked at the SYS_GUID() function, and it seems to generate GUIDs in sorted order, but looking at the documentation, I don't see anything that says that is guaranteed.
If I understand your question correctly, I'm not sure it's possible.
You may be able to use SYS_GUID() and prepend a sequence, to get your desired sort order?
Can you explain more about your use case?
Adding the following in response to comment:
Ok, now I think I understand. What I think you want, is something called an IOT, or Index Organized Table, in Oracle. It's a table that has an index strucure, and all data is clustered, or grouped by the primary key. More information is available here:
http://docs.oracle.com/cd/E16655_01/server.121/e17633/indexiot.htm#CNCPT721
I think that should do what you want.

Should I index primary key column(s) in Oracle

I've recently stopped to think that Primary Keys are not indexes, they're a combination of Unique and Null constraints. And till now, I've never created index for PK columns. My question is if I should create index for PK columns if this column is going to be used in the WHERE part of many queries.
Oracle will create an index for you, or can use an existing one. Whether a unique or non-unique index is used is up to you.
http://docs.oracle.com/cd/B28359_01/server.111/b28310/indexes003.htm#i1006566
A primary key itself is not an index, and nor is a unique constraint -- they are both constraints. However an index is used to support them.
A unique index is rather different as it can exist in the absence of a unique or primary key constraint, and neither constraint type require that the index supporting it be unique.

SQL Timeout and indices

Changing and finding stuff in a database containing a few dozen tables with around half a million rows in the big ones I'm running into timeouts quite often.
Some of these timeouts I don't understand. For example I got this table:
CREATE TABLE dbo.[VPI_APO]
(
[Key] bigint IDENTITY(1,1) NOT NULL CONSTRAINT [PK_VPI_APO] PRIMARY KEY,
[PZN] nvarchar(7) NOT NULL,
[Key_INB] nvarchar(5) NOT NULL,
) ON [PRIMARY]
GO
ALTER TABLE dbo.[VPI_APO] ADD CONSTRAINT [IX_VPI_APOKey_INB] UNIQUE NONCLUSTERED
(
[PZN],
[Key_INB]
) ON [PRIMARY]
GO
I often get timeouts when I search an item in this table like this (during inserting high volumes of items):
SELECT [Key] FROM dbo.[VPI_APO] WHERE ([PZN] = #Search1) AND ([Key_INB] = #Search2)
These timeouts when searching on the unique constraints happen quite often. I expected unique constraints to have the same benefits as indices, was I mistaken? Do I need an index on these fields, too?
Or will I have to search differently to benefit of the constraint?
I'm using SQL Server 2008 R2.
A unique constraint creates an index. Based on the query and the constraint defined you should be in a covering situation, meaning the index provides everything the query needs without a need to back to the cluster or the heap to retrieve data. I suspect that your index is not sufficiently selective or that your statistics are out of date. First try updating the statistics with sp_updatestats. If that doesn't change the behavior, try using UPDATE STATISTICS VPI_APO WITH FULL SCAN. If neither of those work, you need to examine the selectivity of the index using DBCC SHOW_STATISTICS.
My first thought is parameter sniffing.
If you're on SQL Server 2008, try "OPTIMIZE FOR UNKNOWN" rather than parameter masking
2nd thought is change the unique constrant to an index and INCLUDE the Key column explicitly. Internally they are the same, but as an index you have some more flexibility (eg filter, include etc)

Resources