SQL issue with multiple foreign - mysql

Issue:
I'm using PostgreSQL Database.
I have one table (Albums) to be linked to two other tables (Clients, Domains). So if you are Client or Domain you can have Album. But in Albums table owner can handle only single foreign key. How can I solve this issue?
Dream: Single Album can own only (1) Client or Domain. Need fix issue with foreign keys. Albums: id | owner (multiple foreign -> Clients:id or Domains:id) --> can not do this | name. I just need some smart rework.
Tables (now can have Album only Domain):
Albums
Clients
Domains
Albums (table with foreign key yet):
id | owner (foreign key -> Domains:id) | name
Clients:
id | first_name | last_name
Domains:
id | owner | name

Add 2 FK columns, and a CHECK constraint, to enforce only one of them is NOT NULL...
Something like this:
CREATE TABLE albums (
id serial PRIMARY KEY,
client_id integer,
domain_id integer,
name varchar(255) NOT NULL,
FOREIGN KEY (client_id) REFERENCES clients(id),
FOREIGN KEY (domain_id) REFERENCES domains(id),
CHECK ((client_id IS NULL) <> (domain_id IS NULL))
);
To query you can use something like this:
SELECT a.id, COALESCE(c.id, d.id) AS owner_id, COALESCE(c.name, d.name) AS owner_name,
a.name AS title
FROM albums a
LEFT JOIN clients c ON a.client_id = c.id
LEFT JOIN domains d ON a.domain_id = d.id
#e_i_pi's version
CREATE TABLE entities (
id serial PRIMARY KEY,
type integer, -- could be any other type
-- any other "common" values
);
CREATE TABLE client_entities (
id integer PRIMARY KEY, -- at INSERT this comes from table `entities`
name varchar(255) NOT NULL,
);
CREATE TABLE domain_entities (
id integer PRIMARY KEY, -- at INSERT this comes from table `entities`
name varchar(255) NOT NULL,
);
CREATE TABLE albums (
id serial PRIMARY KEY,
owner_id integer FOREIGN KEY REFERENCES entities(id), -- maybe NOT NULL?
name varchar(255) NOT NULL,
);
Query:
SELECT a.id, owner_id, COALESCE(c.name, d.name) AS owner_name, a.name AS title
FROM albums a
LEFT JOIN entities e ON a.owner_id = e.id
LEFT JOIN client_entities c ON e.id = c.id AND e.type = 1 -- depending on the type of `type`
LEFT JOIN domain_entities d ON e.id = d.id AND e.type = 2

Righto, so as suggested in the comment to the answer by #UsagiMiyamoto, there is a way to do this that allows declaration of entity types, with cascading. Note that this solution doesn't support unlimited entity types, as we need to maintain concrete FK constraints. There is a way to do this with unlimited entity types, but involves triggers and quite a bit of nastiness.
Here's the easy to understand solution:
-- Start with a test schema
DROP SCHEMA IF EXISTS "entityExample" CASCADE;
CREATE SCHEMA IF NOT EXISTS "entityExample";
SET SEARCH_PATH TO "entityExample";
-- We'll need this to enforce constraints
CREATE OR REPLACE FUNCTION is_entity_type(text, text) returns boolean as $$
SELECT TRUE WHERE $1 = $2
;
$$ language sql;
-- Unique entity types
CREATE TABLE "entityTypes" (
name TEXT NOT NULL,
CONSTRAINT "entityTypes_ukey" UNIQUE ("name")
);
-- Our client entities
CREATE TABLE clients (
id integer PRIMARY KEY,
name TEXT NOT NULL
);
-- Our domain entities
CREATE TABLE domains (
id integer PRIMARY KEY,
name TEXT NOT NULL
);
-- Our overaching entities table, which maintains FK constraints against clients and domains
CREATE TABLE entities (
id serial PRIMARY KEY,
"entityType" TEXT NOT NULL,
"clientID" INTEGER CHECK (is_entity_type("entityType", 'client')),
"domainID" INTEGER CHECK (is_entity_type("entityType", 'domain')),
CONSTRAINT "entities_entityType" FOREIGN KEY ("entityType") REFERENCES "entityTypes" (name) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "entities_clientID" FOREIGN KEY ("clientID") REFERENCES "clients" (id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "entities_domainID" FOREIGN KEY ("domainID") REFERENCES "domains" (id) ON DELETE CASCADE ON UPDATE CASCADE
);
-- Our albums table, which now can have one owner, but of a dynam ic entity type
CREATE TABLE albums (
id serial PRIMARY KEY,
"ownerEntityID" integer,
name TEXT NOT NULL,
CONSTRAINT "albums_ownerEntityID" FOREIGN KEY ("ownerEntityID") REFERENCES "entities"("id")
);
-- Put the entity type in
INSERT INTO "entityTypes" ("name") VALUES ('client'), ('domain');
-- Enter our clients and domains
INSERT INTO clients VALUES (1, 'clientA'), (2, 'clientB');
INSERT INTO domains VALUES (50, 'domainA');
-- Make sure the clients and domains are registered as entities
INSERT INTO entities ("entityType", "clientID")
SELECT
'client',
"clients".id
FROM "clients"
ON CONFLICT DO NOTHING
;
INSERT INTO entities ("entityType", "domainID")
SELECT
'domain',
"domains".id
FROM "domains"
ON CONFLICT DO NOTHING
;
If you don't like the idea of inserting twice (once in client, once in entites, for example) you can have a trigger on inserts in the clients table, or alternately create an insert function that inserts to both tables at once.

Related

Swap foreign keys in MySql

I have a MySql database with a Users table:
create table Users
(
Id varchar(255) charset utf8mb4 not null primary key,
ProfileId int auto_increment,
#other fields
);
ProfileId field is used as a foreign key for many tables. E.g.:
create table Fingerprints
(
Id int auto_increment primary key,
ProfileId int null,
constraint FK_Fingerprints_Users_ProfileId
foreign key (ProfileId) references Users (ProfileId)
);
I have two Users records and want to swap their ProfileIds without any other changes. How can I do it? I would prefer not to drop foreign key constrain because the database is on production and there are a lot of related tables where I have to do that.
Update:
The data is the following:
User1: ProfileId = 1,
User2: ProfileId = 2
#the other field doesn't matter
I want to swap their profileIds, so user1 contained user2's data from the related table by foreign key and vise versa.
Presumably, you don't want to change the foreign key relationships (they are not defined as cascading) I would suggest that you change the other columns:
update users u join
users u2
on (u2.profileid, u.profileid) in ( (#id1, #id2), (#id2, id1) )
set u.id = u2.id,
u.others = u2.others;

MySQL creating a database table query

I want to create a join table between two or more tables.
The tables are Student, and course.
Join table will be enrolled.
the business rule is that a student can only enroll in one course at a time.
I want to prevent a user from creating additional enrollments after making 1 enrollment in a course.
I am not sure what type of contraint this will be, or if its even possible.
Can anyone help?
thank you
note: I dont think it is possible to create a Primary key as the primary key of another table, ie the studentID of the student table. If i could I would. breaks the rules i think. This would be a foreign key which is not unique.
If the business rule should be ignored, and assume that a student naturally will only enroll in one course at a time.. maybe ill stop worrying...
You could create a unique index for the id_student but this provably will bring problems if a student try to register in other course later. You shoud include the id_course into the unique constraint.
ALTER TABLE table_name ADD CONSTRAINT constraint_name UNIQUE(studentId, course_id)
Other solution could be creating a Trigger.
The trigger should be a "before insert" trigger. This one should serch for information related to the student in the table, if the table doesn´t has information then insert information, else do nothing.
CREATE TRIGGER 'ONE_STUDENT_PER_COURSE'
BEFORE INSERT ON 'Enrollments'
FOR EACH ROW
BEGIN
DECLARE student_id INT;
SELECT n.id_student INTO student_id
FROM table_enrollments n
`IF student_id IS NULL THEN
/* I DON´T REALLY KNOW EXACTLY THE SINTAXIS FOR INSERTING DATA OF THE BEFORE INSERT FOR YOU VERSION OF MYSQL
BUT TRY THIS ONE
*/
INSERT INTO table_enrollments (student_id, course_id) SELECT student_id, course_id FROM inserted
END IF;
END; $$`
You can create unique index in join table.
CREATE UNIQUE INDEX index_name
ON your_join_table (studentId);
Each table can have a primary key. Two tables can have the same primary key defined. (But the implementation depends on the Entity Relationship Model, what we've discovered about the entities and relationships between the entities.
Based on the information provided in the question, a possible implementation of an enrollment table:
CREATE TABLE current_enrollment
( student_id INT UNSIGNED NOT NULL COMMENT 'pk, fk ref student.id'
, course_id INT UNSIGNED NOT NULL COMMENT 'pk, fk ref course.id'
, PRIMARY KEY (student_id, course_id)
, CONSTRAINT FK_currrent_enrollment_student FOREIGN KEY ( student_id )
REFERENCES student (id) ON UPDATE CASCADE ON DELETE RESTRICT
, CONSTRAINT FK_currrent_enrollment_course FOREIGN KEY ( course_id )
REFERENCES course (id) ON UPDATE CASCADE ON DELETE RESTRICT
)
The datatypes of the foreign key columns must match the datatypes of the referenced columns; in this example, i've assumed primary key columns id in both student and course, defined as datatype INT UNSIGNED
In this example, the PRIMARY KEY constraint enforces a unique constraint on the combination of (student_id,course_id). An attempt to insert a second enrollment (same student in the same course) would be a duplicate row, and that would throw a constraint violation, preventing the row from being added.
If enrollment turns out to be an entity in the model, with its own attributes, I'd opt to add a separate id column as a surrogate primary key, with a unique constraint on (student_id,course_id)
CREATE TABLE current_enrollment
( id INT UNSIGNED NOT NULL COMMENT 'pk'
, student_id INT UNSIGNED NOT NULL COMMENT 'fk ref student.id'
, course_id INT UNSIGNED NOT NULL COMMENT 'fk ref course.id'
, enrollment_dt DATETIME
, status VARCHAR(8)
, approval_by VARCHAR(8)
, PRIMARY KEY (id)
, CONSTRAINT current_enrollment_UX1 UNIQUE KEY (student_id, course_id)
, CONSTRAINT FK_currrent_enrollment_student FOREIGN KEY ( student_id )
REFERENCES student (id) ON UPDATE CASCADE ON DELETE RESTRICT
, CONSTRAINT FK_currrent_enrollment_course FOREIGN KEY ( course_id )
REFERENCES course (id) ON UPDATE CASCADE ON DELETE RESTRICT
)

MySQL: How do I get to a column of another table via a mapping table

I am trying to create a database for assignments where there is a table for the tasks, a table for the persons and a table for the assignment of a person to a task.
Now I have tried to access the task table with a select statement and at the same time get all assigned persons but nothing worked. (because a task is also assigned to other tables)
Is there a way or do I have to use several statements for this?
This is how i created my Tables:
CREATE TABLE Locations (ID INT AUTO_INCREMENT, LocationID VARCHAR(255),
PRIMARY KEY (ID));
CREATE TABLE Persons (ID INT AUTO_INCREMENT, FirstName VARCHAR(255), LastName
VARCHAR(255), PRIMARY KEY (ID));
CREATE TABLE Tasks (ID INT AUTO_INCREMENT , TaskName VARCHAR(255), LocationID
INT, PRIMARY KEY (ID),
FOREIGN KEY (LocationID) REFERENCES Locations(ID));
CREATE TABLE Assinment (TaskID INT, PersonID INT,
PRIMARY KEY (TaskID, PersonID), FOREIGN KEY (TaskID)
REFERENCES Tasks(ID), FOREIGN KEY (PersonID)
REFERENCES Persons(ID));
And this is the UML.
I do not want my joins on the Assinment table like
SELECT (FirstName, LastName, TaskName) FROM ((Assinment INNER JOIN Tasks ON
Assinment.TaskID = Tasks.ID) INNER JOIN Persons ON Assinment.PersonID =
Persons.ID)
because the tasks table has more joins (e.g. locations and priorities) so i want my query start with
SELECT (ID, TaskName, FirstName, LastName, LocationName) FROM Tasks [...]
so i can get all data by id of the task
The Output then should give me this table.
Thanks for help :)
EDIT
Ouput added and desired input is now more specified
You can try subqueries :
SELECT column-names
FROM table-name1
WHERE value IN (SELECT column-name
FROM table-name2
WHERE condition)

Is it possible to create and delete SQL Enum values based on a column name

So basically if I had a table like so:
ID | Email | Password | PermissionGroup
could I define permission group's enum values based on all the names from the GroupName column values in a different table
ID | GroupName | PermissionNodes | Inheritance
ENUM is not the right tool for this task.
You're describing a foreign key constraint:
CREATE TABLE groups (
ID SERIAL,
GroupName VARCHAR(255),
PermissionNodes VARCHAR(255), -- not sure what datatype this should be
Inheritance VARCHAR(255) -- nor this
) ENGINE = InnoDB;
CREATE TABLE users (
ID SERIAL,
Email VARCHAR(255),
Password CHAR(60) BINARY,
PermissionGroup BIGINT UNSIGNED,
FOREIGN KEY (PermissionGroup) REFERENCES groups (ID)
) ENGINE = InnoDB;
Note that the foreign key normally references the primary key of the parent table. If one wants to obtain the GroupName of a user's group, one would simply join the tables:
SELECT users.*, groups.GroupName
FROM users JOIN groups ON groups.ID = users.PermissionGroup

mysql opposite of foreign key

I have 2 tables, customers and affiliates. I need to make sure that customers.email and affiliates.email are exclusive. In other words, a person cannot be both a customer and an affiliate. It's basically the opposite of a foreign key. Is there a way to do this?
You can use a table that stores emails and have unique constrain on the email, and reference that table from the customer and affiliate. (still need to ensure that there are no 2 records referencing the same key)
You can use trigger before insert and before update to check if the email is not present.
Or you can leave this validation to the application logic - not in the database, but in the applicationc ode.
There is no key you can do this with, but it sounds like you shouldn't be using two tables. Instead, you can have one table with either customer/affiliate data (that needs to be unique in this table) and another table that has the type (customer/affiliate).
CREATE TABLE People (
pplid,
pplEmail,
ptid,
UNIQUE KEY (pplEmail)
)
CREATE TABLE PeopleType (
ptid,
ptType
)
INSERT INTO PeopleType VALUES (1, 'affiliates'), (2, 'customers');
You can try the following.
Create a new table, which will be a master for customers and affiliates:
CREATE TABLE party
(
id int not null auto_increment primary key ,
party_type enum('customer','affiliate') not null,
email varchar(100),
UNIQUE (id,party_type)
);
--Then
CREATE TABLE customer
(
....
party_id INT NOT NULL,
party_type enum('customer') NOT NULL DEFAULT 'customer',
PRIMARY KEY (party_id,party_type)
FOREIGN KEY (party_id,party_type) REFERENCES party(id,party_type)
);
CREATE TABLE affiliates
(
....
party_id INT NOT NULL,
party_type enum('affiliate') NOT NULL DEFAULT 'affiliate',
PRIMARY KEY (party_id,party_type)
FOREIGN KEY (party_id,party_type) REFERENCES party(id,party_type)
)
-- enum is used because mysql still doesn't have CHECK constraints
This way each party can be only of one type