Saya memiliki masalah memahami mengapa SQL server memutuskan untuk memanggil fungsi yang ditentukan pengguna untuk setiap nilai dalam tabel meskipun hanya satu baris yang harus diambil. SQL sebenarnya jauh lebih kompleks, tapi saya bisa mengurangi masalah ini menjadi ini:
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'
Untuk kueri ini, SQL Server memutuskan untuk memanggil fungsi GetGroupCode untuk setiap nilai tunggal yang ada di Tabel PRODUCT, meskipun taksiran dan jumlah sebenarnya baris yang dikembalikan dari ORDERLINE adalah 1 (itu adalah kunci utama):
Paket yang sama di paket explorer yang menunjukkan jumlah baris:
Tabel:
ORDERLINE: 1.5M rows, primary key: ORDERNUMBER + ORDERLINE + RMPHASE (clustered)
ORDERHDR: 900k rows, primary key: ORDERID (clustered)
PRODUCT: 6655 rows, primary key: PRODUCT (clustered)
Indeks yang digunakan untuk pemindaian adalah:
create unique nonclustered index PRODUCT_FACTORY on PRODUCT (PRODUCT, FACTORY)
Fungsi ini sebenarnya sedikit lebih kompleks, tetapi hal yang sama terjadi dengan fungsi multi-pernyataan dummy seperti ini:
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
Saya dapat "memperbaiki" kinerja dengan memaksa SQL server untuk mengambil 1 produk teratas, meskipun 1 adalah maks yang dapat ditemukan:
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'
Kemudian bentuk rencana juga berubah menjadi sesuatu yang saya harapkan semula:
Saya juga berpikir bahwa indeks PRODUCT_FACTORY yang lebih kecil dari indeks cluster PRODUCT_PK akan memiliki pengaruh, tetapi bahkan dengan memaksa permintaan untuk menggunakan PRODUCT_PK, rencana tersebut masih sama seperti aslinya, dengan 6655 panggilan ke fungsi.
Jika saya meninggalkan ORDERHDR sepenuhnya, maka rencana dimulai dengan loop bersarang antara ORDERLINE dan PRODUCT terlebih dahulu, dan fungsinya disebut hanya sekali.
Saya ingin memahami apa yang bisa menjadi alasan untuk ini karena semua operasi dilakukan menggunakan kunci primer dan cara memperbaikinya jika itu terjadi dalam permintaan yang lebih kompleks yang tidak dapat diselesaikan dengan mudah.
Edit: Buat pernyataan tabel:
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)
)
sumber