我有一个问题,为什么SQL Server决定为表中的每个值调用用户定义的函数,即使应该只提取一行。实际的SQL复杂得多,但是我能够将问题减少到这个程度:
select
S.GROUPCODE,
H.ORDERCATEGORY
from
ORDERLINE L
join ORDERHDR H on H.ORDERID = L.ORDERID
join PRODUCT P on P.PRODUCT = L.PRODUCT
cross apply dbo.GetGroupCode (P.FACTORY) S
where
L.ORDERNUMBER = 'XXX/YYY-123456' and
L.RMPHASE = '0' and
L.ORDERLINE = '01'
对于此查询,SQL Server决定为PRODUCT表中存在的每个单个值调用GetGroupCode函数,即使从ORDERLINE返回的估计行数和实际行数为1(这是主键):
计划浏览器中的同一计划显示行数:
表格:
ORDERLINE: 1.5M rows, primary key: ORDERNUMBER + ORDERLINE + RMPHASE (clustered)
ORDERHDR: 900k rows, primary key: ORDERID (clustered)
PRODUCT: 6655 rows, primary key: PRODUCT (clustered)
用于扫描的索引是:
create unique nonclustered index PRODUCT_FACTORY on PRODUCT (PRODUCT, FACTORY)
该函数实际上稍微复杂一些,但是使用虚拟多语句函数时也会发生以下情况:
create function GetGroupCode (@FACTORY varchar(4))
returns @t table(
TYPE varchar(8),
GROUPCODE varchar(30)
)
as begin
insert into @t (TYPE, GROUPCODE) values ('XX', 'YY')
return
end
我可以通过强制SQL Server获取前1种产品来“修复”性能,尽管可以找到最大的1种产品:
select
S.GROUPCODE,
H.ORDERCAT
from
ORDERLINE L
join ORDERHDR H
on H.ORDERID = M.ORDERID
cross apply (select top 1 P.FACTORY from PRODUCT P where P.PRODUCT = L.PRODUCT) P
cross apply dbo.GetGroupCode (P.FACTORY) S
where
L.ORDERNUMBER = 'XXX/YYY-123456' and
L.RMPHASE = '0' and
L.ORDERLINE = '01'
然后,计划形状也将更改为我原本希望的样子:
尽管索引PRODUCT_FACTORY小于聚集索引PRODUCT_PK也会产生影响,但是即使强制查询使用PRODUCT_PK,该计划仍与原始计划相同,对函数的调用为6655。
如果我完全忽略了ORDERHDR,则该计划首先从ORDERLINE和PRODUCT之间的嵌套循环开始,并且该函数仅被调用一次。
我想了解这可能是什么原因,因为所有操作都是使用主键完成的,如果它发生在无法轻松解决的更复杂的查询中,该如何解决。
编辑:创建表语句:
CREATE TABLE dbo.ORDERHDR(
ORDERID varchar(8) NOT NULL,
ORDERCATEGORY varchar(2) NULL,
CONSTRAINT ORDERHDR_PK PRIMARY KEY CLUSTERED (ORDERID)
)
CREATE TABLE dbo.ORDERLINE(
ORDERNUMBER varchar(16) NOT NULL,
RMPHASE char(1) NOT NULL,
ORDERLINE char(2) NOT NULL,
ORDERID varchar(8) NOT NULL,
PRODUCT varchar(8) NOT NULL,
CONSTRAINT ORDERLINE_PK PRIMARY KEY CLUSTERED (ORDERNUMBER,ORDERLINE,RMPHASE)
)
CREATE TABLE dbo.PRODUCT(
PRODUCT varchar(8) NOT NULL,
FACTORY varchar(4) NULL,
CONSTRAINT PRODUCT_PK PRIMARY KEY CLUSTERED (PRODUCT)
)