Вы можете сначала попробовать индексированное представление перед переходом на решение NoSQL:
http://msdn.microsoft.com/en-us/library/ms187864.aspx
и:
http://msdn.microsoft.com/en-us/library/ms191432.aspx
Использование индексированного представления позволит вам хранить ваши базовые данные в должным образом нормализованных таблицах и поддерживать целостность данных, в то же время предоставляя денормализованное «представление» этих данных.Я бы не рекомендовал это для таблиц с высокой степенью транзакций, но вы сказали, что при чтениях он тяжелее, чем при записи, поэтому вы можете посмотреть, работает ли это для вас.
На основе ваших двух примеров таблиц один из вариантов:
1) Добавить столбец в таблицу User, определенную как:
TaskCount INT NOT NULL DEFAULT (0)
2) Добавить триггер в таблицу задач, определенную как:
CREATE TRIGGER UpdateUserTaskCount
ON dbo.Task
AFTER INSERT, DELETE
AS
;WITH added AS
(
SELECT ins.UserID, COUNT(*) AS [NumTasks]
FROM INSERTED ins
GROUP BY ins.UserID
)
UPDATE usr
SET usr.TaskCount = (usr.TaskCount + added.NumTasks)
FROM dbo.[User] usr
INNER JOIN added
ON added.UserID = usr.UserID
;WITH removed AS
(
SELECT del.UserID, COUNT(*) AS [NumTasks]
FROM DELETED del
GROUP BY del.UserID
)
UPDATE usr
SET usr.TaskCount = (usr.TaskCount - removed.NumTasks)
FROM dbo.[User] usr
INNER JOIN removed
ON removed.UserID = usr.UserID
GO
3) Затемсделайте представление, которое имеет:
SELECT u.UserID,
u.Username,
u.UserDisplayName,
u.TaskCount,
t.TaskID,
t.TaskName
FROM User u
INNER JOIN Task t
ON t.UserID = u.UserID
, а затем следуйте рекомендациям по ссылкам выше (WITH SCHEMABINDING, Unique Clustered Index и т. д.), чтобы сделать его "постоянным".Хотя неэффективно выполнять агрегирование в подзапросе в SELECT, как показано выше, этот конкретный случай предназначен для денормализации в ситуации, когда число операций чтения выше, чем операций записи.Таким образом, индексированное представление сохранит всю структуру, включая агрегирование, физически сохраненным, поэтому каждое чтение не будет пересчитывать ее.
Теперь, если требуется LEFT JOIN, если у некоторых пользователей нет задач, тоИндексированное представление не будет работать из-за 5000 ограничений на их создание.В этом случае вы можете создать реальную таблицу (UserTask), которая является вашей денормализованной структурой, и заполнить ее с помощью триггера только для пользовательской таблицы (при условии, что вы выполняете приведенный выше триггер, который обновляет пользовательскую таблицу на основании измененийТаблица задач) или вы можете пропустить поле TaskCount в пользовательской таблице и просто иметь триггеры в обеих таблицах для заполнения таблицы UserTask.В конце концов, это в основном то, что делает индексированное представление, без необходимости писать триггер (ы) синхронизации.