PostgreSQL 9.1 primary key autoincrement - sql

I'm trying to add a primary key with an autoincrement.
I have read some docs and other questions - there're SERIAL and nextval() statements but neither of them work.
Here's what I made:
CREATE TABLE IF NOT EXISTS "category" (
"id" integer SERIAL PRIMARY KEY,
"name" varchar(30) DEFAULT NULL
); // the error near "SERIAL"
and
CREATE SEQUENCE your_seq;
CREATE TABLE IF NOT EXISTS "category" (
"id" integer PRIMARY KEY nextval('your_seq'),
"name" varchar(30) DEFAULT NULL
); // the error near 'nextval'
What am I doing wrong? Is there a way to increment the primary key by 1.

serial is, more or less, a column type so saying integer serial is like saying text text, just say serial:
CREATE TABLE IF NOT EXISTS "category" (
"id" SERIAL PRIMARY KEY,
"name" varchar(30) DEFAULT NULL
);
If you want to create the sequence yourself then you want to make the default value of id the next value in the sequence and that means saying default nextval('your_seq'):
CREATE SEQUENCE your_seq;
CREATE TABLE IF NOT EXISTS "category" (
"id" integer PRIMARY KEY default nextval('your_seq'),
"name" varchar(30) DEFAULT NULL
);
To simulate the usual serial behavior you'll also want to make the sequence owned by the table:
alter sequence your_seq owned by category.id;
Reading the Serial Types section of the manual might be fruitful.
I'd also recommend that you don't double quote your table and column names unless you have to. PostgreSQL will fold your identifiers to lower case so id and "id" will be the same thing but unnecessary quoting is a bad habit that can easily lead to a big mess of quotes everywhere.

If someone needs to modify an existing table to add a primary key with auto-increment:
ALTER TABLE table_name ADD COLUMN pk_column_name SERIAL PRIMARY KEY;

Related

Use a unique varchar as reference to a foreign key

Warning: possible duplicate
Hi,
I have the following table
CREATE TABLE names (
id uuid DEFAULT uuid_generate_v4(),
name VARCHAR(10) NOT NULL UNIQUE,
PRIMARY KEY (id)
);
Now, uuid basically says, use a unique VARCHAR(10) as an index. The question that I have is: can't I just use name as an id, since it's unique anyway and takes up the same amount of space or would this have negative impact on foreign keys?
I'm not new to SQL, I've just never came across this conflict and now I'm wondering if I've been using some concepts wrong the whole time.

Unique constraint with foreign key

I have two Postgres tables with the following columns:
Command
Column
Type
id
Integer Primary Key
name
VARCHAR(32)
Option
Column
Type
id
Integer Primary Key
name
VARCHAR(32)
command_id
FOREIGN KEY on COMMAND("id")
I want to add another constraint where the Command name column and the Option command_id columns are tied, such that two commands can share the same name provided they are part of different options. How would I make such a constraint? Would it be better to add no constraint but only allow the backend to make the required checks before entering data?
Edit: I realized that I was overthinking it for my simple use case and that storing a JSON field would be fine enough. However, if the table structure happened to be more complex, then the question would still be valid.
if the name from Command table is the same as name column in Option column. then that column in Option table is redundant and you can always fetch the name by FK that you already have (command_id).
but normally you can use composite key for your FK, for example :
create table Options(
id int primary key
, name varchar(32)
, command_id int
, foreign key fk_name (name , command_id) references Command(name, id)
);
and of course name and id in command table should be part of candidate key.

Postgres create table error

I am trying to create my very first table in postgres, but when I execute this SQL:
create table public.automated_group_msg (
automated_group_msg_idx integer NOT NULL DEFAULT nextval ('automated_group_msg_idx'::regclass),
group_idx integer NOT NULL,
template_idx integer NOT NULL,
CONSTRAINT automated_group_msg_pkey PRIMARY KEY (automated_group_msg_idx),
CONSTRAINT automated_group_msg_group_idx_fkey FOREIGN KEY (group_idx)
REFERENCES public.groups (group_idx) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT automated_msg_template_idx_fkey FOREIGN KEY (template_idx)
REFERENCES public.template (template_idx) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE
)
WITH (
OIDS = FALSE
);
I get the following error:
ERROR: relation "automated_group_msg_idx" does not exist
Your error is (likely) because the sequence you're trying to use doesn't exist yet.
But you can create a sequence on the fly using this syntax:
create table public.automated_group_msg (
id serial primary key,
... -- other columns
)
Not directly related to your question, but naming columns with the table name in the name of the column is generally speaking an anti-pattern, especially for primary keys for which id is the industry standard. It also allows for app code refactoring using abstract classes whose id column is always id. It's crystal clear what automated_group_msg.id means and also crystal clear that automated_group_msg.automated_group_msg_id is a train wreck and contains redundant information. Attribute column names like customer.birth_date should also not be over-decorated as customer.customer_birth_date for the same reasons.
You just need to create the sequence before creating the table
CREATE SEQUENCE automated_group_msg_idx;

Relation "table" already exists

I'm building a SQL database and running into the error "relation 'sexes' already exists".
Per SO users that posted about a similar problem, I've attempted: commenting out constraints to see if those were interfering, changing all references to serial/ all primary keys to integers to match up types, dropping all tables if they exist. The error remains the same no matter what I do so I've posted my original code. I could really use some guidance as to what the issue in this case is. Thanks!
CREATE TABLE sexes (
id serial PRIMARY KEY,
string text NOT NULL
);
CREATE TABLE humans (
id serial PRIMARY KEY UNIQUE NOT NULL,
forename text NOT NULL,
surname text NOT NULL,
birthdate date,
sex_id integer REFERENCES sexes(id)
);
CREATE TABLE marriages (
id serial PRIMARY KEY UNIQUE NOT NULL,
partner_1_id integer REFERENCES humans(id),
partner_2_id integer REFERENCES humans(id),
marriage_date date NOT NULL,
divorce_date date NOT NULL,
CONSTRAINT divorced CHECK (marriage_date <= divorce_date),
CONSTRAINT marry_self CHECK (partner_1_id <> partner_2_id)
);
You could use IF NOT EXISTS clause:
Do not throw an error if a relation with the same name already exists. A notice is issued in this case. Note that there is no guarantee that the existing relation is anything like the one that would have been created.
CREATE TABLE IF NOT EXISTS sexes (
id serial PRIMARY KEY,
string text NOT NULL
);
Which is really strange because dropping the tables if they existed before creating new ones threw back a whole new error.
If you want to drop tables you have to do it in right order:
DROP TABLE IF EXISTS marriages;
DROP TABLE IF EXISTS humans;
DROP TABLE IF EXISTS sexes;

Correct way to create a table that references variables from another table

I have these relationships:
User(uid:integer,uname:varchar), key is uid
Recipe(rid:integer,content:text), key is rid
Rating(rid:integer, uid:integer, rating:integer) , key is (uid,rid).
I built the table in the following way:
CREATE TABLE User(
uid INTEGER PRIMARY KEY ,
uname VARCHAR NOT NULL
);
CREATE TABLE Recipes(
rid INTEGER PRIMARY KEY,
content VARCHAR NOT NULL
);
Now for the Rating table: I want it to be impossible to insert a uid\rid that does not exist in User\Recipe.
My question is: which of the following is the correct way to do it? Or please suggest the correct way if none of them are correct. Moreover, I would really appreciate if someone could explain to me what is the difference between the two.
First:
CREATE TABLE Rating(
rid INTEGER,
uid INTEGER,
rating INTEGER CHECK (0<=rating and rating<=5) NOT NULL,
PRIMARY KEY(rid,uid),
FOREIGN KEY (rid) REFERENCES Recipes,
FOREIGN KEY (uid) REFERENCES User
);
Second:
CREATE TABLE Rating(
rid INTEGER REFERENCES Recipes,
uid INTEGER REFERENCES User,
rating INTEGER CHECK (0<=rating and rating<=5) NOT NULL,
PRIMARY KEY(rid,uid)
);
EDIT:
I think User is problematic as a name for a table so ignore the name.
Technically both versions are the same in Postgres. The docs for CREATE TABLE say so quite clearly:
There are two ways to define constraints: table constraints and column constraints. A column constraint is defined as part of a column definition. A table constraint definition is not tied to a particular column, and it can encompass more than one column. Every column constraint can also be written as a table constraint; a column constraint is only a notational convenience for use when the constraint only affects one column.
So when you have to reference a compound key a table constraint is the only way to go.
But for every other case I prefer the shortest and most concise form where I don't need to give names to stuff I'm not really interested in. So my version would be like this:
CREATE TABLE usr(
uid SERIAL PRIMARY KEY ,
uname TEXT NOT NULL
);
CREATE TABLE recipes(
rid SERIAL PRIMARY KEY,
content TEXT NOT NULL
);
CREATE TABLE rating(
rid INTEGER REFERENCES recipes,
uid INTEGER REFERENCES usr,
rating INTEGER NOT NULL CHECK (rating between 0 and 5),
PRIMARY KEY(rid,uid)
);
This is a SQL Server based solution, but the concept applies to most any RDBMS.
Like so:
CREATE TABLE Rating (
rid int NOT NULL,
uid int NOT NULL,
CONSTRAINT PK_Rating PRIMARY KEY (rid, uid)
);
ALTER TABLE Rating ADD CONSTRAINT FK_Rating_Recipies FOREIGN KEY(rid)
REFERENCES Recipies (rid);
ALTER TABLE Rating ADD CONSTRAINT FK_Rating_User FOREIGN KEY(uid)
REFERENCES User (uid);
This ensures that the values inside of Rating are only valid values inside of both the Users table and the Recipes table. Please note, in the Rating table I didn't include the other fields you had, just add those.
Assume in the users table you have 3 users: Joe, Bob and Bill respective ID's 1,2,3. And in the recipes table you had cookies, chicken pot pie, and pumpkin pie respective ID's are 1,2,3. Then inserting into Rating table will only allow for these values, the minute you enter 4 for a RID or a UID SQL throws an error and does not commit the transaction.
Try it yourself, its a good learning experience.
In Postgresql a correct way to implement these tables are:
CREATE SEQUENCE uid_seq;
CREATE SEQUENCE rid_seq;
CREATE TABLE User(
uid INTEGER PRIMARY KEY DEFAULT nextval('uid_seq'),
uname VARCHAR NOT NULL
);
CREATE TABLE Recipes(
rid INTEGER PRIMARY KEY DEFAULT nextval('rid_seq'),
content VARCHAR NOT NULL
);
CREATE TABLE Rating(
rid INTEGER NOT NULL REFERENCES Recipes(rid),
uid INTEGER NOT NULL REFERENCES User(uid),
rating INTEGER CHECK (0<=rating and rating<=5) NOT NULL,
PRIMARY KEY(rid,uid)
);
There is no real difference between the two options that you have written.
A simple (i.e. single-column) foreign key may be declared in-line with the column declaration or not. It's merely a question of style. A third way should be to omit foreign key declarations from the CREATE TABLE entirely and later add them using ALTER TABLE statements; done in a transaction (presumable along with all the other tables, constraints, etc) the table would never exist without its required constraints. Choose whichever you think is easiest fora human coder to read and understand i.e. is easiest to maintain.
EDIT: I overlooked the REFERENCES clause in the second version when I wrote my original answer. The two versions are identical in terms of referential integrity, there are just two ways of syntax to do this.