Prevent Duplicate Entries in SQL Application - sql

I have an application that's running on two different machines.
The use of the application is pretty simple, we scan a product and it associates the product_id and creates a Unique_ID that's auto-incremental.
Ex: U00001 then the next is U00002
My problem is while both the machines are running, sometimes the Unique_ID is the same for two different products. It's like the creation of the Unique_ID happens at the same time so it duplicates the entry.
What's the best approach for this? Is it a connection problem?

You need a SEQUENCE or IDENTITY column, and then a computed column concatenates the U onto it
CREATE TABLE YourTable (
ID int IDENTITY PRIMARY KEY,
product_id varchar(30),
Unique_ID AS FORMAT(ID, '"U"0000)
)
Or
CREATE SEQUENCE YourTable_IDs AS int START WITH 1 INCREMENT BY 1 MAXVALUE 9999;
CREATE TABLE YourTable (
ID int PRIMARY KEY DEFAULT (NEXT VALUE FOR YourTable_IDs),
product_id varchar(30),
Unique_ID AS FORMAT(ID, '"U"0000)
)

Related

Creating primary key with a static element and an auto-incrementing element without making a composite primary key

The issue
I have two tables. Both will have auto incrementing ID columns which will act as their respective primary key columns.
Table 1 has its ID column auto incrementing beginning with 1 and increasing by 1 for each entry. For reference I have used IDENTITY (1,1) for this.
I would like table two's ID column to behave in the same way but also with a static text/number prefix i.e. M1, M2, M3 or M00001, M00002, M00003 etc.
All of the resources I have found seem to involve the use of a composite primary key; I would like to avoid this.
Additional info
From my reading I have come away with the impression that this method may not be the best or right way to differentiate the primary keys of multiple tables within a database, however, I am struggling to find resources/examples on the best or most common methods to do this. I have explored using composite keys, calculated fields, UUIDs, Hi/Lo algorithm ids and I'm not sure what is the right way to proceed. For context, this is not a big or complicated database.
Even just a link to a good resource on this issue will help me greatly.
In the second table, instead of modifying the index, you can leave it and add a second column with the calculated value. Below is a complete example (I'm using a tabular variable but it looks the same in a normal table) of such a field in the table.
declare #tab as table
(
id int primary key identity(1, 1),
id2 as concat('M', format(id, '00000#')),
someColumn nvarchar(10)
)
insert into #tab (someColumn) values
(N'test 1'), ('test 2'), (N'test 3');
select * from #tab;
The result os the last query is
As you can see the id2 is automatically calculated based on the value of the id index.
If you want to store the id2 then you have to modify the table by adding PERSISTED like in code below
declare #tab as table
(
id int primary key identity(1, 1),
id2 as concat('M', format(id, '00000#')) PERSISTED,
someColumn nvarchar(10)
)
You can find more info about calculated columns here.
There is no suitable solution.
But the following query can be used
IF Not Exists(Select * From sys.tables t where t.name='ids')
Begin
Create Table ids(id int)
Insert into ids values(0)
End
Go
IF Not Exists(Select * From sys.tables t where t.name='tbltextIdentity')
Create Table tbltextIdentity (id varchar(50) primary key ,someColumn nvarchar(10))
Go
--insert into tbltextIdentity values('M000001','Test1')
UPDATE Ids SET Id = id +1 OUTPUT concat('M', format(INSERTED.Id, '00000#')), 'Test1' INTO tbltextIdentity
Go
Select * From tbltextIdentity

Oracle SQL: "GENERATED ALWAYS" with a specified sequence

I have two tables that I would like to let them share the same sequence to populate the primary key ID column. However, I also don't want the user to specify or change the value for the ID column.
By using the code below, I can let two tables share the same sequence.
CREATE TABLE T1
(
ID INTEGER DEFAULT SEQ_1.nextval NOT NULL
);
This code will use its own sequence and prevent users from changing or specifying with INSERT:
CREATE TABLE T1
(
ID INTEGER GENERATED ALWAYS AS IDENTITY NOT NULL
);
Is there a way that can both world? Something like this:
CREATE TABLE T1
(
ID INTEGER GENERATED ALWAYS AS ( SEQ_1.nextval ) NOT NULL
);
Regarding the use case, as #Sujitmohanty30 asked, the reason that I raised this question:
I'm thinking to implement inheritance in the database, consider this UML diagram (I can't directly post images due to insufficient reputation, and sorry for being lack of imagination).
ANIMAL is abstract and all inheritance is mandatory. This means no instance of ANIMAL should be created. Furthermore, there is an one-to-many relationship between ANIMAL and ZOO_KEEPER.
Therefore, I came up with this idea:
CREATE SEQUENCE ANIMAL_ID_SEQ;
CREATE TABLE HORSE
(
ID INT DEFAULT ANIMAL_ID_SEQ.nextval NOT NULL PRIMARY KEY,
HEIGHT DECIMAL(3, 2) NOT NULL
);
CREATE TABLE DOLPHIN
(
ID INT DEFAULT ANIMAL_ID_SEQ.nextval NOT NULL PRIMARY KEY,
LENGTH DECIMAL(3, 2) NOT NULL
);
CREATE MATERIALIZED VIEW LOG ON HORSE WITH ROWID;
CREATE MATERIALIZED VIEW LOG ON DOLPHIN WITH ROWID;
CREATE MATERIALIZED VIEW ANIMAL
REFRESH FAST ON COMMIT
AS
SELECT 'horse' AS TYPE, ROWID AS RID, ID -- TYPE column is used as a UNION ALL marker
FROM HORSE
UNION ALL
SELECT 'dolphin' AS TYPE, ROWID AS RID, ID
FROM DOLPHIN;
ALTER TABLE ANIMAL
ADD CONSTRAINT ANIMAL_PK PRIMARY KEY (ID);
CREATE TABLE ZOO_KEEPER
(
NAME VARCHAR(50) NOT NULL PRIMARY KEY,
ANIMAL_ID INT NOT NULL REFERENCES ANIMAL (ID)
);
In this case, the use of the shared sequence is to avoid collision in ANIMAL mview. It uses DEFAULT to get the next ID of the shared sequence. However, using DEFAULT doesn't prevent users from manually INSERTing the ID field or UPDATE the value of it.
You can create a master view/table and generate the sequence in it.
Then copy it as column values into both tables while inserting.
Another option could be inserting into both tables at same time.Use SEQ.NEXTVAL to insert into first table to get a new ID, and then SEQ.CURRVAL to copy same id in the table.
No, you cant have anything like this because ID is independently generated for each of the tables and this can be done only using sequence when you are inserting the data in both the tables at the same time.
You should normalize your data schema: add column animal_type into the table and create composite primary key on both columns

Making sure Identity column doesn't allow change in another value

I have an identity column known as RowId which is needed. However, I have an issue where I am seeing multiple instances of that same RowID 5-10 times where the customerNumber changes and that should not be allowed. For instance:
RowID: CustomerNumber:
2556892 25231564656522
2556892 25232264686453
How can I make sure my table does not allow the CustomerNumber to change? The point of having an identity column is to have different RowId's when a value is added such as a new CustomerNumber. Somehow instead of the new CustomerNumber getting a new rowId it is being modified and they are changing the CustomerNumber. How can this be prevented?
It sounds like you need a new table for customers:
create table Customers as (
CustomerId int identity(1, 1) primary key,
CustomerNumber varchar(20) unique -- or whatever
-- add more columns about customers
);
Voila! You have only one CustomerId per CustomerNumber. This is appropriate for foreign key relationships. You can then remove CustomerNumber from your current table, rename rowid to CustomerId (which is more descriptive), and define an appropriate foreign key relationship.

Prevent duplicate key usage

Database is Postgresql. For an simplified example I will insert measurement data in various tables. Example DDL for one example table looks like this:
CREATE TABLE
measurement
(
id_meas BIGINT NOT NULL,
...
PRIMARY KEY (id_meas)
);
The process of inserting data currently works like this:
Select max id value from table
Increment id value
Insert next data row using incremented id value
This works as long as there is only one client inserting data. But what if there are > 1 client's inserting so that two clients may select 567 as max id value and both increment this to 568 as next id value to insert. In that case the second client executing the insert command will receive an duplicate key error. Is there a way to prevent those errors other than re-executing the insertion process after an error occurred?
You are looking for a serial column:
CREATE TABLE measurement (
id_meas bigserial primary key,
...
);
bigserial is a bigint that auto-increments (see here). You can also just use serial if an int is big enough.
This puts the database in charge of incrementing the value, rather than the application. You are guaranteed that race conditions will not result in the same value in different records. It is possible that gaps in the value will appear under some circumstances.

Serial numbers per group of rows for compound key

I am trying to maintain an address history table:
CREATE TABLE address_history (
person_id int,
sequence int,
timestamp datetime default current_timestamp,
address text,
original_address text,
previous_address text,
PRIMARY KEY(person_id, sequence),
FOREIGN KEY(person_id) REFERENCES people.id
);
I'm wondering if there's an easy way to autonumber/constrain sequence in address_history to automatically count up from 1 for each person_id.
In other words, the first row with person_id = 1 would get sequence = 1; the second row with person_id = 1 would get sequence = 2. The first row with person_id = 2, would get sequence = 1 again. Etc.
Also, is there a better / built-in way to maintain a history like this?
Don't. It has been tried many times and it's a pain.
Use a plain serial or IDENTITY column:
Auto increment table column
CREATE TABLE address_history (
address_history_id serial PRIMARY KEY
, person_id int NOT NULL REFERENCES people(id)
, created_at timestamp NOT NULL DEFAULT current_timestamp
, previous_address text
);
Use the window function row_number() to get serial numbers without gaps per person_id. You could persist a VIEW that you can use as drop-in replacement for your table in queries to have those numbers ready:
CREATE VIEW address_history_nr AS
SELECT *, row_number() OVER (PARTITION BY person_id
ORDER BY address_history_id) AS adr_nr
FROM address_history;
See:
Gap-less sequence where multiple transactions with multiple tables are involved
Or you might want to ORDER BY something else. Maybe created_at? Better created_at, address_history_id to break possible ties. Related answer:
Column with alternate serials
Also, the data type you are looking for is timestamp or timestamptz, not datetime in Postgres:
Ignoring time zones altogether in Rails and PostgreSQL
And you only need to store previous_address (or more details), not address, nor original_address. Both would be redundant in a sane data model.