1

次のクエリとインデックスを作成する最も効率的な方法は次のとおりです。

SELECT * Persons.LastName A-D
SELECT * Persons.LastName E-L
SELECT * Persons.LastName M-R
SELECT * Persons.LastName S-Z

私は以下を使用していますが、これは非常に非効率的でインデックス作成が困難です:

WHERE LastName LIKE '[a-d]%'

これを行うためのより良い方法についてのアイデアはありますか? これは Filtered Index の良いシナリオかもしれませんが、where 句はより検索可能にする必要があります。

ありがとう

4

3 に答える 3

2

サムが言うように、LIKE '[a-d]%'SARGable (ほぼ) です。ほとんどの場合、最適化されていないためですPredicate(詳細については、以下を参照してください)。

例 #1: このクエリをAdventureWorks2008R2データベースで実行する場合

SET STATISTICS IO ON;
SET NOCOUNT ON;

PRINT 'Example #1:';
SELECT  p.BusinessEntityID, p.LastName
FROM    Person.Person p
WHERE   p.LastName LIKE '[a-a]%'

次に、演算子に基づいて実行計画を取得しますIndex Seek(最適化された述語: 緑の四角形、最適化されていない述語: 赤の四角形): ここに画像の説明を入力 の出力SET STATISTICS IOは次のとおりです。

Example #1:
Table 'Person'. Scan count 1, logical reads 7

これは、サーバーがバッファー プールから 7 ページを読み取る必要があることを意味します。また、この場合、インデックスには、および句でIX_Person_LastName_FirstName_MiddleName必要なすべての列が含まれます: LastName および BusinessEntityID。テーブルにクラスター化インデックスがある場合、すべての非クラスター化インデックスにはクラスター化インデックス キーの列が含まれます (BusinessEntityID は PK_Person_BusinessEntityID クラスター化インデックスのキーです)。SELECTFROMWHERE

しかし:

1)次の理由により、クエリはすべての列を表示する必要がありますSELECT *(これは悪い習慣です):BusinessEntityID、LastName、FirstName、MiddleName、PersonType、...、ModifiedDate。

2) インデックス (IX_Person_LastName_FirstName_MiddleName前の例) には、必要なすべての列が含まれていません。これが、このクエリの場合、このインデックスが非カバー インデックスである理由です。

ここで、次のクエリを実行すると、diff が取得されます。[実際の] 実行計画 (SSMS、Ctrl + M):

SET STATISTICS IO ON;
SET NOCOUNT ON;

PRINT 'Example #2:';
SELECT  p.*
FROM    Person.Person p
WHERE   p.LastName LIKE '[a-a]%';
PRINT @@ROWCOUNT;

PRINT 'Example #3:';
SELECT  p.*
FROM    Person.Person p
WHERE   p.LastName LIKE '[a-z]%';
PRINT @@ROWCOUNT;

PRINT 'Example #4:';
SELECT  p.*
FROM    Person.Person p WITH(FORCESEEK)
WHERE   p.LastName LIKE '[a-z]%';
PRINT @@ROWCOUNT;

結果:

Example #2:
Table 'Person'. Scan count 1, logical reads 2805, lob logical reads 0
911

Example #3:
Table 'Person'. Scan count 1, logical reads 3817, lob logical reads 0 
19972

Example #4:
Table 'Person'. Scan count 1, logical reads 61278, lob logical reads 0
19972

実行計画: ここに画像の説明を入力

さらに、クエリは、「Person.Person」で作成されたすべてのインデックスのページ数を示します。

SELECT i.name, i.type_desc,f.alloc_unit_type_desc, f.page_count, f.index_level FROM sys.dm_db_index_physical_stats(
    DB_ID(), OBJECT_ID('Person.Person'), 
    DEFAULT, DEFAULT, 'DETAILED' ) f 
INNER JOIN sys.indexes i ON f.object_id = i.object_id AND f.index_id = i.index_id
ORDER BY i.type


name                                    type_desc    alloc_unit_type_desc page_count index_level
--------------------------------------- ------------ -------------------- ---------- -----------
PK_Person_BusinessEntityID              CLUSTERED    IN_ROW_DATA          3808       0
PK_Person_BusinessEntityID              CLUSTERED    IN_ROW_DATA          7          1
PK_Person_BusinessEntityID              CLUSTERED    IN_ROW_DATA          1          2
PK_Person_BusinessEntityID              CLUSTERED    ROW_OVERFLOW_DATA    1          0
PK_Person_BusinessEntityID              CLUSTERED    LOB_DATA             1          0
IX_Person_LastName_FirstName_MiddleName NONCLUSTERED IN_ROW_DATA          103        0
IX_Person_LastName_FirstName_MiddleName NONCLUSTERED IN_ROW_DATA          1          1

...

Example #1ここで、とExample #2(両方とも 911 行を返す)を比較すると、

`SELECT p.BusinessEntityID, p.LastName ... p.LastName LIKE '[a-a]%'`
vs.
`SELECT * ... p.LastName LIKE '[a-a]%'`

次に、2 つの差分が表示されます。

a) 7 つの論理読み取りと 2805 の論理読み取り

b) Index Seek(#1) 対Index Seek+ Key Lookup(#2)。

(#2) クエリのパフォーマンスがはるかに悪いことがわかりSELECT *ます (7 ページ対 2805 ページ)。

ここで、 を比較するExample #3Example #4(どちらも 19972 行を返します)

`SELECT * ... LIKE '[a-z]%`
vs.
`SELECT * ... WITH(FORCESEEK) LIKE '[a-z]%`

次に、2 つの差分が表示されます。

a) 3817 の論理読み取り (#3) 対 61278 の論理読み取り (#4) および

b) Clustered Index Scan(PK_Person_BusinessEntityID には 3808 + 7 + 1 + 1 + 1 = 3818 ページあります) vs. Index Seek+ Key Lookup.

Index Seek+ (#4) クエリのパフォーマンスがはるかに悪いことがわかりKey Lookupます (3817 ページ対 61278 ページ)。この場合、and Index SeekonIX_Person_LastName_FirstName_MiddleNameKey Lookupon PK_Person_BusinessEntityID(クラスター化インデックス) を使用すると、「クラスター化インデックス スキャン」よりもパフォーマンスが低下することがわかります。

そして、これらすべての悪い実行計画は、SELECT *.

于 2013-04-18T20:12:34.447 に答える
1

私はあなたの説明計画を見て、STATISTICS IO と STATISTICS time をオンにして、何かが飛び出すかどうかを確認します。

于 2013-04-18T17:04:54.587 に答える
1

あなたの述語はサーガブルです。

インデックス付きフィールドでこのクエリを実行すると、次のようになります。

SELECT  *
FROM    persons
WHERE   last_name >= 'a'
        AND last_name < 'e'

次の計画を作成します。

  |--Nested Loops(Inner Join, OUTER REFERENCES:([MYDB].[dbo].[PERSONS].[ID]) OPTIMIZED)
       |--Index Seek(OBJECT:([MYDB].[dbo].[PERSONS].[IX_PERSONS_LAST_NAME]), SEEK:([MYDB].[dbo].[PERSONS].[LAST_NAME] >= 'a' AND [MYDB].[dbo].[PERSONS].[LAST_NAME] < 'E'),  WHERE:([MYDB].[dbo].[PERSONS].[LAST_NAME] like '[a-d]%') ORDERED FORWARD)
       |--Clustered Index Seek(OBJECT:([MYDB].[dbo].[PERSONS].[IX_PERSONS_LAST_NAME]), SEEK:([MYDB].[dbo].[PERSONS].[ID]=[MYDB].[dbo].[PERSONS].[ID]) LOOKUP ORDERED FORWARD)

これは、次のクエリを実行するのと同じです:

SELECT  *
FROM    persons
WHERE   last_name >= 'a'
        AND last_name < 'e'
于 2013-04-18T16:46:49.273 に答える