Обновление избыточных / денормализованных данных автоматически в SQL Server - PullRequest
9 голосов
/ 25 января 2011

Использование высокого уровня избыточных, денормализованных данных в моих проектах БД для повышения производительности. Я часто буду хранить данные, которые обычно должны быть объединены или рассчитаны. Например, если у меня есть таблица Пользователь и таблица Задача , я бы сохранял избыточно Имя пользователя и Имя_пользователя в каждом Задача Запись. Другим примером этого является хранение агрегатов, например, хранение TaskCount в таблице Пользователь .

  • Пользователь
    • Идентификатор_пользователь
    • Имя пользователя
    • UserDisplayName
    • TaskCount
  • Задача
    • TaskID
    • TaskName
    • Идентификатор_пользователь
    • UserName
    • UserDisplayName

Это очень важно для повышения производительности, поскольку приложение выполняет намного больше операций чтения, чем операций вставки, обновления или удаления, а некоторые значения, такие как Имя пользователя , изменяются редко. Однако большим недостатком является то, что целостность должна обеспечиваться с помощью кода приложения или триггеров. Это может быть очень громоздким с обновлениями.

У меня вопрос, может ли это быть сделано автоматически в SQL Server 2005/2010 ... возможно, через постоянное / постоянное представление. Кто-нибудь порекомендует другое возможное решение или технологию. Я слышал, что БД на основе документов, такие как CouchDB и MongoDB, могут обрабатывать денормализованные данные более эффективно.

1 Ответ

10 голосов
/ 25 января 2011

Вы можете сначала попробовать индексированное представление перед переходом на решение 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.В конце концов, это в основном то, что делает индексированное представление, без необходимости писать триггер (ы) синхронизации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...