Postgres 2 different FKs in both directions - postgresql

I've run into a situation where we have 2 tables that have 2 different foreign key relationships, one is 1-many, the other is 1-1, but they are in opposite directions. I'm wondering if postgres has a way to validate that the combination of the FKs must match, probably easier to show in an example.
table product {
id: PK
name ...etc
preferred_vendor_product: FK to vendor_product id
}
table vendor_product {
id: PK
product_id: FK to product (a product could have many vendor products)
price...etc
}
Is there a way to enforce that when I add a preferred vendor product to product, that not only the vendor_product must exist, but it's value in product_id must match the product in question.

Just have a third table preferred_vendor_product that just has a fkey pointing to vendor_product. That table presumably should have a primary key of (vendor_id, product_id).

Related

0-to-many and 1- to-many relationship in PostgreSQL code

From a PostgreSQL article on foreign keys constraints at https://www.postgresql.org/docs/current/ddl-constraints.html:
Say you have the product table :
CREATE TABLE products (
product_no integer PRIMARY KEY,
name text,
price numeric
);
Let's also assume you have a table storing orders of those products.
We want to ensure that the orders table only contains orders of
products that actually exist. So we define a foreign key constraint in
the orders table that references the products table:
CREATE TABLE orders (
order_id integer PRIMARY KEY,
product_no integer REFERENCES products (product_no),
quantity integer
);
Now it is impossible to create orders with non-NULL product_no entries
that do not appear in the products table.
Is this a 0-to-many relationship?
How can I set a one-to-many relationship?
By doing this?
CREATE TABLE orders (
order_id integer PRIMARY KEY,
product_no integer REFERENCES products (product_no) NOT NULL,
quantity integer
);
If that is the case, why does pgadmin4 generate this crow foot notation that denotes a zero-to-many when I configure the table as above?
this crow foot notation denotes a zero-to-many relationship.
No, it doesn't. Do not confuse the cardinality (which is directional) with the type of the relation. The circle denotes the lower bound of the orders cardinality (and may be omitted) - see "many" vs "zero or many"/"one or many" crowfoot notation? on Software Engineering.
Marking the product_no as NOT NULL only changes a zero-or-one-to-many relationship into an exactly-one-to-many relationship. Both are commonly referred to as "one-to-many" or "1:N" relationships. This would be expressed as either a dash-dash or dash-circle (or, amiguously, just dash) for the cardinality of products.
Now the circle (or dash) on the crow foot is just about the many side (orders) - is it …-to-zero-or-many or …-to-one-or-many? And in your schema, it is indeed a zero-or-many cardinality, since there can be any number of orders - including 0 - for a product.
Notice that an actual 0,1:1,N, 1:1..N or 1..N:1..N relationship (not …:0,N) is surprisingly hard to represent in SQL since you have a chicken-and-egg problem, see How to Create a real one-to-one relationship in SQL Server or 1:N relationship where N must be at least one entry).

RDBMS Ref. Integrity: A child with too many parents?

I have a general design question. Consider these 3 tables:
Table Restaurants:
RID pk auto_increment
etc...
Table Vacations:
VID pk auto_increment
etc...
Table Movies:
MID pk auto_increment
etc...
And now imagine we want to create a list "Top things to do when COVID is over" of selected records from these 3 different tables. The list may contain any mix of records from these tables. What comes to mind then is:
Table Todo:
Type [ one of R, V, M ]
ID [ the ID of the parent item ]
But how would you enforce referential integrity on this thing? I.e., how do we ensure that when a restaurant is deleted from Restaurants, it will also drop from Todo?
(I am aware of how to accomplish these things with triggers; Curious if there's a combination of entities that will accomplish this with pure RDBMS ref. int.)
Thank you!
You can add nullable foreign key columns in your todo table for each target table you have. So your table will look like:
Table Todo:
RID fk (nullable)
VID fk (nullable)
MID fk (nullable)
The type column isn't needed anymore as you can check which column is filled with a foreign key. Obviously you have to add a CHECK constraint to ensure that exactly one foreign key must be set.

How to use Entity Framework 6 to update many-to-many relationship?

Someone else asked a similar question here: How can I use EF6 to update a many to many table
I mention that up front because I couldn't get any of the solutions given to work.
I also studied the solution give on Code Project: http://www.codeproject.com/Tips/893609/CRUD-Many-to-Many-Entity-Framework, but this doesn't work for me either.
I'm trying to keep this as simple as possible.
I have two tables: dbo.Teacher and dbo.Student. Each has an "ID" column that servers as a primary key. I also have a third table called dbo.StudentTeacher which has exactly two columns, both are non-nullable and foreign keyed to the previous two tables; in other words, it establishes a many-to-many relationship between teachers and students. As expected, the EDMX designed shows only dbo.Student and dbo.Teacher and infers the relationship between them.
Here is a script for the above; there is nothing else in the database.
CREATE TABLE dbo.Teacher
(
Id INT NOT NULL PRIMARY KEY,
Name VARCHAR(MAX)
);
CREATE TABLE dbo.Student
(
Id INT NOT NULL PRIMARY KEY,
Name VARCHAR(MAX)
);
CREATE TABLE dbo.TeacherStudent
(
TeacherId INT NOT NULL FOREIGN KEY REFERENCES Teacher(Id),
StudentId INT NOT NULL FOREIGN KEY REFERENCES Student(Id)
);
INSERT INTO Teacher(Id, Name)
VALUES
(101, 'Tom');
INSERT INTO Student(Id, Name)
VALUES
(201, 'Sue'),
(202, 'Stan');
INSERT INTO TeacherStudent(TeacherId, StudentId)
VALUES
(101, 201);
Now that I've established my data structures, I want to accomplish a very simple task. From the script above, you can see that we have one teacher named "Tom" who has a student named "Sue". We also have a student named "Stan" with no teacher. My task is to modify the database so that Sue is no longer Tom's student and Stan becomes Tom's student.
To accomplish this, I wrote the following code:
class Program
{
static void Main(string[] args)
{
using (var entities = new TestEntities())
{
// There is only one teacher in the system.
Teacher teacher = entities.Teachers.Single();
// This teacher has a student #201: Sue.
// I want to replace her with student #202: Stan.
teacher.Students.Clear();
teacher.Students.Add(new Student() { Id = 202 });
entities.SaveChanges();
}
}
}
It looks very simple: clear the students associated with Tom and then add Stan as Tom's student. However, when I run the code, I get the following error: Unable to update the EntitySet 'TeacherStudent' because it has a DefiningQuery and no <DeleteFunction> element exists in the <ModificationFunctionMapping> element to support the current operation.
I tried simplifying the problem by trying to just remove Sue from being Tom's student without adding Stan, and I get the exact same error message.
As I understand, this error normally occurs when Entity Framework doesn't have enough information to do what you want it to do, but I really can't see what's missing. There are two simple tables with a join table between them and I need to be able to change which rows are related to which other rows.
I should also note that if I'm not mistaken, the change that I wish to make in this example should affect only the dbo.TeacherStudent table; the other two tables should not be touched.
Okay, after some more Google-Fu, I figured it out.
Even tho the join table must have only two columns with each column foreign keyed to the two tables to be related, the join table still needs a primary key, which can be a composite of the two foreign keys.
Thus, dbo.TeacherStudent should be created with this:
CREATE TABLE dbo.TeacherStudent
(
TeacherId INT NOT NULL FOREIGN KEY REFERENCES Teacher(Id),
StudentId INT NOT NULL FOREIGN KEY REFERENCES Student(Id),
PRIMARY KEY(TeacherId, StudentId)
);

Entity Framework - How to Insert to table with foreign keys without retrieving foreign table rows first

I'm having a hard time finding the exact answer to this question, so my apologies if this is redundant.
So I have 3 tables defined such that:
Person :PersonId, FirstName, LastName
Company: CompanyId, CompanyName
Order: OrderId, PersonId, CompanyId
On the Order table, there is a foreign key defined on the PersonId and CompanyId columns, thus, my Order entity class generated by EF has a navigation properties of type Person (not PersonId) and Company.
So, to insert into the Order table, I first need to query the person and company tables to get the person and company entities. Then I can construct the Order object using the Person and Company entities and save it to the db.
In my scenario, I am being passed a PersonId and CompanyId.
In classic SQL I would just do INSERT INTO Order Set (CompanyId, PersonId) - 1 database call. But with EF, I have to do 3 db calls. This seems like overkill.
Is there any way around this?
PS - I'm using EF 6. I know I could generate an expression and make it single call..but that would still yield two subselects.
You can just include foreign key properties in addition to the navigation properties and then set them using the ids you have. If you do this will not have to go to the database to get related entities for just a sake of setting the relationship.

What 'possible data inconsistency' is entity framework worried about in a 'foreign key participating in multiple relationships' situation?

Suppose the following database schema:
Table A: AId (PK)
Table B: BId (PK)
Table C: CId (PK)
Table AB: AId, BId (composite PK, FKs to A and B), Data
Table BC: BId, CId (composite PK, FKs to B and C), Data
Table ABC: AId, BId, CId, Data
In the database, ABC has two FKs: one to AB on AId and BId, and one to BC on BId and CId.
Use the EF Designer and attempt to create a Model from this database.
If you have Include foreign key columns in the model checked, it works; but having FK Columns in the model isn't very nice.
If you have Include foreign key columns in the model unchecked, only one of the FKs from ABC will be successfully mapped. To see what went wrong, you have to view the .edmx xml (thanks Craig!) and you see this error:
warning 6037: Foreign key constraint 'FK_ABC_BC' has been omitted from the storage model. Column 'BId' of table 'Model.Store.ABC' is a foreign key participating in multiple relationships. A one-to-one Entity Model will not validate since data inconsistency is possible.
I've read the only other mention of this problem I can find on SO, and I don't think this is the same problem. I can't see anything wrong at a database design level. I'm going to work round this for the time being by imposing surrogate keys on AB and BC, but what I'd really like to know is:
What possible data inconsistency is EF worried about happening here, if it created a model to match the database?
And is there anything I can do to persuade it that everything's going to be OK?
My opinion is that EF is too clever in this scenario and it prevents you from using entity where you can assign only one relation and make the entity non-savable because relation to second entity will not exists.
There is also possibility that EF has some internal problem with tracking state of independent associations if more than one association is based on the same foreign key column but that is just another guess. Generally database features used to map EF features cannot be shared among multiple constructions. The only exceptions I can think about now are primary keys and in their own way discriminator columns.
I would like to mention that I don't like this type of relations in database at all.