#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).
- Удалить элементы в
old - new
(установить разницу илиExcept
) - Добавьте элементы в
new - old
(установите разницу илиExcept
) - (Элементы в
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;