У мене виникають проблеми з розумінням того, чому SQL-сервер вирішує викликати визначену користувачем функцію для кожного значення таблиці, навіть якщо слід отримати лише один рядок. Фактичний 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 вирішує викликати функцію GetGroupCode для кожного окремого значення, яке існує в таблиці PRODUCT, навіть якщо оцінка та фактична кількість рядків, повернених з 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-сервер взяти перший продукт 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)
)