我目前有这些表格:
CREATE TABLE #SECURITY_TEMP (ID CHAR(30))
CREATE TABLE #SECURITY_TEMP_PRICE_HISTORY (ID CHAR(30), PRICEDATE DATE, PRICE FLOAT)
CREATE TABLE #SECURITY_POST (ID CHAR(30), SECPOS int)
INSERT INTO #SECURITY_TEMP (ID) VALUES ('APPL') ,('VOD'),('VOW3'), ('AAA')
INSERT INTO #SECURITY_TEMP_PRICE_HISTORY (ID,PRICEDATE, PRICE) VALUES
('APPL', '20150101',10.4), ('APPL', '20150116',15.4), ('APPL', '20150124',22.4),
('VOD', '20150101', 30.5), ('VOD', '20150116',16.5), ('VOD', '20150124',16.5),
('VOW3', '20150101', 45.5), ('VOW3', '20150116',48.8) ,('VOW3', '20150124',50.55),
('AAA', '20100118', 0.002)
INSERT INTO #SECURITY_POST (ID,SECPOS) VALUES ('APPL', 100), ('VOD', 350), ('VOW3', 400)
我希望有一个干净的表格,在通过日期时向我显示该证券的安全ID,安全位置和最新可用价格。
现在,当我执行以下操作时:
SELECT sec.ID, sec.SECPOS, t.PRICE
FROM #SECURITY_POST as SEC INNER JOIN #SECURITY_TEMP_PRICE_HISTORY as t
ON sec.ID = t.ID
WHERE t.PriceDate = '20150101'
GROUP BY sec.ID, secPos, t.price
我得到了正确的结果
1. ID SECPOS PRICE
2. APPL 100 10.4
3. VOD 350 30.5
4. VOW3 400 45.5
但是,可能存在个别情况,即股票价格无法获得。从这个意义上说,我希望能够获得最新的价格。
做
SELECT sec.ID, sec.SECPOS, t.PRICE
FROM #SECURITY_POST as SEC INNER JOIN
#SECURITY_TEMP_PRICE_HISTORY as t
ON sec.ID = t.ID
WHERE t.PriceDate = '20150117'
GROUP BY sec.ID, secPos, t.price
由于没有数据而返回0行,并且正在执行
SELECT sec.ID, sec.SECPOS, t.PRICE
FROM #SECURITY_POST as SEC INNER JOIN
#SECURITY_TEMP_PRICE_HISTORY as t
ON sec.ID = t.ID
WHERE t.PriceDate <= '20150117'
GROUP BY sec.ID, sec.secPos, t.price
HAVING sec.secpos <> 0
返回重复的行。
我尝试了很多不同的方法,但我无法得到我想要的输出。此外,我还希望能够获得一个价格最接近日期的列(称为START_DATE
)和一个价格最接近第二个日期的列(称为END_DATE
)和一列这将是位置Price@END_DATE - Price@START_DATE
。价格始终取自#SECURITY_TEMP_PRICE_HISTORY
。
然而,我的SQL知识只是令人尴尬,我无法找到一个很好的有效方法。任何帮助,将不胜感激。另请注意#SECURITY_PRICE_HISTORY table may contain more securities than the #SECURITY_POST
表。
答案 0 :(得分:12)
这应该可以解决问题。 OUTER APPLY
是一个连接运算符,(如CROSS APPLY
)允许派生表具有外部引用。
SELECT
s.ID,
s.SecPos,
t.Price
t.PriceDate
FROM
#SECURITY_POST s
OUTER APPLY (
SELECT TOP 1 *
FROM #SECURITY_TEMP_PRICE_HISTORY t
WHERE
s.ID = t.ID
AND t.PriceDate <= '20150117'
ORDER BY t.PriceDate DESC
) t
;
您可能还需要考虑标记非常旧的安全价格,或将最近一次安全的查询限制在特定时期(一周或一个月或某事)。
确保您的价格历史记录表中包含(ID, PriceDate)
的索引,以便子查询查找可以使用范围搜索,您的表现可以很好。确保在安全表上进行任何日期数学运算,而不是历史表,否则您将强制价格查询子查询不可搜索,这对性能不利,因为搜索范围是不可能的。
如果没有找到安全价格,OUTER APPLY
仍会允许该行存在,因此价格将显示为NULL
。如果您希望在找不到合适价格时不显示证券,请使用CROSS APPLY
。
对于问题的第二部分,您可以使用两个OUTER APPLY
操作执行此操作,如下所示:
DECLARE
@StartDate date = '20150101',
@EndDate date = '20150118';
SELECT
S.ID,
S.SecPos,
StartDate = B.PriceDate,
StartPrice = B.Price,
EndDate = E.PriceDate,
EndPrice = E.Price,
Position = B.Price - E.Price
FROM
#SECURITY_POST S
OUTER APPLY (
SELECT TOP 1 *
FROM #SECURITY_TEMP_PRICE_HISTORY B
WHERE
S.ID = B.ID
AND B.PriceDate <= @StartDate
ORDER BY B.PriceDate DESC
) B
OUTER APPLY (
SELECT TOP 1 *
FROM #SECURITY_TEMP_PRICE_HISTORY E
WHERE
S.ID = E.ID
AND E.PriceDate <= @EndDate
ORDER BY E.PriceDate DESC
) E
;
使用您的数据,会产生以下结果集:
ID SecPos StartDate StartPrice EndDate EndPrice Position
---- ------ ---------- ---------- ---------- -------- --------
APPL 100 2015-01-01 10.4 2015-01-16 15.4 -5
VOD 350 2015-01-01 30.5 2015-01-16 16.5 14
VOW3 400 2015-01-01 45.5 2015-01-16 48.8 -3.3
最后,虽然并非所有人都同意,但我建议您使用ID
而不是SecurityID
的表名为ID
列命名。根据我的经验,使用ID
只会导致问题。
注意:有一种方法可以使用Row_Number()
窗口函数来解决此问题。如果您的股票价格与股票数量相比相对较少,并且您正在查找历史表中大多数股票的价格,那么您可以通过该方法获得更好的表现。但是,如果每个股票有很多价位,或者你只是过滤几只股票,那么你可能会用我给你看过的方法获得更好的表现。