Reputation: 67283
I have two tables, Cars and Drivers, that are joined by a third table, CarDrivers, in a many-to-many relationship.
My UI allows the user to check any number of drivers associated with the current car. Each check indicates that a row should be entered into the CarDrivers table.
My question is: what's the most efficient way to update those rows when the user submits the form?
I need to go through and add a row into CarDrivers for each item that was checked and delete one for each item that was unchecked, while leaving those that have not changed.
The only way I see is to go through each combination, one at a time, and add those that don't already exist or remove those that need to be removed. Is there a slicker way?
I can use Entity Frameworks 4, ADO.NET, straight SQL queries or stored procedures.
Upvotes: 5
Views: 3359
Reputation: 57063
This is exactly the scenario the MERGE
syntax was invented to handle, notably the WHEN NOT MATCHED BY SOURCE
clause.
Broadly speaking, put the current state values into a staging table then using MERGE
to handle the INSERT
and DELETE
actions in one hit.
Here's a brief sketch:
CREATE TABLE Cars (VIN INTEGER NOT NULL UNIQUE);
CREATE TABLE Drivers (driver_licence_number INTEGER NOT NULL UNIQUE);
CREATE TABLE CarDrivers
(
VIN INTEGER NOT NULL REFERENCES Cars (VIN),
driver_licence_number INTEGER NOT NULL
REFERENCES Drivers (driver_licence_number)
);
INSERT INTO Cars VALUES (1), (2), (3);
INSERT INTO Drivers VALUES (22), (55), (99);
INSERT INTO CarDrivers VALUES (1, 22), (1, 55);
CREATE TABLE CarDrivers_staging
(
VIN INTEGER NOT NULL REFERENCES Cars (VIN),
driver_licence_number INTEGER NOT NULL
REFERENCES Drivers (driver_licence_number)
);
INSERT INTO CarDrivers_staging
VALUES (1, 55), -- remains
(1, 99); -- insert
-- DELETE (1, 22)
MERGE INTO CarDrivers
USING CarDrivers_staging S
ON S.VIN = CarDrivers.VIN
AND S.driver_licence_number = CarDrivers.driver_licence_number
WHEN NOT MATCHED THEN
INSERT (VIN, driver_licence_number)
VALUES (VIN, driver_licence_number)
WHEN NOT MATCHED BY SOURCE THEN
DELETE;
Upvotes: 5
Reputation: 5919
I doubt this is more efficient, but "slicker", maybe, particularly if you don't have a huge amount of data. When a user submits a form that updates the cars-drivers relationships, why not first delete ALL relationships in CarDrivers related to them and then insert just the ones they checked? Alternatively, you could have a uniq constraint on both columns in CarDrivers, and then just worry about inserting and deleting, rather than checking for existing records in your code.
Upvotes: 2