Ich habe ein Problem zu verstehen, warum SQL Server für jeden Wert in der Tabelle eine benutzerdefinierte Funktion aufruft, obwohl nur eine Zeile abgerufen werden sollte. Das eigentliche SQL ist viel komplexer, aber ich konnte das Problem auf folgendes reduzieren:
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'
Bei dieser Abfrage ruft SQL Server die GetGroupCode-Funktion für jeden einzelnen Wert in der PRODUCT-Tabelle auf, obwohl die geschätzte und tatsächliche Anzahl der von ORDERLINE zurückgegebenen Zeilen 1 beträgt (dies ist der Primärschlüssel):
Gleicher Plan im Plan-Explorer mit den Zeilenzahlen:
Tabellen:
ORDERLINE: 1.5M rows, primary key: ORDERNUMBER + ORDERLINE + RMPHASE (clustered)
ORDERHDR: 900k rows, primary key: ORDERID (clustered)
PRODUCT: 6655 rows, primary key: PRODUCT (clustered)
Der für den Scan verwendete Index lautet:
create unique nonclustered index PRODUCT_FACTORY on PRODUCT (PRODUCT, FACTORY)
Die Funktion ist eigentlich etwas komplexer, aber dasselbe passiert mit einer Dummy-Funktion mit mehreren Anweisungen wie der folgenden:
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
Ich konnte die Leistung "korrigieren", indem ich SQL Server zwang, das Top-1-Produkt abzurufen, obwohl 1 das Maximum ist, das jemals gefunden werden kann:
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'
Dann ändert sich auch die Grundrissform so, wie ich es ursprünglich erwartet hatte:
Ich denke auch, dass der Index PRODUCT_FACTORY kleiner als der Clustered-Index PRODUCT_PK ist, aber selbst wenn die Abfrage zur Verwendung von PRODUCT_PK gezwungen wird, bleibt der Plan mit 6655 Aufrufen der Funktion derselbe.
Wenn ich ORDERHDR komplett weglasse, beginnt der Plan mit einer verschachtelten Schleife zwischen ORDERLINE und PRODUCT und die Funktion wird nur einmal aufgerufen.
Ich würde gerne verstehen, was der Grund dafür sein könnte, da alle Vorgänge mit Primärschlüsseln ausgeführt werden und wie dies behoben werden kann, wenn dies in einer komplexeren Abfrage geschieht, die nicht so einfach zu lösen ist.
Bearbeiten: Tabellenanweisungen erstellen:
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)
)