Генерировать строку в минуту дня из малонаселенной таблицы базы данных - PullRequest
2 голосов
/ 29 октября 2009

У меня есть таблица, заполненная строками с метками времени, вставленными в (по существу) случайную точку дня.

Мне нужно сгенерировать промежуточные итоги с 1 строкой в ​​минуту (поэтому для одного дня всегда будет ровно 24 * 60 строк), например,

Date                Quantity    Running Total
2009-10-29 06:30           1                1
2009-10-29 06:31           5                6
2009-10-29 06:32           10              16  
2009-10-29 06:33           11              27   
2009-10-29 06:34           22              49

... ...

Есть какие-нибудь мысли о том, как это сделать? Один SQL-запрос был бы идеальным, но не обязательным, производительность довольно важна (менее 5 секунд для таблицы, содержащей 500 тыс. Строк, из которых 70 тыс. Интересны для этого запроса)

Мое окончательное решение (более или менее).

Фактический сценарий был такой. У меня есть две таблицы, одна из которых содержит Order с отношением 1: n к таблице Fills.

Мне нужно было показать текущую среднюю цену и накопленную сумму за каждую минуту в торговый день

DECLARE @StartDate AS DATETIME, @EndDate AS DATETIME
SET @StartDate = '2009-10-28';
SET @EndDate = '2009-10-29';

-- Generate a Temp Table containing all the dates I'm interested in
WITH DateIntervalsCTE AS
(
 SELECT 0 i, @StartDate AS Date
 UNION ALL
 SELECT i + 1, DATEADD(minute, i, @StartDate )
 FROM DateIntervalsCTE 
 WHERE DATEADD(minute, i, @StartDate ) < @EndDate
)
SELECT DISTINCT Date 
INTO #Dates
FROM DateIntervalsCTE
OPTION (MAXRECURSION 32767);

SELECT 
 d.Date
 , mo3.symbol
 , ISNULL(SUM(mf.Quantity),0) AS CumulativeTotal
 , ROUND(ISNULL(SUM(mf.Quantity * mf.Price)/SUM(mf.Quantity),0),4) AS AveragePrice
FROM 
 #Dates AS d
 CROSS JOIN (
    SELECT DISTINCT mo2.Symbol, mo2.OrderID 
    FROM 
     Orders AS mo2 
     INNER JOIN Fills AS mf2 ON mo2.OrderID = mf2.OrderID
    WHERE CONVERT(DATETIME,CONVERT(CHAR(10),mf2.FillDate,101)) = @StartDate
    ) AS mo3
 LEFT JOIN Fills AS mf ON mo3.OrderID = mf.OrderID AND CONVERT(DATETIME,CONVERT(CHAR(16),mf.FillDate,120)) < = d.Date
WHERE
 d.Date >= DATEADD(mi,390, @StartDate) -- 06:30
 AND d.Date <= DATEADD(mi,780, @StartDate) -- 13:00
GROUP BY d.Date, mo3.symbol
ORDER BY mo3.Symbol, d.Date

Я еще не закончил все свои тесты, но похоже, что он добился цели, спасибо за помощь!

Ответы [ 2 ]

6 голосов
/ 29 октября 2009

Убедитесь, что столбец даты имеет индекс и производительность должна быть разумной.

SELECT t.Date,
COUNT(*) AS Quantity,
(SELECT COUNT(*) FROM Table WHERE Date < t.Date) AS RunningTotal
FROM Table t
GROUP BY t.Date

Получение таблицы, заполненной одной строкой в ​​минуту, может быть выполнено чрезвычайно быстро следующим образом:

DECLARE @StartDate smalldatetime
DECLARE @EndDate smalldatetime

SET @StartDate = '1 jan 2009' --MIN(TimeStamp) FROM Table
SET @EndDate = '2 jan 2009' --MAX(TimeStamp) FROM Table

SET @StartDate = DATEADD(minute,-DATEPART(minute,@StartDate),@StartDate)
SET @EndDate = DATEADD(minute,-DATEPART(minute,@EndDate),@EndDate)


; WITH DateIntervalsCTE AS
(
SELECT 0 i, @startdate AS Date
UNION ALL
SELECT i + 1, DATEADD(minute, i, @startdate )
FROM DateIntervalsCTE 
WHERE DATEADD(minute, i, @startdate ) <= @enddate
)
SELECT DISTINCT Date FROM DateIntervalsCTE
OPTION (MAXRECURSION 32767);

При условии, что вам нужны данные <~ 22 дней из-за ограничений рекурсии. </p>

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

DECLARE @StartDate smalldatetime
DECLARE @EndDate smalldatetime
DECLARE @t TABLE (Date smalldatetime,Quantity int,RunningTotal int)
SET @StartDate = '1 jan 2009' --MIN(TimeStamp) FROM Table
SET @EndDate = '2 jan 2009' --MAX(TimeStamp) FROM Table

SET @StartDate = DATEADD(minute,-DATEPART(minute,@StartDate),@StartDate)
SET @EndDate = DATEADD(minute,-DATEPART(minute,@EndDate),@EndDate)


; WITH DateIntervalsCTE AS
(
SELECT 0 i, @startdate AS Date
UNION ALL
SELECT i + 1, DATEADD(minute, i, @startdate )
FROM DateIntervalsCTE 
WHERE DATEADD(minute, i, @startdate ) <= @enddate
)
INSERT INTO @t (Date) 
SELECT DISTINCT Date FROM DateIntervalsCTE
OPTION (MAXRECURSION 32767);

UPDATE t SET Quantity = (SELECT COUNT(d.TimeStamp) FROM Table d WHERE Date = t.date)
from @t t

update t2 set runningtotal = (SELECT SUM(Quantity) FROM @t WHERE date <= t2.date)
from @t t2

select * from @t
0 голосов
/ 29 октября 2009

Вы наверняка захотите использовать group by.Сложная (бесполезная) часть в том, что он будет синтетическим, то есть вам придется создавать его самостоятельно.Есть множество способов сделать это

GROUP BY год (yourdate), месяц (yourdate), день (yourdate) и т. Д. ...

За исключением того, что я не помню, есть ли у меня на голове функции часов () и минут ().

Вы также можете использовать функцию datepart .

Затем вы можете сложить все это в один столбец для создания красивого ярлыка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...