在尝试使用 NoSQL 解决方案之前,您可能需要先尝试使用 Indexed View:
http://msdn.microsoft.com/en-us/library/ms187864.aspx
并且:
http://msdn.microsoft.com/en-us/library/ms191432.aspx
使用索引视图可以让您将基础数据保存在规范化的表中,并在保持数据完整性的同时为您提供该数据的非规范化“视图”。我不建议在高事务性表上使用此方法,但您说它读取的次数比写入的次数多,因此您可能想看看这是否适用于您。
根据您的两个示例表之一,一个选项是:
1)向用户表添加一列,定义为:
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
然后按照上面的链接建议(使用SCHEMABINDING,唯一聚集索引等)进行优化,使其“持久化”。虽然在SELECT子查询中执行聚合操作效率低下,但这种情况旨在将其非规范化,在读操作高于写操作的情况下。因此,创建索引视图将保留整个结构,包括聚合操作,物理存储,因此每次读取都不需要重新计算。
现在,如果需要LEFT JOIN,如果某些用户没有任何任务,则由于创建它们的5000限制,索引视图将无法工作。在这种情况下,您可以创建一个真实的表(UserTask),它是您的非规范化结构,并通过触发器仅在用户表上填充(假设您执行我上面展示的触发器,该触发器基于任务表中的更改更新用户表),或者您可以跳过用户表中的TaskCount字段,只需在两个表上创建触发器以填充UserTask表即可。最后,这基本上就是索引视图所做的事情,只是您不必编写同步触发器。