'Replicate 'ON DELETE SET NULL' behavior from combination of two non-unique columns on another table

The Situation

I have a full-stack web application with two MySQL tables: channel_strips and mic_lookup.

DESCRIBE `channel_strips`;

Field           Type            Null    Key
preset_id       varchar(127)    NO      PRI 
mic_function    varchar(225)    YES
phantom_power   tinyint(1)      YES
...
DESCRIBE `mic_lookup`;

Field           Type            Null    Key
Microphone      varchar(40)     NO      PRI 
mic_function    varchar(225)    NO
phantom_power   tinyint(1)      NO
...
(many other columns)

I want the channel_strips table to always hold only combinations of mic_function and phantom_power values that can be currently found in the mic_lookup table (or null values).

What is working

On the HTML end, I've limited the input to these columns in channel_strips with a <select> element that gets values from this mysqli query: SELECT DISTINCT `mic_function`, `phantom_power` FROM `mic_lookup`; This successfully restricts the user input.

The Problem

The one situation I've identified where this fails is when entries are deleted or changed in mic_lookup such that one pre-existing combination of mic_function and phantom_power is eliminated. As a result, channel_strips could still have a combination of the two columns that is actually no longer an option. In this situation, I'd like those two columns to be nullified on rows where they hold the old combination, essentially emulating an ON DELETE SET NULL statement as if it were a foreign key.

What I've tried

For a while, I had an intermediate table, mic_functions with a single column, mic_function, which served as a foreign key to both tables' mic_function columns. However, this was before I realized that phantom_power needed to be included. Furthermore, it was very confusing from a user's perspective, since intuitively you would want to set these values in the mic_lookup table.

My next idea was to create a view instead so I'd have a 'table' that automatically updates, and reference that as a foreign key - maybe something like...

CREATE VIEW mic_functions AS(
    SELECT DISTINCT
        `mic_function`,
        `phantom_power`
    FROM
        `mic_lookup`
);
ALTER VIEW mic_functions ADD CONSTRAINT PK_mic_function PRIMARY KEY(`mic_function`, `phantom_power`);

Of course, this doesn't work. You can't add a primary key to a VIEW.

Finally, I suppose I could write a bunch of php to query and perform a series of checks on channel_strips every time the mic_lookup table is updated, and execute an appropriate UPDATE query if the checks are violated, but it seems to me like there ought to be a simpler way to handle this on the SQL side of things. Maybe using SQL checks or triggers or a combination of the two would work, but I have no experience with checks and triggers.

Note

phantom_power is boolean, and I'm using MySQL version 10.4.21-MariaDB



Solution 1:[1]

I found a solution using triggers, by joining the tables and filtering down to where the joined primary key is null:

CREATE TRIGGER `mic_lookup_update` AFTER UPDATE
ON
    `mic_lookup` FOR EACH ROW
UPDATE
    `channel_strips` c
LEFT JOIN `mic_lookup` m ON
    c.mic_function = m.mic_function AND c.phantom_power = m.phantom_power
SET
    c.mic_function = NULL,
    c.phantom_power = NULL
WHERE
    m.Microphone IS NULL;
CREATE TRIGGER `mic_lookup_delete` AFTER DELETE
ON
    `mic_lookup` FOR EACH ROW
UPDATE
    `channel_strips` c
LEFT JOIN `mic_lookup` m ON
    c.mic_function = m.mic_function AND c.phantom_power = m.phantom_power
SET
    c.mic_function = NULL,
    c.phantom_power = NULL
WHERE
    m.Microphone IS NULL;

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Jeffrey Hall