SQL虽然使存储过程变得如此缓慢

时间:2009-09-04 00:34:16

标签: sql-server-2005 optimization

我开发了一个存储过程,它从表VisitorsInfluences中获取数据,并在每个星系的中间构建五个星系,其中“受影响最大的访问者”,以及每个星系周围受它们影响最大的访问者。到目前为止这是否清楚?

我不知道为什么,存储过程有时需要大约6或7或10秒才能运行,并且在网站上显示的速度非常慢。

你可以帮我解决这个问题吗?我不知道花了这么长时间,但我知道哪个查询是,所以我将在下面评论。

谢谢,

布赖恩

存储过程:

ALTER proc [dbo].[XInfluencedByYCloudGetXml] (@VisitorId int, @VisitorIdLogged int, @mindTopicId varchar(17))
as
set nocount on
declare @threshold decimal(6,2),
        @FirstObj varchar(17),
        @GalaxyId int,
        @MainObjTitle varchar(255),
        @MyMindTopicId varchar(17),
        @CoId varchar(17)

set @threshold = 0.0001
set @GalaxyId = 1

select @MyMindTopicId = topicid from MyMindTopicVisitor where visitorid = @visitoridLogged

declare @MMIV table
(
    VisitorId int not null,
    AuthorId varchar(17) not null,
    MMIV decimal(6,4),
    PRIMARY KEY(VisitorId, AuthorId)
)


declare @Universe table
(
    GalaxyId int,
    VisitorId int,
    CoId varchar(20),
    ObjType varchar(35),
    ObjTitle varchar(255),
    ObjFontSize tinyint,
    ObjPosition tinyint
)

/* Get the most influencing authors for the visitor */
insert into @MMIV
select distinct top 5
    vi.VisitorIdX,
    vi.AuthorIdY,
    vi.[value]
from
    VisitorsInfluences vi
inner join
    tblcontentobjects co on co.coid = vi.authoridy
where
    vi.visitoridx = @visitorid
    and co.visitorid <> @visitorid
    and vi.[value] >= @threshold
group by
    vi.VisitorIdX,
    vi.AuthorIdY,
    vi.[value]
order by
    vi.[value] desc


/* Loop until MMIV is empty */
WHILE (select count(*) from @MMIV) > 0
BEGIN
    select top 1
        @FirstObj = authorid 
    from    
        @MMIV 
    order by 
        MMIV desc

    --Insert the center object in the current galaxy
    /* DEBUG:   En esta query tenes que hacer lo mismo que te dije para la font. Chequear el valor de influencia para el visitorLogged
                y dependiendo de ese valor setear el tamaño de fuente */
    insert into @Universe
    select
        @GalaxyId,
        tco.visitorid,
        @FirstObj,
        'Person',
        tco.firstname + ' ' + tco.lastname,
        case 
            when 
                (
                    select 
                        [value]
                    from
                        visitorsinfluences vi
                    where 
                        vi.visitoridx = @visitorIdLogged 
                        and vi.authoridy = tco.coid
                ) >= 0.66 
                then 5 
                else 
                    case 
                        when 
                            (
                                select 
                                    [value]
                                from
                                    visitorsinfluences vi
                                where 
                                    vi.visitoridx = @visitorIdLogged 
                                    and vi.authoridy = tco.coid
                            ) >= 0.33 
                            then 3 
                            else 1 
                    end 
        end as font,
        4
    from
        @MMIV mm
    inner join
        tblcontentobjects tco on tco.coid = mm.authorid
    where
        mm.authorid = @FirstObj

    delete from @MMIV where authorid = @FirstObj

    --Insert the cluster objects in the current galaxy
    /* DEBUG:   En esta query tenes que hacer lo que te dije para la font. Chequear el valor de influencia para el visitorLogged
                y dependiendo de ese valor setear el tamaño de fuente */
    **insert into @Universe
    select top 5
        @GalaxyId as galaxyid,
        vi.visitoridx as visitoridx,
        co.coid as coid,
        'Person' as cotype,
        co.firstname + ' ' + co.lastname as visitorname,
        case 
            when 
                (
                    select 
                        [value]
                    from
                        visitorsinfluences vi
                    where 
                        vi.visitoridx = @visitorIdLogged 
                        and vi.authoridy = co.coid
                ) >= 0.66 
                then 5 
                else 
                    case 
                        when 
                            (
                                select 
                                    [value]
                                from
                                    visitorsinfluences vi
                                where 
                                    vi.visitoridx = @visitorIdLogged 
                                    and vi.authoridy = co.coid
                            ) >= 0.33 
                            then 3 
                            else 1 
                    end 
        end as font,
        case when vi.[value] >= 0.66 then 3 else case when vi.[value] >= 0.33 then 2 else 1 end end as position
    from
        VisitorsInfluences vi   
    inner join 
        tblcontentobjects co on vi.visitoridx = co.visitorid
    left join 
        @universe u on u.coid = co.coid
    left join
        @mmiv mm on mm.authorid = co.coid
    where
        vi.authoridy = @FirstObj
        and [value] >= convert(real,@threshold)
        and vi.visitoridx <> @visitorid
        --and vi.visitoridx not in (select visitorid from @Universe)
        --and co.coid not in (select coid from @Universe)
        --and co.coid not in (select authorid from @mmiv)       
        and u.coid is null
        and mm.authorid is null
        and u.visitorid is null
    /*group by
        vi.visitoridx,
        co.coid,
        v.firstname,
        v.lastname,
        case when vi.[value] = 1 then 5 else case when vi.[value] >= (@threshold / 2) then 3 else 1 end end,
        case when vi.[value] >= 0.66 then 3 else case when vi.[value] >= 0.33 then 2 else 1 end end*/
    order by
        vi.[value] desc**




    if ((select count(*) from @Universe where GalaxyId = @GalaxyId) = 1) and @GalaxyId <= 5
    begin
        insert into @Universe
        select top 5
            @GalaxyId as galaxyid,
--              convert(varchar, vi.visitoridx) as visitoridx,
            vi.visitoridx as visitoridx,
            co.coid as coid,
            'Person' as cotype,
            v.firstname + ' ' + v.lastname as visitorname,
            0 as font,
            case when vi.[value] >= 0.66 then 3 else case when vi.[value] >= 0.33 then 2 else 1 end end as position
        from
            VisitorsInfluences vi
        inner join
            tblvisitor v on v.visitorid = vi.visitoridx
        inner join 
            tblcontentobjects co on v.visitorid = co.visitorid
        left join
            @universe u on u.coid = co.coid
        left join
            @mmiv mm on mm.authorid = co.coid
        where
            vi.authoridy=@FirstObj-- and vi.visitoridx = v.visitorid
            and vi.visitoridx <> @visitorid
--              and convert(varchar, vi.visitoridx) not in (select objid from @Universe)
            --and vi.visitoridx not in (select visitorid from @Universe)
            --and co.coid not in (select coid from @Universe)
            --and co.coid not in (select authorid from @mmiv)
            and u.coid is null
            and mm.authorid is null
            and u.visitorid is null
        order by
            vi.[value] desc
    end

--  delete from @MMIV where authorid in (select ObjId from @Universe)
    delete from @MMIV where visitorid in (select visitorid from @Universe)

    set @GalaxyId = @GalaxyId + 1
END

--Getting the XML output

select 
    @MainObjTitle = rtrim(ltrim(firstname)) + ' ' + ltrim(rtrim(lastname))
from 
    tblcontentobjects
where 
    visitorid = @visitorid

select 
    @CoId = co.coid
from
    tblcontentobjects co
where
    co.visitorid = @visitorid


SELECT
    @MainObjTitle as '@MainObjTitle',
    @CoId as '@CoId',
    (
        SELECT
            s.GalaxyID AS [@Id],
            (
                SELECT
                    U.VisitorId AS [@VisitorId],
                    U.CoId AS [@CoId],
                    U.ObjType AS [@Type],
                    U.ObjTitle AS [Title],
                    U.ObjFontSize as [FontSize],
                    U.ObjPosition as [Position],
                    co.[role] as [Role],
                    co.Affiliation as [Org],
                    case when ctr.topicid is null then 0 else 1 end as [IsInMyMind],
                    isnull(imgs.coviewurllink, '') as [coPicture],
                    case 
                        when co.visitorid is null then ''
                        when exists (
                                        select  *
                                        from    visitorrequests vrs
                                        where   vrs.RequestDate > dateadd(mi, -10, getdate()) and
                                                vrs.visitorid = co.visitorid
                                    ) then '_online'
                            else '_offline'
                    end as [IsOnline],
                    case when mctr.topicid is null then 0 else 1 end as [HasSocialNetworkProfile]
                FROM
                    @Universe AS U
                inner join  tblcontentobjects co 
--              on          convert(varchar, co.visitorid) = U.ObjId or co.coid = U.ObjId
                on          co.visitorid = U.VisitorId or co.coid = U.CoId
                inner join  tblvisitor v
                on          v.visitorid = co.visitorid
                left join
                (
                    select  img.*, cir.coidb
                    from    tblcointerrelations cir
                    inner join tblcontentobjects img
                    on      img.coid = cir.coida
                    where   img.cotype='images'
                ) imgs
                on          co.coid = imgs.coidb
                left join   tblCoTopicRelations ctr
                on          ctr.coid = co.coid and ctr.topicid = @MyMindTopicId
                left join   tblCoTopicRelations mctr
                on          mctr.coid = co.coid and mctr.topicid = @mindTopicId
                WHERE       
                    U.GalaxyID = s.GalaxyID
                ORDER BY
                    U.ObjPosition DESC
                FOR XML PATH('Object'), TYPE
            )
        FROM
            (
                SELECT      GalaxyID
                FROM        @Universe
                GROUP BY    GalaxyID
            ) AS s
        ORDER BY
            GalaxyId
        FOR XML PATH('Galaxy'), TYPE
    )
FOR XML PATH('Universe')`

3 个答案:

答案 0 :(得分:4)

似乎有很多东西可供选择来解释存储过程缓慢的原因。这是一些想法。

  • 执行计划缓存。过程是一个重要的程序过程。第一次运行它将根据初始参数存储执行计划。根据您的表大小和索引,首先缓存的计划可能不适合其他方案
  • 表变量。您选择在临时表中使用表变量(以@开头并存在于内存中)(以#开头并存在于磁盘上)。这可能是好事也可能是坏事。这取决于你的条件,但如果选择了错误的话可能会受到伤害
  • 排序依据。您经常使用它们,排序需要付出代价。
  • WHILE循环。一种非常程序化的方法。您正在使用SQL,因此您应该考虑基于集合的环境
  • 相关的子查询。对于外部集合中的每一行,必须运行相关的子查询。这将加起来。
  • 您在WHERE子句中执行数据类型转换([value]&gt; = convert(real,@ threshold))。这是一个性能损失。
  • @universe表变量没有主键,索引或群集
  • 在case语句下有一个4级嵌套相关子查询,最终会转换为XML 3次
  • 总体而言......您的存储过程做得太多了。以某种方式提前卸载这项工作。预先计算的东西。添加索引。您关注报告性能,因此请围绕报告设计架构,并尽可能快地响应。

您需要查看执行计划并寻找高额费用。对过程进行小的更改,并比较之前和之后的执行计划,以查看更改如何影响性能。做基准。知道你从哪里开始,你就可以衡量你走了多远。

运行此语句,然后运行该过程 SET STATISTICS IO ON 走 查看结果,对于每个触摸的表,请查看“逻辑读取”。具有最高逻辑读取的那些需要您的注意。它们可以松散地转换为对该对象的索引的需求。

答案 1 :(得分:2)

首先我输了:

while (select count(*) from @mmiv) > 0

并将其替换为:

declare @temp_count int
select @temp_count = count(*) from @mmiv

while @temp_count > 0

以下是从@mmiv删除记录的地方:

select @temp_count = (@temp_count - 1)

这样可以减少开销,因为每次循环执行序列时都不会执行count()。您还可以考虑将一些具有多个联接的选择放入视图中, here is why

答案 2 :(得分:0)

感谢各位的回答。

我终于解决了它。问题是缺少索引(例如在值字段上),现在效果很好。

谢谢,

布赖恩