Auto increment issues postgresql - sql

Facing some issues with the auto-increment property in postgresql
I created a table say emp
create table emp
( empid serial,
empname varcha(50),
primary key (empid)
);
I inserted one value with empid as blank:
insert into emp (empname) values ('test1');
Next insert by specifying the empid value:
insert into emp (empid,empname) values (2,'test2');
Now, the next time if I insert a value without specifying the empid value, it will give an error because it will try to insert the empid as 2:
insert into emp (empname) values ('test3');
ERROR: duplicate key value violates unique constraint "emp_pkey"
DETAIL: Key (empid)=(2) already exists.
Can someone help me with a workaround for this issue so that with or without specifying a value, the autoincrement should pick up the max(value) +1 ??
Thanks

You can't cleanly mix use of sequences and fixed IDs. Inserting values without using the sequence won't update the sequence, so you'll get collisions.
If you're doing your manual insertions in a bulk load phase or something you can:
BEGIN
LOCK TABLE the_table IN ACCESS EXCLUSIVE MODE
Do your INSERTs
SELECT setval('seq_name', 14), replacing 14 with the new sequence value. This can be a subquery against the table.
COMMIT
... but in general, it's better to just avoid mixing sequence ID generation and manually assigned IDs.

Related

postgres doesn't autogenerate PK if the PK is inserted manually

I have a simple table like this:
CREATE TABLE IF NOT EXISTS myval
(
id integer NOT NULL DEFAULT nextval('myval_myval_id_seq'::regclass),
name character varying(255),
CONSTRAINT "PK_aa671c3359a0359082a84ecb801" PRIMARY KEY (id)
)
the sequence definition is:
CREATE SEQUENCE IF NOT EXISTS myval_myval_id_seq
INCREMENT 1
START 1
MINVALUE 1
MAXVALUE 2147483647
CACHE 1
OWNED BY myval.myval_id;
when I insert data along with the primary key:
INSERT INTO myval(id, name) VALUES (1, 'sdf');
INSERT INTO myval(id, name) VALUES (2, 'sdf');
INSERT INTO myval(id, name) VALUES (3, 'sdf');
INSERT INTO myval(id, name) VALUES (4, 'sdf');
then, I insert it without the PK:
INSERT INTO myval(name) VALUES ('new sdf');
it gives an error saying:
duplicate key value violates unique constraint "PK_aa671c3359a0359082a84ecb801",
DETAIL: Key (myval_id)=(1) already exists.
I expected it to start with PK value of 5 but, instead it gives an error. Can we configure postgres to skip conflicting values and generate from the closest available value to use instead of throwing an error?
The best way to avoid such conflicts is to use identity columns - in this case a GENERATED ALWAYS AS IDENTITY seems the right option.
CREATE TABLE IF NOT EXISTS myval
(
id integer GENERATED ALWAYS AS IDENTITY,
name character varying(255),
CONSTRAINT "PK_aa671c3359a0359082a84ecb801" PRIMARY KEY (id)
);
This will work like a sequence (serial), however it will fail if the user tries to manually insert a value in this column
INSERT INTO myval (id,name)
VALUES (1,'foor');
ERROR: cannot insert a non-DEFAULT value into column "id"
DETAIL: Column "id" is an identity column defined as GENERATED ALWAYS.
TIP: Use OVERRIDING SYSTEM VALUE to override.
If for whatever reason you must override this behavior in a certain INSERT statement you can do so using OVERRIDING SYSTEM VALUE, as the error message above suggests
INSERT INTO myval (id,name) OVERRIDING SYSTEM VALUE
VALUES (1,'foo');
You might be able to achieve a sequential value using serial even if the user screws things up with inserts, e.g. using trigger functions. But such an architecture is hard to maintain and imho is definitely not worth the trouble.
Demo: db<>fiddle

ODP.Net, Entity Framework: ORA-00947 (not enough values) when trying to add a record with Store Generated Identity

I have simple table where I want to insert new records.
The table has an ID column which is set as identity and generated always.
StoreGeneratedPattern=Identity is set for the ID column.
When I try to add a new record to the table in VB.Net
rec = New TEST_TABLE
ctx.TEST_TABLE.Add(rec)
ctx.SaveChanges()
It results in get ORA-00947: not enough values.
It seems as if the Entity Framework is creating an invalid SQL request. Something like INSERT INTO TEST_TABLE (ID, NAME) VALUES('Tom');.
How can I solve this situation?
How can I check, which SQL request is sent to Oracle?
Adding lines with direct sql queries works perfectly:
INSERT INTO TEST_TABLE (NAME) VALUES('Tom');
1 line affected.
The table's schemata is as follows:
CREATE TABLE "TEST_TABLE"
("NAME" VARCHAR2(20) NOT NULL ENABLE,
"ID" NUMBER GENERATED ALWAYS AS IDENTITY
MINVALUE 1 MAXVALUE 999999999999999999999999999 INCREMENT BY 1 START WITH 1 NOORDER NOCYCLE NOT NULL ENABLE,
CONSTRAINT "TEST_TABLE_PK" PRIMARY KEY ("ID") ENABLE
);
CREATE UNIQUE INDEX "TEST_TABLE_PK" ON "TEST_TABLE" ("ID");
Edit: I solved the problem: StoreGeneratedValue=Identity was set for NAME, not for ID. After correcting this, everything just works fine.
"Not enough values" comes when there are too few values passed on insert statement. Like this
insert into table_name(col1, col2, col3) values(1, 2)
I've passed 2 values whereas there are 3 columns listed in the beginning of insert. Thus, I almost sure, underlying insert statement lacks of a value.
You need to check content of inserting routine on VB side if posiible.
As a workaround I'd try to replace "GENERATED ALWAYS" with "GENERATED BY DEFAULT" and run the VB code with StoreGeneratedPattern=None

Is there a way to prevent a query from setting the serial primary key?

I've got a bunch of tables with the 'serial' keyword on a primary key so that auto-increment will work. The problem is that I can make a query to insert a row using any id number which overrides the auto-increment. Is there a reason for this? Or, is there a way to prevent a user from adding/changing this value?
Here's an example of my table config:
create table if not exists departments (
department_id serial primary key,
name varchar(64) not null unique
);
if I run the following query, I can add any number to primary key:
insert into departments (department_id, name) values (9001, 'FooBar')
I think I want to prevent this from happening. I'd like to get some opinions.
Use an identity column:
create table if not exists departments (
department_id integer primary key generated always as identity,
name varchar(64) not null unique
);
This will prevent an insert to override the generated value. You can still circumvent that by specifying OVERRIDING SYSTEM VALUE as part of your INSERT statement. But unless you specify that option, providing a value for the column will result in an error.
Related: PostgreSQL: serial vs identity
Unless '9001' isn't already in the registers, it shouldn't cause any trouble.
If the filed 'department_id' is already auto increment you can just run your insert statement like
INSERT INTO departments (name) VALUES ('FooBar')
I'm using Microsoft sql server and you can control the insert of value in identity column by below sql command
SET IDENTITY_INSERT [ [ database_name . ] schema_name . ] table_name { ON | OFF }
By setting it to 'OFF' you cannot insert a value in identity column.
For more info refer: https://learn.microsoft.com/en-us/sql/t-sql/statements/set-identity-insert-transact-sql?view=sql-server-ver15

SQL Beginner trying to insert data on tables

I have started my journey in learning SQL and right I am having trouble creating and inserting data into tables. Here is the code that I have tried, I get an error message saying that there aren't enough values. I am using Oracle.
Create table project
(
proj_id number(10),
medic_name varchar2(10),
purpose varchar2(12),
start_date date,
end_date date,
pi_id null,
CONSTRAINT pkprojid primary key (proj_id),
CONSTRAINT fkproject foreign key (pi_id) references researcher
);
alter session set nls_date_format = 'mm/dd/yyyy';
Insert into project values (PR001, 'Medic1', 'heart', '09/01/2017', '07/31/2019');
Insert into project values (PR002, 'Medic1', 'diabetes', '10/01/2016', '07/31/2020);
Insert into project values (PR003, 'Medic3', 'lung', '11/1/2014', '12/31/2020');
Insert into project values (PR004, 'Medic3', 'blood', '01/10/2017', '07/31/2019');
Insert into project values (PR005, 'Medic5', 'blood', '07/10/2018', '01/31/2020');
alter session set nls_date_format = 'mm/dd/yyyy';
Insert into project values (PR001, 'Medic1', 'heart', '09/01/2017', '07/31/2019');
Issues:
Your table has 6 columns, you are only passing 5 for insert; it seems like you are missing last column (pi_id), hence the error message that you are getting. If you want to skip the last column (which is possible since it is declared as nullable), you can explictly list the column when inserting
first column (proj_id) is of number datatype; PR001 is not a number (neither a string, since it is not quoted: this is a syntax error); did you mean 1 instead? Or, if you want to insert string values, you need to change the datatype of column proj_id to varchar(N) (N being the maximum length of the string, in bytes).
Here is an insert statement that should work for your current table definition:
insert into project(proj_id, medic_name, purpose, start_date, end_date)
values (1, 'Medic1', 'heart', '09/01/2017', '07/31/2019');
Note: there is a missing quote at the end of the date on the second insert statement; I assume that this is a typo.

What happens when Identity seed reaches an existent value in a primary key?

I have an identity column that is also the primary key, of INT datatype. Due to the issue discussed here (cache loss), the identity has gaps and I chose to reseed to the previous value. In concrete terms, I have a situation that looks like this:
Table1
ID_PK Field1
---------------
28 'd'
29 'e'
30 'h'
1029 'f'
1030 'g'
I looked around and couldn't find a clear answer to what happens when I make an insertion and the seed reaches the existent value that would break the constraint. Suppose I were to insert values 'x' and 'y' in two separated queries to the table, I can think of the following possibilities:
The identity will be reseeded before the first insertion and I will have both values inserted correctly.
The first insertion will fail, then the column will be reseeded, and only then the second insertion would succeed.
Neither will work and I will have to explicitly call DBCC CHECKIDENT to reseed before inserting values in the table
So, which is it? Or none of the above? Would this behavior be different if I inserted a multi-row result query into Table1? Thanks in advance
For completeness anyway, here's a script you can use to test:
USE Sandbox;
GO
CREATE TABLE test(ID int IDENTITY(1,1) PRIMARY KEY CLUSTERED, string char(1));
GO
INSERT INTO test (string)
VALUES ('a'),('b'),('c'),('d');
GO
SELECT *
FROM test;
GO
DELETE FROM test
WHERE string IN ('b','c');
GO
SELECT *
FROM test;
GO
DBCC CHECKIDENT ('dbo.test', RESEED, 1);
GO
INSERT INTO test (string)
VALUES ('e'),('f');
GO
SELECT *
FROM test;
GO
INSERT INTO test (string)
VALUES ('g');
GO
SELECT *
FROM test;
GO
DROP TABLE test;
Running this script will give you the answer you need. If you wonder why I have used 1 as the RESEED value, this is explained in the documentation:
The following example forces the current identity value in the
AddressTypeID column in the AddressType table to a value of 10.
Because the table has existing rows, the next row inserted will use 11
as the value, that is, the new current increment value defined for the
column value plus 1.
In my script, this means that the next row to be inserted after the RESEED will have a value of 2 for its IDENTITY, not 1 (as rows already existing in the table (ID's 1 and 4)).
As several have said in the comments though, there's really no need to use RESEED on an IDENTITY column. If you need to maintain a sequence, you should (unsurprisingly) be using a SEQUENCE: CREATE SEQUENCE (Transact-SQL)
It depends:
Scenario 1
You get duplicates in the IDENTITY column, as no unique index or PK constraint.
create table I (
id int identity(1,1) not null,
i int null
)
Scenario 2
You get the following error as the inserted value conflicts with the Primary Key constraint:
Msg 2627, Level 14, State 1, Line 1 Violation of PRIMARY KEY
constraint 'PK__I__3213E83FE0B0E009'. Cannot insert duplicate key in
object 'dbo.I'. The duplicate key value is (11). The statement has
been terminated.
create table I (
id int identity(1,1) not null primary key,
i int null
)
This proves that IDENTITY on it's own does not guarantee uniqueness, only a UNIQUE CONSTRAINT does that.
To close, turns out it's (2).
First insertion fails, reseed is automatic to the highest value, and only next insertion suceeds. Multi-value insertions behave the same if any of the values would break the primary key constraint.