Редактирование записей в таблице ассоциаций в отношениях «многие ко многим»

#c# #asp.net #sql

#c# #asp.net #sql

Вопрос:

Это то, что беспокоило меня некоторое время, и я, наконец, хочу получить на это ответ.

Допустим, у меня есть сотрудники и отделы. Между ними существует связь «многие ко многим» с ассоциативной таблицей с именем EmployeeDepartments.

 Employees
-------------------
EmployeeID (PK)

Departments
-------------------
DepartmentID (PK)

EmployeeDepartments
-------------------
EmployeeID (FK)
DepartmentID (FK)
  

У меня есть страница, где пользователь может редактировать сотрудника. Существует список флажков, в котором указаны все отделы, и будут отмечены отделы, к которым принадлежит сотрудник. Затем пользователь может проверить / снять флажок, к каким отделам принадлежит сотрудник.

Когда я редактирую сотрудника и иду сохранять данные, как я должен обращаться с этими отделами?

Способ, которым я это делал, заключается в удалении каждой записи в таблице EmployeeDepartments для этого сотрудника. И затем для каждого отдела, выбранного пользователем, я добавляю его в EmployeeDepartments.

Это работает, но должен быть более эффективный способ сделать это, это просто кажется неправильным. Если сотрудник находится в 5 отделах, и я собираюсь добавить этого сотрудника в другой отдел, мне придется удалить 5 записей из EmployeeDepartments, а затем добавить 6 записей.

Ответ №1:

Просто обработайте дельту изменения. То есть, посмотрите на «выбранные» в данный момент элементы, затем на элементы для выбора, а затем либо добавьте, либо удалите соответствующие. Это может быть вычислено с использованием операций set (которые тривиальны в LINQ).

  1. Удалить элементы в old - new (установить разницу или Except )
  2. Добавьте элементы в new - old (установите разницу или Except )
  3. (Элементы в new ^ old не должны быть затронуты)

При желании большую часть этой работы можно передать базе данных. (Удалить, за исключением случаев, когда … и вставить, если не существует… например. Также смотрите стандартную инструкцию SQL MERGE и нестандартные эквиваленты и расширения).

Убедитесь, что правильно используете транзакции базы данных для обеспечения согласованности.

Удачного кодирования.

Комментарии:

1. Спасибо, я не знал, что база данных может обрабатывать подобные вещи, мне придется разобраться с этим.

2. @Steven То, что это может быть, не означает, что это должно быть 😉 Не забывайте о сложности реализации — чем проще, тем часто лучше. Кроме того, не будет никакой практической выгоды для такого небольшого набора данных (и это может быть даже «медленнее», чем генерирование нескольких «простых» вставок и удалений на основе дельты).

3. MERGE является стандартным SQL: он был представлен в SQL: 2003 , но другие продукты SQL, в частности SQL Server, предоставляют полезные расширения.

Ответ №2:

Если ваш продукт SQL поддерживает это, вы можете использовать стандартный SQL MERGE . Если вашим продуктом SQL является SQL Server, то вам повезло: у него MERGE есть отличное расширение IF NOT MATCHED BY SOURCE , которое позволяет вам DELETE в дополнение к INSERT и UPDATE .

Вот простой пример (например, отсутствие ограничений ссылочной целостности):

 CREATE TABLE EmployeeDepartments 
(
 EmployeeID INTEGER NOT NULL, 
 DepartmentID INTEGER NOT NULL, 
 UNIQUE (DepartmentID, EmployeeID)
);

INSERT INTO EmployeeDepartments (EmployeeID, DepartmentID)
   VALUES (1, 1), 
          (1, 2);
  

Допустим, после редактирования вы помещаете значения в промежуточную таблицу:

 CREATE TABLE StagingTable 
(
 EmployeeID INTEGER NOT NULL, 
 DepartmentID INTEGER NOT NULL, 
 UNIQUE (DepartmentID, EmployeeID)
);

INSERT INTO StagingTable (EmployeeID, DepartmentID)
   VALUES (1, 1), 
          (1, 3);
  

Простым английским языком строка {1, 3} будет вставлена, строка {1, 2} будет удалена, а строка {1, 1} останется:

 MERGE INTO EmployeeDepartments
   USING StagingTable AS S1 
      ON EmployeeDepartments.EmployeeID = S1.EmployeeID
         AND EmployeeDepartments.DepartmentID = S1.DepartmentID
WHEN NOT MATCHED THEN
   INSERT (EmployeeID, DepartmentID)
      VALUES (EmployeeID, DepartmentID)
WHEN NOT MATCHED BY SOURCE THEN
   DELETE;