How to alter PostgreSQL column with entries to be a nextval id - sql

I have a problem with a really big database with following scheme:
id | date | other columns...
The id column is from type integer. It would be ideal if it where from type integer with a nextval constraint. Many of the id entries have unique id's incremented when they where added.
The problem is all rows added since a specific date have no id and the value is null.
Is it possible to add such constraints to tables with existing values (plus null values) so that the null values are filled with integer id's?
And is this possible without losing the old id's and in the best case with ascending order in relation to the date column?
thanks and greetings

You need to first update the existing rows with a unique, non-null value:
update the_table
set id = new_id
from (
select ctid,
(select max(id) from the_table) + row_number() over (order by date) as new_id
from the_table
where id is null
) t
where t.ctid = the_table.ctid;
I am not sure if the order of the IDs is guaranteed using this approach, but it's likely that it does.
Now, that the column doesn't contain any NULL values, we can either change it automatically assign new values.
The next steps depend on whether you want to make this an identity column or simply a column with a default from a sequence (essentially a (discouraged) serial column)
Staying with a "serial"
We need to create a sequence and sync it with the highest value in the column.
create sequence the_table_id_seq;
select setval('the_table_id_seq', max(id))
from the_table;
Then use this for the default and link the sequence to the column.
alter table the_table
alter id set not null,
alter id set default nextval('the_table_id_seq') ;
alter sequence the_table_id_seq owned by the_table.id;
Using an identity column (recommended)
To make this a proper (recommended) identity column (Postgres 10 and later) you can do it like this:
alter table the_table
alter id set not null,
alter id add generated always as identity;
Now adding the identity attribute created a new sequence which we need to sync with the existing values in the column:
select setval(pg_get_serial_sequence('the_table', 'id'), max(id))
from the_table;
Alternatively, you could have manually looked up the current max value and provide that directly when specifying the identity default:
alter table the_table
alter id set not null,
alter id add generated always as identity (start with 42);

Related

Postgresql Unique constraint not valid

I need to enforce a unique constraint on the table in a text field to make sure users dont input rows containing the same address. I do not want to validate it though, old records need to be kept
I have made some check constraints with a not valid clause but not sure how to make it unique
How to make a unique check constraint not valid
I would add a new column to the table:
ALTER TABLE atable ADD is_new boolean DEFAULT FALSE NOT NULL;
ALTER TABLE atable ALTER is_new SET DEFAULT TRUE;
Then old rows will be marked, and you can
CREATE UNIQUE ON atable (col1) WHERE is_new;
which will act as a “conditional unique constraint”.
Note that adding a column with a default value will cause a (potentially slow) table rewrite before version 11.
If I understand correctly, you can do this by using a flag. But you want to set the flag so exactly one row for each existing address has the flag set.
ALTER TABLE t ADD address_for_duplicates boolean DEFAULT true NOT NULL;
Then, for existing rows, I will assume that you have a primary key, pk:
update t
set address_for_duplicates = (seqnum = 1)
from (select t.*, row_number() over (partition by address order by pk) as seqnum
from t
) tt
where tt.pk = t.pk;
Now add a filtered unique index:
create unique index unq_t_address_some_duplicates
on t(address)
where address_for_duplicates;
This will prevent existing addresses from being duplicated (again) as well as new addresses.
You can't:
https://www.postgresql.org/docs/current/sql-altertable.html
"ADD table_constraint [ NOT VALID ]
This form adds a new constraint to a table using the same constraint syntax as CREATE TABLE, plus the option NOT VALID, which is currently only allowed for foreign key and CHECK constraints."
Also CHECK constraints can only work on the current row so they can't enforce uniqueness across all rows. To do what you want you are looking at a ON INSERT trigger.

PostgreSQL - create an auto-increment column for non-primary key

I am with PostgreSQL 9.5 X64 integrated with the open-source Parse Server. My table has the following structure.
objectId (text with fixed 10 characters),
item_id (integer),
item_name (text with various length)
The objectId is the primary key due to use of Parse Server. It is automatically generated by Parse Server. The item_id is not a primary key. I would like to have item_id automatically increment by 1 when a new record is created. How can this be achieved in Create Table?
Add a default value with a sequence:
CREATE SEQUENCE mytable_item_id_seq OWNED BY mytable. item_id;
ALTER TABLE mytable ALTER item_id SET DEFAULT nextval('mytable_item_id_seq');
To make that work, you have to exclude the item_id column from all INSERT statrments, because the default value is only used if no value is specified for the column.
You may try making the item_id column SERIAL. I don't know whether or not it's possible to alter the current item_id column to make it serial, so we might have to drop that column and then add it back, something like this:
ALTER TABLE yourTable DROP COLUMN item_id;
ALTER TABLE yourTable ADD COLUMN item_id SERIAL;
If there is data in the item_id column already, it may not make sense from a serial point of view, so hopefully there is no harm in deleting it.

Oracle 12c: How can I modify an existing primary key column to an identity column?

I have a table which contains a primary key column which is auto incremented from application. How can I modify the column to be an identity column in Oracle 12c?
A sample case is provided below-
create table tmp_identity (
id number(100) primary key,
value varchar2(100)
);
Say we populated the table with following data-
ID VALUE
---------------
1 Sample 1
2 Sample 2
3 Sample 3
What we are planning to do is to turn this id column into an identity column which will-
Auto increment by 1
Start from 4
How can I do it?
If it is not possible, then is there any work-around available for this?
You can't turn an existing column it into a real identity column, but you can get a similar behaviour by using a sequence as the default for the column.
create sequence seq_tmp_identity_id
start with 4
increment by 1;
Then use:
alter table tmp_identity
modify id
default seq_tmp_identity_id.nextval;
to make the column use the sequence as a default value. If you want you can use default on null to overwrite an explicit null value supplied during insert (this is as close as you can get to an identity column)
If you want a real identity column you will need to drop the current id column and then re-add it as an identity column:
alter table tmp_identity drop column id;
alter table tmp_identity
add id number(38)
generated always as identity;
Note that you shouldn't add the start with 4 in this case so that all rows get a new unique number
You can rename the column from id to identity by using following query
ALTER TABLE tmp_identity
RENAME COLUMN id TO identity;
To auto increment by 1 and start from 4, we can use sequence.
CREATE SEQUENCE identity_incre
MINVALUE 4
START WITH 4
INCREMENT BY 1;
To use the identity_incre sequence in your query
INSERT INTO suppliers
(identity, VALUE)
VALUES
(identity_incre.NEXTVAL, 'sample4');

AutoID column for a table

I was asked by my manager to add an AutoID column to the table UserTracking, so that all the existing entries could be populated with a value, and for this to be in the same order as the CreateDate field (i.e. ID 1 is in the earliest entry).
I did this:
alter table dbo.UserTracking add AutoID bigint NULL
go
update dbo.UserTracking
set AutoId=AutoId.AutoId
from dbo.UserTracking as ut
join
(
select CreateDate,row_number() over (order by CreateDate) as AutoId
from UserTracking
) as AutoId
on ut.CreateDate=AutoId.CreateDate
go
create index IDX__UserTracking__AutoId on dbo.UserTracking (AutoId)
go
BUT I was told that we need the AutoID column to be an actual auto ID (so that it has automatic new values on record insert) and it should be called ID to be consistent with other tables.
From performance point of view it would be perfect if the new ID (INT) column is our primary key but it can be hard to do. For now we need the auto ID with an index on it.
I'm not entirely sure how to do it myself and I wouldn't want to mess it up. Could someone please help?
Drop the existing column (AutoID) , you cannot make an existing column an Identity column.
Just add another column and make it Identity column.
ALTER TABLE dbo.UserTracking
ADD ID INT IDENTITY(1,1)
GO
It will populates the column itself you don't have to do anything .

SQL Server: how to add new identity column and populate column with ids?

I have a table with huge amount of data. I'd like to add extra column id and use it as a primary key. What is the better way to fill this column with values from one 1 to row count
Currently I'm using cursor and updating rows one by one. It takes hours. Is there a way to do that quicker?
Thank you
Just do it like this:
ALTER TABLE dbo.YourTable
ADD ID INT IDENTITY(1,1)
and the column will be created and automatically populated with the integer values (as Aaron Bertrand points out in his comment - you don't have any control over which row gets what value - SQL Server handles that on its own and you cannot influence it. But all rows will get a valid int value - there won't be any NULL or duplicate values).
Next, set it as primary key:
ALTER TABLE dbo.YourTable
ADD CONSTRAINT PK_YourTable PRIMARY KEY(ID)
If you want to add row numbers in a specific order you can do ROW_NUMBER() into a new table then drop the original one. However, depending on table size and other business constraints, you might not want to do that. This also implies that there is a logic according to which you will want the table sorted.
SELECT ROW_NUMBER() OVER (ORDER BY COL1, COL2, COL3, ETC.) AS ID, *
INTO NEW_TABLE
FROM ORIGINAL_TABLE