PostgreSQL: How to link 2 tables? - sql

I have a table defined like this:
CREATE TABLE public.journeys (
journey_id uuid NOT NULL UNIQUE DEFAULT uuid_generate_v4(),
name text NOT NULL,
user_id uuid NOT NULL,
date_created timestamptz NOT NULL,
date_deleted timestamptz NULL,
route_id uuid NOT NULL,
CONSTRAINT fk_users
FOREIGN KEY(user_id)
REFERENCES users(user_id)
);
What I want to do now is create a second table that will connect to this table above. Here's its definition:
CREATE TABLE public.routes (
route_id uuid NOT NULL UNIQUE DEFAULT uuid_generate_v4(),
idx smallint NOT NULL,
date timestamptz NULL,
latitude real NOT NULL,
longitude real NOT NULL,
CONSTRAINT route_key
PRIMARY KEY (route_id, idx),
CONSTRAINT fk_journeys
FOREIGN KEY(route_id)
REFERENCES journeys(route_id)
);
The notion is that for every Journey there will be a connected Route that simply consists of a series of Latitude, Longitude points. So for a given route_id in journeys there will be N records in routes. Every record in a given route will share the same route_id but each one will have a unique idx (ie. 0, 1, 2, ...).
This is the error I'm getting when I try creating public.routes:
SQL Error [42830]: ERROR: there is no unique constraint matching given keys for referenced table "journeys"
What am I doing wrong and how do I fix this?
Robert

I read several more threads on the subject and then realized that that journeys.route_id was not being declared as UNIQUE.
So within the public.journeys declaration, this fixed the problem:
route_id uuid NOT NULL UNIQUE,

Related

Upgrading H2 database gives constraint not found exception

I am trying to upgrade my H2 dependency which I use on my testcases from 1.4.200 to 2.1.212 but it gives a constraint not found exception when I try to do so. The SQL is like this:
CREATE TABLE itineraries
(
id SERIAL,
itinerary_id VARCHAR(36) NOT NULL,
user_id VARCHAR(36) NOT NULL,
created_at TIMESTAMP,
version INTEGER DEFAULT 1,
update_timestamp BIGINT,
CONSTRAINT itineraries_pkey PRIMARY KEY (itinerary_id, user_id),
CONSTRAINT itineraries_user_id_fkey FOREIGN KEY (user_id) REFERENCES users (user_id)
);
CREATE UNIQUE INDEX itineraries_id_key ON itineraries (id);
CREATE TABLE subscriptions
(
subscription_id VARCHAR(36) PRIMARY KEY NOT NULL,
type VARCHAR(10) NOT NULL,
status VARCHAR(20),
last_updated TIMESTAMP NOT NULL,
itinerary_id VARCHAR(36) NOT NULL,
itinerary_db_id BIGINT,
CONSTRAINT subscriptions_it_itinerary_id_fkey FOREIGN KEY (itinerary_db_id) REFERENCES itineraries (id)
);
Which gives the following error:
Cause: org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "PRIMARY KEY | UNIQUE (ID)" not found; SQL statement:
What needs to be changed about the SQL? Since to me it seems like the unique index is created before the create table query.
Unique indexes and unique constraints are different things.
You need to create a constraint and you don't need an index in H2, because unique constraints in H2 create indexes automatically.
CREATE TABLE itineraries
(
id BIGINT GENERATED BY DEFAULT AS IDENTITY CONSTRAINT itineraries_id_key UNIQUE,
…
I changed PostgreSQL-compatibility SERIAL to standard-compliant BIGINT GENERATED BY DEFAULT AS IDENTITY, because SERIAL is a legacy data type and it creates an INTEGER column, but it is referenced by subscriptions.itinerary_db_id with BIGINT data type, it isn't strictly required, but normally you should use the same data type for both columns, you can choose BIGINT, INTEGER or some other numeric type.
Usually it is more reasonable to create a primary key constraint for id column and a unique constraint for (itinerary_id, user_id), but you may have own reasons for such schema.
Also VARCHAR(36) looks like a some data type for UUID values, H2 has more efficient specialized UUID data type for this purpose.

Is it possible to find the row using something other than the primaryKey on SQLite?

I want to be able to find the value using either network_id or username.
Yet the following sintax gives the error of more than one primary key (as expected).
CREATE TABLE Player(
network_id TEXT not null,
username varchar2(50) not null,
value INTEGER,
CONSTRAINT player_pk1 PRIMARY KEY (username),
CONSTRAINT player_pk2 PRIMARY KEY (network_id)
);
Is there a way that I could do this in Sqlite?
A primary key has three components to its definition:
NOT NULL
UNIQUE
Only one per table
That is why you cannot have more than one. But you can have any number of NOT NULL UNIQUE columns:
CREATE TABLE Player(
network_id TEXT not null,
username varchar2(50) not null,
value INTEGER,
CONSTRAINT player_pk1 PRIMARY KEY (username),
CONSTRAINT unq_player_network_id UNIQUE (network_id)
);

Transforming into Normal Form

There are zones, avatars and instances of zones. Avatar must belong to zero or one instance for each zone.
CREATE TABLE zones (
id SERIAL NOT NULL PRIMARY KEY,
name VARCHAR NOT NULL,
...
);
CREATE TABLE avatars (
id SERIAL NOT NULL PRIMARY KEY,
...
);
CREATE TABLE instances (
id SERIAL NOT NULL PRIMARY KEY,
zone_id INTEGER REFERENCES zones NOT NULL,
...
);
CREATE TABLE avatar_instances (
avatar_id INTEGER REFERENCES avatars NOT NULL,
zone_id INTEGER REFERENCES zones NOT NULL,
instance_id INTEGER REFERENCES instances NOT NULL,
PRIMARY KEY(avatar_id, zone_id)
);
I'm not happy with the schema above, because zone_id in each record in avatar_instances has to agree with the zone_id inside of the respective instances row.
Ideally I'd like a unique index on avatar_instances which "reaches inside" of the instances table to see instances.zone_id.
e.g.
CREATE TABLE avatar_instances (
avatar_id INTEGER REFERENCES avatars NOT NULL,
instance_id INTEGER REFERENCES instances NOT NULL,
PRIMARY KEY(avatar_id, instance.zone_id)
);
How can I transform this schema into Nth normal form, while preserving the restriction that 'each avatar must belong to zero or one instance for each zone'?
Create a unique and add a composite FK referencing unique.
CREATE TABLE instances (
id SERIAL NOT NULL PRIMARY KEY,
zone_id INTEGER REFERENCES zones NOT NULL,
UNIQUE (zone_id, id)
);
CREATE TABLE avatar_instances (
avatar_id INTEGER REFERENCES avatars NOT NULL,
zone_id INTEGER NOT NULL,
instance_id INTEGER NOT NULL,
CONSTRAINT fk_ai2i FOREIGN KEY (zone_id, instance_id) REFERENCES instances (zone_id, id),
PRIMARY KEY(avatar_id, zone_id)
);
Allow null in avatar_instances.instance_id if Avatar must belong to zero or one instance for each zone.

Postgres constraint and foreign key

Is it possible to enforce a constraint and foreign key only when all values are not null? For example in a polymorphic relation one object would have multiple foreign keys, but often only one is used, so there is a violation. How can I avoid this?
CREATE TABLE IF NOT EXISTS acos (
id SERIAL PRIMARY KEY,
created_at timestamp,
updated_at timestamp,
owner_id varchar(64) NOT NULL,
stack_id varchar(64) DEFAULT NULL,
qac_id varchar(64) DEFAULT NULL,
rights varchar(1024)[],
)
Either stack_id or qac_id is set, but never both.
Same goes for the following constraint:
CONSTRAINT name_owner_id UNIQUE
(
name, owner_id
)
I would like to ignore the constraint when either name or owner_id is null.
Unless I misunderstand you, PostgreSQL already works the way you want by default:
You can have the same entries twice in a UNIQUE constraint as long as one of them is NULL.
If a foreign key column is NULL, the constraint is not enforced, as long as you stick with the default MATCH SIMPLE.
For a condition like “one of two values must be NOT NULL”, you can use a check constraint.

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".