Sql 如何以每个记录都与“上一个”记录联接的方式自联接表?
我有一个MS SQL表,其中包含以下列的股票数据:Id、Symbol、Date、Open、High、Low、Close 我想自己加入这个表,这样我就可以每天为Close获得%的更改 我必须创建一个查询,该查询将以一种方式连接表本身,即每个记录也包含来自上一个会话的数据。请注意,我不能使用昨天的日期 我的想法是这样做:Sql 如何以每个记录都与“上一个”记录联接的方式自联接表?,sql,sql-server,performance,sql-server-2008,Sql,Sql Server,Performance,Sql Server 2008,我有一个MS SQL表,其中包含以下列的股票数据:Id、Symbol、Date、Open、High、Low、Close 我想自己加入这个表,这样我就可以每天为Close获得%的更改 我必须创建一个查询,该查询将以一种方式连接表本身,即每个记录也包含来自上一个会话的数据。请注意,我不能使用昨天的日期 我的想法是这样做: select * from quotes t1 inner join quotes t2 on t1.symbol = t2.symbol and t2.date = (selec
select * from quotes t1
inner join quotes t2
on t1.symbol = t2.symbol and
t2.date = (select max(date) from quotes where symbol = t1.symbol and date < t1.date)
DECLARE @Today DATETIME
SELECT @Today = DATEADD(DAY, 0, DATEDIFF(DAY, 0, CURRENT_TIMESTAMP))
;WITH today AS
(
SELECT Id ,
Symbol ,
Date ,
[OPEN] ,
High ,
LOW ,
[CLOSE],
DATEADD(DAY, -1, Date) AS yesterday
FROM quotes
WHERE date = @today
)
SELECT *
FROM today
LEFT JOIN quotes yesterday ON today.Symbol = yesterday.Symbol
AND today.yesterday = yesterday.Date
with OrderedQuotes as
(
select
row_number() over(order by Symbol, Date) RowNum,
ID,
Symbol,
Date,
Open,
High,
Low,
Close
from Quotes
)
select
a.Symbol,
a.Date,
a.Open,
a.High,
a.Low,
a.Close,
a.Date PrevDate,
a.Open PrevOpen,
a.High PrevHigh,
a.Low PrevLow,
a.Close PrevClose,
b.Close-a.Close/a.Close PctChange
from OrderedQuotes a
join OrderedQuotes b on a.Symbol = b.Symbol and a.RowNum = b.RowNum + 1
但是我不知道这是不是正确的/最快的方法。在考虑性能时,我应该考虑什么?例如,在符号、日期对上添加唯一索引是否会提高性能
该表中每年将有大约100000条新记录。我正在使用MS SQL Server 2008您可以执行以下操作:
select * from quotes t1
inner join quotes t2
on t1.symbol = t2.symbol and
t2.date = (select max(date) from quotes where symbol = t1.symbol and date < t1.date)
DECLARE @Today DATETIME
SELECT @Today = DATEADD(DAY, 0, DATEDIFF(DAY, 0, CURRENT_TIMESTAMP))
;WITH today AS
(
SELECT Id ,
Symbol ,
Date ,
[OPEN] ,
High ,
LOW ,
[CLOSE],
DATEADD(DAY, -1, Date) AS yesterday
FROM quotes
WHERE date = @today
)
SELECT *
FROM today
LEFT JOIN quotes yesterday ON today.Symbol = yesterday.Symbol
AND today.yesterday = yesterday.Date
with OrderedQuotes as
(
select
row_number() over(order by Symbol, Date) RowNum,
ID,
Symbol,
Date,
Open,
High,
Low,
Close
from Quotes
)
select
a.Symbol,
a.Date,
a.Open,
a.High,
a.Low,
a.Close,
a.Date PrevDate,
a.Open PrevOpen,
a.High PrevHigh,
a.Low PrevLow,
a.Close PrevClose,
b.Close-a.Close/a.Close PctChange
from OrderedQuotes a
join OrderedQuotes b on a.Symbol = b.Symbol and a.RowNum = b.RowNum + 1
这样你就限制了你今天的成绩,如果这是一种选择的话
编辑:列为其他问题的CTE可能工作得很好,但在处理10万行或更多行时,我倾向于犹豫是否使用行数。如果前一天可能并不总是昨天,我倾向于在自己的查询中取出前一天的支票,然后将其用作参考:
DECLARE @Today DATETIME, @PreviousDay DATETIME
SELECT @Today = DATEADD(DAY, 0, DATEDIFF(DAY, 0, CURRENT_TIMESTAMP));
SELECT @PreviousDay = MAX(Date) FROM quotes WHERE Date < @Today;
WITH today AS
(
SELECT Id ,
Symbol ,
Date ,
[OPEN] ,
High ,
LOW ,
[CLOSE]
FROM quotes
WHERE date = @today
)
SELECT *
FROM today
LEFT JOIN quotes AS previousday
ON today.Symbol = previousday.Symbol
AND previousday.Date = @PreviousDay
如果我正确理解您的需求,一种选择是使用递归cte:
WITH RNCTE AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date) rn
FROM quotes
),
CTE AS (
SELECT symbol, date, rn, cast(0 as decimal(10,2)) perc, closed
FROM RNCTE
WHERE rn = 1
UNION ALL
SELECT r.symbol, r.date, r.rn, cast(c.closed/r.closed as decimal(10,2)) perc, r.closed
FROM CTE c
JOIN RNCTE r on c.symbol = r.symbol AND c.rn+1 = r.rn
)
SELECT * FROM CTE
ORDER BY symbol, date
如果您需要每个符号的运行总数作为百分比变化,那么很容易为该金额添加一个额外的列-不完全确定您的意图是什么,所以上面只是将当前已结算金额除以以前的已结算金额。您所拥有的一切都很好。我不知道将子查询转换为联接是否有帮助。但是,是您要求的,因此实现方法可能是再次将表连接到自身
select *
from quotes t1
inner join quotes t2
on t1.symbol = t2.symbol and t1.date > t2.date
left outer join quotes t3
on t2.symbol = t3.symbol and t2.date > t3.date
where t3.date is null
您可以使用选项和排名功能
;WITH cte AS
(
SELECT symbol, date, [Open], [High], [Low], [Close],
ROW_NUMBER() OVER(PARTITION BY symbol ORDER BY date) AS Id
FROM quotes
)
SELECT c1.Id, c1.symbol, c1.date, c1.[Open], c1.[High], c1.[Low], c1.[Close],
ISNULL(c2.[Close] / c1.[Close], 0) AS perc
FROM cte c1 LEFT JOIN cte c2 ON c1.symbol = c2.symbol AND c1.Id = c2.Id + 1
ORDER BY c1.symbol, c1.date
为了提高性能避免排序和RID查找,请使用此索引
CREATE INDEX ix_symbol$date_quotes ON quotes(symbol, date) INCLUDE([Open], [High], [Low], [Close])
在上进行简单演示,您可以执行以下操作:
select * from quotes t1
inner join quotes t2
on t1.symbol = t2.symbol and
t2.date = (select max(date) from quotes where symbol = t1.symbol and date < t1.date)
DECLARE @Today DATETIME
SELECT @Today = DATEADD(DAY, 0, DATEDIFF(DAY, 0, CURRENT_TIMESTAMP))
;WITH today AS
(
SELECT Id ,
Symbol ,
Date ,
[OPEN] ,
High ,
LOW ,
[CLOSE],
DATEADD(DAY, -1, Date) AS yesterday
FROM quotes
WHERE date = @today
)
SELECT *
FROM today
LEFT JOIN quotes yesterday ON today.Symbol = yesterday.Symbol
AND today.yesterday = yesterday.Date
with OrderedQuotes as
(
select
row_number() over(order by Symbol, Date) RowNum,
ID,
Symbol,
Date,
Open,
High,
Low,
Close
from Quotes
)
select
a.Symbol,
a.Date,
a.Open,
a.High,
a.Low,
a.Close,
a.Date PrevDate,
a.Open PrevOpen,
a.High PrevHigh,
a.Low PrevLow,
a.Close PrevClose,
b.Close-a.Close/a.Close PctChange
from OrderedQuotes a
join OrderedQuotes b on a.Symbol = b.Symbol and a.RowNum = b.RowNum + 1
如果将最后一个连接更改为左连接,则会为每个符号的第一个日期获得一行,但不确定是否需要该行。类似的内容在SQLite中适用:
SELECT ..
FROM quotes t1, quotes t2
WHERE t1.symbol = t2.symbol
AND t1.date < t2.date
GROUP BY t2.ID
HAVING t2.date = MIN(t2.date)
鉴于SQLite是一种最简单的类型,在MSSQL中,这也可能只需要很少的更改就可以工作。对符号、日期进行索引
你能展示一些示例数据并把它放在小提琴里吗?1使用左连接而不是内部连接来处理新产品。然后是一个常规查询,它总是可以被过滤以排除右边为空的记录。@PieterGeerkens:我只对没有空值的行感兴趣,所以内部联接是故意存在的。左键联接是否会提高性能?我不这么认为……2012年的表现更容易/更好,但有滞后/LEAD@MichalB.:那很好;在你不得不违反DRY之前,因为你一开始没有提前考虑。我现在坚持使用2005,这就是为什么我要用dateadd来确定日期,在2008年和更高的时间有更简单的方法。这是正确的方向,但是你必须考虑到周末和假期。周末和假期需要一个日历表。这比编写代码来计算复活节要容易得多,例如,在任何给定的年份。如果股票在前一个交易日没有在市场上交易,这也将不起作用,而且这种情况经常发生。对于此类交易,应使用上一交易日(例如2天前)计算百分比变化。所以不幸的是,没有一天是所有股票的交易日…@MichalB。嗯,我想知道在这种情况下,更重要的是什么,寻找每个符号的最后交易日,或者查看任何符号的最后交易日,即如果A股昨天交易,B股没有交易,我是否仍然应该在昨天使用ISNULLs来反映B昨天没有变化?您可能希望也可能不希望通过T1中的字段分组来折叠结果。因为只有一个t1的结果和一个t2的结果,而且因为您想要昨天的值,所以可能不是。