{"id":726,"date":"2013-04-20T23:27:33","date_gmt":"2013-04-20T21:27:33","guid":{"rendered":"http:\/\/www.sqlserver.fr\/blog\/?p=726"},"modified":"2026-05-02T14:30:53","modified_gmt":"2026-05-02T12:30:53","slug":"index-couvrants","status":"publish","type":"post","link":"https:\/\/www.sqlserver.fr\/blog\/index-couvrants\/","title":{"rendered":"Index couvrants"},"content":{"rendered":"<p>Tr\u00e8s souvent, lorsqu&rsquo;une requ\u00eate donn\u00e9e tra\u00eene un peu, une solution pour l&rsquo;optimiser consiste \u00e0 s&rsquo;attacher \u00e0 positionner des index ad\u00e9quats. Un \u00e9l\u00e9ment important dans le design des index r\u00e9side dans la notion d&rsquo;index couvrants.<!--more--><!--more--><\/p>\n<p>Dans cet article, nous nous appuierons sur la base d&rsquo;exemple de SQL Server 2012, AdventureWorks2012, disponible <a title=\"Sample Databases SQL Server\" href=\"https:\/\/github.com\/Microsoft\/sql-server-samples\/releases\/tag\/adventureworks\" target=\"_blank\" rel=\"noopener noreferrer\">ici<\/a>.<\/p>\n<p>Dans un premier temps, \u00a0les colonnes candidates pour un index sont les colonnes utilis\u00e9es pour filtrer les r\u00e9sultats de la requ\u00eates. Ainsi, prenons la requ\u00eate ci-dessous :<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"sql\">select PersonType,LastName,FirstName\r\nfrom Person.Person\r\nwhere PersonType='SP'<\/pre>\n<p>Lorsqu&rsquo;on l&rsquo;ex\u00e9cute, on obtient le plan d&rsquo;ex\u00e9cution suivant :<\/p>\n<p><a href=\"https:\/\/www.sqlserver.fr\/blog\/wp-content\/uploads\/2013\/04\/FullScan1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-861\" alt=\"FullScan\" src=\"https:\/\/www.sqlserver.fr\/blog\/wp-content\/uploads\/2013\/04\/FullScan1.png\" width=\"367\" height=\"71\" srcset=\"https:\/\/www.sqlserver.fr\/blog\/wp-content\/uploads\/2013\/04\/FullScan1.png 367w, https:\/\/www.sqlserver.fr\/blog\/wp-content\/uploads\/2013\/04\/FullScan1-300x58.png 300w\" sizes=\"auto, (max-width: 367px) 100vw, 367px\" \/><\/a><\/p>\n<p>On remarque un parcours complet de la table (Index Scan), ce qui au final donne un nombre de lectures assez \u00e9lev\u00e9.<\/p>\n<p>Table &lsquo;Person&rsquo;. Scan count 1, logical reads 3820, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.<\/p>\n<p>Si l&rsquo;on positionne d\u00e9sormais un index sur la colonne PersonType, on s&rsquo;aper\u00e7oit qu&rsquo;il est utilis\u00e9 et que le nombre de pages lues chute de mani\u00e8re drastique. Jusqu&rsquo;ici, tout va bien.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"sql\">create index IX_Person_PersonType ON Person.Person(PersonType)<\/pre>\n<p><a href=\"https:\/\/www.sqlserver.fr\/blog\/wp-content\/uploads\/2013\/04\/IndexSeek.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-862\" alt=\"IndexSeek\" src=\"https:\/\/www.sqlserver.fr\/blog\/wp-content\/uploads\/2013\/04\/IndexSeek.png\" width=\"502\" height=\"167\" srcset=\"https:\/\/www.sqlserver.fr\/blog\/wp-content\/uploads\/2013\/04\/IndexSeek.png 502w, https:\/\/www.sqlserver.fr\/blog\/wp-content\/uploads\/2013\/04\/IndexSeek-300x99.png 300w\" sizes=\"auto, (max-width: 502px) 100vw, 502px\" \/><\/a><\/p>\n<p>Table &lsquo;Person&rsquo;. Scan count 1, logical reads 53, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.<\/p>\n<p>Mais lorsque le filtre ne restreint plus uniquement sur quelques enregistrements, le surco\u00fbt des op\u00e9rations Key Lookup devient trop important.<\/p>\n<p>Pour rappel, une recherche de cl\u00e9 (Key Lookup) correspond au fait d&rsquo;aller chercher dans un autre index les colonnes manquantes (dans notre cas les colonnes LastName et FirstName) alors que l&rsquo;on n&rsquo;a \u00e0 notre disposition que les colonnes pr\u00e9sentes dans les feuilles de notre index non clustered (dans notre cas PersonType et\u00a0BusinessEntityID).<\/p>\n<p>Lorsque le nombre de recherches de cl\u00e9s (Key Lookup) devient trop important, le moteur SQL Server peut m\u00eame d\u00e9cider d&rsquo;aller chercher les donn\u00e9es manquantes autrement que via un Key Lookup.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"sql\">select PersonType,LastName,FirstName\r\nfrom Person.Person\r\nwhere PersonType='VC'<\/pre>\n<p><a href=\"https:\/\/www.sqlserver.fr\/blog\/wp-content\/uploads\/2013\/04\/Intermediaire.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-863\" alt=\"Intermediaire\" src=\"https:\/\/www.sqlserver.fr\/blog\/wp-content\/uploads\/2013\/04\/Intermediaire.png\" width=\"512\" height=\"161\" srcset=\"https:\/\/www.sqlserver.fr\/blog\/wp-content\/uploads\/2013\/04\/Intermediaire.png 512w, https:\/\/www.sqlserver.fr\/blog\/wp-content\/uploads\/2013\/04\/Intermediaire-300x94.png 300w\" sizes=\"auto, (max-width: 512px) 100vw, 512px\" \/><\/a><\/p>\n<p>Le syst\u00e8me pr\u00e9f\u00e8re ici effectuer un scan complet d&rsquo;un autre index pour obtenir les colonnes demand\u00e9es. Le moteur s&rsquo;appuie ici sur l&rsquo;index non clustered dont le parcours est le moins co\u00fbteux, c&rsquo;est-\u00e0-dire l&rsquo;index d\u00e9di\u00e9 aux colonnes FirstName et LastName.<\/p>\n<p>Si notre requ\u00eate concerne d&rsquo;autres colonnes, par exemple la colonne Demographics, le fonctionnement est diff\u00e9rent. En effet, il n&rsquo;y a pas d&rsquo;index s&rsquo;appuyant sur cette colonne. Au final, si la solution la moins co\u00fbteuse est celle passant par un Key Lookup (en comparaison avec un scan complet de l&rsquo;index Clustered), m\u00eame si cela nous oblige tout de m\u00eame \u00e0 avoir un nombre de lectures qui croit assez rapidement.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"sql\">select PersonType,Demographics\r\nfrom Person.Person\r\nwhere PersonType='SC'<\/pre>\n<p><a href=\"https:\/\/www.sqlserver.fr\/blog\/wp-content\/uploads\/2013\/04\/Intermediaire2.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-864\" alt=\"Intermediaire2\" src=\"https:\/\/www.sqlserver.fr\/blog\/wp-content\/uploads\/2013\/04\/Intermediaire2.png\" width=\"517\" height=\"159\" srcset=\"https:\/\/www.sqlserver.fr\/blog\/wp-content\/uploads\/2013\/04\/Intermediaire2.png 517w, https:\/\/www.sqlserver.fr\/blog\/wp-content\/uploads\/2013\/04\/Intermediaire2-300x92.png 300w\" sizes=\"auto, (max-width: 517px) 100vw, 517px\" \/><\/a><\/p>\n<p>Table &lsquo;Person&rsquo;. Scan count 1, logical reads 2320, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.<\/p>\n<p>Afin de r\u00e9duire le co\u00fbt d&rsquo;une telle requ\u00eate, une technique habituelle consiste \u00e0 ajouter des colonnes aux index utilis\u00e9s. Ainsi, dans le cas des colonnes LastName et FirstName, on pourrait ajouter les colonnes \u00e0 l&rsquo;index d\u00e9fini sur PersonType.<\/p>\n<p>Mais lorsque la colonne requise est la colonne Demographics, il n&rsquo;est pas possible d&rsquo;ajouter cette colonne directement \u00e0 l&rsquo;index car son type de donn\u00e9es, XML, n&rsquo;est pas triable.<\/p>\n<p>N\u00e9anmoins, SQL Server, depuis sa version 2005, nous permet d&rsquo;ajouter des colonnes, m\u00eame d&rsquo;un type de donn\u00e9es non triable, au niveau terminal des index (niveau auquel les pages sont appel\u00e9es feuilles).<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"sql\">create index IX_Person_PersonType_Demographics ON Person.Person(PersonType) INCLUDE (Demographics)<\/pre>\n<p><a href=\"https:\/\/www.sqlserver.fr\/blog\/wp-content\/uploads\/2013\/04\/SeekFinal.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-865\" alt=\"SeekFinal\" src=\"https:\/\/www.sqlserver.fr\/blog\/wp-content\/uploads\/2013\/04\/SeekFinal.png\" width=\"366\" height=\"76\" srcset=\"https:\/\/www.sqlserver.fr\/blog\/wp-content\/uploads\/2013\/04\/SeekFinal.png 366w, https:\/\/www.sqlserver.fr\/blog\/wp-content\/uploads\/2013\/04\/SeekFinal-300x62.png 300w\" sizes=\"auto, (max-width: 366px) 100vw, 366px\" \/><\/a><\/p>\n<p>Table &lsquo;Person&rsquo;. Scan count 1, logical reads 51, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.<\/p>\n<p>On constate effectivement qui le nombre de lectures est redevenue tr\u00e8s faible, ce qui correspond donc \u00e0 de bonnes performances pour la requ\u00eate.<\/p>\n<p>Ce type d&rsquo;index, d\u00e9di\u00e9 \u00e0 une ou plusieurs requ\u00eates type et apportant toutes les colonnes requises par ces requ\u00eates, est appel\u00e9 index couvrant. Comme toujours avec les index, on a une am\u00e9lioration des performances de lecture mais au d\u00e9triment des performances lors des \u00e9critures.<\/p>\n<p>Le mot cl\u00e9 INCLUDE permet de lister des colonnes qui ne seront positionn\u00e9es que dans les niveaux terminaux de l&rsquo;index. M\u00eame si ces colonnes incluses sont souvent indiqu\u00e9es au niveau des conseils li\u00e9s aux index manquants, il faut toutefois faire attention \u00e0 ne pas trop en abuser, car l&rsquo;espace consomm\u00e9 peut rapidement augmenter (en dupliquant des donn\u00e9es \u00e0 outrance) et cela peut d\u00e9grader les performances li\u00e9es aux modifications.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Tr\u00e8s souvent, lorsqu&rsquo;une requ\u00eate donn\u00e9e tra\u00eene un peu, une solution pour l&rsquo;optimiser consiste \u00e0 s&rsquo;attacher \u00e0 positionner des index ad\u00e9quats. Un \u00e9l\u00e9ment important dans le design des index r\u00e9side dans la notion d&rsquo;index couvrants.<\/p>\n","protected":false},"author":7,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-726","post","type-post","status-publish","format-standard","hentry","category-article_sql"],"_links":{"self":[{"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/posts\/726","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/comments?post=726"}],"version-history":[{"count":22,"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/posts\/726\/revisions"}],"predecessor-version":[{"id":1933,"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/posts\/726\/revisions\/1933"}],"wp:attachment":[{"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/media?parent=726"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/categories?post=726"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/tags?post=726"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}