Exclusion constraint for unique constraint, is there a difference? (bis) - postgresql

Assume the following table:
CREATE TABLE zoo (
cage INTEGER,
animal TEXT,
);
What is the real, effective difference between:
ALTER TABLE zoo ADD CONSTRAINT x EXCLUDE USING gist (cage WITH =, animal WITH <>)
and:
CREATE UNIQUE INDEX ON zoo(cage, animal)
?
I took this example from https://www.postgresql.org/docs/current/static/btree-gist.html and was confused why they are representing this with an exclude constraint instead of good-old unique constraint. So I am wondering if there is really a difference.

The two do different things.
The exclusion constraint is doing just what the documentation says -- it is guaranteeing that a cage has exactly one type of animal. No lions and sheep in the cage together.
The unique index/constraint says that there are no duplicate animals in the cage. So, a lion and sheep is fine (from that perspective). But two lions or two sheep is not. (Of course, the lion and sheep example is likely to quickly result in a satisfied unique constraint).
This type of "exclusion" constraint could be handled using foreign key constraints. Something like this:
CREATE TABLE cages (
CageId serial,
AnimalType varchar(255) -- or whatever
);
CREATE TABLE CageAnimals (
CageAnimalId serial,
CageId int references Cages(CageId)
AnimalName varchar(255)
);
(The model would be a bit more complicated in real life.)

Related

PostgreSQL database: Get rid of redundant transitive relation (maybe 3NF is failed)

I'm creating a hybrid between "X-Com Enemy Unknown" and "The Sims". I maintain game state in a database--PostgreSQL--but my question is structural, not engine-specific.
As in X-Com, there are some bases in different locations, so I create a table named Base with ID autoincrement identity as primary key.
Every base has some facilities in its territory, so I create a table named Facility with a foreign key Facility.Base_ID, referring to Base.ID.
Every base has some landing crafts in its hangars, so I create a table named Craft with a foreign key Craft.Base_ID, referring to Base.ID.
Every base has some troopers in its barracks, so I create a table named Trooper with a foreign key Trooper.Base_ID, referring to Base.ID.
Just to this point, everything seems to be ok, doesn't it? However...
I want to have some sort of staff instruction. Like in the X-Com game, every trooper can be assigned to some craft for offense action, or can be unassigned. In addition, every trooper can be assigned to some facility (or can be unassigned) for defense action. So, I have to add nullable foreign keys Trooper.Craft_ID and Trooper.Facility_ID, referring to Craft.ID and Facility.ID respectively.
That database has a redundancy. If some trooper is assigned to a craft or to a facility (or both), it has two (or even three) relations to the base--one direct relation through its Base_ID and some indirect relations as Facility(Trooper.Facility_ID).Base_ID and Craft(Trooper.Craft_ID).Base_ID. Even if I get rid of Trooper.Base_ID (e.g. I can make both assignment mandatory and create a mock craft and a mock facility in every base), I can't get rid of both trooper-facility-base and trooper-craft-base relations.
In addition to this redundancy, there is a worse problem--in case of a mistake, some trooper can be assigned to a craft from one base and to a facility from another base, that's a really nasty situation. I can prohibit it in the application business logic tier, but it's still allowed by the database.
There can be some constraints to apply, but is there any structural modification to the schema that can get rid of the redundancy and potential inconsistency as a result of a good structure, not as a result of constraints?
CREATE TABLE base (
id int PRIMARY KEY
);
CREATE TABLE facility (
id int PRIMARY KEY,
base_id int REFERENCES base
);
CREATE TABLE craft (
id int PRIMARY KEY,
base_id int REFERENCES base
);
CREATE TABLE trooper (
id int PRIMARY KEY,
assigned_facility_id int REFERENCES facility,
assigned_craft_id int REFERENCES craft,
base_id int REFERENCES base
);
Now I want to get some sort of constraints on a trooper t so that
facilities.get(t.assigned_facility_id).base_id IS NULL OR EQUAL TO t.base_id
crafts.get(t.assigned_craft_id).base_id IS NULL OR EQUAL TO t.base_id
This hypothetical constraint has to be applied to table trooper, because it applies in boundaries of each trooper row separately. Constraints on one table have to check equality between fields of two other tables.
I would like to create a database schema where there is exactly one way, having a trooper.id, to find its referenced base.id. How do I normalise my schema?

PostgreSQL table inheritance and constraints

I have a DDL for some tables similar to the following:
CREATE TABLE devices
(
device_uuid uuid NOT NULL,
manufacturer_uuid NOT NULL,
...
CONSTRAINT device_manufacturer_uuid_fkey FOREIGN_KEY (manufacturer_uuid)
REFERENCES manufacturer (manufacturer_uuid) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT devices_device_uuid_key UNIQUE (device_uuid),
CONSTRAINT devices_pkey PRIMARY KEY (device_uuid)
);
I might have different types of devices, e.g. "peripherals", "power", "graphics", etc.
There are two approaches I can take:
Add a simple type column to this table
Pros: I have a pretty simple database structure.
Cons: There could potentially be hundreds of thousands of entries, which could lead to performance problems. The list of "devices" would be searched fairly regularly, and searching through all those entries to find all those of type "peripherals" every time might not be great.
Inherit from the above table for every type.
The DDL for this would look similar to the following (I believe):
CREATE TABLE devicesperipherals
(
type device NOT NULL,
CONSTRAINT devicesperipherals_manufacturer_uuid_fkey FOREIGN_KEY (manufacturer_uuid)
REFERENCES manufacturer (manufacturer_uuid) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT devicesperipherals_device_uuid_key UNIQUE (device_uuid)
CONSTRAINT devices_manufacturer_uuid_fkey FOREIGN_KEY (manufacturer_uuid)
REFERENCES manufacturer (manufacturer_uuid) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
)
INHERITS (devices)
WITH (
OIDS=FALSE
);
Pros: From what I know about table inheritance, this approach should have better performance.
Cons: More complex database structure, more space required for indexing.
Which is the preferred approach in such a case? Why?

How to Cross-Reference Data from Other Tables for Partition Checks?

I have a main, parent table 'transaction_', which I would like to partition. I know that I can easily partition based on any of the fields listed in transaction_, including foreign keys, using the check constraint within any child table. Essentially what I would like to know is whether, in my check constraint, I can somehow refer to other fields in a table for which I have a foreign key. I would like to avoid having too many foreign keys from the seller and client tables in my transaction_ table as that seems like a lot of unnecessary duplication.
CREATE SEQUENCE transaction_id_seq;
CREATE TABLE transaction_ (
transaction_id bigint PRIMARY KEY DEFAULT nextval('transaction_id_seq'),
seller_id int REFERENCES seller(id),
client_id int REFERENCES client(id),
purchase_date date,
purchase_time time,
price real,
quantity int
);
CREATE TABLE seller (
id int PRIMARY KEY,
name text,
location text,
open_time time,
close_time time
);
CREATE TABLE client (
id int PRIMARY KEY,
name text,
billing_suburb text,
billing_zipcode int
);
So for example, I think that I can do the following:
CREATE TABLE transaction_client1_20130108 (
CHECK ( client_id = 1 AND purchase_date = DATE '2013-01-08')
) INHERITS (transaction_);
I would like to do something like the following:
CREATE TABLE transaction_sellerZip90210_20130108 (
CHECK ( client(billing_zipcode) = 90210 AND purchase_date = DATE '2013-01-08')
) INHERITS (transaction_);
Using the following but happy to update if that provides a better solution:
mydb=#SELECT version();
PostgreSQL 9.1.11 on x86_64-unknown-linux-gnu, compiled by gcc (Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1, 64-bit
whether, in my check constraint, I can somehow refer to other fields in a table for which I have a foreign key
Not directly. CHECK constraints may not contain subqueries. However, you can work around that by declaring a LANGUAGE SQL function that does the work you want and using that from the CHECK constraint.
This isn't safe, though. The query planner expects that a CHECK constraint will be accurate and truthful, and may make optimization decisions based on it. So it's not a good idea to trick the system by adding a roundabout constraint on another table.
Instead, I recommend using triggers to sanity-check things like this, enforcing the check at the time any DML is run.

How do I implement circular constraints in PostgreSQL?

I want to enforce that a row in one table must have a matching row in another table, and vice versa. I'm currently doing it like this to work around the fact that you can't REFERENCE a table that hasn't been created yet. Is there a more natural way that I'm not aware of?
CREATE TABLE LE (id int PRIMARY KEY);
CREATE TABLE LE_TYP (id int PRIMARY KEY, typ text);
ALTER TABLE LE ADD CONSTRAINT
twowayref FOREIGN KEY (id) REFERENCES LE_TYP (id) DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE LE_TYP ADD CONSTRAINT
twowayref_rev FOREIGN KEY (id) REFERENCES LE (id) DEFERRABLE INITIALLY DEFERRED;
What you are doing is ideal where you have circular constraints. There is nothing that can be improved on in your solution.
There are however two points that are worth mentioning briefly just for the next reader. The first is that with this solution in your code isn't obvious why you are choosing the order you are. If you are doing this you probably want to be careful about which constraint is deferred. Usually this is obvious, but you probably want to choose your insert order based on other foreign keys referencing the appropriate tables.
The other is that of the idea of whether it is desirable to engineer around circular dependencies. On one hand it may seem like a good idea when doing so seems like an obvious simplification.
It is however worth pointing out that in a case like this you may want to look at multiple table inheritance instead. In this way you might do:
CREATE TABLE p1 (
id serial not null,
attribute1 int not null,
attribute2 text not null,
check (attribute1 > 1 or length(attribute2) > 10)
);
CREATE TABLE p2 (
id int not null, -- shared primary key with p1
attribute3 int not null,
attribute4 text not null
);
CREATE TABLE combined (
primary key (id)
) INHERITS (p1, p2);
This puts all your columns in one table, but gives you logical interfaces to that table as if it is two tables joined on a common field.

Primary key defined by many attributes?

Can I define a primary key according to three attributes? I am using Visual Paradigm and Postgres.
CREATE TABLE answers (
time SERIAL NOT NULL,
"{Users}{userID}user_id" int4 NOT NULL,
"{Users}{userID}question_id" int4 NOT NULL,
reply varchar(255),
PRIMARY KEY (time, "{Users}{userID}user_id", "{Users}{userID}question_id"));
A picture may clarify the question.
Yes you can, just as you showed.(though I question your naming of the 2. and 3. column.)
From the docs:
"Primary keys can also constrain more than one column; the syntax is similar to unique constraints:
CREATE TABLE example (
a integer,
b integer,
c integer,
PRIMARY KEY (a, c)
);
A primary key indicates that a column or group of columns can be used as a unique identifier for rows in the table. (This is a direct consequence of the definition of a primary key. Note that a unique constraint does not, by itself, provide a unique identifier because it does not exclude null values.) This is useful both for documentation purposes and for client applications. For example, a GUI application that allows modifying row values probably needs to know the primary key of a table to be able to identify rows uniquely.
A table can have at most one primary key (while it can have many unique and not-null constraints). Relational database theory dictates that every table must have a primary key. This rule is not enforced by PostgreSQL, but it is usually best to follow it.
"
Yes, you can. There is just such an example in the documentation.. However, I'm not familiar with the bracketed terms you're using. Are you doing some variable evaluation before creating the database schema?
yes you can
if you'd run it - you would see it in no time.
i would really, really, really suggest to rethink naming convention. time column that contains serial integer? column names like "{Users}{userID}user_id"? oh my.