using foreign key value directly in INSERT INTO statement - sql

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)
)

Related

sql state 42830 : FK referring to a single PK in a Table with composite primary key

DROP TABLE IF EXISTS "Table_1";
CREATE TABLE "Table_1" (
"id" SERIAL,
"other_id" int4 NOT NULL,
"col3" varchar(250) COLLATE pg_catalog."default",
"col4" text COLLATE pg_catalog."default",
"col5" int4,
"col6" int4
);
DROP TABLE IF EXISTS "Table_2";
CREATE TABLE "Table_2" (
"id" SERIAL,
"id_table_2" int4 NOT NULL,
"col3" varchar(20) COLLATE pg_catalog."default",
"col4" int4
);
DROP TABLE IF EXISTS "Table_3";
CREATE TABLE "Table_3" (
"id" SERIAL,
"id_table_3" int4 NOT NULL,
"id_item" int4 NOT NULL,
"col4" int4,
"col5" int4);
CREATE INDEX "first_fkey" ON "Table_1" USING btree (
"other_id" pg_catalog."int4_ops" ASC NULLS LAST
);
ALTER TABLE "Table_1" ADD CONSTRAINT "Table_1_pkey" PRIMARY KEY ("id", "other_id");
CREATE INDEX "second_fkey" ON "Table_2" USING btree (
"id_table_2" pg_catalog."int4_ops" ASC NULLS LAST
);
CREATE INDEX "third_1_fkey" ON "Table_3" USING btree (
"id_table_3" pg_catalog."int4_ops" ASC NULLS LAST,
"id_item" pg_catalog."int4_ops" ASC NULLS LAST
);
CREATE INDEX "third_2_fkey" ON "Table_3" USING btree (
"id_table_3" pg_catalog."int4_ops" ASC NULLS LAST
);
ALTER TABLE "Table_3" ADD CONSTRAINT "Table_3_pkey" PRIMARY KEY ("id", "id_table_3");
ALTER TABLE "Table_2" ADD CONSTRAINT "Table_2_pkey" PRIMARY KEY ("id");
ALTER TABLE "Table_3" ADD CONSTRAINT "Table_3_fkey" FOREIGN KEY ("id_item", "id_table_3") REFERENCES "Table_1" ("id", "other_id") ON DELETE NO ACTION ON UPDATE NO ACTION;
ALTER TABLE "Table_2" ADD CONSTRAINT "Table_2_fkey" FOREIGN KEY ("id_table_2") REFERENCES "Table_1" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION;
Basically this is the SQL I made to create Table_1, Table_2 and Table_3, with primary keys, and Foreign in Table_2,Table_3 referring to Table_1's main id or couple Primary key, problem is I keep getting this error :
ERROR: there is no unique constraint matching given keys for referenced table "Table_1"
SQL state: 42830
So far I've did everything by the book, the error suggests that I dont have a unique constraint, while I did create Primary key as soon as I created the table, so I'm totally lost right now.
using PostgreSql 12.
You have a composite primary key:
ALTER TABLE "Table_1" ADD CONSTRAINT "Table_1_pkey" PRIMARY KEY ("id", "other_id");
This is silly. You know that id is unique, because it is serial. So, just keep it as the primary key:
ALTER TABLE "Table_1" ADD CONSTRAINT "Table_1_pkey" PRIMARY KEY ("id");
More suggestions:
Do not use double quotes around identifiers. They just make queries harder to read and write.
Postgres now recommends id int generated always as identity instead of serial.
I recommend doing inline primary key definitions: id int generated always as identity primary key.
If you want an index on the other column, then add that as a separate index.
So the issue was That the composite primary key doesnt affect unique constraint on each column, So it had to be done manually for each column like this :
CONSTRAINT id_tb3 UNIQUE ("id")
inside the concerned table. and it stopped showing up the issue.

ERROR: No unique constraint matching when having FK

I have three tables that are linked together
My script:
-- Ticket --
CREATE TABLE public.ticket (
id bigint NOT NULL,
libelle character varying(255) NOT NULL,
description character varying(255) NOT NULL,
status character varying(255) NOT NULL,
date_creation timestamp NOT NULL,
date_modification timestamp NOT NULL,
user_createur_id bigint,
referent_realisateur_id bigint,
CONSTRAINT pk_ticket PRIMARY KEY (id)
);
-- Ticket_Avance TABLE --
CREATE TABLE public.ticket_avance (
id bigint NOT NULL,
date_livraison_souhaite timestamp NOT NULL,
date_engagement_livraison timestamp NOT NULL,
referent_demandeur_id bigint
);
ALTER TABLE public.ticket_avance ADD CONSTRAINT "fk_ticket_ticketAvance" FOREIGN KEY (id)
REFERENCES public.ticket (id) MATCH SIMPLE
ON DELETE NO ACTION ON UPDATE NO ACTION;
-- Demande_Travaux TABLE --
DROP TABLE IF EXISTS public.demande_travaux CASCADE;
CREATE TABLE public.demande_travaux (
id bigint NOT NULL,
contrat_id bigint
);
ALTER TABLE public.demande_travaux ADD CONSTRAINT "fk_ticketAvance_DDT" FOREIGN KEY (id)
REFERENCES public.ticket_avance (id) MATCH FULL
ON DELETE NO ACTION ON UPDATE NO ACTION;
I have this error on the demand_travaux creation
SQL Error [42830]: ERROR: there is no unique constraint matching given keys for referenced table "ticket_avance"
ERROR: there is no unique constraint matching given keys for referenced table "ticket_avance"
ERROR: there is no unique constraint matching given keys for referenced table "ticket_avance"
You have forgotten to declare column ID in table ticket_avance as primary key.
Please use the following SQL:
-- Ticket_Avance TABLE --
CREATE TABLE public.ticket_avance (
id bigint NOT NULL,
date_livraison_souhaite timestamp NOT NULL,
date_engagement_livraison timestamp NOT NULL,
referent_demandeur_id bigint,
CONSTRAINT pk_avance PRIMARY KEY (id) --- add this
);
you need add constraint in Ticket_Avance table because you provide reference this id to demande_travaux
-- Ticket_Avance TABLE --
CREATE TABLE ticket_avance (
id bigint NOT NULL,
date_livraison_souhaite timestamp NOT NULL,
date_engagement_livraison timestamp NOT NULL,
referent_demandeur_id bigint,
CONSTRAINT pk_ticket_avance PRIMARY KEY (id) //constraint that you need
);
ALTER TABLE demande_travaux ADD CONSTRAINT "fk_ticketAvance_DDT" FOREIGN KEY (id)
REFERENCES ticket_avance (id) MATCH FULL
ON DELETE NO ACTION ON UPDATE NO ACTION;
here is the demo link of your full query
A foreign key constraint has to target a primary key or unique constraint. The database has to be able to identify a single row in the "parent" table.
You could add primary key constraints:
ALTER TABLE public.ticket_avance ADD PRIMARY KEY (id);
In addition, you should have an index on the column on which the foreign key is defined, particularly if you plan to delete parent rows. With the primary key above, you have such an index on id, but you also should have one on demande_travaux.
The simplest way is to define id as primary key there too:
ALTER TABLE public.demande_travaux ADD PRIMARY KEY (id);

Fill in bridge table with query

I have four tables, the main purpose of these tables is to have a many to many keyword to message relationship. each keyword can have many messages and each message can have many keywords they are related together if the category id matches.
CREATE TABLE public.trigger_category
(
id integer NOT NULL DEFAULT nextval('trigger_category_id_seq'::regclass),
description text COLLATE pg_catalog."default" NOT NULL,
CONSTRAINT trigger_category_id PRIMARY KEY (id)
)
CREATE TABLE public.trigger_keyword
(
id integer NOT NULL DEFAULT nextval('trigger_keyword_id_seq'::regclass),
keyword text COLLATE pg_catalog."default" NOT NULL,
category_id bigint NOT NULL,
CONSTRAINT trigger_keyword_id PRIMARY KEY (id),
CONSTRAINT trigger_keyword_category_id_fkey FOREIGN KEY (category_id)
REFERENCES public.trigger_category (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE NO ACTION
)
CREATE TABLE public.trigger_message
(
id integer NOT NULL DEFAULT nextval('trigger_message_id_seq'::regclass),
message text COLLATE pg_catalog."default" NOT NULL,
category_id bigint NOT NULL,
CONSTRAINT trigger_message_id PRIMARY KEY (id),
CONSTRAINT trigger_message_category_id_fkey FOREIGN KEY (category_id)
REFERENCES public.trigger_category (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION
)
CREATE TABLE public.trigger_keyword_trigger_message
(
trigger_keyword_id bigint NOT NULL,
trigger_message_id bigint NOT NULL,
CONSTRAINT trigger_keyword_trigger_message_trigger_keyword_id_trigger_mess PRIMARY KEY (trigger_keyword_id, trigger_message_id),
CONSTRAINT trigger_keyword_trigger_message_trigger_keyword_id_fkey FOREIGN KEY (trigger_keyword_id)
REFERENCES public.trigger_keyword (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE NO ACTION,
CONSTRAINT trigger_keyword_trigger_message_trigger_message_id_fkey FOREIGN KEY (trigger_message_id)
REFERENCES public.trigger_message (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE NO ACTION
)
I manually insert keywords in the trigger_keyword table and I manually insert messages in the trigger_message table, if they are related then they would get the same category_id
Is it possible to write a query that would automatically go through the rows and if a keyword and message have the same category_id then it would create all the appropriate rows for the bridge table trigger_keyword_trigger_message?
You could achieve this with an Oracle Merge Query.
The USING clause selects all records to insert, and the WHEN MATCHED does to inserts in the bridge table.
MERGE INTO trigger_keyword_trigger_message tktm
USING (
SELECT tk.id tk_id, tm.id tm_id
FROM
trigger_keyword tk
INNER JOIN trigger_message tm on tm.category_id = tk.category_id
) us
WHEN MATCHED THEN
INSERT (tktm.trigger_keyword_id, tktm.trigger_message_id)
VALUES (us.tk_id, us.tm_id)
;

Inheritance PostgreSQL

I have 2 tables under PostgreSQL:
a table property that is the mother table (id, created_at ..) and
a habitation table (surface, room, etc...) that inherits property. On the mother table, I have a many-many relationship table. I have added results in the "habitation" table.
When I try to insert "property_table" with habitation IDs, I have a SQL error telling me that the property_id ID does not exist. It exists when I go to see the property or habitation table. I want to insert habitation IDs (so property) in property_tag , should I force insert? Help please
The SQL schemas :
CREATE TABLE "property"
(
id serial NOT NULL,
created_at timestamp with time zone NOT NULL DEFAULT now(),
updated_at timestamp with time zone NOT NULL DEFAULT now(),
address_id int NOT NULL,
permission int NOT NULL DEFAULT 6,
user_id int NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (address_id) REFERENCES "address"(id),
FOREIGN KEY (user_id) REFERENCES "user"(id)
);
CREATE TABLE habitation (
total_surface float,
living_surface float,
ground_surface float,
ground_surface_unity character varying(15) DEFAULT 'm2',
room integer,
bedroom integer,
floor integer,
level integer,
year integer
) INHERITS (property);
CREATE TABLE property_tag
(
tag_id int NOT NULL,
property_id int NOT NULL,
PRIMARY KEY (tag_id, property_id),
FOREIGN KEY (tag_id) REFERENCES "tag"(id) ON DELETE CASCADE,
FOREIGN KEY (property_id) REFERENCES "property"(id) ON DELETE CASCADE
);
CREATE TABLE "tag"
(
id serial NOT NULL,
created_at timestamp with time zone NOT NULL DEFAULT now(),
updated_at timestamp with time zone NOT NULL DEFAULT now(),
name character varying(127) NOT NULL,
user_id int NOT NULL,
color character varying(7) NOT NULL DEFAULT 'A4A4A8',
PRIMARY KEY (id),
FOREIGN KEY (user_id) REFERENCES "user"(id) ON DELETE CASCADE
);
When you insert into table "habitation", the row exists physically only in table "habitation", but not in "property".
Your table "property_tag" references to table "property" (which have no rows, cause rows are in table "habitation") and therefore you are getting error during insert.
You have to change your reference in table "property_tag" from "property" to "habitation".

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();