游标性能问题不佳

时间:2016-12-29 01:40:05

标签: sql-server tsql

我的查询运行速度非常慢。经过一些观察,我觉得这是因为在查询中使用了光标。

CREATE PROCEDURE [dbo].[spTest] (@INwww  varchar(6))  AS

declare @curwwwbegdate datetime
declare @prevwwwbegdate datetime
declare @PrevwwwNum varchar(6)
set NOCOUNT ON
set @CurwwwBegDate = (select Begindate from wwwNUM where wwwnumber = @Inwww)
set @PrevwwwNum = (SELECT TOP 1 wwwNUMBER from wwwNUM WHERE BEGINDATE < @curwwwbegdate
order by begindate desc)
CREATE TABLE #ContRevExp (Inum int, lwww varchar(6),BookNum varchar(13), ldate datetime, lllLoc varchar(8), currentrev varchar(20), currentexp varchar(20), oldrev varchar(20), oldexp varchar(20), ContFileLoc varchar(100), bookloc varchar(3))
create table #wwwdates (wdLoc varchar(3),wdwww varchar(6), wdBegDate datetime, wdEndDate datetime)
create table #LocTable (loc varchar(3))
insert into #LocTable
SELECT LocCode from Location WHERE  STATUS = 1 AND CCC = 1

DECLARE LocCursor INSENSITIVE CURSOR FOR
select * from #LocTable
OPEN LocCursor
declare @Loc varchar(3)
FETCH NEXT FROM LocCursor into @Loc
WHILE (@@FETCH_STATUS = 0)
BEGIN
insert into #wwwdates (wdLoc, wdwww, wdBegDate, wdEndDate)
 select @Loc,@Inwww,
(select top 1 wwwllldate from cccswwwllled where lllwww = @PrevwwwNum and branch = @Loc),
(select  top 1 wwwllldate from cccswwwllled where lllwww = @Inwww and branch = @Loc)

FETCH NEXT FROM LocCursor into @Loc
END
deallocate LocCursor
Drop table #LocTable

DECLARE LocwwwCursor INSENSITIVE CURSOR FOR
select wdLoc, wdBegDate, wdEndDate from #wwwdates
declare @INFILELOC as varchar(3)
declare @INBEGDATE  datetime
declare @INENDDATE  datetime
OPEN LocwwwCursor
FETCH NEXT FROM LocwwwCursor into @INFILELOC,  @INBEGDATE, @INENDDATE
WHILE (@@FETCH_STATUS = 0)
BEGIN

declare @INVNUM int
declare @INDATE datetime

create table #cccs (cInvNum int)
insert into #cccs
Select distinct cccInv_Num from cccswwwllled
left join cccsexport on cccsExport.inv_num = cccinv_num
where cccswwwllled.Branch = @INFILELOC
and (cccswwwllled.impexp <> 1 or cccswwwllled.impexp is null)
and lllwww < @INwww and ContBookIndicator = 'C'
and cccsexport.lastupdate >= @INBEGDATE and cccsexport.lastupdate < @INENDDATE 
DECLARE cccCursor INSENSITIVE CURSOR FOR
select * from #cccs

OPEN cccCursor
declare @cnum int
FETCH NEXT FROM cccCursor into @cnum
WHILE (@@FETCH_STATUS = 0)
BEGIN
insert into #ContRevExp (Inum , lwww ,BookNum, ldate , lllloc , currentrev , currentexp, oldrev, oldexp, contfileloc, bookloc )

select top 1 cccInv_Num, lllwww,
(Select top 1 cccNumber from cccsExport where inv_num = @cnum and lastupdate <= @INENDDATE order by lastupdate desc ),
wwwlllDate,CE.FileNum,
0,
--case when CE.STATUS = 0 then '0' else convert(varchar(20),CE.TotalCost + CE.AllinRpoCost) end,
case when CE.STATUS = 0 then '0' else convert(varchar(20),CE.TotalExpense) end,
0,
--(Select top 1 convert(varchar(20),TotalCost + Allinrpocost) from cccsExport where inv_num = @cnum and lastupdate <= @INBEGDATE order by lastupdate desc ),
(Select top 1 convert(varchar(20),TotalExpense) from cccsExport where inv_num = @cnum and lastupdate <= @INBEGDATE order by lastupdate desc ),
'Cont/File/Loc',
BookLocation from cccswwwllled as CWL
left join cccsexport as CE on CE.inv_num = CWL.cccInv_Num
where CWL.cccInv_Num = @cnum 
and CWL.lllwww < @INwww and CWL.ContBookIndicator = 'C'
and CE.lastupdate >= @INBEGDATE and CE.lastupdate <= @INENDDATE
order by CE.lastupdate desc
FETCH NEXT FROM cccCursor into @cnum
END
deallocate cccCursor
drop table #cccs

FETCH NEXT FROM LocwwwCursor into @INFILELOC,  @INBEGDATE, @INENDDATE
END
deallocate LocwwwCursor
drop table #wwwdates

select bookloc,lwww,booknum,Inum,oldrev,oldexp,currentrev,currentexp,lllloc,
case when contfileloc is null then 'NOT' ELSE contfileloc end as 'Cont' from #ContRevExp
where (currentrev <> oldrev) or (currentexp <> oldexp)
order by bookloc, booknum
drop table #ContRevExp

我对光标并不强烈。我的问题是如何修改代码以提高性能?

修改

真正的问题是我使用ado.net来检索Web应用程序中的数据,从服务器下载数据到客户端的整个时间大约是10秒,通过使用telerik justtrace工具,我发现存储过程花了99%的时间。

在查询中,我发现有一个嵌套游标用法。 cccCursorLocwwwCursor。这意味着有两个while循环,一个在另一个循环中。我想这是主要原因,所以我希望用其他技能替换游标。

示例数据:

因为有很多桌子。我试图弄清楚逻辑。我们从表Location获取数据:

  

临时表#LocTable只有一列。样本数据是

AAA
BBB
CCC
DDD
EEE
FFF
GGG

我相信LocCursor正在处理上述数据的每个值 存储过程的输入是Inwww,假设它是200218。 我们的查询set @CurwwwBegDate = (select Begindate from wwwNUM where wwwnumber = @Inwww)将返回日期时间2002-04-28 00:00:00.000

另一个查询set @PrevwwwNum = (SELECT TOP 1 wwwNUMBER from wwwNUM WHERE BEGINDATE < @curwwwbegdate order by begindate desc)将返回200217。临时表#wwwdates具有如下样本数据。

wdLoc   wdWeek  wdBegDate                      wdEndDate
AAA     200218  NULL                           NULL
BBB     200218  NULL                           NULL
CCC     200218  NULL                           NULL
DDD     200218  2002-05-06 13:39:31.000        2002-05-07 16:52:44.000
EEE     200218  NULL                           NULL
FFF     200218  2002-05-06 13:39:40.000        2002-05-07 16:53:42.000
GGG     200218  NULL                           NULL

我认为游标LocwwwCursor从临时表#wwwdatae获取值,然后插入#cccs。我还没有得到样本数据。

最终结果喜欢(我只用一行演示)

ARL ARL 200510  IETARL0510087   150547816   $0.00   155.21  $0.00   155.00  ARL SOMETHING

2 个答案:

答案 0 :(得分:2)

上面的SP中有不必要的aggerate循环。您的大多数逻辑都可以通过分组和聚合进行压缩并提高性能。例如,下面的整个块可以大大简化。

替换这个....

declare @prevwwwbegdate datetime
declare @PrevwwwNum varchar(6)
set NOCOUNT ON
set @CurwwwBegDate = (select Begindate from wwwNUM where wwwnumber = @Inwww)
set @PrevwwwNum = (SELECT TOP 1 wwwNUMBER from wwwNUM WHERE BEGINDATE < @curwwwbegdate
order by begindate desc)
CREATE TABLE #ContRevExp (Inum int, lwww varchar(6),BookNum varchar(13), ldate datetime, lllLoc varchar(8), currentrev varchar(20), currentexp varchar(20), oldrev varchar(20), oldexp varchar(20), ContFileLoc varchar(100), bookloc varchar(3))
create table #wwwdates (wdLoc varchar(3),wdwww varchar(6), wdBegDate datetime, wdEndDate datetime)
create table #LocTable (loc varchar(3))
insert into #LocTable
SELECT LocCode from Location WHERE  STATUS = 1 AND CCC = 1

DECLARE LocCursor INSENSITIVE CURSOR FOR
select * from #LocTable
OPEN LocCursor
declare @Loc varchar(3)
FETCH NEXT FROM LocCursor into @Loc
WHILE (@@FETCH_STATUS = 0)
BEGIN
insert into #wwwdates (wdLoc, wdwww, wdBegDate, wdEndDate)
 select @Loc,@Inwww,
(select top 1 wwwllldate from cccswwwllled where lllwww = @PrevwwwNum and branch = @Loc),
(select  top 1 wwwllldate from cccswwwllled where lllwww = @Inwww and branch = @Loc)

FETCH NEXT FROM LocCursor into @Loc
END
deallocate LocCursor
Drop table #LocTable

有了这个......

create table #wwwdates (wdLoc varchar(3),wdwww varchar(6), wdBegDate datetime, wdEndDate datetime)
insert into #wwwdates 
SELECT
    LocCode,@Inwww,MAX(Prev_MaxDate),MAX(In_MaxDate)
FROM
(
    SELECT LocCode,
        Prev_MaxDate=CASE WHEN cccswwwllled.lllwww=@PrevwwwNum THEN wwwllldate ELSE NULL END,
        In_MaxDate=CASE WHEN cccswwwllled.lllwww=@Inwww THEN wwwllldate ELSE NULL END
    from 
        Location 
        INNER JOIN cccswwwllled ON cccswwwllled.lllwww IN(@PrevwwwNum,@Inwww) AND cccswwwllled.Branch=Location.LocCode
    WHERE Location.STATUS = 1 AND Location.CCC = 1
)AS X
GROUP BY
    LocCode

答案 1 :(得分:0)

您应该删除游标并创建基于集合的解决方案。你和mssql引擎在游标解决方案中并不强大:)。

  1. 反思你想做什么。 加入wwwNUM + lllwww +位置进入视图类似+检查wwwnumber,branch,LocCode + STATUS + CCC上的索引(取决于数据类型)

    create view MyViewWithNameWhichDescriptContain
    as
    select 
        l.LocCode,
        n.wwwNUMBER Inwww,
        n.Begindate InwwwDate,
        c.wwwllldate Inwww
    from Location l, wwwNUM n, cccswwwllled c
    where l.[STATUS] = 1 AND l.CCC = 1
        and l.LocCode = c.branch
        and c.lllwww =  n.wwwnumber
    
  2. 从此视图中选择

      if object_id('tempdb..#wwwMyDatesPerLocCode') is not null drop table #wwwMyDatesPerLocCode
      select *
      into #wwwMyDatesPerLocCode
      from (
           select *, row_number() over(partition by Inwww order by InwwwDate desc OrbyCol
           from MyViewWithNameWhichDescriptContain) t
      where OrbyCol in (1,2)
    
  3. 创建视图cccswwwllled + cccsexport 让我们说llledExported

  4. 让selfjoin访问后代(wwwMyDatesPerLocCode,其中OrbyCol = 1 + wwwMyDatesPerLocCode,其中OrbyCol = 2)并连接与视图llledExported以获取您的过滤器信息
  5. 再次
  6. 检查所有表格上的索引
  7. Finnaly你不需要变量和游标。尝试加入。如果您要从所有表中添加一些数据,并添加索引的描述,我可以使其更加详细。