Sto cercando di migliorare questa (sotto-) query facente parte di una query più grande:
select SUM(isnull(IP.Q, 0)) as Q,
IP.OPID
from IP
inner join I
on I.ID = IP.IID
where
IP.Deleted=0 and
(I.Status > 0 AND I.Status <= 19)
group by IP.OPID
Sentry Plan Explorer ha indicato alcune ricerche chiave relativamente costose per la tabella dbo. [I] eseguite dalla query sopra.
Tabella dbo.I
CREATE TABLE [dbo].[I] (
[ID] UNIQUEIDENTIFIER NOT NULL,
[OID] UNIQUEIDENTIFIER NOT NULL,
[] UNIQUEIDENTIFIER NOT NULL,
[] UNIQUEIDENTIFIER NOT NULL,
[] UNIQUEIDENTIFIER NULL,
[] UNIQUEIDENTIFIER NOT NULL,
[] CHAR (3) NOT NULL,
[] CHAR (3) DEFAULT ('EUR') NOT NULL,
[] DECIMAL (18, 8) DEFAULT ((1)) NOT NULL,
[] CHAR (10) NOT NULL,
[] DECIMAL (18, 8) DEFAULT ((1)) NOT NULL,
[] DATETIME DEFAULT (getdate()) NOT NULL,
[] VARCHAR (35) NULL,
[] NVARCHAR (100) NOT NULL,
[] NVARCHAR (100) NULL,
[] NTEXT NULL,
[] NTEXT NULL,
[] NTEXT NULL,
[] NTEXT NULL,
[Status] INT DEFAULT ((0)) NOT NULL,
[] DECIMAL (18, 2) NOT NULL,
[] DECIMAL (18, 2) NOT NULL,
[] DECIMAL (18, 2) NOT NULL,
[] DATETIME DEFAULT (getdate()) NULL,
[] DATETIME NULL,
[] NTEXT NULL,
[] NTEXT NULL,
[] TINYINT DEFAULT ((0)) NOT NULL,
[] DATETIME NULL,
[] VARCHAR (50) NULL,
[] DATETIME DEFAULT (getdate()) NOT NULL,
[] VARCHAR (50) NOT NULL,
[] DATETIME NULL,
[] VARCHAR (50) NULL,
[] ROWVERSION NOT NULL,
[] DATETIME NULL,
[] INT NULL,
[] TINYINT DEFAULT ((0)) NOT NULL,
[] UNIQUEIDENTIFIER NULL,
[] TINYINT DEFAULT ((0)) NOT NULL,
[] TINYINT DEFAULT ((0)) NOT NULL,
[] NVARCHAR (50) NULL,
[] TINYINT DEFAULT ((0)) NOT NULL,
[] UNIQUEIDENTIFIER NULL,
[] UNIQUEIDENTIFIER NULL,
[] TINYINT DEFAULT ((0)) NOT NULL,
[] TINYINT DEFAULT ((0)) NOT NULL,
[] UNIQUEIDENTIFIER NULL,
[] DECIMAL (18, 2) NULL,
[] DECIMAL (18, 2) NULL,
[] DECIMAL (18, 2) DEFAULT ((0)) NOT NULL,
[] UNIQUEIDENTIFIER NULL,
[] DATETIME NULL,
[] DATETIME NULL,
[] VARCHAR (35) NULL,
[] DECIMAL (18, 2) DEFAULT ((0)) NOT NULL,
CONSTRAINT [PK_I] PRIMARY KEY NONCLUSTERED ([ID] ASC) WITH (FILLFACTOR = 90),
CONSTRAINT [FK_I_O] FOREIGN KEY ([OID]) REFERENCES [dbo].[O] ([ID]),
CONSTRAINT [FK_I_Status] FOREIGN KEY ([Status]) REFERENCES [dbo].[T_Status] ([Status])
);
GO
CREATE CLUSTERED INDEX [CIX_Invoice]
ON [dbo].[I]([OID] ASC) WITH (FILLFACTOR = 90);
Tabella dbo.IP
CREATE TABLE [dbo].[IP] (
[ID] UNIQUEIDENTIFIER DEFAULT (newid()) NOT NULL,
[IID] UNIQUEIDENTIFIER NOT NULL,
[OID] UNIQUEIDENTIFIER NOT NULL,
[Deleted] TINYINT DEFAULT ((0)) NOT NULL,
[] UNIQUEIDENTIFIER NULL,
[] UNIQUEIDENTIFIER NULL,
[] UNIQUEIDENTIFIER NULL,
[] UNIQUEIDENTIFIER NULL,
[] UNIQUEIDENTIFIER NULL,
[]UNIQUEIDENTIFIER NULL,
[] UNIQUEIDENTIFIER NULL,
[] UNIQUEIDENTIFIER NULL,
[] INT NOT NULL,
[] VARCHAR (35) NULL,
[] NVARCHAR (100) NOT NULL,
[] NTEXT NULL,
[] DECIMAL (18, 4) DEFAULT ((0)) NOT NULL,
[] NTEXT NULL,
[] NTEXT NULL,
[] DECIMAL (18, 4) DEFAULT ((0)) NOT NULL,
[] DECIMAL (18, 4) DEFAULT ((0)) NOT NULL,
[] DECIMAL (4, 2) NOT NULL,
[] INT DEFAULT ((1)) NOT NULL,
[] DATETIME DEFAULT (getdate()) NOT NULL,
[] VARCHAR (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[] DATETIME NULL,
[] VARCHAR (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[] ROWVERSION NOT NULL,
[] INT DEFAULT ((1)) NOT NULL,
[] DATETIME NULL,
[] UNIQUEIDENTIFIER NULL,
[] DECIMAL (18, 4) DEFAULT ((1)) NOT NULL,
[] DECIMAL (18, 4) DEFAULT ((1)) NOT NULL,
[] INT DEFAULT ((0)) NOT NULL,
[] UNIQUEIDENTIFIER NULL,
[] UNIQUEIDENTIFIER NULL,
[] UNIQUEIDENTIFIER NULL,
[] UNIQUEIDENTIFIER NULL,
[]UNIQUEIDENTIFIER NULL,
[]NVARCHAR (35) NULL,
[] VARCHAR (35) NULL,
[] NVARCHAR (35) NULL,
[] NVARCHAR (35) NULL,
[] NVARCHAR (35) NULL,
[] NVARCHAR (35) NULL,
[] NVARCHAR (35) NULL,
[] NVARCHAR (35) NULL,
[] UNIQUEIDENTIFIER NULL,
[] VARCHAR (12) NULL,
[] VARCHAR (4) NULL,
[] NVARCHAR (50) NULL,
[] NVARCHAR (50) NULL,
[] VARCHAR (35) NULL,
[] UNIQUEIDENTIFIER NULL,
[] UNIQUEIDENTIFIER NULL,
[] TINYINT DEFAULT ((0)) NOT NULL,
[] UNIQUEIDENTIFIER NULL,
[] TINYINT DEFAULT ((0)) NOT NULL,
[] TINYINT DEFAULT ((0)) NOT NULL,
[] NVARCHAR (50) NULL,
[] TINYINT DEFAULT ((0)) NOT NULL,
[] DECIMAL (18, 2) NULL,
[]TINYINT DEFAULT ((1)) NOT NULL,
[] UNIQUEIDENTIFIER NULL,
[] UNIQUEIDENTIFIER NULL,
[] UNIQUEIDENTIFIER NULL,
[] TINYINT DEFAULT ((1)) NOT NULL,
CONSTRAINT [PK_IP] PRIMARY KEY NONCLUSTERED ([ID] ASC) WITH (FILLFACTOR = 90),
CONSTRAINT [FK_IP_I] FOREIGN KEY ([IID]) REFERENCES [dbo].[I] ([ID]) ON DELETE CASCADE NOT FOR REPLICATION,
CONSTRAINT [FK_IP_XType] FOREIGN KEY ([XType]) REFERENCES [dbo].[xTYPE] ([Value]) NOT FOR REPLICATION
);
GO
CREATE CLUSTERED INDEX [IX_IP_CLUST]
ON [dbo].[IP]([IID] ASC) WITH (FILLFACTOR = 90);
La tabella "I" ha circa 100.000 righe, l'indice cluster ha 9.386 pagine.
La tabella IP è la "figlia" - tabella di I e ha circa 175.000 righe.
Ho provato ad aggiungere un nuovo indice seguendo la regola dell'ordine della colonna dell'indice: "WHERE-JOIN-ORDER- (SELECT)"
per indirizzare le ricerche chiave e creare una ricerca indice:
CREATE NONCLUSTERED INDEX [IX_I_Status_1]
ON [dbo].[Invoice]([Status], [ID])
La query estratta ha immediatamente utilizzato questo indice. Ma la query più grande originale di cui fa parte, non l'ha fatto. Non lo ha nemmeno usato quando l'ho costretto ad usare WITH (INDEX (IX_I_Status_1)).
Dopo un po 'ho deciso di provare un altro nuovo indice e sono passato all'ordine delle colonne indicizzate:
CREATE NONCLUSTERED INDEX [IX_I_Status_2]
ON [dbo].[Invoice]([ID], [Status])
WOHA! Questo indice è stato utilizzato dalla query estratta e anche dalla query più grande!
Quindi ho confrontato le statistiche IO delle query estratte costringendole a utilizzare [IX_I_Status_1] e [IX_I_Status_2]:
Risultati [IX_I_Status_1]:
Table 'I'. Scan count 5, logical reads 636, physical reads 16, read-ahead reads 574
Table 'IP'. Scan count 5, logical reads 1134, physical reads 11, read-ahead reads 1040
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0
Risultati [IX_I_Status_2]:
Table 'I'. Scan count 1, logical reads 615, physical reads 6, read-ahead reads 631
Table 'IP'. Scan count 1, logical reads 1024, physical reads 5, read-ahead reads 1040
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0
OK, potrei capire che la query mega-large-monster potrebbe essere troppo complessa per fare in modo che il server SQL prenda il piano di esecuzione ideale e potrebbe mancare il mio nuovo indice. Ma non capisco perché l'indice [IX_I_Status_2] sembra essere più adatto e più efficiente per la query.
Poiché la query filtra innanzitutto la tabella I per colonna STATUS e quindi si unisce alla tabella IP, non capisco perché [IX_I_Status_2] sia migliore e utilizzato da SQL Server anziché [IX_I_Status_1]?