How to add unique constraint in postgres on two columns where value of one column is `true` - sql

I have a table in which I need unique EmpId. Duplicate is when there are more than one entry with same EmpId where isDeleted = false
CREATE TABLE someTable (
id serial primary key,
EmpId character varying(15) NOT NULL,
EmpName character varying(15),
isDeleted boolean,
unique (EmpId , isDeleted )//where isDeleted is false
)

There is no constraint of that kind, but you can create a partial unique index (unique constraints are implemented with unique indexes under the hood):
CREATE UNIQUE INDEX ON sometable (empid) WHERE NOT isdeleted;
That will do exactly what you want.

i think for this, you can't use UNIQUE constraint but need to create a custom function
CREATE FUNCTION check_empID(varchar, boolean) RETURNS BOOLEAN
AS
// your implementation
then use it with CHECK constraint
CREATE TABLE someTable (
id serial primary key,
EmpId character varying(15) NOT NULL,
EmpName character varying(15),
isDeleted boolean,
CONSTRAINT ck_active_emp CHECK (check_empID(EmpId, isDeleted))
)
since the function will use the table, then you can create the table without the constraint so you can create and test your function. then alter the table to add the constraint later

Related

using foreign key value directly in INSERT INTO statement

is it possible to use the value of the foreign key directly with an INSERT INTO statement? I am using Postgresql and the tables are consttructed as follows:
CREATE TABLE public.sensors
(
name character varying(100) COLLATE pg_catalog."default",
description text COLLATE pg_catalog."default",
id integer NOT NULL DEFAULT nextval('sensors_id_seq'::regclass),
CONSTRAINT sensors_pkey PRIMARY KEY (id)
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
ALTER TABLE public.sensors
OWNER to postgres;
Now I also have another table, defined as:
CREATE TABLE public.testmap
(
sensor_id integer NOT NULL,
"timestamp" timestamp with time zone NOT NULL,
value "char" NOT NULL,
CONSTRAINT ragmap_sensor_id_fkey FOREIGN KEY (sensor_id)
REFERENCES public.sensors (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
ALTER TABLE public.ragmap
OWNER to postgres;
Now, I try to insert a record directly into the testmap table as:
INSERT INTO testmap (sensor_id, timestamp, value) VALUES (1, NOW(), 'r')
I have a record inserted into the sensors table where the id is 1. However, when I try the INSERT INTO operation, I get:
DETAIL: Key (sensor_id)=(1) is not present in table "sensors".
Is there a way to use the INSERT INTO with the foreignh key directly without having to resort to another Select for the relevant row selection in the sensors table?
Your code doesn't even work. The column id is specified more than once for sensors.
I don't recommend having a character column as a primary key. If you do so, you should be explicit about your types:
INSERT INTO testmap (sensor_id, timestamp, value)
VALUES ('1', NOW(), 'r');
The problem is that your foreign key reference is a number but the primary key is an integer.
Instead, define the primary key to be a number:
CREATE TABLE public.sensors (
id serial primary key,
name character varying(100) COLLATE pg_catalog."default",
description text COLLATE pg_catalog."default",
id integer NOT NULL DEFAULT nextval('sensors_id_seq'::regclass),
CONSTRAINT sensors_pkey PRIMARY KEY (id)
)

PostgreSQL CHECK Constraint on columns other than foreign keys

I have a situation where I want to create a table that associates records from other tables by the id. A constraint of the association is that the year must be the same in the record being associated in each table... Is there a way to get PostgreSQL to CHECK this condition on INSERT?
Table 1:
CREATE TABLE "tenant"."report" (
"id" UUID NOT NULL DEFAULT "pascal".uuid_generate_v1(),
CONSTRAINT "report_pkc_id" PRIMARY KEY ("id"),
"reporting_period" integer NOT NULL,
"name" VARCHAR(64) NOT NULL,
CONSTRAINT "report_uc__name" UNIQUE ("reporting_period", "name"),
"description" VARCHAR(2048) NOT NULL
);
Table 2:
CREATE TABLE "tenant"."upload_file" (
"id" UUID NOT NULL DEFAULT "pascal".uuid_generate_v1(),
CONSTRAINT "upload_file_pkc_id" PRIMARY KEY ("id"),
"file_name" VARCHAR(256) NOT NULL,
"reporting_period" integer
)
Association Table:
CREATE TABLE "tenant"."report_upload_files"
(
"report_id" UUID NOT NULL,
CONSTRAINT "report_upload_files_pkc_tenant_id" PRIMARY KEY ("report_id"),
CONSTRAINT "report_upload_files_fkc_tenant_id" FOREIGN KEY ("report_id")
REFERENCES "tenant"."report" ("id") MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE,
"upload_file_id" UUID NOT NULL,
CONSTRAINT "report_upload_files_fkc_layout_id" FOREIGN KEY ("upload_file_id")
REFERENCES "tenant"."upload_file" ("id") MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE
)
I want to add something like to the association table CREATE statement:
CHECK ("tenant"."report"."reporting_period" = "tenant"."upload_file"."reporting_period")
You're solving problems that you've created yourself.
Your data model is a typical one-to-many relationship. You don't need an association table. Also, you don't need the same column in two related tables, one of them is redundant. Use the model as shown below to avoid typical problems resulting from lack of normalization.
create table tenant.report (
id uuid primary key default pascal.uuid_generate_v1(),
reporting_period integer not null,
name varchar(64) not null,
description varchar(2048) not null,
unique (reporting_period, name)
);
create table tenant.upload_file (
id uuid primary key default pascal.uuid_generate_v1(),
report_id uuid references tenant.report(id),
file_name varchar(256) not null
);
Using this approach there's no need to ensure that the reporting periods match between the associated records.
BTW, I would use text instead of varchar(n) and integer (serial) instead of uuid.
Using a TRIGGER function I was able to achieve the desired effect:
CREATE FUNCTION "tenant".report_upload_files_create() RETURNS TRIGGER AS
$report_upload_files_create$
BEGIN
IF NOT EXISTS (
SELECT
*
FROM
"tenant"."report",
"tenant"."upload_file"
WHERE
"tenant"."report"."id" = NEW."report_id"
AND
"tenant"."upload_file"."id" = NEW."upload_file_id"
AND
"tenant"."report"."reporting_period" = "tenant"."upload_file"."reporting_period"
)
THEN
RAISE EXCEPTION 'Report and Upload File reporting periods do not match';
END IF;
RETURN NEW;
END
$report_upload_files_create$ LANGUAGE plpgsql;
CREATE TRIGGER "report_upload_files_create" BEFORE INSERT ON "tenant"."report_upload_files"
FOR EACH ROW EXECUTE PROCEDURE "tenant".report_upload_files_create();

How do I make a serial field auto increment and be a foreign key - Postgres

I have 2 tables and I am trying to create a Foreign Key between the two. Here is the structure of my tables:
create table users (
id serial,
user_name varchar(50)
);
create table playlists (
id serial,
user_id integer references users(id)
);
I keep getting this error:
ERROR: there is no unique constraint matching given keys for referenced table "users"
Why is there not a unique constraint? If I create the id in the users table as integer PRIMARY KEY, then everything works fine. How do I fix this where the users id auto increments and can be the FK in the playlists table?
Creating a column of type serial doesn't make it the primary key or constraint it in any way. serial just creates an integer column, creates a sequence, and attaches the sequence to the column to provide default values. From the fine manual:
In the current implementation, specifying:
CREATE TABLE tablename (
colname SERIAL
);
is equivalent to specifying:
CREATE SEQUENCE tablename_colname_seq;
CREATE TABLE tablename (
colname integer NOT NULL DEFAULT nextval('tablename_colname_seq')
);
ALTER SEQUENCE tablename_colname_seq OWNED BY tablename.colname;
If you want your id serial columns to be primary keys (which you almost certainly do), then say so:
create table users (
id serial not null primary key,
user_name varchar(50)
);
create table playlists (
id serial not null primary key,
user_id integer references users(id)
);

Postgresql multiple tables with same foreign key unique constraint

I have following tables on PostgreSQL 9.4
CREATE TABLE "user" (
id SERIAL PRIMARY KEY,
email CHARACTER VARYING NOT NULL,
password CHARACTER VARYING NOT NULL
);
CREATE TABLE "dealer" (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES "user" (id) ON DELETE RESTRICT
);
CREATE TABLE "affiliate" (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES "user" (id) ON DELETE RESTRICT
);
Is it possible to force user_id value to be unique across tables dealer and affiliate?
There are different setups to use for inheritance in SQL and for this you could just use an integer column type in the table user that marks the type of the user and would reference to table user_type (id,name) that would have the values 1,dealer and 2,affiliate:
CREATE TABLE user_type (
id INTEGER PRIMARY KEY, --could be SERIAL
name text
);
INSERT INTO user_type VALUES (1,'dealer'), (2, 'affiliate');
CREATE TABLE "user" (
id SERIAL PRIMARY KEY,
email CHARACTER VARYING NOT NULL,
password CHARACTER VARYING NOT NULL,
user_type INTEGER REFERENCES user_type NOT NULL,
UNIQUE(id,user_type)
);
This in itself wouldn't force uniqueness across tables so after implementing this you would have the following options:
Drop the tables dealer and affiliate - you won't need them if you rely on the type field to see which one the user is.
If you have to keep those inherited tables you can:
Use triggers - these triggers check the uniqueness and would be actived on INSERT or UPDATE
Another (a bit clumsy) solution: add user_type field to both subtables like this:
CREATE TABLE "dealer" (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL,
user_type INTEGER NOT NULL DEFAULT 1 check (user_type = 1),
FOREIGN KEY (user_id,user_type) REFERENCES "user"(id,user_type) ON DELETE RESTRICT
);
CREATE TABLE "affiliate" (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL,
user_type INTEGER NOT NULL DEFAULT 2 check (user_type = 2),
FOREIGN KEY (user_id,user_type) REFERENCES "user"(id,user_type) ON DELETE RESTRICT
);
The checks and foreign keys together make sure you cannot have both types of user in the main table. Note that user_id might be used as the PRIMARY KEY in the subtables too. Currently a row in user might have several dealer rows linked to it so at least you might want to set user_id foreign keys in subtables as UNIQUE.

Postgres: generate IDs automatically

Objective: Have postgres generate ids automatically
CREATE TABLE user_privilege (
id bigint NOT NULL,
name character varying(255) NOT NULL,
version integer
);
CREATE TABLE
INSERT INTO user_privilege (name, version) values ('XYZ', 1);
ERROR: null value in column "id" violates not-null constraint
ALTER TABLE user_privilege ALTER COLUMN id SET DEFAULT nextval('user_privilege_id_seq'::regclass);
ERROR: relation "user_privilege_id_seq" does not exist
Thanks!
EDIT:
I want to keep my id as bigint as all other tables have id as bigint.
You need to use either SERIAL or BIGSERIAL, not BIGINT.
CREATE TABLE user_privilege (
id BIGSERIAL NOT NULL,
It's not clear whether your table has a PRIMARY KEY or UNIQUE constraint. But it should.
You have to create the sequence at first:
CREATE SEQUENCE user_privilege_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
and after you can use it:
ALTER TABLE ONLY user_privilege ALTER COLUMN id SET DEFAULT nextval('user_privilege_id_seq'::regclass);
Here is the create sequence documentation