SQL Server'ın neden tablodaki her değer için kullanıcı tanımlı işlevi çağırmaya karar verdiğini anlama konusunda bir sorunum var; Gerçek SQL çok daha karmaşık, ancak sorunu bu şekilde azaltabildim:
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'
Bu sorgu için, SQL Server, ORDERLINE'dan döndürülen tahmini ve gerçek satır sayısı 1 olsa bile, PRODUCT Tablosunda bulunan her bir değer için GetGroupCode işlevini çağırmaya karar verir.
Satır sayımlarını gösteren plan gezgini ile aynı plan:
Tablolar:
ORDERLINE: 1.5M rows, primary key: ORDERNUMBER + ORDERLINE + RMPHASE (clustered)
ORDERHDR: 900k rows, primary key: ORDERID (clustered)
PRODUCT: 6655 rows, primary key: PRODUCT (clustered)
Tarama için kullanılan dizin:
create unique nonclustered index PRODUCT_FACTORY on PRODUCT (PRODUCT, FACTORY)
İşlev aslında biraz daha karmaşıktır, ancak aynı şey bunun gibi kukla bir çoklu ifade işlevi ile de olur:
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 sunucusunu ilk 1 ürünü almaya zorlayarak performansı "düzeltebildim";
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'
O zaman plan şekli aynı zamanda başlangıçta olmasını beklediğim bir şey olarak değişir:
Ayrıca PRODUCT_FACTORY indeksinin, PRODUCT_PK kümelenmiş indeksinden daha küçük olması bir etki yaratacaktır, ancak PRODUCT_PK'yi kullanmak için sorguyu zorlamakla birlikte, plan hala 6655 işlev çağrılarıyla aynıdır.
ORDERHDR'yi tamamen terk edersem, plan ilk önce ORDERLINE ve PRODUCT arasında iç içe döngüle başlar ve işlev yalnızca bir kez çağrılır.
Tüm işlemler birincil anahtarlar kullanılarak yapıldığından ve bunun kolayca çözülemeyen daha karmaşık bir sorguda gerçekleşirse nasıl düzeltileceği için bunun nedeninin ne olabileceğini anlamak isterim.
Düzenleme: Tablo ifadeleri oluşturun:
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)
)