Как не сканировать все записи для запроса верхних строк по сложным условиям - PullRequest
0 голосов
/ 29 декабря 2018

У меня есть таблица и данные, подобные этим:

create table AmountObjects
(
  objectId integer,
  unixTimestamp integer,
  amount integer,

  PRIMARY KEY  
  (
      [objectId] ASC,
      [unixTimestamp] ASC  
  )
);

insert into AmountObjects values (1, 1, 33);
insert into AmountObjects values (1, 2, 33);
insert into AmountObjects values (1, 3, 33);
insert into AmountObjects values (1, 4, 33);
insert into AmountObjects values (1, 5, 33);
insert into AmountObjects values (1, 6, 33);
insert into AmountObjects values (1, 7, 33);
insert into AmountObjects values (1, 8, 33);
insert into AmountObjects values (1, 9, 33);
insert into AmountObjects values (1, 10, 33);

Я хочу запросить последние записи, отфильтрованные по дате и кумулятивному количеству, но такие запросы сканируют все записи по объекту:

select 
    a.objectId,
    a.unixTimestamp,
    a.amount,
    s.total
from AmountObjects a
cross apply
(
    select sum(amount) total from AmountObjects stat 
    where a.unixTimestamp <= stat.unixTimestamp and a.objectId = stat.objectId 
) s
where 
    unixTimestamp >= 9
    or s.total <= 150

Мой вопрос: как запросить данные без проверки всех данных по объекту ?

Спасибо

Ответы [ 2 ]

0 голосов
/ 29 декабря 2018

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

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

WITH DistinctObjects
     AS (SELECT DISTINCT objectId
         FROM   AmountObjects a),
     MinTimeStampsByObjectId
     AS (SELECT do.objectId,
                ca.minUnixTimeStamp
         FROM   DistinctObjects do
                CROSS APPLY (SELECT ISNULL((SELECT TOP 1 unixTimeStamp
                                            FROM   (SELECT *,
                                                           SUM(ao.amount)
                                                             OVER (
                                                               ORDER BY ao.unixTimeStamp DESC) AS total
                                                    FROM   AmountObjects ao
                                                    WHERE  ao.objectId = do.objectId) d
                                            WHERE  total > 150
                                            ORDER  BY d.unixTimeStamp DESC), -1))ca(minUnixTimeStamp))
SELECT ca2.*
FROM   MinTimeStampsByObjectId mts
       CROSS APPLY (SELECT *,
                           SUM(ao.amount)
                             OVER (
                               ORDER BY ao.unixTimeStamp DESC) AS total
                    FROM   AmountObjects ao
                    WHERE  ao.objectId = mts.objectId
                           AND ao.unixTimeStamp > IIF(mts.minUnixTimeStamp > 8,8,mts.minUnixTimeStamp)) ca2 
0 голосов
/ 29 декабря 2018

Это должно реализовать ту же логику и быть более эффективным:

select a.*
from (select a.objectId, a.unixTimestamp, a.amount,
             sum(a.amount) over (partition by a.objectId order by a.unixTimeStamp desc) as total
      from AmountObjects a
     ) a
where unixTimestamp >= 9 or total <= 150;

Однако оно все равно будет сканировать все строки.

...