2013-01-14 64 views
1

我試圖在SQL Server中編寫查詢,但它對具有大約3000萬行(TGS_INFO)的表執行表掃描,因此查詢運行速度非常緩慢。SQL Server未使用索引

實際的查詢比較複雜,但是我將它簡化爲一個更簡單的版本,但仍然存在相同的問題。

SELECT DISTINCT UNIT_ITEMS.DBKEY, 
    UNIT_ITEMS.ID, 
    UNIT_ITEMS.LOCATION1, 
    UNIT_ITEMS.LOCATION2 
FROM UNIT_ITEMS 
INNER JOIN TGS.dbo.TGS_INFO 
ON UNIT_ITEMS.UNIT_ID = TGS_INFO.UNIT_ID AND 
    UNIT_ITEMS.ITEM_ID = TGS_INFO.ITEM_ID AND 
    UNIT_ITEMS.LOCATION1 = TGS_INFO.LOCATION1 AND 
    UNIT_ITEMS.LOCATION2 = TGS_INFO.LOCATION2 

這是執行計劃。

StmtText 
    |--Sort(DISTINCT ORDER BY:([DbName].[dbo].[UNIT_ITEMS].[DBKEY] ASC, [DbName].[dbo].[UNIT_ITEMS].[ITEM_ID] ASC, [DbName].[dbo].[UNIT_ITEMS].[LOCATION1] ASC, [DbName].[dbo].[UNIT_ITEMS].[LOCATION2] ASC)) 
     |--Hash Match(Inner Join, HASH:([DbName].[dbo].[UNIT_ITEMS].[UNIT_ID], [DbName].[dbo].[UNIT_ITEMS].[ITEM_ID], [DbName].[dbo].[UNIT_ITEMS].[LOCATION1], [DbName].[dbo].[UNIT_ITEMS].[LOCATION2])=([Expr1008], [Expr1009], [Expr1010], [Expr1011]), RESIDUAL:([DbName].[dbo].[UNIT_ITEMS].[UNIT_ID]=[Expr1008] AND [DbName].[dbo].[UNIT_ITEMS].[ITEM_ID]=[Expr1009] AND [DbName].[dbo].[UNIT_ITEMS].[LOCATION1]=[Expr1010] AND [DbName].[dbo].[UNIT_ITEMS].[LOCATION2]=[Expr1011])) 
      |--Table Scan(OBJECT:([DbName].[dbo].[UNIT_ITEMS])) 
      |--Compute Scalar(DEFINE:([Expr1008]=CONVERT_IMPLICIT(int,[TGS].[dbo].[TGS_INFO].[UNIT_ID],0), [Expr1009]=CONVERT_IMPLICIT(nvarchar(50),[TGS].[dbo].[TGS_INFO].[ITEM_ID],0), [Expr1010]=CONVERT_IMPLICIT(nvarchar(50),[TGS].[dbo].[TGS_INFO].[LOCATION1],0), [Expr1011]=CONVERT_IMPLICIT(int,[TGS].[dbo].[TGS_INFO].[LOCATION2],0))) 
       |--Table Scan(OBJECT:([TGS].[dbo].[TGS_INFO])) 

TGS_INFO和UNIT_ITEMS在UNIT_ID和ITEM_ID上都有非聚簇索引。如前所述,TGS_INFO有大約3000萬行,但它們均勻地分佈在大約一千個不同的UNIT_ID上。 UNIT_ITEMS始終只包含一個UNIT_ID。

這裏有指標:

CREATE NONCLUSTERED INDEX [IX_UNIT_ID_ITEM_ID] ON [dbo].[TGS_INFO] 
(
    [UNIT_ID] ASC, 
    [ITEM_ID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 

CREATE NONCLUSTERED INDEX [IX_UNIT_ID_ITEM_ID] ON [dbo].[UNIT_ITEMS] 
(
    [UNIT_ID] ASC, 
    [ITEM_ID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 

正如我在評論中所提到的,所有的列是VARCHAR(50)TGS_INFO。 UNIT_ITEMS中的所有列都是整數。

爲了記錄,我沒有設計TGS_INFO的模式。

+1

每個表只能有一個聚簇索引。什麼是您的指數的列順序? – Kermit

+2

對,哈希似乎不對。你也嘗試過沒有DISTINCT?這是DISTINCT真的有必要嗎? –

+1

您所匹配的所有列中的數據類型是否匹配?我不明白爲什麼它會進行隱式數據轉換。如果將'UNIT_ITEMS.LOCATION1 = TGS_INFO.LOCATION1 AND UNIT_ITEMS.LOCATION2 = TGS_INFO.LOCATION2'移動到'WHERE'子句怎麼辦? –

回答

0

我注意到執行計劃顯示如下:

|--Compute Scalar(DEFINE:([Expr1008]=CONVERT_IMPLICIT(int,[TGS].[dbo].[TGS_INFO].[UNIT_ID],0), [Expr1009]=CONVERT_IMPLICIT(nvarchar(50),[TGS].[dbo].[TGS_INFO].[ITEM_ID],0), [Expr1010]=CONVERT_IMPLICIT(nvarchar(50),[TGS].[dbo].[TGS_INFO].[LOCATION1],0), [Expr1011]=CONVERT_IMPLICIT(int,[TGS].[dbo].[TGS_INFO].[LOCATION2],0))) 

我想不出一個很好的理由,查詢引擎做這些列上隱式數據類型轉換,除非兩者之間的數據類型表格在您用於連接的列上不匹配。

您也可以嘗試將UNIT_ITEMS.LOCATION1 = TGS_INFO.LOCATION1 AND UNIT_ITEMS.LOCATION2 = TGS_INFO.LOCATION2移動到WHERE子句,因爲它們未被索引覆蓋。查詢引擎通常足夠聰明,可以解釋這一點,但這是可以嘗試的。

+0

將東西移動到where子句並不適用於SQL Server。這個技巧主要用於像MySQL這樣的弱查詢優化器的RDBMS。 – usr

3

如果在索引中不包括LOCATION1LOCATION2,則單獨的索引無法滿足連接。將這些列添加到兩個表上的索引。

您可能還必須包含查詢中引用的所有其他列。

+0

UNIT_ID,ITEM_ID,LOCATION1和LOCATION2是TGS_INFO表上的候選鍵,因此創建索引似乎對我來說是多餘的。 – Tmdean

+0

但你會錯的。所有的FK應該被索引。即使他們沒有被用作PK,所有潛在的鑰匙也應該對他們有一個獨特的索引。只有官方PK自動獲取索引。你可以在一個複合索引中完成。 – HLGEM

+2

@Tmdean你關心表現還是多餘? –