web-dev-qa-db-fra.com

Dois-je indexer un champ de bits dans SQL Server?

Je me souviens avoir lu à un moment donné que l'indexation d'un champ avec une cardinalité faible (un faible nombre de valeurs distinctes) ne valait pas la peine d'être faite. J'admets que je ne connais pas suffisamment le fonctionnement des index pour comprendre pourquoi.

Alors, que se passe-t-il si j'ai une table de 100 millions de lignes et que je sélectionne des enregistrements dans lesquels un champ de bits est 1? Et disons qu'à tout moment, il n'y a qu'une poignée d'enregistrements où le champ de bits est 1 (par opposition à 0). Vaut-il la peine d’indexer ce champ de bits ou non? Pourquoi?

Bien sûr, je peux juste le tester et vérifier le plan d'exécution, et je le ferai, mais je suis également curieux de connaître la théorie derrière cela. Quand la cardinalité est-elle importante et quand ne l'est-elle pas?

89
jeremcc

Considérez ce qu'est un index dans SQL - et l'index est en réalité un bloc de mémoire pointant vers d'autres morceaux de mémoire (c'est-à-dire des pointeurs vers des lignes). L'index est divisé en pages afin que des parties de l'index puissent être chargées et déchargées de la mémoire en fonction de l'utilisation.

Lorsque vous demandez un ensemble de lignes, SQL utilise l'index pour rechercher les lignes plus rapidement que l'analyse de table (en examinant chaque ligne).

SQL a des index clusterisés et non clusterisés. D'après ma compréhension des index clusterisés, ils regroupent des valeurs d'index similaires dans la même page. Ainsi, lorsque vous demandez toutes les lignes correspondant à une valeur d'index, SQL peut renvoyer ces lignes à partir d'une page de mémoire en cluster. C'est pourquoi il est déconseillé d'essayer de regrouper l'index d'une colonne GUID - vous n'essayez pas de regrouper des valeurs aléatoires.

Lorsque vous indexez une colonne entière, l'index de SQL contient un ensemble de lignes pour chaque valeur d'index. Si vous avez une plage de 1 à 10, vous aurez alors 10 pointeurs d'index. En fonction du nombre de lignes, cela peut être paginé différemment. Si votre requête recherche l'index correspondant à "1", puis où Nom contient "Fred" (en supposant que la colonne Nom ne soit pas indexée), SQL obtient très rapidement l'ensemble des lignes correspondant à "1", puis la table effectue une analyse pour trouver le reste.

SQL cherche donc à réduire le nombre de lignes de travail sur lesquelles il doit effectuer une itération. 

Lorsque vous indexez un champ de bits (ou une plage étroite), vous réduisez uniquement le nombre de lignes de travail par le nombre de lignes correspondant à cette valeur. Si vous avez un petit nombre de lignes correspondantes, cela réduirait beaucoup votre temps de travail. Pour un grand nombre de lignes avec une distribution 50/50, cela peut vous rapporter très peu de gain de performance par rapport à la mise à jour de l'indice.

La raison pour laquelle tout le monde dit de tester est que SQL contient un optimiseur très intelligent et complexe qui peut ignorer un index s'il estime que l'analyse de table est plus rapide, utiliser un tri ou organiser des pages mémoire comme bon lui semble. 

65
Geoff Cox

Je viens de rencontrer cette question par le biais d'une autre. En supposant que votre déclaration selon laquelle seule une poignée d'enregistrements assume la valeur 1 (et que ce sont ceux-là qui vous intéressent), un index filtré peut constituer un bon choix. Quelque chose comme:

create index [IX_foobar] on dbo.Foobar (FooID) where yourBitColumn = 1

Cela créera un index beaucoup plus petit que l'optimiseur est suffisamment intelligent pour utiliser lorsqu'il s'agit d'un prédicat dans votre requête.

15
Ben Thul

100 millions d'enregistrements, seuls quelques-uns ayant le champ de bits défini sur 1? Oui, je pense que l'indexation du champ de bits accélérerait considérablement l'interrogation des enregistrements bit = 1. Vous devez obtenir une durée de recherche logarithmique à partir de l'index, puis ne toucher que les quelques pages avec des enregistrements bit = 1. Sinon, vous devez toucher toutes les pages du tableau des 100 millions d’enregistrements.

Là encore, je ne suis certainement pas un expert en base de données et il se peut que quelque chose d'important manque.

9
C. Dragon 76

Bien que je ne pense pas que j'indexerais JUST un bit de colonne à part, il est très courant d'inclure des colonnes de bits dans un index composé.

Un exemple simple serait un index sur ACTIVE, LASTNAME au lieu de simplement nom, lorsque votre application recherche presque toujours des clients actifs.

7
BradC

Au cas où vous ne l'auriez pas lu, Jason Massie a récemment écrit un article traitant de ce sujet.

http://statisticsio.com/Home/tabid/36/articleType/ArticleView/articleId/302/Never-Index-a-BIT.aspx

Edit: Nouvel emplacement d'article - http://sqlserverpedia.com/blog/sql-server-bloggers/never-index-a-bit

Machine Wayback pour l'emplacement de l'ancien article "Nouveau": http://web.archive.org/web/20120201122503/http://sqlserverpedia.com/blog/sql-server-bloggers/never-index-a -bit/

Le nouvel emplacement Pedia de SQL Server est Toadworld, qui contient un nouvel article de Kenneth Fisher sur ce sujet:

http://www.toadworld.com/platforms/sql-server/b/weblog/archive/2014/02/17/dba-myths-an-index-on-a-bit-column-will-never- be-used.aspx

7
Jeff

Si votre distribution est assez connue et déséquilibrée, comme 99% des lignes sont bit = 1 et les 1% sont bit = 0, lorsque vous créez une clause WHERE avec bit = 1, un balayage complet de la table aura environ le même temps le balayage d'index. Si vous voulez avoir une requête rapide où bit = 0, le meilleur moyen que je connaisse est de créer un index filtré, en ajoutant une clause WHERE bit = 0. De cette façon, cet index ne stockera que la ligne 1%. Ensuite, faire un WHERE bit = 0 laissera simplement l'optimiseur de requête choisir cet index et toutes les lignes de celui-ci seront bit = 0. Vous avez également l'avantage de disposer d'une très petite quantité d'espace disque nécessaire pour comparer un index complet du bit .

6
Philippe Boucher

Comme d'autres l'ont dit, vous voudrez mesurer cela. Je ne me rappelle pas où j'ai lu ceci, mais une colonne doit avoir une cardinalité très élevée (environ 95%) pour qu'un index soit efficace. Votre meilleur test serait de construire l’index et d’examiner les plans d’exécution pour les valeurs 0 et 1 du champ BIT. Si vous voyez une opération de recherche d'index dans le plan d'exécution, vous savez que votre index sera utilisé. 

La meilleure chose à faire serait de tester avec une table SELECT * FROM de base WHERE BitField = 1; interrogez et développez lentement la fonctionnalité à partir de là, étape par étape jusqu'à obtenir une requête réaliste pour votre application, en examinant le plan d'exécution à chaque étape pour vous assurer que la recherche d'index est toujours utilisée. Certes, rien ne garantit que ce plan d'exécution sera utilisé en production, mais il y a de bonnes chances qu'il le soit.

Certaines informations peuvent être trouvées sur les forums sql-server-performance.com et dans le article référencé

2
Jeremiah Peschka

"Je me souviens avoir lu à un moment donné que l'indexation d'un champ avec une cardinalité faible (un faible nombre de valeurs distinctes) ne valait pas la peine d'être faite"

Cela parce que SQL Server trouvera presque toujours plus efficace de faire une analyse de table plutôt que de lire l'index. Donc, fondamentalement, votre index ne sera jamais utilisé et c'est un gaspillage de le maintenir. Comme d'autres l'ont déjà dit, un indice composé pourrait convenir.

2
DJ.

Si votre objectif est de rendre plus rapide la recherche d'enregistrements dont la valeur du champ binaire est égale à «1», essayez une vue indexée de votre table de base contenant uniquement les enregistrements où votre champ binaire est égal à «1». Dans l'édition Enterprise, si une requête pouvait utiliser une vue indexée au lieu d'une table spécifiée pour améliorer les performances de la requête, elle utilisera la vue. En théorie, cela augmenterait la vitesse de sélection des requêtes qui ne recherchent que les enregistrements avec une valeur de champ de bits de «1». 

http://www.Microsoft.com/technet/prodtechnol/sql/2005/impprfiv.mspx

Tout cela suppose que vous êtes Microsoft SQL Server 2005 Enterprise. La même chose pourrait s'appliquer à 2008, je ne suis pas familier avec cette version.

2
Jeremy

Bien sûr, cela vaut la peine, surtout si vous devez récupérer les données avec cette valeur. Ce serait similaire à utiliser une matrice clairsemée au lieu d'utiliser une matrice normale.

Désormais, avec SQL 2008, vous pouvez utiliser des fonctions de partitionnement et filtrer les données contenues dans un index. L'inconvénient des versions précédentes serait que l'index serait créé pour toutes les données, mais ceci peut être optimisé en stockant les valeurs intéressantes dans un groupe de fichiers séparé.

2
Bogdan Maxim

Vous ne pouvez pas indexer un champ de bits dans SQL Server 2000, comme indiqué dans la documentation en ligne à ce moment-là:

bit

Type de données entier 1, 0 ou NULL.

Remarques

Les colonnes de type bit ne peuvent pas avoir d’index.

Oui, si vous ne disposez que de quelques lignes, sur des millions, un index vous aidera. Mais si vous voulez le faire dans ce cas, vous devez faire de la colonne un tinyint.

Note: Enterprise Manager ne vous laissera pas créer un index sur une colonne de bits. Si vous le souhaitez, vous pouvez toujours créer manuellement un index sur une colonne de bits:

CREATE INDEX IX_Users_IsActiveUsername ON Users
(
   IsActive,
   Username
)

Mais SQL Server 2000 n’utilisera pas réellement un tel index - exécuter une requête où l’index serait un candidat idéal, par exemple:

SELECT TOP 1 Username 
FROM Users
WHERE IsActive = 0

SQL Server 2000 effectuera une analyse de table à la place, agissant comme si l'index n'existait même pas. Si vous changez la colonne en un tinyint, SQL Server 2000 effectuera une recherche d'index. En outre, la requête suivante non couverte:

SELECT TOP 1 * 
FROM Users
WHERE IsActive = 0

Il effectuera une recherche d'index, suivie d'une recherche de signet.


SQL Server 2005 prend en charge de manière limitée les index sur les colonnes de bits. Par exemple:

SELECT TOP 1 Username 
FROM Users
WHERE IsActive = 0

provoquera une recherche d'index à travers l'index de couverture. Mais le cas non couvert:

SELECT TOP 1 * 
FROM Users
WHERE IsActive = 0

ne provoquera pas une recherche d'index suivie d'une recherche de signet, il effectuera une analyse de table (ou une analyse d'index en cluster), plutôt que d'effectuer la recherche d'index suivie d'une recherche de signet.

Vérifié par expérimentation et observation directe.

1
Ian Boyd

En soi, non, car il en résulte une très faible sélectivité. Dans le cadre d'un index composé. très probablement, mais seulement après d'autres colonnes d'égalité.

1
Craig Nicholson

Si vous voulez savoir si un index a les effets souhaités: testez et testez à nouveau.

En général, vous ne voulez pas d'un index qui ne restreigne pas suffisamment votre table, à cause du coût de maintenance d'un index. (coût> profit). Mais si l’indice dans votre cas divise la table en deux, vous pouvez gagner quelque chose, mais le mettre sur la table. Tout dépend de la taille/structure exacte de votre tableau et de la manière dont vous l’utilisez (nombre de lectures/écritures).

1
thijs

réponse très tardive ...

Oui, cela peut être utile selon l'équipe SQL CAT _ (mis à jour, a été consolidé)

1
gbn

Ian Boyd a raison lorsqu'il dit que vous ne pouvez pas le faire via Enterprise Manager for SQL 2000 (voir sa note concernant sa création via T-SQL.

0
John B

La cardinalité est un facteur, l’autre est la mesure dans laquelle l’indice divise vos données. Si vous avez environ la moitié 1 et la moitié 0, cela aidera. (En supposant que cet index soit un meilleur chemin à choisir qu'un autre index). Cependant, à quelle fréquence insérez-vous et mettez-vous à jour? L'ajout d'index pour les performances SELECT nuit également aux performances INSERT, UPDATE et DELETE, donc gardez cela à l'esprit. 

Je dirais que si le 1 au 0 (ou l'inverse) n'est pas meilleur que 75% à 25%, ne vous inquiétez pas.

0
Anthony Potts

Vous devez être intelligent ici pour interroger, vous devez connaître la valeur de charge sur votre colonne si la charge de true est plus dans votre système et vous voulez vérifier toutes les valeurs vraies enregistrez votre requête pour qu'elle ne vérifie pas faux .. cela vous aidera beaucoup , c'est juste un truc.

0
Chetan Verma

mesure temps de réponse avant et après et voir si cela en vaut la peine; théoriquement, cela devrait améliorer les performances des requêtes utilisant les champs indexés, mais cela dépend vraiment de la distribution des valeurs true/false et des autres champs impliqués dans les requêtes qui vous intéressent

0
Steven A. Lowe

Est-ce une requête commune? Cela peut valoir la peine de chercher la "poignée" d’enregistrements, mais ne vous aidera pas beaucoup sur les autres rangées. Existe-t-il d'autres moyens d'identifier les données?

0
jason saldo