Pracuję nad optymalizacją niektórych zapytań.
W przypadku poniższego zapytania
SET STATISTICS IO ON;
DECLARE @OrderStartDate DATETIME2 = '27 feb 2016';
DECLARE @OrderEndDate DATETIME2 = '28 feb 2016';
SELECT o.strBxOrderNo
, o.sintOrderStatusID
, o.sintOrderChannelID
, o.sintOrderTypeID
, o.sdtmOrdCreated
, o.sintMarketID
, o.strOrderKey
, o.strOfferCode
, o.strCurrencyCode
, o.decBCShipFullPrice
, o.decBCShipFinal
, o.decBCShipTax
, o.decBCTotalAmount
, o.decWrittenTotalAmount
, o.decBCWrittenTotalAmount
, o.decBCShipOfferDisc
, o.decBCShipOverride
, o.decTotalAmount
, o.decShipTax
, o.decShipFinal
, o.decShipOverride
, o.decShipOfferDisc
, o.decShipFullPrice
, o.lngAccountParticipantID
, CONVERT(DATE, o.sdtmOrdCreated, 120) as OrderCreatedDateConverted
FROM tablebackups.dbo.tblBOrder o
WHERE o.sdtmOrdCreated >= @OrderStartDate
AND o.sdtmOrdCreated < @OrderEndDate
AND EXISTS (
SELECT *
FROM tablebackups.dbo.tblBOrderItem oi
WHERE oi.strBxOrderNo = o.strBxOrderNo
AND oi.decCatItemPrice > 0
)
OPTION (RECOMPILE);
Utworzyłem następujący indeks FILTROWANY:
-- table dbo.tblBorderItem
CREATE NONCLUSTERED INDEX IX_tblBOrderItem_decCatItemPrice_INCL
ON dbo.tblBorderItem
(
strBxOrderNo ASC
, sintOrderSeqNo ASC
, decCatItemPrice
)
INCLUDE
(
blnChargeShipping
, decBCCatItemPrice
, decBCCostPrice
, decBCFinalPrice
, decBCOfferDiscount
, decBCOverrideDiscount
, decBCTaxAmount
, decCostPrice
, decFinalPrice
, decOfferDiscount
, decOverrideDiscount
, decTaxAmount
, decWasPrice
, dtmOrdItemCreated
, sintOrderItemStatusId
, sintOrderItemType
, sintQuantity
, strItemNo
)
WHERE decCatItemPrice > 0
WITH (DROP_EXISTING = ON, FILLFACTOR = 95);
Ten indeks nie jest używany tylko w szczególności dla tego zapytania, istnieją inne zapytania, które używają tego samego indeksu, dlatego kolumny ZAWIERANE.
W przypadku tego zapytania chcę po prostu sprawdzić (ISTNIEJE), czy zamówienie zawiera dowolny element decCatItemPrice > 0
.
SQL Server wykonuje skanowanie indeksu, jak widać na poniższych zdjęciach.
- Statystyki zostały właśnie zaktualizowane.
- Tabela pozycji zawiera 41 208 wierszy w teście.
Uwaga: nie wybieram żadnych kolumn z tabeli przedmiotów.
Tabela przedmiotów ma na żywo 164 309 397. Chciałbym uniknąć skanowania.
pytania:
Dlaczego SQL Server nie wykonuje wyszukiwania indeksu?
Czy są inne czynniki / rzeczy, które należy wziąć pod uwagę, aby ulepszyć to zapytanie?
(4537 row(s) affected) Table 'tblBorder'. Scan count 1, logical reads
116, physical reads 0, read-ahead reads 0, lob logical reads 0, lob
physical reads 0, lob read-ahead reads 0. Table 'tblBorderItem'. Scan
count 1, logical reads 689, physical reads 0, read-ahead reads 0, lob
logical reads 0, lob physical reads 0, lob read-ahead reads 0.
(1 row(s) affected)
to jest definicja i indeksy w tabeli tblBorderItem
IF OBJECT_ID('[dbo].[tblBorderItem]') IS NOT NULL
DROP TABLE [dbo].[tblBorderItem]
GO
CREATE TABLE [dbo].[tblBorderItem] (
[strBxOrderNo] VARCHAR(20) NOT NULL,
[sintOrderSeqNo] SMALLINT NOT NULL,
[sintOrderItemStatusId] SMALLINT NOT NULL,
[sintNameStructureID] SMALLINT NOT NULL,
[strItemNo] VARCHAR(20) NOT NULL,
[sintQuantity] SMALLINT NOT NULL,
[strCurrencyCode] VARCHAR(3) NOT NULL,
[decCostPrice] DECIMAL(18,4) NOT NULL,
[decCatItemPrice] DECIMAL(18,2) NOT NULL,
[decOfferDiscount] DECIMAL(18,2) NOT NULL,
[decOverrideDiscount] DECIMAL(18,2) NOT NULL,
[decFinalPrice] DECIMAL(18,2) NOT NULL,
[decTaxAmount] DECIMAL(18,2) NOT NULL,
[strBCCurrencyCode] VARCHAR(3) NOT NULL,
[decBCCostPrice] DECIMAL(18,4) NOT NULL,
[decBCCatItemPrice] DECIMAL(18,4) NOT NULL,
[decBCOfferDiscount] DECIMAL(18,4) NOT NULL,
[decBCOverrideDiscount] DECIMAL(18,4) NOT NULL,
[decBCFinalPrice] DECIMAL(18,4) NOT NULL,
[decBCTaxAmount] DECIMAL(18,4) NOT NULL,
[dtmOrdItemCreated] DATETIME NOT NULL,
[blnChargeShipping] BIT NOT NULL,
[lngTimeOfOrderQtyOnHand] INT NULL,
[sdtmTimeOfOrderDueDate] SMALLDATETIME NULL,
[lngProdSetSeqNo] INT NULL,
[lngProdRelationId] INT NULL,
[lngProdRelationMemberId] INT NULL,
[decWasPrice] DECIMAL(18,2) NULL,
[sintOrderItemType] SMALLINT NULL,
[tsRowVersion] TIMESTAMP NULL,
[sdtmOrderItemStatusUpdated] SMALLDATETIME NULL,
CONSTRAINT [PK_tblBOrderItem]
PRIMARY KEY CLUSTERED
([strBxOrderNo] asc, [sintOrderSeqNo] asc)
WITH FILLFACTOR = 100)
GO
CREATE NONCLUSTERED INDEX
[IX_tblBOrderItem__dtmOrdItemCreated]
ON [dbo].[tblBorderItem] ([dtmOrdItemCreated] asc)
WITH FILLFACTOR = 100
CREATE NONCLUSTERED INDEX [IX_tblBOrderItem__sintOrderItemStatusId]
ON [dbo].[tblBorderItem] ([sintOrderItemStatusId] asc)
INCLUDE ([sdtmOrderItemStatusUpdated],
[sintOrderSeqNo], [strBxOrderNo], [strItemNo])
WITH FILLFACTOR = 100
CREATE NONCLUSTERED INDEX [IX_tblBOrderItem__
sintOrderItemStatusId_decFinalPrice_
sdtmOrderItemStatusUpdated_
include_strBxOrderNo]
ON [dbo].[tblBorderItem]
([sintOrderItemStatusId] asc,
[decFinalPrice] asc,
[sdtmOrderItemStatusUpdated] asc)
INCLUDE ([strBxOrderNo])
WITH FILLFACTOR = 100
CREATE NONCLUSTERED INDEX [IX_tblBOrderItem__strBxOrderNo]
ON [dbo].[tblBorderItem]
([strBxOrderNo] asc)
WITH FILLFACTOR = 100
CREATE NONCLUSTERED INDEX [IX_tblBOrderItem__strItemNo]
ON [dbo].[tblBorderItem] ([strItemNo] asc)
WITH FILLFACTOR = 100
CREATE NONCLUSTERED INDEX
[IX_tblBOrderItem_decCatItemPrice_INCL]
ON [dbo].[tblBorderItem]
([strBxOrderNo] asc, [sintOrderSeqNo] asc, [decCatItemPrice] asc)
INCLUDE ([blnChargeShipping],
[decBCCatItemPrice], [decBCCostPrice], [decBCFinalPrice],
[decBCOfferDiscount], [decBCOverrideDiscount],
[decBCTaxAmount], [decCostPrice], [decFinalPrice],
[decOfferDiscount], [decOverrideDiscount],
[decTaxAmount], [decWasPrice], [dtmOrdItemCreated],
[sintOrderItemStatusId], [sintOrderItemType],
[sintQuantity], [strItemNo])
WHERE ([decCatItemPrice]>(0))
WITH FILLFACTOR = 95
to jest definicja i indeksy w tabeli tblBorder
IF OBJECT_ID('[dbo].[tblBorder]') IS NOT NULL
DROP TABLE [dbo].[tblBorder]
GO
CREATE TABLE [dbo].[tblBorder] (
[strBxOrderNo] VARCHAR(20) NOT NULL,
[uidOrderUniqueID] UNIQUEIDENTIFIER NOT NULL,
[sintOrderStatusID] SMALLINT NOT NULL,
[sintOrderChannelID] SMALLINT NOT NULL,
[sintOrderTypeID] SMALLINT NOT NULL,
[blnIsBasket] BIT NOT NULL,
[sdtmOrdCreated] SMALLDATETIME NOT NULL,
[sintMarketID] SMALLINT NOT NULL,
[strOrderKey] VARCHAR(20) NOT NULL,
[strOfferCode] VARCHAR(20) NOT NULL,
[lngShippedToParticipantID] INT NOT NULL,
[lngOrderedByParticipantID] INT NOT NULL,
[lngShipToAddressID] INT NOT NULL,
[lngAccountAddressID] INT NOT NULL,
[lngAccountParticipantID] INT NOT NULL,
[lngOrderedByAddressID] INT NOT NULL,
[lngOrderTakenBy] INT NOT NULL,
[strCurrencyCode] VARCHAR(3) NOT NULL,
[decShipFullPrice] DECIMAL(18,2) NOT NULL,
[decShipOfferDisc] DECIMAL(18,2) NOT NULL,
[decShipOverride] DECIMAL(18,2) NOT NULL,
[decShipFinal] DECIMAL(18,2) NOT NULL,
[decShipTax] DECIMAL(18,2) NOT NULL,
[strBCCurrencyCode] VARCHAR(3) NOT NULL,
[decBCShipFullPrice] DECIMAL(18,4) NOT NULL,
[decBCShipOfferDisc] DECIMAL(18,4) NOT NULL,
[decBCShipOverride] DECIMAL(18,4) NOT NULL,
[decBCShipFinal] DECIMAL(18,4) NOT NULL,
[decBCShipTax] DECIMAL(18,4) NOT NULL,
[decTotalAmount] DECIMAL(18,2) NOT NULL,
[decBCTotalAmount] DECIMAL(18,4) NOT NULL,
[decWrittenTotalAmount] DECIMAL(18,2) NULL,
[decBCWrittenTotalAmount] DECIMAL(18,4) NULL,
[blnProRataShipping] BIT NOT NULL,
[blnChargeWithFirstShipment] BIT NOT NULL,
[sintShippingServiceLevelID] SMALLINT NOT NULL,
[sintShippingMethodID] SMALLINT NOT NULL,
[sdtmDoNotShipUntil] SMALLDATETIME NULL,
[blnHoldUntilComplete] BIT NOT NULL,
[tsRowVersion] TIMESTAMP NULL,
CONSTRAINT [PK_tblBOrder]
PRIMARY KEY CLUSTERED
([strBxOrderNo] asc) WITH FILLFACTOR = 100)
GO
CREATE NONCLUSTERED INDEX
[IX_tblBOrder__lngAccountAddressID]
ON [dbo].[tblBorder]
([lngAccountAddressID] asc, [sintOrderStatusID] asc)
WITH FILLFACTOR = 100
CREATE NONCLUSTERED INDEX
[IX_tblBOrder__lngAccountParticipantID]
ON [dbo].[tblBorder]
([lngAccountParticipantID] asc)
WITH FILLFACTOR = 100
CREATE NONCLUSTERED INDEX
[IX_tblBOrder__lngOrderedByAddressID]
ON [dbo].[tblBorder]
([lngOrderedByAddressID] asc, [sintOrderStatusID] asc)
WITH FILLFACTOR = 100
CREATE NONCLUSTERED INDEX
[IX_tblBOrder__lngOrderedByParticipantID]
ON [dbo].[tblBorder] ([lngOrderedByParticipantID] asc)
WITH FILLFACTOR = 100
CREATE NONCLUSTERED INDEX
[IX_tblBOrder__lngShippedToParticipantID]
ON [dbo].[tblBorder]
([lngShippedToParticipantID] asc)
WITH FILLFACTOR = 100
CREATE NONCLUSTERED INDEX
[IX_tblBOrder__lngShipToAddressID]
ON [dbo].[tblBorder]
([lngShipToAddressID] asc, [sintOrderStatusID] asc)
WITH FILLFACTOR = 100
CREATE NONCLUSTERED INDEX
[IX_tblBOrder__sdtmOrdCreated_sintMarketID__include_strBxOrderNo]
ON [dbo].[tblBorder]
([sdtmOrdCreated] asc, [sintMarketID] asc)
INCLUDE ([strBxOrderNo])
WITH FILLFACTOR = 100
CREATE NONCLUSTERED INDEX
[IX_tblBOrder_sdtmOrdCreated_INCL]
ON [dbo].[tblBorder]
([sdtmOrdCreated] asc)
INCLUDE ([decBCShipFinal], [decBCShipFullPrice],
[decBCShipOfferDisc], [decBCShipOverride],
[decBCShipTax], [decBCTotalAmount], [decBCWrittenTotalAmount],
[decShipFinal], [decShipFullPrice], [decShipOfferDisc],
[decShipOverride], [decShipTax], [decTotalAmount],
[decWrittenTotalAmount], [lngAccountParticipantID],
[lngOrderedByParticipantID], [sintMarketID],
[sintOrderChannelID], [sintOrderStatusID],
[sintOrderTypeID], [strBxOrderNo], [strCurrencyCode],
[strOfferCode], [strOrderKey])
WITH FILLFACTOR = 100
CREATE NONCLUSTERED
INDEX [IX_tblBOrder_sintMarketID_sdtmOrdCreated]
ON [dbo].[tblBorder]
([sintMarketID] asc, [sdtmOrdCreated] asc)
INCLUDE ([sintOrderChannelID], [strBxOrderNo])
WITH FILLFACTOR = 100
CREATE NONCLUSTERED
INDEX [IX_tblBOrder__sintOrderChannelID_sdtmOrdCreated_INCL]
ON [dbo].[tblBorder]
([sintOrderChannelID] asc, [sdtmOrdCreated] asc)
INCLUDE ([decBCShipFinal], [decBCShipFullPrice],
[decBCShipTax], [decShipFinal], [decShipFullPrice],
[decShipTax], [lngAccountParticipantID], [sintMarketID],
[sintOrderTypeID], [strBxOrderNo],
[strCurrencyCode], [strOrderKey])
WITH FILLFACTOR = 100
CREATE NONCLUSTERED INDEX [IX_tblBOrder_strBxOrderNo_sdtmOrdCreated_incl]
ON [dbo].[tblBorder] ([strBxOrderNo] asc,
[sdtmOrdCreated] asc)
INCLUDE ([sintOrderChannelID], [sintOrderTypeID], [sintMarketID],
[strOrderKey], [lngAccountParticipantID], [strCurrencyCode],
[decShipFullPrice], [decShipFinal], [decShipTax],
[decBCShipFullPrice], [decBCShipFinal],
[decBCShipTax])
Wniosek
Zastosowałem swój indeks w systemie LIVE i zaktualizowałem procedurę składowaną, aby używać SMALLDATETIME, aby dopasować typy danych w bazie danych dla zaangażowanych kolumn.
Następnie, patrząc na plan zapytań, widzę poniższy obrazek:
dokładnie tak, jak chciałem.
Myślę, że optymalizator zapytań w tym przypadku dobrze się spisał, aby uzyskać najlepszy plan zapytań w obu środowiskach i cieszę się, że nie dodałem żadnych wskazówek dotyczących zapytań.
Nauczyłem się z 3 opublikowanymi odpowiedziami. podziękowania dla Maxa Vernona , Paula White'a i Daniela Hutmachera za ich odpowiedzi.
źródło