聚合函数/分组查询性能

时间:2011-01-26 17:50:40

标签: sql sql-server tsql sql-server-2008

此查询有效(感谢那些有帮助的人)生成30天的移动平均值。

SELECT x.symbol, x.dseqkey, AVG(y.VOLUME) moving_average
FROM STOCK_HIST x, STOCK_HIST y
WHERE x.dseqkey>=29 AND x.dseqkey BETWEEN y.dseqkey AND y.dseqkey+29
AND Y.Symbol=X.Symbol 
GROUP BY x.symbol, x.dseqkey
ORDER BY x.dseqkey DESC

然而表现非常糟糕。我正在运行上面的视图(STOCK_HIST),它将两个表(A和B)放在一起。表A包含每日库存量和超过9,000种库存的日期,这些库存可追溯到40年前(每年300多行,每9,000种库存)。表B是“日期密钥”表,它将表A中的日期链接到DSEQKEY(int)。

我对绩效改进有哪些选择?我听说观点很方便,但效果不佳。我应该只将表A和B中所需的列复制到单个表中,然后运行上面的查询吗?我在股票代码+日期(A)和DSEQKEY(B)上的表A和B上有索引。

这是否会影响我的表现?我怎样才能改善这个?

修改

根据要求,我已发布了2个表格和下面的视图。此外,现在视图和每个表上都有一个聚簇索引。我对任何建议持开放态度,因为产生deisred结果的查询仍然很慢:

SELECT
      x.symbol
    , x.dseqkey
    , AVG(y.VOLUME) moving_average
FROM STOCK_HIST x
JOIN STOCK_HIST y ON x.dseqkey BETWEEN y.dseqkey AND y.dseqkey+29 AND Y.Symbol=X.Symbol
WHERE    x.dseqkey >= 15000
GROUP BY x.symbol, x.dseqkey
ORDER BY x.dseqkey DESC ;

这是观点:

CREATE VIEW [dbo].[STOCK_HIST]
WITH SCHEMABINDING
AS
SELECT
      dbo.DATE_MASTER.date
    , dbo.DATE_MASTER.year
    , dbo.DATE_MASTER.quarter
    , dbo.DATE_MASTER.month
    , dbo.DATE_MASTER.week
    , dbo.DATE_MASTER.wday
    , dbo.DATE_MASTER.day
    , dbo.DATE_MASTER.nday
    , dbo.DATE_MASTER.wkmax
    , dbo.DATE_MASTER.momax
    , dbo.DATE_MASTER.qtrmax
    , dbo.DATE_MASTER.yrmax
    , dbo.DATE_MASTER.dseqkey
    , dbo.DATE_MASTER.wseqkey
    , dbo.DATE_MASTER.mseqkey
    , dbo.DATE_MASTER.qseqkey
    , dbo.DATE_MASTER.yseqkey
    , dbo.DATE_MASTER.tom
    , dbo.QP_HISTORY.Symbol
    , dbo.QP_HISTORY.[Open]  as propen
    , dbo.QP_HISTORY.High    as prhigh
    , dbo.QP_HISTORY.Low     as prlow
    , dbo.QP_HISTORY.[Close] as prclose
    , dbo.QP_HISTORY.Volume
    , dbo.QP_HISTORY.QRS
FROM dbo.DATE_MASTER
INNER JOIN dbo.QP_HISTORY ON dbo.DATE_MASTER.date = dbo.QP_HISTORY.QPDate ;

这里是DATE_MASTER表:

CREATE TABLE [dbo].[DATE_MASTER] (
      [date]    [datetime] NULL
    , [year]    [int] NULL
    , [quarter] [int] NULL
    , [month]   [int] NULL
    , [week]    [int] NULL
    , [wday]    [int] NULL
    , [day]     [int] NULL
    , [nday]    nvarchar NULL
    , [wkmax]   [bit] NOT NULL
    , [momax]   [bit] NOT NULL
    , [qtrmax]  [bit] NOT NULL
    , [yrmax]   [bit] NOT NULL
    , [dseqkey] [int] IDENTITY(1,1) NOT NULL
    , [wseqkey] [int] NULL
    , [mseqkey] [int] NULL
    , [qseqkey] [int] NULL
    , [yseqkey] [int] NULL
    , [tom]     [bit] NOT NULL
) ON [PRIMARY] ;

这是QP_HISTORY表:

CREATE TABLE [dbo].[QP_HISTORY] (
      [Symbol] varchar    NULL
    , [QPDate] [date]     NULL
    , [Open]   [real]     NULL
    , [High]   [real]     NULL
    , [Low]    [real]     NULL
    , [Close]  [real]     NULL
    , [Volume] [bigint]   NULL
    , [QRS]    [smallint] NULL
) ON [PRIMARY] ;

这是观点(STOCK_HIST)指数

CREATE UNIQUE CLUSTERED INDEX [ix_STOCK_HIST] ON [dbo].[STOCK_HIST]   
(
    [Symbol] ASC,  
    [dseqkey] ASC,  
    [Volume] ASC  
)

这是QP_HIST指数

CREATE UNIQUE CLUSTERED INDEX [IX_QP_HISTORY] ON [dbo].[QP_HISTORY] 
(
    [Symbol] ASC,
    [QPDate] ASC,
    [Close] ASC,
    [Volume] ASC
)

这是DATE_MASTER的索引

CREATE UNIQUE CLUSTERED INDEX [IX_DATE_MASTER] ON [dbo].[DATE_MASTER] 
(
    [date] ASC,
    [dseqkey] ASC,
    [wseqkey] ASC,
    [mseqkey] ASC
)

我没有设置任何主键。这有助于提高绩效吗?

编辑 - 进行建议的更改后,查询速度比以前慢。在10米44秒跑的是目前30米并且仍在运行。

我做了所有请求的更改,除了我没有更改Date_Master中的日期名称,我没有从QP_Hist中删除QPDate列。 (我有理由这样做,并且没有看到它影响性能,因为我在查询中没有提到它。)

修改过的查询

select x.symbol, x.dmdseqkey, avg(y.volume) as moving_average 
from dbo.QP_HISTORY as x
join dbo.QP_HISTORY as y on (x.dmdseqkey between y.dmdseqkey and (y.dmdseqkey + 29))                          
                         and (y.symbol = x.symbol)  
where x.dmdseqkey >= 20000 
group by x.symbol, x.dmdseqkey 
order by x.dmdseqkey desc ;

PK on QP_History

ALTER TABLE [dbo].[QP_HISTORY]
   ADD CONSTRAINT [PK_QP_HISTORY] PRIMARY KEY CLUSTERED ([Symbol] ASC, DMDSeqKey] ASC)

QP_History上的FK

ALTER TABLE [dbo].[QP_HISTORY] ADD  CONSTRAINT [FK_QP_HISTORY_DATE_MASTER] FOREIGN KEY([DMDSeqKey]) REFERENCES [dbo].[DATE_MASTER] ([dseqkey])

在Date_Master上的PK

ALTER TABLE [dbo].[DATE_MASTER]
 ADD  CONSTRAINT [PK_DATE_MASTER] PRIMARY KEY CLUSTERED ([dseqkey] ASC)

编辑

这是执行计划

4 个答案:

答案 0 :(得分:4)

首先,单独加入过滤器。

(编辑:修复ON条款)

SELECT x.symbol, x.dseqkey, AVG(y.VOLUME) moving_average
FROM
    STOCK_HIST x
    JOIN
    STOCK_HIST y ON x.dseqkey BETWEEN y.dseqkey AND y.dseqkey+29
                                AND Y.Symbol=X.Symbol 
WHERE x.dseqkey>=29
GROUP BY x.symbol, x.dseqkey
ORDER BY x.dseqkey DESC

另外,你有什么索引 - 我建议索引(dseqkey,symbol)INCLUDE(VOLUME)

编辑3:你不能在聚集索引中拥有INCLUDE,我的不好。你的语法还可以。

请尝试这些排列......目标是找到JOIN和WHERE的最佳索引,然后是ORDER BY。

CREATE UNIQUE CLUSTERED INDEX [ix_STOCK_HIST] ON [dbo].[STOCK_HIST] (...

    ...[Symbol] ASC, [dseqkey] ASC, [Volume] ASC )

    ...[dseqkey] ASC, [Symbol] ASC, [Volume] ASC )

    ...[Symbol] ASC, [dseqkey] DESC, [Volume] ASC )

    ...[dseqkey] DESC, [Symbol] ASC, [Volume] ASC )

答案 1 :(得分:3)

SQL Server不支持LAGLEAD中提供的OraclePostgreSQL条款,也不支持MySQL等会话变量。

计算移动窗口的聚合是SQL Server中的一个难点。

所以天知道我不想这样说,但是,在这种情况下,基于CURSOR的解决方案可能更有效。

答案 2 :(得分:1)

尝试在视图上放置聚集索引。这将使视图像普通表一样持久化到磁盘,并且不必每次都访问您的表。

应该加快速度。

要获得更好的答案,请将链接发布到原始问题,看看是否能找到更好的解决方案。

答案 3 :(得分:0)

好的,所以我会从最后开始。我想实现这个模型。

enter image description here

有了这个,你可以直接在历史表上运行查询,不需要视图并加入dbo.DATE_MASTER

select
      x.symbol
    , x.dseqkey
    , avg(y.volume) as moving_average
from dbo.QP_HISTORY as x
join dbo.QP_HISTORY as y on  (x.dSeqKey between y.dSeqKey and (y.dSeqKey + 29))
                         and (y.symbol = x.symbol) 
where x.dseqkey >= 15000
group by x.symbol, x.dseqkey
order by x.dseqkey desc 
OPTION (ORDER GROUP) ;

QP_HISTORYSTOCK_HISTORY视图更窄,因此查询应该更快。 "冗余列删除"来自联接的计划用于下一代SQL Server(Denali),因此暂时较窄通常意味着更快 - 至少对于大型表来说。此外,join on ..where子句与PK(Symbol, dSeqKey)很匹配。

现在,如何实现这个目标:

a)将[date]中的dbo.DATE_MASTER列修改为类型date而不是datetime。将其重命名为FullDate以避免混淆。不是绝对必要,而是为了保持我的理智。

b)将PK添加到dbo.DATE_MASTER

alter table dbo.DATE_MASTER add constraint primary key pk_datemstr (dSeqKey);

c)在表格QP_HISTORY中添加列dSeqKey并填充该列以匹配QPDate个日期。

d)从表中删除QPDate列。

e)将PK和FK添加到QP_HISTORY

alter table dbo.QP_HISTORY
   add constraint pk_qphist  primary key (Symbol, dSeqKey)
 , add constraint fk1_qphist foreign key (dSeqKey)
                             references dbo.DATE_MASTER(dSeqKey) ;

f)至少暂时放弃你问题末尾提到的所有索引。

g)我没有看到Symbol字段的大小。将其定义为尽可能窄。

h)首先在开发系统上说,实现并测试它。