Can MySQL allow reference targets to be missing? - mysql

I have two tables in a MySQL 5.7.x DB. Table A, whose data I maintain, and table B, which I can only read. I would like to define a relationship (somewhat like a foreign key) from a column in table A to refer to values in a column in table B. The exception is that the referenced column in table B does not always contain the value to which the column in table A refers.
I don't mind that B's column doesn't have the value. As long as the relationship is defined for advisory purposes, it's OK. However, if I try to use a foreign key relationship for this purpose, it requires the value from A to be in B, too.
How can I define the relationship without the requirement? That is, can I let it be optional?
I'm looking for this to be specifically in the direction of A referring to a value in B, but B doesn't contain that value, and that's OK. Not as one person suggested, the column in A is null, not pointing to any value in B.
I've searched for an answer to this online, but I can't find the solution. Maybe I'm not phrasing the search terms well or I'm using the wrong terminology.
I think this may be possible with MySQL 8's NOT ENFORCED option, but that's only possible with CHECK type of constraints.

Foreign keys in MySQL can be enforced or not at session level (something really unorthodox and IMHO a design mistake):
SET FOREIGN_KEY_CHECKS = 0;
-- Do stuff
SET FOREIGN_KEY_CHECKS = 1;
But it's all-or-nothing. Advisory foreign keys is not really a concept in relational databases design. At first sight, I can't see what problem it would solve.

Related

One to many relationship

To start off, we have the following scenario (not exactly like this but for confidential reason I will not take the risk to explicitly describe the project) where let's say A person can Have Many home addresses and obviously an address belong to at most one person. This is pretty much a one to many relationship where there are 2 tables (Person and Addresses) with the second one holding a FK referencing the owner. But sadly i was told that my system should also allow users to enter Addresses first, so a FK that does not exist in the Person table yet. I came to the conclusion that i just have to drop that constraint. Do you thing it's a common thing to do? Furthermore I thought to maintain consistency even without FK (declared explicitly), if someone update the Person ID, since i dont want the user to do it in both table, is it possible to create a Trigger (I'm using Mysql server) that will update the second table automatically (if that person has an address)? If yes, any hint to how to write that (I'm not familiar with triggers)
You can maintain the 1:N relationship by creating the kind of intermediate table normally associated with M:N relationships but adding a uniqueness constraint on the address referencing field; alternatively, you can just make address' reference to person nullable, making it an optional reference.
[Edit]
Scripts from MySQL dump wrap their contents with something like:
SET #OLD_FOREIGN_KEY_CHECKS=##FOREIGN_KEY_CHECKS;
SET FOREIGN_KEY_CHECKS=0;
....
SET FOREIGN_KEY_CHECKS=#OLD_FOREIGN_KEY_CHECKS;
As you can probably guess, it temporarily turns off foreign key checks to allow inserting interdependent data out of order. However, you should be sure of the trustworthiness of the import data as I believe it does not recheck the data when turned back on (so it can allow data that violates FK constraints to persist).
If you need the ability to add "address" before "person" with user entered data, then you are back the original situation of needing optional keys or another table; or just not actually saving the "address" info (but instead holding it locally in the data entry program) until after the "person" info is sufficient/complete.

MySQL Foreign Key to different table depending on column

I'm not sure the proper terminology for this type of relationship or even if it's good or bad. Hopefully I can clear up the lousy title.
I have a table that has the following fields:
id(pk), type, data(fk)
I want to be able to change the table that data points to depending on what "type" is. Depending on the type, there might be different types of data I need to pull. I'm basically trying to create a variable relationship.
You cannot do this in MySQL. You want conditional relationships.
You have basically three choices.
You can ignore putting these relationships in the database.
You can use before insert and before update triggers to enforce the constraints.
You can add a separate column for each type of id and add the constraints to those columns.

Polymorphic database design : does this approach have a name?

I have a base enitiy (items) that will host a vast range of item types (>200) with totaly different properties. I want a clean portable and fast solution and have come up with an idea that maby has a name I'm unaware of.
Here it goes:
items-entity holds base class fields + additional fields for subclass fields but with dummie-names, ItemID,ItemNo,ItemTypeID,int1,int2,dec1,dec2,dec3,str1,str2
referenced itemtype-record holds name of type and child enity (1:n):
itemtypefields [itemtypeid,name,type,realfield]
example in [53,MaxPressure,dec,dec3]
It's limitations:
hard to estimate field requirements in baseclass
harder to add domains/checkconstraints based on child type
need application layer to translate tagged sql to real query
Only possible to query one type at a time since shared attributes may be defined to different "real-fields".
3rd bullet explained:
select ItemNo,_MaxPressure_ from items where ItemTypeID=10 and _MaxPressure_>42
should translate to:
select ItemNo,dec3 as MaxPressure from items where ItemType=10 and dec3>42
(can't do that with sp's or udf's right - or whould it be possible?)
But benefits of:
Performance
Ease of CRUD-operations
Easier to sort/filter at application level.
Now - does it have a name?
This antipattern is called One True Lookup Table.
In a relational database, each column needs to be defined as one logical type. I don't mean one SQL data type like INT or VARCHAR, I mean everything in that column from start to finish must be from the same set of values, and you should be able to tell one value apart from another value.
You can't put shoe size and average temperature and threads per inch into the same column of a given table, and still call it a relation.
Basically, your database would not be a database at all -- it would be a spreadsheet.
Read almost any book by C. J. Date, such as SQL and Relational Theory for a proper explanation of relations and types.
Re your comment:
Read the Q again before lecuturing about elementary books and mocking about semi structured data.
Okay, I have re-read your post.
The classic use of One True Lookup Table isn't exactly what you're doing, but what you're doing shares the same problems with OTLT.
Suppose you have "MaxPressure" stored in column dec3 for ItemType 10. Suppose there are a fixed set of valid choices for the value of MaxPressure, and you want to put those in another lookup table, so that no one can enter an invalid MaxPressure value.
Now: declare a foreign key constraint on dec3 referencing your MaxPressures lookup table. You can't -- the problem is that the foreign key constraint applies to the dec3 column in all rows, not just those rows where ItemType is 10.
The reason is that you're storing more than one set of values in a single column. The same problem arises for any other kind of constraint -- unique constraints, check constraints, even NOT NULL. And you can't declare a DEFAULT value for the column either, because you probably have a different correct default for each ItemType (and some ItemTypes have no default for that attribute).
The reason that I referred to the C. J. Date book is that he gives a crisp definition for a type: it's a named finite set, over which the equality operation is defined. That is, you can tell if the value "42" on one row is the same as the value "42" on another row. In a relational column, that must be true because they must come from the same original set of values. In your table, dec3 could have the value "42" when it's MaxPressure, but "42" for another ItemType when it's threads per inch. Therefore they aren't the same value "42". If you had a unique constraint, these two 42's would not be considered duplicates. If you had a foreign key, each of the different 42's would reference a different lookup table, etc.
What you're doing is not a valid relational database design.
Don't bristle at my referring you to a resource on relational database design unless you understand that.

Two-way foreign key constraint in a 1:1 relation

I am using a MySQL database. In my relational data model, I've got two entities that relate 1:1 to each other. In my schema, a 1:1 relation is set up by putting a FK field in one of the two tables, that relates to the PK of the other table. Both tables have PKs and they are both auto increment BIGINTs.
I am wondering whether it would be possible to have an ON DELETE CASCADE behaviour on them that works both ways.
i.e. A 1:1 B, means that [ deleting A also deletes B ] as well as [ deleting B also deletes A ].
I realise that this may not be absolutely necessary in terms of proper application design, but I am just wondering whether it is actually possible. As far as I recall, you can't put an FK constraint on a PK.
It'd be impossible to insert such records if you have a 2-way relationship enforced. Chicken-and-egg. Record in table #1 can't be inserted because there's no matching record in table #2, and table #2 cannot be inserted into because there's nothing in table #1 to hook to.
You can disable FK constraints temporarily (set foreign_key_checks = 0), but this should never be done in a "real" system. It's intended more for loading dumps where the table load order cannot be guaranteed.

How to restrict a column value in SQLite / MySQL

I would like to restrict a column value in a SQL table. For example, the column values can only be "car" or "bike" or "van". My question is how do you achieve this in SQL, and is it a good idea to do this on the DB side or should I let the application restrict the input.
I also have the intention to add or remove more values in the future, for example, "truck".
The type of Databases I am using are SQLite and MySQL.
Add a new table containing these means of transport, and make your column a foreign key to that table. New means of transport can be added to the table in future, and your column definition remains the same.
With this construction, I would definitively choose to regulate this at the DB level, rather than that of the application.
For MySQL, you can use the ENUM data type.
column_name ENUM('small', 'medium', 'large')
See MySQL Reference: The ENUM Type
To add to this, I find it's always better to restrict on the DB side AND on the app side. An Enum plus a Select box and you're covered.
Yes, it is recommended to add check constraints. Check constraints are used to ensure the validity of data in a database and to provide data integrity. If they are used at the database level, applications that use the database will not be able to add invalid data or modify valid data so the data becomes invalid, even if the application itself accepts invalid data.
In SQLite:
create table MyTable
(
name string check(name = "car" or name = "bike" or name = "van")
);
In MySQL:
create table MyTable
(
name ENUM('car', 'bike', 'van')
);
You would use a check constraint. In SQL Server it works like this
ALTER TABLE Vehicles
ADD CONSTRAINT chkVehicleType CHECK (VehicleType in ('car','bike','van'));
I'm not sure if this is ANSI standard but I'm certain that MySQL has a similar construct.
If you want to go with DB-side validation, you can use triggers. See this for SQLite, and this detailed how-to for MySQL.
So the question is really whether you should use Database validation or not. If you have multiple clients -- whether they are different programs, or multiple users (with possibly different versions of the program) -- then going the database route is definitely best. The database is (hopefully) centralized, so you can decouple some of the details of validation. In your particular case, you can verify that the value being inserted into the column is contained in a separate table that simply lists valid values.
On the other hand, if you have little experience with databases, plan to target several different databases, and don't have the time to develop expertise, perhaps simple application level validation is the most expedient choice.
To add some beginner level context to the excellent answer of #NGLN above.
First, one needs to check the foreign key constraint is active, otherwise sqlite won't limit to the input to the column to the reference table:
PRAGMA foreign_key;
...which gives a response of 0 or 1, indicating on or off.
To set the foreign key constraint:
PRAGMA foreign_keys = ON;
This needs to be set to ensure that sqlite3 enforces the constraint.
I found it simplest to just set the primary key of the reference table to be the type. In the OP's example:
CREATE TABLE IF NOT EXISTS vehicle_types(
vehicle_type text PRIMARY KEY);
Then, one can insert 'car', 'bike' etc into the vehicle_types table (and more in the future) and reference that table in the foreign key constraint in the child table (the table in which the OP wished to reference the type of vehicle):
CREATE TABLE IF NOT EXISTS ops_original_table(
col_id integer PRIMARY KEY,
...many other columns...
vehicle_type text NOT NULL,
FOREIGN KEY (vehicle_type) REFERENCES vehicle_types(vehicle_type);
Outwith the scope of the OP's question but also take note that when setting up a foreign key constraint thought should be given to what happens to the column in child table (ops_original_table) if a parent table value (vehicle_types) is deleted or updated. See this page for info