Каков наилучший способ сделать исторические сравнения данных базы данных? - PullRequest
4 голосов
/ 21 января 2010

У меня есть база данных, в которой есть список сотрудников организации. Существует одна основная таблица сотрудников и количество объединенных таблиц.

Я хочу начать отслеживать показатели неделя за неделей и месяц за месяцем по этим данным, чтобы я мог делать такие вещи, как:

1 марта: численность персонала 100 (+1, -2 в феврале)
1 апреля: численность персонала 101 (+3, -2 в марте)
1 мая: численность персонала 105 (+10, -6 в апреле)

Я пытаюсь найти лучший способ сделать это. сделать я:

  1. Делайте полный снимок базы данных первого числа каждого месяца, и мое приложение запрашивает несколько баз данных для генерации этих отчетов.

  2. попытаться отследить все изменения в некоторой таблице истории, срабатывающей в БД, и объединить эту информацию, чтобы попытаться построить текущее состояние каждого месяца.

  3. есть другие предложения?

Ответы [ 2 ]

2 голосов
/ 23 января 2010

Если вы просто хотите отслеживать, когда новые сотрудники нанимаются или увольняются, вам следует начать с добавления соответствующих полей в саму таблицу сотрудников: HireDate date NOT NULL и TerminationDate date NULL.

Тогда очень легко определить численность персонала (и детали) в любой конкретный день:

SELECT EmployeeID, EmployeeName, ...
FROM Employees
WHERE HireDate <= @EndDate
AND (TerminationDate IS NULL OR TerminationDate > @BeginDate)

Если вам нужно отслеживать изменений (т.е. изменение заголовка), то подход, который даст вам максимальную гибкость, заключается в ведении таблицы истории в реальном времени с триггерами (или встроенной в вашу базу данных). изменить отслеживание, если доступно). Я бы не советовал делать полные снимки, поскольку они будут занимать огромное количество места на протяжении всего жизненного цикла приложения.

Ваша таблица истории должна содержать все поля в вашей базовой таблице, плюс еще два - измененная дата и тип транзакции. Возможно, также 3-е поле автономера / последовательности / идентификатора. Версия T-SQL:

CREATE TABLE EmployeeHistory
(
    TransactionID int NOT NULL IDENTITY(1, 1)
        CONSTRAINT PK_EmployeeHistory PRIMARY KEY CLUSTERED,
    TransactionDate datetime NOT NULL,
    TransactionType tinyint NOT NULL,    -- 1 = Add, 2 = Change, 3 = Delete
    EmployeeID int NOT NULL,
    EmployeeName varchar(100) NOT NULL,
    ...
)

Затем сохраните его с помощью триггера:

CREATE TRIGGER tr_Employees_History
ON Employees
FOR INSERT, UPDATE
AS BEGIN
    INSERT EmployeeHistory (TransactionDate, TransactionType, EmployeeID, ...)
        SELECT
            GETDATE(),
            CASE
                WHEN d.EmployeeID IS NULL THEN 1
                WHEN (i.TerminationDate IS NOT NULL) AND
                     (d.TerminationDate IS NULL) THEN 3
                ELSE 2
            END,
            i.EmployeeID, i.EmployeeName, ...
        FROM inserted i
        LEFT JOIN deleted d
        ON d.EmployeeID = i.EmployeeID
END

Я собираюсь предположить, что вы не удаляете записи о сотрудниках и просто устанавливаете TerminationDate; если вы удалите вместо этого (пожалуйста, не делайте этого), вам нужно будет написать аналогичный триггер DELETE вместо второй строки CASE WHEN i.TerminationDate ....

Теперь заполняем таблицу истории:

INSERT EmployeeHistory (TransactionDate, TransactionType, EmployeeID, ...)
    SELECT HireDate, 1, EmployeeID, ...
    FROM Employees

Примечание - если у вас нет HireDate, просто замените его на GETDATE() - ваша история будет действительна только с того момента, как вы ее заполнили.

Теперь, если вы хотите получить исторический «снимок», вы можете сделать это:

CREATE FUNCTION dbo.GetEmployeeSnapshot(@ReportDate datetime)
RETURNS TABLE
AS RETURN
    WITH History_CTE AS
    (
        SELECT
            TransactionType, EmployeeID, EmployeeName, ...,
            ROW_NUMBER() OVER (ORDER BY TransactionDate DESC) AS RowNum
            FROM EmployeeHistory
            WHERE TransactionDate <= @ReportDate
    )
    SELECT *
    FROM History_CTE
    WHERE RowNum = 1
    AND TransactionType IN (1, 2)    -- Filter out terminated employees

И если этот запрос выполняется медленно, если вам нужно ускорить определенные агрегаты, такие как численность персонала, , то и только тогда вы должны начать думать о таблицах моментальных снимков:

CREATE TABLE HeadcountHistory
(
    ReportDate datetime NOT NULL
        CONSTRAINT PK_HeadcountHistory PRIMARY KEY CLUSTERED,
    HeadCount int NOT NULL
)

и обновление proc:

CREATE PROCEDURE dbo.UpdateHeadcountHistory
AS

DECLARE @ReportDate datetime
SET @ReportDate = GETDATE()

INSERT HeadcountHistory (HeadCount)
    SELECT @ReportDate, COUNT(*)
    FROM dbo.GetEmployeeSnapshot(@ReportDate)

Запустите этот последний sproc как часть запланированного задания, и тогда у вас будет денормализованная таблица отчетов для нужных вам агрегатов.

Что-нибудь более сложное, чем это, и я думаю, что вы захотите вместо этого начать изучать хранилище данных.

1 голос
/ 21 января 2010

Если вы просто выполняете это по расписанию, то я бы создал сводную таблицу данных ... раз в месяц запускаю процесс для подсчета и добавляю в сводную таблицу строку, представляющую данные. Таким образом, вы можете оглянуться на историю и сгенерировать любую статистику, которая вам нужна. Возможно, вы захотите генерировать эти данные чаще, чем планируете сообщать (скажем, еженедельно) ... при условии, что у вас более высокое разрешение, чем у отчетного периода, у вас должны быть все необходимые данные.

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