The only way I could think of is logically break the bi-directional foreign keys in a procedural way.
This approach can have huge impact to your application side if you don't have some flags for visualization
state or status
Something like
INSERT
dummy not visible rows to Users (with something like Id = -1
for dummy values)
Add to LinkingTable
an alternative column to point back to Users
, I'll call it U_ComesFrom
ALTER TABLE LinkingTagble ADD U_ComesFrom_U_id INT DEFAULT(-1)
Add FOREIGN KEY
with a NOCHECK
ALTER TABLE LinkingTable WITH NOCHECK
FOREIGN KEY (U_ComesFrom_U_id)
REFERENCES Users (Id) ;
Add to Users
column
ALTER TABLE Users ADD MarkedForDeletion BIT NOT NULL DEFAULT(0)
Then your SQL would look like
BEGIN TRANSACTION
UPDATE J
SET U_Comes_From_U_id = U_ID, U_id = -1 -- or some N/R value that you define in Users
FROM Users U
inner join LinkingTable J on U.id = J.U_id
inner join Groups G on J.G_id = G.id
WHERE G.Name = 'Whatever'
and U.Name not in ('Exclude list')
UPDATE U
SET MarkedForDeletion = 1
FROM Users
inner join LinkingTable J on U.id = J.U_ComesFrom_U_id
WHERE U_id > 0
DELETE FROM LinkingTable
WHERE U_ComesFrom_U_id > 0
DELETE FROM Users
WHERE MarkedForDeletion = 1
COMMIT
This approach would impact the performance since each transaction would be at least 4 DML operations per bi-directional keys.