У меня есть таблица данных SQL со следующей структурой:
CREATE TABLE Data(
Id uniqueidentifier NOT NULL,
Date datetime NOT NULL,
Value decimal(20, 10) NULL,
RV timestamp NOT NULL,
CONSTRAINT PK_Data PRIMARY KEY CLUSTERED (Id, Date)
)
Число различных идентификаторов варьируется от 3000 до 50000.
Размер таблицы варьируется до более миллиарда строк.
Один идентификатор может занимать от нескольких строк до 5% таблицы.
Единственный наиболее выполненный запрос в этой таблице:
SELECT Id, Date, Value, RV
FROM Data
WHERE Id = @Id
AND Date Between @StartDate AND @StopDate
Теперь мне нужно реализовать пошаговый поиск данных по подмножеству идентификаторов, включая обновления.
Затем я использовал схему запроса, в которой вызывающая сторона предоставляет конкретную версию строки, извлекает блок данных и использует максимальное значение версии строки возвращаемых данных для последующего вызова.
Я написал эту процедуру:
CREATE TYPE guid_list_tbltype AS TABLE (Id uniqueidentifier not null primary key)
CREATE PROCEDURE GetData
@Ids guid_list_tbltype READONLY,
@Cursor rowversion,
@MaxRows int
AS
BEGIN
SELECT A.*
FROM (
SELECT
Data.Id,
Date,
Value,
RV,
ROW_NUMBER() OVER (ORDER BY RV) AS RN
FROM Data
inner join (SELECT Id FROM @Ids) Ids ON Ids.Id = Data.Id
WHERE RV > @Cursor
) A
WHERE RN <= @MaxRows
END
Где @MaxRows
будет варьироваться от 500 000 до 2 000 000, в зависимости от того, как клиент будет запрашивать свои данные.
Я пробовал разные подходы:
- Индексирование по (Id, RV):
CREATE NONCLUSTERED INDEX IDX_IDRV ON Data(Id, RV) INCLUDE(Date, Value);
Используя индекс, запрос ищет строки, где RV = @Cursor
для каждого Id
из них @Ids
, читает следующие строки, затем объединяет результат и сортирует.
Эффективность зависит от относительного положения @Cursor
стоимости.
Если он близок к концу данных (по заказу RV), запрос выполняется мгновенно, а если нет, то запрос может занять до нескольких минут (никогда не позволяйте ему выполняться до конца).
проблема с этим подходом заключается в том, что @Cursor
либо в конце данных, и сортировка не является болезненной (даже не требуется, если запрос возвращает меньше строк @MaxRows
), либо она находится позади, и запрос должен сортировать @MaxRows * LEN(@Ids)
строки.
- Индексирование по RV:
CREATE NONCLUSTERED INDEX IDX_RV ON Data(RV) INCLUDE(Id, Date, Value);
Используя индекс, запрос ищет строку, где RV = @Cursor
затем читает каждую строку, отбрасывая незапрошенные идентификаторы, пока не достигнет @MaxRows
.
Эффективность зависит от% запрашиваемых идентификаторов ( LEN(@Ids) / COUNT(DISTINCT Id)
) и их распределения.
Больше запрашиваемого Id% означает меньше отброшенных строк, что означает более эффективное чтение, меньшее количество запрашиваемых Id% означает больше отброшенных строк, что означает больше чтений для того же количества результирующих строк.
Проблема с этим подходом состоит в том, что если запрошенные идентификаторы содержат только несколько элементов, возможно, потребуется получить весь индекс, чтобы получить нужные строки.
- Использование отфильтрованного индекса или индексированных представлений
CREATE NONCLUSTERED INDEX IDX_RVClient1 ON Data(Id, RV) INCLUDE(Date, Value)
WHERE Id IN (/* list of Ids for specific client*/);
Или
CREATE VIEW vDataClient1 WITH SCHEMABINDING
AS
SELECT
Id,
Date,
Value,
RV
FROM dbo.Data
WHERE Id IN (/* list of Ids for specific client*/)
CREATE UNIQUE CLUSTERED INDEX IDX_IDRV ON vDataClient1(Id, Rv);
Этот метод обеспечивает совершенно эффективные планы индексирования и выполнения запросов, но имеет недостатки: 1. На практике мне придется реализовать динамический SQL для создания индексов или представлений и изменить процедуру запроса для использования правильного индекса или представления. 2. Мне придется поддерживать один индекс или представление существующим клиентом, включая хранилище. 3. Каждый раз, когда клиенту нужно будет изменить свой список запрашиваемых идентификаторов, мне нужно будет удалить индекс или просмотреть и воссоздать его.
Кажется, я не могу найти метод, который бы соответствовал моим потребностям.
Я ищу лучшие идеи для реализации постепенного извлечения данных. Эти идеи могут подразумевать переработку запрашивающей схемы или схемы базы данных, хотя я бы предпочел лучший подход к индексированию, если он есть.
Value
столбец. @crokusek: Заказ по RV, ID вместо RV только не увеличит рабочую нагрузку сортировки без какой-либо выгоды, я не понимаю причины вашего комментария. Из того, что я прочитал, RV должен быть уникальным, если не вставлять данные конкретно в этот столбец, чего нет в приложении.