-- +goose Up -- Каталог разрезов отвечает по учётным колонкам объекта, а не по его -- содержимому. Без индекса это невозможно: `bucket` объявлена `WITHOUT ROWID`, -- то есть строка целиком, вместе со сжатым `payload`, живёт в дереве первичного -- ключа. Агрегат по всем строкам тащил бы за собой страницы содержимого — при -- 260 тысячах объектов за год это сотни мегабайт чтения на каждый запрос -- каталога, притом что сам ответ несёт три десятка строк. -- -- Индекс ПОКРЫВАЮЩИЙ: в нём есть всё, что спрашивает каталог, поэтому обращения -- к самой таблице не будет вовсе. Порядок колонок задан двумя запросами: -- -- 1. разрезы метрики — GROUP BY metric, layer (+ units, чтобы расхождение -- единиц было видно строкой, а не выбиралось молча); -- 2. общие часы двух слоёв — обход hour_utc по убыванию внутри (metric, -- layer), поэтому hour_utc стоит третьим и до колонок значений. -- -- Первичный ключ (metric, layer, hour_utc) в индекс дописывается самим SQLite: -- у таблицы `WITHOUT ROWID` строка адресуется им. -- -- Цена — около 60 байт на объект (≈16 МБ за год) и одна вставка в дерево на -- запись объекта. Платит её только настоящее изменение: широкий проход, у -- которого сошёлся хеш содержимого, объект не переписывает вовсе. CREATE INDEX bucket_catalog ON bucket (metric, layer, hour_utc, first_ts, last_ts, points, units); -- +goose Down DROP INDEX bucket_catalog;