TSQL Расчет ежедневной суммы по записям с перекрывающимися диапазонами дат

У меня 3 таблицы, 1 (PortfolioInstrument) содержит инструменты (Instrument), которые хранятся в портфолио с холдингом (Холдинг) в диапазоне дат (DateAdded, DateRemoved). Другое (Цена) ежедневно удерживает цены закрытия (TradeDate) ([Закрыть]) для каждого инструмента (Инструмент). Может быть полезно 3-е, (CalcDate) содержит даты (CalcDate), которые мы пересчитываем холдинги и добавляем и удаляем инструменты из портфеля.

SELECT SUM([Close]*Holding), TradeDate 
FROM Price p1 INNER JOIN PortfolioInstrument pio ON pio.Instrument = p1.Instrument 
AND pio.Portfolio = 3 
WHERE EXISTS (SELECT TradeDate FROM Price p 
INNER JOIN PortfolioInstrument pi ON pi.Instrument = p.Instrument AND Portfolio = 3
WHERE TradeDate >= pi.DateAdded AND 
(TradeDate < pi.DateRemoved OR pi.DateRemoved IS NULL) 
AND p1.ID = p.ID GROUP BY TradeDate) GROUP BY TradeDate

Вот пример набора данных PortfolioInstrument

ID  Portfolio Instrument Holding    DateAdded               DateRemoved

16256   3   410     714.28571       2007-10-01 00:00:00.0   2007-11-01 00:00:00.0
16257   3   611     564.97174       2007-10-01 00:00:00.0   2007-11-01 00:00:00.0
16258   3   538     1,797.75281     2007-10-01 00:00:00.0   2007-11-01 00:00:00.0
...
16302   3   5352    1,067,319.75    2008-02-01 00:00:00.0   2008-04-01 00:00:00.0
16303   3   5353    1,057,800.875   2008-02-01 00:00:00.0   2008-04-01 00:00:00.0
16304   3   11952   0               2008-02-29 00:00:00.0   2008-04-01 00:00:00.0
16305   3   11952   261,484,400     2008-04-01 00:00:00.0   2008-05-01 00:00:00.0
...
16315   3   8374    14,199.99902    2009-01-30 00:00:00.0   
16316   3   11952   246,102,960     2009-01-30 00:00:00.0   2009-02-27 00:00:00.0
16317   3   11952   246,148,912     2009-02-27 00:00:00.0   2009-04-01 00:00:00.0

The problem with this is that it includes all Holdings that have a DateRemoved < TradeDate so there is a jump each re-calculation date where they should get removed from the set. Had a look at various DateDiff methods on Stackoverflow but cannot work out how to group using them in this case. Also note that the cash instrument (Instrument = 11952) comes into the portfolio at some point and then gets an entry for every month thereafter, as you can see it reduces to 0 for some months, this should not matter I think in the SQL produced.

Спасибо.

Дэвид

0
nl ja de

2 ответы

It is not very clear why you are using another instance of the same join like that. If you want to exclude particular holdings where DateRemoved <= TradeDate, you could just check that directly in the WHERE clause:

SELECT SUM(p1.[Close]*pio.Holding), TradeDate 
FROM Price p1
INNER JOIN PortfolioInstrument pio
ON pio.Instrument = p1.Instrument AND pio.Portfolio = 3 
WHERE p1.TradeDate >= pio.DateAdded
  AND (p1.TradeDate < pio.DateRemoved OR pio.DateRemoved IS NULL) 
GROUP BY p1.TradeDate
;

However, if you want to discard an entire group of same TradeDate rows in which at least one row satisfies the condition DateRemoved <= TradeDate, you could use a HAVING clause, like this:

SELECT SUM(p1.[Close]*pio.Holding), TradeDate 
FROM Price p1
INNER JOIN PortfolioInstrument pio
ON pio.Instrument = p1.Instrument AND pio.Portfolio = 3 
GROUP BY p1.TradeDate
HAVING COUNT(CASE WHEN p1.TradeDate <= pio.DateRemoved) THEN 1 END) = 0
;

Unlike a WHERE clause, which applies to individual rows, HAVING is evaluated for a group of rows. In this case, the COUNT() function is used to count how many rows in the group have p1.TradeDate <= pio.DateRemoved. If there's at least one, the group will be discarded from the output, because the requirement that I'm assuming here is that there be no such rows.

1
добавлено

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

0
добавлено
Я вижу, что опаздываю на шоу. Если есть вероятность, что вы можете пересмотреть решение SQL, пожалуйста, взгляните на мои предложения.
добавлено автор Andriy M, источник
SqlCom.ru - Стиль жизни SQL
SqlCom.ru - Стиль жизни SQL
908 участник(ов)

Правила чата - https://t.me/sqlcom/88269 @sqlcom - основной канал (только MS SQL) @sql_ninja - второй канал (SQL вопросы начального уровня и свободное общение) @Gopnegbot - Викторина по SQL Server (наберите в привате /quiz). Предложения в @sql_ninja

SQL_Ninja
SQL_Ninja
340 участник(ов)

Правила чата - https://t.me/sqlcom/88269 @sqlcom - основной канал (только SQL) @sql_ninja - второй канал (SQL вопросы начального уровня и свободное общение) @Gopnegbot - Викторина по SQL Server (наберите в привате /quiz)