在迁移到 NoSQL 解决方案之前,您可能需要先尝试索引视图:
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
然后按照上面链接中的建议(WITH SCHEMABINDING、唯一聚集索引等)使其“持久化”。虽然如上所示在 SELECT 中的子查询中进行聚合是低效的,但这种特定情况旨在在读取高于写入的情况下进行非规范化。因此,索引视图将保持整个结构(包括聚合)物理存储,因此每次读取都不会重新计算它。
现在,如果在某些用户没有任何任务的情况下需要 LEFT JOIN,那么由于创建它们的 5000 个限制,索引视图将无法工作。在这种情况下,您可以创建一个真实表(UserTask),它是您的非规范化结构,并通过仅用户表上的触发器填充它(假设您执行上面显示的触发器,它根据更改更新用户表任务表),或者您可以跳过用户表中的任务计数字段,只在两个表上使用触发器来填充用户任务表。最后,这基本上就是索引视图所做的事情,而无需编写同步触发器。