这是我在 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 Server 不会为每个可能的输入存储执行计划,因此对于某些查询,执行计划可能是最优的,也可能不是最优的。
回答您的问题:您必须创建一个 nvarchar 变量并将查询构建为字符串,然后执行它。
实际代码之前的一些注释:
使用想象的设置,这就是您想要做的:
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;
补充说明: