Лучший способ получить табличную статистику

Я разрабатываю решение для SQL Server 2012 Express и версии для разработчиков (с последним пакетом обновления).

В моей базе данных есть таблица CODES с кодами. В этой таблице есть столбец FLAG, указывающий, что код был напечатан, прочитан или удален. Коды сгруппированы по другому столбцу, LEVEL. Таблица CODES имеет CODE и LEVEL в качестве первичного ключа.

Я собираюсь обновить таблицу CODES очень быстро, и если я выполню SELECT COUNT(code) FROM CODES WHERE FLAG=1, чтобы прочитать все коды, то когда-нибудь я заблокирую эту таблицу, и когда у нас будет много строк, SELECT COUNT ЦП достигнет 100%.

Итак, у меня есть другая таблица, STATISTICS для хранения того, сколько codes было напечатано, прочитано или отброшено. Когда я обновляю строку в таблице CODES, я добавляю 1 в таблицу STATISTICS. Я пробовал это двумя способами:

С оператором UPDATE после обновления таблицы CODES.

declare @printed bigint;
set @printed = (Select CODES_PRINTED from STADISTICS where LEVEL = @level)

if (@printed is null)
begin
    insert dbo.STADISTICS(LEVEL, CODES_PRINTED) values (@level, 1)
end
else
begin
    update dbo.STADISTICS set CODES_PRINTED = (@printed + 1) where LEVEL = @level;
end

С TRIGGER в таблице CODES.

ALTER trigger [dbo].[UpdateCodesStatistics] on [dbo].[CODES]
after update
as
    SET NOCOUNT ON;

    if UPDATE(FLAG)
    BEGIN
        declare @flag as tinyint;
        declare @level as tinyint;

        set @flag = (SELECT FLAG FROM inserted);
        set @level = (SELECT LEVEL FROM inserted);

        -- If we have printed a new code
        if (@flag = 1)
        begin
            declare @printed bigint;
            set @printed = (Select CODES_PRINTED from STADISTICS where LEVEL = @level)

            if (@printed is null)
            begin
                insert dbo.STADISTICS(LEVEL, CODES_PRINTED) values (@level, 1)
            end
            else
            begin
                update dbo.STADISTICS set CODES_PRINTED = (@printed + 1) where LEVEL = @level;
            end
        end
    END

Но в обоих случаях я потерял данные. После запуска моей программы я проверяю таблицу CODES и таблицу STATISTICS, и данные статистики не совпадают: у меня меньше напечатанных кодов и кодов чтения в STATISTICS, чем в таблице CODES.

Это таблица STATISTICS, которую я сейчас использую:

CREATE TABLE [dbo].[BATCH_STATISTICS](
    [CODE_LEVEL] [tinyint] NOT NULL,
    [CODES_REQUESTED] [bigint] NOT NULL CONSTRAINT [DF_BATCH_STATISTICS_CODES_REQUESTED]  DEFAULT ((0)),
    [CODES_PRINTED] [bigint] NOT NULL CONSTRAINT [DF_BATCH_STATISTICS_CODES_PRINTED]  DEFAULT ((0)),
    [CODES_READ] [bigint] NOT NULL CONSTRAINT [DF_BATCH_STATISTICS_CODES_READ]  DEFAULT ((0)),
    [CODES_DROPPED] [bigint] NOT NULL CONSTRAINT [DF_BATCH_STATISTICS_CODES_DROPPED]  DEFAULT ((0)),
    [CODES_NOREAD] [bigint] NOT NULL CONSTRAINT [DF_BATCH_STATISTICS_CODES_NOREAD]  DEFAULT ((0)),
 CONSTRAINT [PK_BATCH_STATISTICS] PRIMARY KEY CLUSTERED 
(
    [CODE_LEVEL] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

Кстати, обновляю и вставляю очень быстро (более 1200 строк в минуту).

Любая идея, что происходит или как я могу сделать это лучше?


person VansFannel    schedule 25.11.2014    source источник


Ответы (1)


inserted и deleted может содержать несколько (или нет) строк. Таким образом, такие идиомы, как set @flag = (SELECT FLAG FROM inserted), в корне не работают. Судя по вашему описанию, вам может подойти индексированное представление. , что-то вроде этого:

CREATE VIEW dbo.Statistics
WITH SCHEMABINDING
AS
    SELECT LEVEL, COUNT_BIG(*) as CODES_PRINTED
    FROM dbo.Codes
    WHERE Flag = 1
    GROUP BY LEVEL

и:

CREATE UNIQUE CLUSTERED INDEX IX_Statistics ON dbo.Statistics (LEVEL)

И теперь SQL Server будет (за кулисами) поддерживать эти данные автоматически, и вам не нужно писать какие-либо триггеры (или явно поддерживать отдельную таблицу)

person Damien_The_Unbeliever    schedule 25.11.2014
comment
Спасибо за Ваш ответ. Если мне нужна статистика по прочитанным и удаленным кодам, нужны ли мне еще два индексированных представления? - person VansFannel; 25.11.2014
comment
Это зависит - я понятия не имею, как выглядят ваши данные - вы перебрасываете такие фразы, как read, dropped и printed, но все, что у меня есть, это ваш существующий код, который, кажется, работает с точки зрения столбцов с именами Flag и Level. Вся статистика может существовать в одном проиндексированном представлении, или вам может понадобиться несколько представлений. Как я уже сказал, я не знаю вашей модели данных, поэтому в настоящее время не могу дать лучший совет. - person Damien_The_Unbeliever; 25.11.2014
comment
Я думаю, что индексированные представления могут повлиять на производительность записи. Кроме того, они также могут вызывать другие проблемы. Например, если одна или несколько базовых таблиц часто обновляются, то, в зависимости от агрегатов, которые мы выполняем в индексированном представлении, мы можем усилить конкуренцию за блокировку индекса представления. Так что это не лучшее решение. - person VansFannel; 25.11.2014
comment
@VansFannel - это почти всегда лучшее решение, чем пытаться вручную написать триггеры и реализовать отдельную таблицу, эмулирующую индексированное представление, как выглядит ваше текущее решение. Что заставляет вас думать, что написанные вами триггеры будут иметь меньшее влияние на производительность, чем код, встроенный в SQL Server для обслуживания индексированных представлений? - person Damien_The_Unbeliever; 25.11.2014
comment
Я предоставил все данные, которые вам нужно знать. В зависимости от значения флага код был напечатан, прочитан или отброшен. Легко понять, что для краткости я предоставил печатный код. - person VansFannel; 25.11.2014
comment
@VansFannel - нет, вам это может быть легко узнать, потому что вы сейчас работаете с этими данными. Я понятия не имею, как определить, что конкретная строка была read или dropped. возможно значение флага 0 означает удаление. возможно значение флага 22 означает чтение. Эта информация нет в вашем вопросе, и все, что я могу смутно предположить на данный момент, это то, что значение флага 1, по-видимому, означает printed. Я также понятия не имею, где Level влияет на это, помимо того факта, что, по-видимому, существует несколько уровней, и о них нужно сообщать отдельно - но только для печати или для других? - person Damien_The_Unbeliever; 25.11.2014
comment
Я обновил свой вопрос с более подробной информацией. Будет несколько уровней и кодов на этих уровнях. - person VansFannel; 25.11.2014