在SQL Server中自动更新冗余/非规范化数据

Ste*_*ols 9 sql-server sql-server-2005 denormalization

在我的数据库设计中使用高级冗余非规范化数据来提高性能.我经常会存储通常需要加入或计算的数据.例如,如果我有一个User表和一个Task表,我会在每个Task记录中冗余地存储UsernameUserDisplayName.另一个例子是存储聚合,例如将TaskCount存储在User表中.

  • 用户
    • 用户身份
    • 用户名
    • UserDisplayName
    • TaskCount
  • 任务
    • 的TaskID
    • 任务名称
    • 用户身份
    • 用户名
    • UserDisplayName

这非常适合性能,因为应用程序具有比插入,更新或删除操作更多的读取,并且因为某些值(如用户名)很少更改.然而,最大的缺点是必须通过应用程序代码或触发器强制执行完整性.更新可能会非常麻烦.

我的问题是这可以在SQL Server 2005/2010中自动完成...也许通过持久/永久视图.有人会推荐另一种可能的解决方案或技术 我听说基于文档的数据库(如CouchDB和MongoDB)可以更有效地处理非规范化数据.

Sol*_*zky 10

您可能希望在转移到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)
Run Code Online (Sandbox Code Playgroud)

2)在Task表上添加一个Trigger,定义如下:

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
Run Code Online (Sandbox Code Playgroud)

3)然后做一个View:

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
Run Code Online (Sandbox Code Playgroud)

然后按照上面链接中的建议(WITH SCHEMABINDING,Unique Clustered Index等)使其"持久化".虽然如上所示在SELECT中的子查询中进行聚合是低效的,但是这种特定情况旨在在具有比写入更高的读取的情况下进行非规范化.因此,执行索引视图将保持整个结构(包括聚合)的物理存储,因此每次读取都不会重新计算它.

现在,如果某些用户没有任何任务需要LEFT JOIN,那么由于创建它们的5000限制,索引视图将不起作用.在这种情况下,您可以创建一个真正的表(UserTask),它是您的非规范化结构,并通过用户表上的触发器填充它(假设您执行上面显示的触发器,它根据更改的用户表更新用户表)任务表)或者您可以跳过用户表中的TaskCount字段,并在两个表上都有触发器来填充UserTask表.最后,这基本上是索引视图无需编写同步触发器即可完成的操作.