How to convert integer to serial - postgresql

I have a table which has a primary key column "gid" and its type is "Integer NOT NULL".
I want to convert it into "Serial NOT NULL" so that I can insert some values into this table.
I used following commands to convert it into serial:
CREATE SEQUENCE test_table_gid_seq
INCREMENT 1
MINVALUE 1
MAXVALUE 2147483648 START 1
CACHE 1;
ALTER TABLE test_table ALTER COLUMN gid
SET DEFAULT nextval('test_table_gid_seq'::regclass);
This command converted integer to serial. But while I entered some data to the table following error occurred:
ERROR: duplicate key value violates unique constraint "pk_test".
Please help me to solve this. Is there any other method to convert integer to serial?

Select max value of gid column (max_gid):
SELECT max(gid) FROM test_table;
And use it as start point for sequence (probably, max_gid+1):
ALTER SEQUENCE test_table_gid_seq RESTART WITH max_gid;

Related

postgresql 10 altering serial column error 42p01

I am facing an issue when correcting an existing table to use serial column on the primary key. In order to simulate the issue I created a new table:
CREATE TABLE markets."TestSequence" (
"Id" integer NOT NULL,
"Name" text COLLATE pg_catalog."default",
CONSTRAINT "PK_TestSequence" PRIMARY KEY ("Id")
);
Then I ran the query that is causing problem:
ALTER TABLE markets."TestSequence" ALTER COLUMN "Id" TYPE integer;
ALTER TABLE markets."TestSequence" ALTER COLUMN "Id" SET NOT NULL;
CREATE SEQUENCE "TestSequence_Id_seq" AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE NO CYCLE;
ALTER TABLE markets."TestSequence" ALTER COLUMN "Id" SET DEFAULT (nextval('"TestSequence_Id_seq"'));
ALTER SEQUENCE "TestSequence_Id_seq" OWNED BY "TestSequence"."Id";
I get the following error:
ERROR: relation "TestSequence" does not exist
SQL state: 42P01
According to the doc OWNED BY does not take any schema prefix. So I tried to create the table without schema and it works fine.
CREATE TABLE "TestSequence" (
"Id" integer NOT NULL,
"Name" text COLLATE pg_catalog."default",
CONSTRAINT "PK_TestSequence" PRIMARY KEY ("Id")
);
and run the corresponding alter queries:
ALTER TABLE "TestSequence" ALTER COLUMN "Id" TYPE integer;
ALTER TABLE "TestSequence" ALTER COLUMN "Id" SET NOT NULL;
CREATE SEQUENCE "TestSequence_Id_seq" AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE NO CYCLE;
ALTER TABLE "TestSequence" ALTER COLUMN "Id" SET DEFAULT (nextval('"TestSequence_Id_seq"'));
ALTER SEQUENCE "TestSequence_Id_seq" OWNED BY "TestSequence"."Id";
How can I make this work for relations with schema?
The doc you have linked says, for owned by
The specified table must have the same owner and be in the same schema
as the sequence.
You haven't specified a schema for the sequence, so it is created in public by default, which is not the same as the table schema.
Try creating the sequence as
CREATE SEQUENCE markets."TestSequence_Id_seq" AS integer ...
That being said, nothing prevents you from specifying the schema of both the sequence and the table
ALTER SEQUENCE markets."TestSequence_Id_seq" OWNED BY markets."TestSequence"."Id";

ERROR: more than one owned sequence found in Postgres

I'm setting up a identity column to my existing columns for the Patient table.
Here I would like to use GENERATED ALWAYS AS IDENTITY.
So I setup the identity column by using the following statement (previously it was serial):
ALTER TABLE Patient ALTER PatientId
ADD GENERATED ALWAYS AS IDENTITY (START WITH 1);
For the existing patient table I have a total of 5 records. (patientId 1 to 5)
When I insert a new record after the identity setup, it will throw an error like:
more than one owned sequence found
Even after resetting the identity column, I still get the same error.
ALTER TABLE Patient ALTER COLUMN PatientId RESTART WITH 6;
Let me know if you have any solutions.
Update: This bug has been fixed in PostgreSQL v12 with commit 19781729f78.
The rest of the answer is relevant for older versions.
A serial column has a sequence that is owned by the column and a DEFAULT value that gets the net sequence value.
If you try to change that column into an identity column, you'll get an error that there is already a default value for the column.
Now you must have dropped the default value, but not the sequence that belongs to the serial column. Then when you converted the column into an identity column, a second sequence owned by the column was created.
Now when you try to insert a row, PostgreSQL tries to find and use the sequence owned by the column, but there are two, hence the error message.
I'd argue that this is a bug in PostgreSQL: in my opinion, it should either have repurposed the existing sequence for the identity column or given you an error that there is already a sequence owned by the column, and you should drop it. I'll try to get this bug fixed.
Meanwhile, you should manually drop the sequence left behind from the serial column.
Run the following query:
SELECT d.objid::regclass
FROM pg_depend AS d
JOIN pg_attribute AS a ON d.refobjid = a.attrelid AND
d.refobjsubid = a.attnum
WHERE d.classid = 'pg_class'::regclass
AND d.refclassid = 'pg_class'::regclass
AND d.deptype <> 'i'
AND a.attname = 'patientid'
AND d.refobjid = 'patient'::regclass;
That should give you the name of the sequence left behind from the serial column. Drop it, and the identity column should behave as desired.
This is not an answer -- apologies, but this allows me to show, with a vivid image, the crazy behavior that I (unintentionally) uncovered this morning...
All I had to do was this:
alter TABLE db.generic_items alter column generic_item_id drop default;
alter TABLE db.generic_items alter column generic_item_id add generated by default as identity;
and now when scripting the table to SQL I get (abbreviated):
CREATE TABLE db.generic_items
(
generic_item_id integer NOT NULL GENERATED BY DEFAULT AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),
generic_item_id integer NOT NULL GENERATED BY DEFAULT AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),
generic_item_name character varying(50) COLLATE pg_catalog."default" NOT NULL,
CONSTRAINT pk_generic_items PRIMARY KEY (generic_item_id),
)
I am thankful for the answer posted above, by Laurenz Albe! As he explains, just delete the sequence that was used for the serial default, and this craziness goes away and the table looks normal again.
Again, this is NOT AN ANSWER, but commenting did not let me add enough text.
Apology. Continues from my earlier comment(s).
This is what I executed and it shows, imo, that the manual fix is not sufficient, and with large tables, the repetitive trick I used (see below) would be impractical and potentially wrong because adopting an id belonging to a deleted row.
-- pls disregard the absence of 2 id rows, this is the final situation
\d vaste_data.studie_type
Table "vaste_data.studie_type"
Column | Type | Collation | Nullable | Default
--------+-----------------------+-----------+----------+----------------------------------
id | integer | | not null | generated by default as identity
naam | character varying(25) | | not null |
Indexes:
"pk_tstudytype_tstudytype_id" PRIMARY KEY, btree (id)
Referenced by:
TABLE "stuwadoors" CONSTRAINT "fk_t_stuwadoors_t_studytype" FOREIGN KEY (study_type_id) REFERENCES vaste_data.studie_type(id)
TABLE "psux" CONSTRAINT "study_studytype_fk" FOREIGN KEY (studie_type_id) FOREIGN KEY (studie_type_id) REFERENCES vaste_data.studie_type(id)
alter table vaste_data.studie_type alter column id drop default;
ALTER TABLE
alter table vaste_data.studie_type alter column id add generated by default as identity;
ALTER TABLE
-- I chose to show both sequences so I could try to drop either one.
SELECT d.objid::regclass
FROM pg_depend AS d
JOIN pg_attribute AS a ON d.refobjid = a.attrelid AND
d.refobjsubid = a.attnum
WHERE d.classid = 'pg_class'::regclass
AND d.refclassid = 'pg_class'::regclass
AND a.attname = 'id'
AND d.refobjid = 'vaste_data.studie_type'::regclass;
objid
-----------------------------------------
vaste_data.studie_type_id_seq
vaste_data.tstudytype_tstudytype_id_seq
(2 rows)
drop sequence vaste_data.studie_type_id_seq;
ERROR: cannot drop sequence vaste_data.studie_type_id_seq because column id of table vaste_data.studie_type requires it
HINT: You can drop column id of table vaste_data.studie_type instead.
\d vaste_data.studie_type_id_seq
Sequence "vaste_data.studie_type_id_seq"
Type | Start | Minimum | Maximum | Increment | Cycles? | Cache
---------+-------+---------+------------+-----------+---------+-------
integer | 1 | 1 | 2147483647 | 1 | no | 1
Sequence for identity column: vaste_data.studie_type.id
alter sequence vaste_data.studie_type_id_seq start 6;
ALTER SEQUENCE
drop sequence vaste_data.tstudytype_tstudytype_id_seq;
DROP SEQUENCE
insert into vaste_data.studie_type (naam) values('Overige leiding');
ERROR: duplicate key value violates unique constraint "pk_tstudytype_tstudytype_id"
DETAIL: Key (id)=(1) already exists.
...
ERROR: duplicate key value violates unique constraint "pk_tstudytype_tstudytype_id"
DETAIL: Key (id)=(5) already exists.
insert into vaste_data.studie_type (naam) values('Overige leiding');
INSERT 0 1

Changing primary key int type to serial

Is there a way to change existing primary key type from int to serial without dropping the table? I already have a lot of data in the table and I don't want to delete it.
Converting an int to a serial more or less only means adding a sequence default to the value, so to make it a serial;
Pick a starting value for the serial, greater than any existing value in the table
SELECT MAX(id)+1 FROM mytable
Create a sequence for the serial (tablename_columnname_seq is a good name)
CREATE SEQUENCE test_id_seq MINVALUE 3 (assuming you want to start at 3)
Alter the default of the column to use the sequence
ALTER TABLE test ALTER id SET DEFAULT nextval('test_id_seq')
Alter the sequence to be owned by the table/column;
ALTER SEQUENCE test_id_seq OWNED BY test.id
A very simple SQLfiddle demo.
And as always, make a habit of running a full backup before running altering SQL queries from random people on the Internet ;-)
-- temp schema for testing
-- ----------------------------
DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;
CREATE TABLE bagger
( id INTEGER NOT NULL PRIMARY KEY
, tralala varchar
);
INSERT INTO bagger(id,tralala)
SELECT gs, 'zzz_' || gs::text
FROM generate_series(1,100) gs
;
DELETE FROM bagger WHERE random() <0.9;
-- SELECT * FROM bagger;
-- CREATE A sequence and tie it to bagger.id
-- -------------------------------------------
CREATE SEQUENCE bagger_id_seq;
ALTER TABLE bagger
ALTER COLUMN id SET NOT NULL
, ALTER COLUMN id SET DEFAULT nextval('player_id_seq')
;
ALTER SEQUENCE bagger_id_seq
OWNED BY bagger.id
;
SELECT setval('bagger_id_seq', MAX(ba.id))
FROM bagger ba
;
-- Check the result
-- ------------------
SELECT * FROM bagger;
\d bagger
\d bagger_id_seq

Return ID in MyBatis and PostgreSQL

My table plugins has just two columns: ID and DTYPE. I would like to store a String in DTYPE column. ID should increment automatically and be returned by insert method. For that purpose I created manually "id_sequence" like this:
CREATE SEQUENCE id_sequence
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 1
CACHE 1;
OWNED BY plugin.id;
This is my mapper.xml file:
<insert id="insert" useGeneratedKeys="true" parameterType="String">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
SELECT CAST(nextval('id_sequence') as INTEGER)
</selectKey>
INSERT INTO plugins (ID, DTYPE)
VALUES ( #{id}, #{plugin})
RETURNING ID;
</insert>
and corresponding mapper.java:
int insert(String plugin);
Problem statement: 1) I cannot get a proper value of ID (it is null), 2) I get an error
SQL: INSERT INTO public.ucpoplugin (ID, DTYPE) VALUES ( ?, CORRECT_STRING)
Cause: org.postgresql.util.PSQLException: FEHLER: Column »id« is of type bigint, but the sentence is has Type character varying.
I recomend to use auto increment on that id column and then do not pass just string as parameter, but pass whole object. After insert that property will be filled.
See: http://edwin.baculsoft.com/2010/12/beginning-mybatis-3-part-3-how-to-get-tables-generated-ids/
Or: Returning values from MyBatis <insert> mapped methods

How to insert value into a column with a default value? [PostgreSQL 9.1]

I have such table:
CREATE TABLE employee (
id INTEGER DEFAULT NEXTVAL('ids'::regclass) NOT NULL,
name CHARACTER VARYING NOT NULL,
employer INTEGER DEFAULT (-1)
);
And I want to insert sth into this table (I want to leave employer as default, -1):
INSERT INTO employee (name, id) VALUES('Doe', 2);
but my PostgreSQL 9.1 is complaining:
ERROR: insert or update on table "employee" violates foreign key constraint "FK_employer"
DETAIL: Key (employer)=(-1) is not present in table "employer".
I know that theres no employer with id = -1 but still, I want it that way. I want to set employer as -1 for this emplyee. Is it possible with postgreSQL?
Make the default null. Is it good?
employer INTEGER DEFAULT null