What happens if a new row is inserted when the primary key exists when using auto increment? - sql

I am using PGsql and transferred some data from another database into my new one. The table has records starting at PK 200. The tables primary key (bigint - autoincrementing) is currently starting at 0. If I continue to insert records, eventually it will reach 200. My question is, will these records create an issue when trying to insert record 200? or will PGsql know the conflict, then find the next available AI index (say 234)?
Thanks! If it will cause a conflict, how can I set the current index of my table to the last index of data? (like 234).

My question is, will these records create an issue when trying to insert record 200?
Assuming that you have a serial column or the-like: yes, this will create an issue. The serial has no knowledge that some sequences are not available, sp this will result in a duplicate key error. Meanwhile the sequence increments also on such errors, and the next call will return the next number, and so on.
This is easily reproducible:
create table t (id serial primary key, val text);
insert into t (id, val) values (2, 'blocker');
-- 1 rows affected
insert into t (val) values ('foo');
-- 1 rows affected
insert into t (val) values ('bar');
-- ERROR: duplicate key value violates unique constraint "t_pkey"
-- DETAIL: Key (id)=(2) already exists.
insert into t (val) values ('baz');
-- 1 rows affected
select * from t order by id;
id | val
-: | :------
1 | foo
2 | blocker
3 | baz
One solution is to reset the sequence: the only safe starting point is the high watermark of the table:
select setval(
't_id_seq',
coalesce((select max(id) + 1 from t), 1),
false
);
Demo on DB Fiddlde: you can uncomment the setval() statement to see how it avoids the error.

Related

PL/pgSQL restart generated sequence

I have an initialization script I use on Spring Boot application start and I create the table if it doesn't exist. Then I delete all the data from the table and execute a bunch on inserts.
create table if not exists employee (
id serial primary key,
name varchar(255)
);
delete from employee;
-- inserts
With each execution of the script, the sequence still continues, so the new rows don't start from one. I have to reset such sequence too, however, it is generated and I dont know its name unless I call this script:
select pg_get_serial_sequence('employee', 'id');
-- returns public.employee_id_seq
I tried to combine it together and reset the sequence based on the output of this funciton, but with no luck. How to reset the generated sequence without knowing its name? My attempt so far cannot resolve the seq sequence from the variable:
do $$
declare
seq varchar(255);
begin
select pg_get_serial_sequence('employee', 'employee_id') into seq;
alter sequence seq restart with 1;
end; $$;
The simplest solution is to use truncate table . . . restart identity instead of delete:
truncate table employee restart identity;
Here is a db<>fiddle.
Truncate table is recommended for other reasons too. For instance, it reclaims the space the table used immediately. And it is much faster. The one difference is that delete triggers are not called (although your table doesn't have any triggers).
It is basically a bad idea to reset a id serial to any number, so you must be very carefully using the following.
especially when you have foreign keys to the id firld
the camand you are looking for is
ALTER SEQUENCE <table_name>_<id_column>_seq RESTART WITH 1
create table if not exists employee (
id serial primary key,
name varchar(255)
);
INSERT INTO employee (name) VALUES ('a'),('B'),('C')
3 rows affected
delete from employee;
3 rows affected
ALTER SEQUENCE employee_id_seq RESTART WITH 1
INSERT INTO employee (name) VALUES ('a')
1 rows affected
SELECT * FROM employee
id | name
-: | :---
1 | a
db<>fiddle here

How to UPDATE or INSERT in PostgreSQL

I want to UPDATE or INSERT a column in PostgreSQL instead of doing INSERT or UPDATE using INSERT ... ON CONFLICT ... because there will be more updates than more inserts and also I have an auto incrementing id column that's defined using SERIAL so it increments the id column everytime it tries to INSERT or UPDATE and that's not what I want, I want the id column to increase only if it's an INSERT so that all ids would be in an order instead
The table is created like this
CREATE TABLE IF NOT EXISTS table_name (
id SERIAL PRIMARY KEY,
user_id varchar(30) NOT NULL,
item_name varchar(50) NOT NULL,
code_uses bigint NOT NULL,
UNIQUE(user_id, item_name)
)
And the query I used was
INSERT INTO table_name
VALUES (DEFAULT, 'some_random_id', 'some_random_name', 1)
ON CONFLICT (user_id, item_name)
DO UPDATE SET code_uses = table_name.code_uses + 1;
Thanks :)
Upserts in PostgreSQL do exactly what you described.
Consider this table and records
CREATE TABLE t (id SERIAL PRIMARY KEY, txt TEXT);
INSERT INTO t (txt) VALUES ('foo'),('bar');
SELECT * FROM t ORDER BY id;
id | txt
----+-----
1 | foo
2 | bar
(2 Zeilen)
Using upserts the id will only increment if a new record is inserted
INSERT INTO t VALUES (1,'foo updated'),(3,'new record')
ON CONFLICT (id) DO UPDATE SET txt = EXCLUDED.txt;
SELECT * FROM t ORDER BY id;
id | txt
----+-------------
1 | foo updated
2 | bar
3 | new record
(3 Zeilen)
EDIT (see coments): this is the expected behaviour of a serial column, since they're nothing but a fancy way to use sequences. Long story short: using upserts the gaps will be inevitable. If you're worried the value might become too big, use bigserial instead and let PostgreSQL do its job.
Related thread: serial in postgres is being increased even though I added on conflict do nothing

Auto-increment primary keys in SQL

I need help with the insert statements for a plethora of tables in our DB.
New to SQL - just basic understanding
Summary:
Table1
Col1 Col2 Col3
1 value1 value1
2 value2 value2
3 value3 value3
Table2
Col1 Col2 Col3
4 value1 value1
5 value2 value2
6 value3 value3
Multiple tables use the same sequence of auto-generated primary keys when user creates a static data record from the GUI.
However, creating a script to upload static data from one environment to the other is something I'm looking for.
Example from one of the tables:
Insert into RULE (PK_RULE,NAME,RULEID,DESCRIPTION)
values
(4484319,'TESTRULE',14,'TEST RULE DESCRIPTION')
How do I design my insert statement so that it reads the last value from the PK column (4484319 here) and auto inserts 4484320 without explicitly mentioning the same?
Note: Our DB has hundreds and thousands of records.
I think there's something similar to (SELECT MAX(ID) + 1 FROM MyTable) which could potentially solve my problem but I don't know how to use it.
Multiple tables use the same sequence of auto-generated primary keys when user creates a static data record from the GUI.
Generally, multiple tables sharing a single sequence of primary keys is a poor design choice. Primary keys only need to be unique per table. If they need to be unique globally there are better options such as UUID primary keys.
Instead, one gives each table their own independent sequence of primary keys. In MySQL it's id bigint auto_increment primary key. In Postgres you'd use bigserial. In Oracle 12c it's number generated as identity.
create table users (
id number generated as identity,
name text not null
);
create table things (
id number generated as identity,
description text not null
);
Then you insert into each, leaving off the id, or setting it null. The database will fill it in from each sequence.
insert into users (name) values ('Yarrow Hock'); -- id 1
insert into users (id, name) values (null, 'Reaneu Keeves'); -- id 2
insert into things (description) values ('Some thing'); -- id 1
insert into things (id, description) values (null, 'Shiny stuff'); -- id 2
If your schema is not set up with auto incrementing, sequenced primary keys, you can alter the schema to use them. Just be sure to set each sequence to the maximum ID + 1. This is by far the most sane option in the long run.
If you really must draw from a single source for all primary keys, create a sequence and use that.
create sequence master_seq
start with ...
Then get the next key with nextval.
insert into rule (pk_rule, name, ruleid, description)
values (master_seq.nextval, 'TESTRULE', 14, 'TEST RULE DESCRIPTION')
Such a sequence goes up to 1,000,000,000,000,000,000,000,000,000 which should be plenty.
The INSERT and UPDATE statements in Oracle have a ...RETURNING...INTO... clause on them which can be used to return just-inserted values. When combined with a trigger-and-sequence generated primary key (Oracle 11 and earlier) or an identity column (Oracle 12 and up) this lets you get back the most-recently-inserted/updated value.
For example, let's say that you have a table TABLE1 defined as
CREATE TABLE TABLE1 (ID1 NUMBER
GENERATED ALWAYS AS IDENTITY
PRIMARY KEY,
COL2 NUMBER,
COL3 VARCHAR2(20));
You then define a function which inserts data into TABLE1 and returns the new ID value:
CREATE OR REPLACE FUNCTION INSERT_TABLE1(pCOL2 NUMBER, vCOL3 VARCHAR2)
RETURNS NUMBER
AS
nID NUMBER;
BEGIN
INSERT INTO TABLE1(COL2, COL3) VALUES (pCOL2, vCOL3)
RETURNING ID1 INTO nID;
RETURN nID;
END INSERT_TABLE1;
which gives you an easy way to insert data into TABLE1 and get the new ID value back.
dbfiddle here

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.

I don't understand how postgresql's nextval() work, can someone explain?

I'm just starting to wade into backend development after my first few months on the job as a front end dev. I'm working with postgreSQL and can't seem to wrap my head around the nextval() function. I read this, but it's not clear to me.
http://www.postgresql.org/docs/current/interactive/functions-sequence.html
what are the benefits/use cases for nexval()?
NEXTVAL is a function to get the next value from a sequence.
Sequence is an object which returns ever-increasing numbers, different for each call, regardless of transactions etc.
Each time you call NEXTVAL, you get a different number.
This is mainly used to generate surrogate primary keys for you tables.
You can create a table like this:
CREATE SEQUENCE mysequence;
CREATE TABLE mytable (id BIGINT NOT NULL PRIMARY KEY, value INT);
and insert values like this:
INSERT
INTO mytable (id, value)
VALUES
(NEXTVAL('mysequence'), 1),
(NEXTVAL('mysequence'), 2);
and see what you get:
SELECT * FROM mytable;
id | value
----+-------
1 | 1
2 | 2
PostgreSQL offers a nice syntax sugar for this:
CREATE TABLE mytable (id BIGSERIAL PRIMARY KEY, value INT);
which is equivalent to
CREATE SEQUENCE mytable_id_seq; -- table_column_'seq'
CREATE TABLE mytable (id BIGINT NOT NULL PRIMARY KEY DEFAULT NEXTVAL('mytable_id_seq'), value INT); -- it's not null and has a default value automatically
and can be used like this:
INSERT
INTO mytable (value)
VALUES (1),
(2); -- you can omit id, it will get filled for you.
Note that even if you rollback your insert statement or run concurrent statements from two different sessions, the returned sequence values will never be the same and never get reused (read the fine print in the docs though under CYCLE).
So you can be sure all the values of your primary keys will be generated unique within the table.