SQL Выбор строк с различными интервалами

У меня есть ситуация, когда у меня есть огромная таблица, содержащая огромное количество строк, которая выглядит (например):

id          Timestamp               Value
14574499    2011-09-28 08:33:32.020 99713.3000
14574521    2011-09-28 08:33:42.203 99713.3000
14574540    2011-09-28 08:33:47.017 99713.3000
14574559    2011-09-28 08:38:53.177 99720.3100
14574578    2011-09-28 08:38:58.713 99720.3100
14574597    2011-09-28 08:39:03.590 99720.3100
14574616    2011-09-28 08:39:08.950 99720.3100
14574635    2011-09-28 08:39:13.793 99720.3100
14574654    2011-09-28 08:39:19.063 99720.3100
14574673    2011-09-28 08:39:23.780 99720.3100
14574692    2011-09-28 08:39:29.167 99758.6400
14574711    2011-09-28 08:39:33.967 99758.6400
14574730    2011-09-28 08:39:40.803 99758.6400
14574749    2011-09-28 08:39:49.297 99758.6400

Итак, правила: Временные метки могут составлять любое количество секунд в секундах, 5 с, 30 с, 60 с и т.д., Это зависит от того, сколько лет прошло запись (архивирование).

Я хочу иметь возможность запросить эту таблицу, чтобы выбрать каждую n-ю строку на основе метки времени.

Итак, например:

Выберите * из таблицы, где intervalBetweenTheRows = 30s

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

Итак, каждая n-я строка, основанная на времени между каждой строкой

Любые идеи?!

Карл

Для тех, кто вас интересует, рекурсивный CTE был довольно медленным, я подумал о немного другом методе:

SELECT TOP 500
    MIN(pvh.[TimeStamp]) as [TimeStamp],
    AVG(pvh.[Value]) as [Value]
FROM
    PortfolioValueHistory pvh
WHERE
    pvh.PortfolioID = @PortfolioID
    AND pvh.[TimeStamp] >= @StartDate
    AND pvh.[TimeStamp] <= @EndDate
GROUP BY
    FLOOR(DateDiff(Second, '01/01/2011 00:00:00', pvh.[TimeStamp]) / @ResolutionInSeconds)
ORDER BY 
    [TimeStamp] ASC

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

Это используется для построения графика исторических данных, поэтому среднее значение делает меня в порядке.

Это было самое быстрое исполнение, основанное на размере таблицы, которое я мог бы придумать

Спасибо за вашу помощь.

Ответ 1

Предполагая, что требование состоит в том, что определитель для того, возвращается ли строка или нет, зависит от времени, прошедшего с предыдущей возвращенной строки, для этого необходим процедурный подход. Рекурсивные CTE могут быть немного эффективнее, чем курсор.

WITH RecursiveCTE
     AS (SELECT TOP 1 *
         FROM @T
         ORDER BY [Timestamp]
         UNION ALL
         SELECT id,
                [Timestamp],
                Value
         FROM   (
                --Can't use TOP directly
                SELECT T.*,
                       rn = ROW_NUMBER() OVER (ORDER BY T.[Timestamp])
                 FROM   @T T
                        JOIN RecursiveCTE R
                          ON T.[Timestamp] >=
                                 DATEADD(SECOND, 30, R.[Timestamp])) R
         WHERE  R.rn = 1)
SELECT *
FROM RecursiveCTE

Ответ 2

Это не так элегантно, как Martin S CTE, но вместо этого использует интерполяцию на предопределенных точках выборки, чтобы получить первый образец между каждой парой отсчетов. Если в течение периода нет образца, запись не возвращается.

DECLARE @SampleTime DATETIME
DECLARE @NumberSamples INT
DECLARE @SampleInterval INT

SET @SampleTime = '2011-09-28 08:33:32.020' -- Start time
SET @NumberSamples = 20 -- Or however many sample intervals you need to evaluate
SET @SampleInterval = 30 -- Seconds

CREATE TABLE #tmpTimesToSample
(
    SampleID INT,
    SampleTime DATETIME NULL
)

-- Works out the time intervals, 0 to 19
INSERT INTO #tmpTimesToSample(SampleID, SampleTime)
SELECT TOP (@NumberSamples)
    sv.number,
    DATEADD(ss, sv.number * @SampleInterval, @SampleTime)
FROM
    master..spt_values sv
WHERE 
    type = 'p'
ORDER BY
    sv.number ASC

-- Now interpolate these sample intervals back into the data table
SELECT ID, [TimeStamp], Value
FROM
(
    SELECT mt.Id, mt.[TimeStamp], mt.Value, row_number() over (partition by tmp.SampleID order by tmp.SampleID) as RowNum
    FROM #tmpTimesToSample tmp RIGHT OUTER JOIN MyTable mt
    on mt.[TimeStamp] BETWEEN tmp.SampleTime and DATEADD(ss, @SampleInterval, tmp.SampleTime)
) x
WHERE x.RowNum = 1 -- Only want the first sample in each bin

DROP TABLE #tmpTimesToSample

Данные теста:

CREATE TABLE MyTable
(
    ID BIGINT,
    [TimeStamp] DATETIME,
    [Value] DECIMAL(18,4)
)
GO

insert into MyTable values(14574499, '2011-09-28 08:33:32.020', 99713.3000)
insert into MyTable values(14574521    ,'2011-09-28 08:33:42.203',  99713.3000)
insert into MyTable values(14574540    ,'2011-09-28 08:33:47.017', 99713.3000)
insert into MyTable values(14574559    ,'2011-09-28 08:38:53.177', 99720.3100)
insert into MyTable values(14574578    ,'2011-09-28 08:38:58.713', 99720.3100)
insert into MyTable values(14574597    ,'2011-09-28 08:39:03.590', 99720.3100)
insert into MyTable values(14574616    ,'2011-09-28 08:39:08.950', 99720.3100)
insert into MyTable values(14574635    ,'2011-09-28 08:39:13.793', 99720.3100)
insert into MyTable values(14574654    ,'2011-09-28 08:39:19.063', 99720.3100)
insert into MyTable values(14574673    ,'2011-09-28 08:39:23.780', 99720.3100)
insert into MyTable values(14574692    ,'2011-09-28 08:39:29.167', 99758.6400)
insert into MyTable values(14574711    ,'2011-09-28 08:39:33.967', 99758.6400)
insert into MyTable values(14574730    ,'2011-09-28 08:39:40.803', 99758.6400)
insert into MyTable values(14574749    ,'2011-09-28 08:39:49.297', 99758.6400)
go

Ответ 3

Это даст вам все строки с интервалом 30 миллисекунд в следующую строку. Обе строки будут рядом.

Select T1.*, T2.*
From MyTable T1
    Inner Join MyTable T2
        On DateDiff (millisecond, T1.Value, T2.Value) = 30