使用局部变量时,估计行和实际行存在很大差异

问题描述 投票:0回答:1

这是我在 Stackoverflow 上的第一篇文章,所以我希望我正确遵循所有协议!

我正在努力处理一个存储过程,在该存储过程中我创建了一个表变量,并使用内部联接用插入语句填充该表。插入本身很简单,但它变得复杂,因为内连接是在局部变量上完成的。由于优化器没有这个变量的统计数据,我的估计行数变得混乱了。

导致麻烦的具体代码:

declare @minorderid int
select @minorderid = MIN(lo.order_id)
from [order] lo with(nolock)
where lo.order_datetime >= @datefrom

insert into @OrderTableLog_initial
(order_id, order_log_id, order_id, order_datetime, account_id, domain_id)

    select ot.order_id, lol.order_log_id, ot.order_id, ot.order_datetime, ot.account_id, ot.domain_id
    from [order] ot with(nolock)

inner join order_log lol with(nolock)
on ot.order_id = lol.order_id
and ot.order_datetime >= @datefrom

where (ot.domain_id in (1,2,4) and lol.order_log_id not in ( select order_log_id 
                                        from dbo.order_log_detail lld with(nolock)
                                        where order_id >= @minorderid
                                    )
or
(ot.domain_id = 3 and ot.order_id not IN (select order_id 
                                        from dbo.order_log_detail_spa llds with(nolock)
                                        where order_id >= @minorderid
                                        )
))

order by lol.order_id, lol.order_log_id

@datefrom 局部变量也在存储过程中提前声明:

    declare @datefrom datetime
    if datepart(hour,GETDATE()) between 4 and 9
    begin
        set @datefrom = '2011-01-01'
    end
    else
    begin
        set @datefrom = DATEADD(DAY,-2,GETDATE())   
    end

我还使用临时表而不是表变量对此进行了测试,但没有任何变化。但是,当我用固定日期戳替换局部变量

>= @datefrom
时,我的估计和实际值几乎相同。

ot.order_datetime >= @datefrom
= SQL Sentry 计划资源管理器

ot.order_datetime >= '2017-05-03 18:00:00.000'
= SQL Sentry 计划资源管理器

我开始明白,有一种方法可以通过将此代码转换为动态 sp 来解决此问题,但我不确定如何执行此操作。如果有人能给我关于如何做到这一点的建议,我将不胜感激。也许我必须使用完全其他的方法?如果我忘了什么,请原谅我,这是我的第一篇文章。

编辑:

MSSQL 版本 = 11.0.5636

我还使用跟踪标志 2453 进行了测试,但没有成功

最诚挚的问候, 彼得

sql t-sql optimization sql-execution-plan local-variables
1个回答
1
投票

确实,您所经历的行为是由于变量造成的。 SQL Server 不会为每个可能的输入存储执行计划,因此对于某些查询,执行计划可能是最优的,也可能不是最优的。

回答您的问题:您必须创建一个 nvarchar 变量并将查询构建为字符串,然后执行它。

实际代码之前的一些注释:

  • 这很容易出现 SQL 注入(一般情况下)
  • SQL Server 将单独存储计划,这意味着它们将使用更多内存,并可能从缓存中删除其他计划

使用想象的设置,这就是您想要做的:

DECLARE @inputDate DATETIME2 = '2017-01-01 12:21:54';

DELCARE @dynamiSQL NVARCHAR(MAX) = CONCAT('SELECT col1, col2 FROM MyTable WHERE myDateColumn = ''', FORMAT(@inputDate, 'yyyy-MM-dd HH:mm:ss'), ''';');

INSERT INTO @myTableVar (col1, col2)
EXEC sp_executesql @stmt = @dynamicSQL;

补充说明:

  • 您可以尝试使用 EXISTS 和 NOT EXISTS 来代替 IN 和 NOT IN。
  • 您可以尝试使用临时表(#myTempTable)代替局部变量,并在其上放置一些索引。物理临时表可以在处理大量数据时表现更好,并且您可以在其上放置索引。 (有关更多信息,您可以访问这里:SQL Server 中的临时表和表变量有什么区别? 或查看官方文档)
© www.soinside.com 2019 - 2024. All rights reserved.