한 행만 가져와야하지만 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'
이 쿼리의 경우 ORDERLINE에서 반환 된 예상 및 실제 행 수가 1 (기본 키) 인 경우에도 SQL Server는 PRODUCT Table에 존재하는 모든 단일 값에 대해 GetGroupCode 함수를 호출하기로 결정합니다.
행 탐색기를 표시하는 계획 탐색기의 동일한 계획 :
테이블 :
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)
)