¿Debo indexar un campo de bits en SQL Server?

99

Recuerdo haber leído en un momento que no vale la pena indexar un campo con cardinalidad baja (un número bajo de valores distintos). Admito que no sé lo suficiente sobre cómo funcionan los índices para entender por qué es así.

Entonces, ¿qué pasa si tengo una tabla con 100 millones de filas y estoy seleccionando registros donde un campo de bits es 1? Y digamos que en cualquier momento, solo hay un puñado de registros donde el campo de bits es 1 (en lugar de 0). ¿Vale la pena indexar ese campo de bits o no? ¿Por qué?

Por supuesto, puedo probarlo y verificar el plan de ejecución, y lo haré, pero también tengo curiosidad por la teoría detrás de esto. ¿Cuándo importa la cardinalidad y cuándo no?

jeremcc
fuente
¿Es esta una consulta común? Puede valer la pena cuando busque el "puñado" de registros, pero no le ayudará mucho en las otras filas. ¿Existen otras formas de identificar los datos?
Jason Saldo
4
Si bien no creo que indexaría SOLO una columna de bits por sí misma, es muy común incluir columnas de bits como parte de un índice compuesto. Un ejemplo simple sería un índice en ACTIVE, LASTNAME en lugar de solo lastname, cuando su aplicación casi siempre busca clientes activos.
BradC
"Recuerdo haber leído en un momento que no vale la pena indexar un campo con baja cardinalidad (un número bajo de valores distintos)" Eso porque SQL Server casi siempre encontrará que es más eficiente simplemente hacer un escaneo de tabla que leer el índice. Entonces, básicamente, su índice nunca se usará y es un desperdicio mantenerlo. Como han dicho otros, podría estar bien en un índice compuesto.
DJ.
5
Yo no estaría de acuerdo. Si su distribución es 50/50, nunca usaría el índice, ya que sería más rápido hacer un escaneo de tabla. Sin embargo, si solo tiene 5, 1 valores y 1 millón de valores 0, es muy probable que utilice el índice al buscar 1.
Kibbee
1
En el ejemplo que dio, estaría más inclinado a poner Apellido primero. Depende de la carga de trabajo de la consulta específica, pero en general, tener primero la columna más selectiva significa que es más probable que se utilice el índice.
Mitch Wheat

Respuestas:

72

Considere lo que es un índice en SQL, y el índice es en realidad un trozo de memoria que apunta a otros trozos de memoria (es decir, punteros a filas). El índice se divide en páginas para que porciones del índice se puedan cargar y descargar de la memoria según el uso.

Cuando solicita un conjunto de filas, SQL usa el índice para encontrar las filas más rápidamente que el escaneo de tablas (mirando cada fila).

SQL tiene índices agrupados y no agrupados. Mi comprensión de los índices agrupados es que agrupan valores de índice similares en la misma página. De esta manera, cuando solicita todas las filas que coinciden con un valor de índice, SQL puede devolver esas filas desde una página de memoria agrupada. Esta es la razón por la que intentar agrupar indexar una columna GUID es una mala idea: no intenta agrupar valores aleatorios.

Cuando indexa una columna de entero, el índice de SQL contiene un conjunto de filas para cada valor de índice. Si tiene un rango de 1 a 10, entonces tendría 10 punteros de índice. Dependiendo de cuántas filas haya, esto se puede paginar de manera diferente. Si su consulta busca el índice que coincide con "1" y luego donde Nombre contiene "Fred" (asumiendo que la columna Nombre no está indexada), SQL obtiene el conjunto de filas que coinciden con "1" muy rápidamente, luego la tabla escanea para encontrar el resto.

Entonces, lo que SQL realmente está haciendo es tratar de reducir el conjunto de trabajo (número de filas) sobre el que tiene que iterar.

Cuando indexa un campo de bits (o algún rango estrecho), solo reduce el conjunto de trabajo por el número de filas que coinciden con ese valor. Si tiene una pequeña cantidad de filas que coinciden, reduciría mucho su conjunto de trabajo. Para una gran cantidad de filas con una distribución 50/50, es posible que obtenga muy poca ganancia de rendimiento en comparación con mantener el índice actualizado.

La razón por la que todo el mundo dice probar es porque SQL contiene un optimizador muy inteligente y complejo que puede ignorar un índice si decide que el escaneo de la tabla es más rápido, o puede usar una clasificación, o puede organizar las páginas de memoria como quiera.

Geoff Cox
fuente
Entonces, parece que si solo tuviera un puñado de filas donde el campo de bits es 1 (por ejemplo, haciendo un seguimiento de "IsProcessed"), entonces un índice sería bueno porque los ordenará por valor y luego podrá seleccionar el pequeño conjunto de trabajo muy rápidamente. Si está de acuerdo, agregue eso y lo aceptaré.
jeremcc
2
Lo que quiero decir en mi comentario anterior es que esta afirmación: "Cuando indexa un campo de bits (o algún rango estrecho), solo reduce el conjunto de trabajo a la mitad" no es cierta si la distribución está fuertemente ponderada hacia un valor. Pero me gusta el resto de tu respuesta, así que si lo arreglas, lo aceptaré.
jeremcc
1
Hecho. Estaba pensando que para un millón de filas, un campo de bits tendría una distribución del 50%, pero tiene razón en que para un espacio problemático en particular podría reducir mucho el conjunto de trabajo.
Geoff Cox
Vale la pena mirar los planes de ejecución con y sin el índice, y ver si se está utilizando el índice y si realmente reduce el costo de sus consultas. ¡Fácil y científico!
onupdatecascade
¿Qué hay de indexar un campo de bits + otro campo? P.ej. en un registro de actividad web, se indexaría la marca de tiempo, pero otro índice útil podría estar en un campo de bits "IsHTTPS" + marca de tiempo, para ver rápidamente todas las acciones https. ¿Eso también sería ineficiente?
ingrediente_15939
19

Me encontré con esta pregunta a través de otra. Suponiendo que su declaración de que solo un puñado de registros asume el valor de 1 (y que esos son los que le interesan), entonces un índice filtrado podría ser una buena opción. Algo como:

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

Esto creará un índice sustancialmente más pequeño que el optimizador es lo suficientemente inteligente para usar cuando ese es un predicado en su consulta.

Ben Thul
fuente
1
Vale la pena señalar que el predicado en la consulta debe estar codificado en el valor en el índice filtrado. Si pasa el valor en un parámetro yourBitColumn = @value, el optimizador no puede determinar si el índice filtrado es utilizable.
geofftnz
2
Hay formas de evitar esto, pero tienes razón; el optimizador necesita una garantía en el momento de la compilación de que los valores para cualquier predicado que coincida con el predicado del índice filtrado sean estáticos / invariantes, ya que es trabajo del optimizador crear un plan general que funcione para cualquier conjunto de parámetros.
Ben Thul
9

¿100 millones de registros y solo unos pocos tienen el campo de bits establecido en 1? Sí, creo que indexar el campo de bits definitivamente aceleraría la consulta de los registros bit = 1. Debe obtener el tiempo de búsqueda logarítmica del índice y luego tocar solo las pocas páginas con registros bit = 1. De lo contrario, tendría que tocar todas las páginas de la tabla de registros de 100 millones.

Por otra parte, definitivamente no soy un experto en bases de datos y podría estar perdiendo algo importante.

C. Dragón 76
fuente
8

Si su distribución es bastante conocida y desequilibrada, como el 99% de las filas son bit = 1 y el 1% son bit = 0, cuando haga una cláusula WHERE con bit = 1, un escaneo completo de la tabla será aproximadamente al mismo tiempo que el escaneo de índice. Si desea tener una consulta rápida donde bit = 0, la mejor manera que conozco es crear un índice filtrado, agregando una cláusula WHERE bit = 0. De esa manera, ese índice solo almacenará la fila del 1%. Luego, hacer un DONDE bit = 0 simplemente permitirá que el optimizador de consultas elija ese índice, y todas las filas serán bit = 0. También tiene la ventaja de tener una cantidad muy pequeña de espacio en disco requerido para comparar un índice completo en el bit .

Philippe Boucher
fuente
2
Si el 99% de las filas son bit = 1, el optimizador debe ignorar el índice y hacer un escaneo de la tabla. Usar el índice en realidad será peor que un escaneo de tabla, al menos en una unidad de rotación, más E / S y lecturas no consecutivas del disco. El índice filtrado (equivalente a Postgres: índice parcial) es el camino a seguir. Supongo que debido a que han pasado años desde la pregunta, esta respuesta no obtuvo los votos que merecía.
Andrew Lazarus
7

Si bien no creo que indexaría SOLO una columna de bits por sí misma, es muy común incluir columnas de bits como parte de un índice compuesto.

Un ejemplo simple sería un índice en ACTIVE, LASTNAME en lugar de solo lastname, cuando su aplicación casi siempre busca clientes activos.

BradC
fuente
7
En el ejemplo que dio, estaría más inclinado a poner Apellido primero. Depende de la carga de trabajo de la consulta específica, pero en general, tener primero la columna más selectiva significa que es más probable que se utilice el índice.
Mitch Wheat
7

En caso de que no lo haya leído, Jason Massie escribió un artículo recientemente que discutía este mismo tema.

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

Editar: Ubicación del nuevo artículo: http://sqlserverpedia.com/blog/sql-server-bloggers/never-index-a-bit

Wayback machine para la ubicación del artículo anteriormente "Nuevo": http://web.archive.org/web/20120201122503/http://sqlserverpedia.com/blog/sql-server-bloggers/never-index-a-bit/

La nueva ubicación de SQL Server Pedia es Toadworld, que tiene un nuevo artículo de Kenneth Fisher que trata este tema:

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

máquina de retorno: http://web.archive.org/web/20150508115802/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

Jeff
fuente
este artículo ya no es visible
Homero 6 de
@ Homer6 Agregué un enlace a lo que parece ser el nuevo hogar para este artículo.
Jeff
El nuevo enlace va a la página de inicio de Toad World.
N West
Encontré el artículo usando la máquina Wayback y encontré un nuevo artículo relacionado. Espero que esto ayude.
Jeff
2

Por supuesto que vale la pena, especialmente si necesita recuperar los datos por ese valor. Sería similar a usar una matriz dispersa en lugar de usar una matriz normal.

Ahora, con SQL 2008 puede utilizar funciones de partición y puede filtrar los datos que van en un índice. La desventaja de las versiones anteriores sería que el índice se haría para todos los datos, pero esto se puede optimizar almacenando los valores interesantes en un grupo de archivos separado.

Bogdan Maxim
fuente
2

Como han dicho otros, querrás medir esto. No recuerdo dónde leí esto, pero una columna debe tener una cardinalidad muy alta (alrededor del 95%) para que un índice sea efectivo. Su mejor prueba para esto sería construir el índice y examinar los planes de ejecución para los valores 0 y 1 del campo BIT. Si ve una operación de búsqueda de índice en el plan de ejecución, entonces sabe que se utilizará su índice.

Su mejor curso de acción sería probar con una tabla SELECT * FROM básica DONDE BitField = 1; consulte y desarrolle lentamente la funcionalidad desde allí paso a paso hasta que tenga una consulta realista para su aplicación, examinando el plan de ejecución con cada paso para asegurarse de que la búsqueda de índice todavía se esté utilizando. Es cierto que no hay garantía de que este plan de ejecución se utilice en producción, pero es muy probable que así sea.

Puede encontrar alguna información en los foros de sql-server-performance.com y en el artículo de referencia

Jeremías Peschka
fuente
No es tanto la cardinalidad de la columna como un todo lo que importa. Es la selectividad de la cláusula WHERE. Entonces, si hay pocas columnas con valor 1, aún puede ser bueno indexar. Si es 50/50 (por ejemplo, hombre / mujer), entonces no vale la pena.
WW.
2

"Recuerdo haber leído en un momento que no vale la pena indexar un campo con cardinalidad baja (un número bajo de valores distintos)"

Eso porque SQL Server casi siempre encontrará más eficiente simplemente hacer un escaneo de tabla que leer el índice. Entonces, básicamente, su índice nunca se usará y es un desperdicio mantenerlo. Como han dicho otros, podría estar bien en un índice compuesto.

DJ.
fuente
2

Si su objetivo es hacer que la consulta de registros donde el valor del campo de bits sea igual a '1' sea más rápida, puede probar una vista indexada de su tabla base que solo contiene registros donde su campo de bits es igual a '1'. En la edición empresarial, si una consulta puede utilizar una vista indexada en lugar de una tabla específica para mejorar el rendimiento de la consulta, utilizará la vista. En teoría, esto aumentaría la velocidad de las consultas seleccionadas que solo buscan registros con un valor de campo de bits de '1'.

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

Todo esto supone que eres Microsoft SQL Server 2005 Enterprise. Lo mismo podría aplicarse a 2008, no estoy familiarizado con esa versión.


fuente
2

Si desea saber si un índice tiene los efectos que desea: pruebe y vuelva a probar.

En general, no desea un índice que no limite su tabla lo suficiente, debido al costo de mantener un índice. (costo> beneficio). Pero si el índice en su caso corta la mesa a la mitad, puede ganar algo pero poniéndolo sobre la mesa. Todo depende del tamaño / estructura exactos de su tabla y de cómo la esté utilizando (número de lecturas / escrituras).

esto
fuente
1

Por sí solo, no, ya que resulta en muy poca selectividad. Como parte de un índice compuesto. muy posiblemente, pero solo después de otras columnas de igualdad.

Craig Nicholson
fuente
1

No puede indexar un campo de bits en SQL Server 2000, como se indicó en los Libros en pantalla en ese momento:

poco

Tipo de datos entero 1, 0 o NULL.

Observaciones

Las columnas de tipo bit no pueden tener índices.

Sí, si solo tiene un puñado de filas, entre millones, un índice le ayudará. Pero si desea hacerlo en este caso, debe hacer que la columna a tinyint.

Nota : Enterprise Manager no le permitirá crear un índice en una columna de bits. Si lo desea, aún puede crear manualmente un índice en una columna de bits:

CREATE INDEX IX_Users_IsActiveUsername ON Users
(
   IsActive,
   Username
)

Pero SQL Server 2000 en realidad no usará dicho índice, ejecutando una consulta donde el índice sería un candidato perfecto, por ejemplo:

SELECT TOP 1 Username 
FROM Users
WHERE IsActive = 0

En su lugar, SQL Server 2000 realizará una exploración de la tabla, actuando como si el índice ni siquiera existiera. Si cambia la columna a un tinyint, SQL Server 2000 hará una búsqueda de índice. Además, la siguiente consulta no cubierta:

SELECT TOP 1 * 
FROM Users
WHERE IsActive = 0

Realizará una búsqueda de índice, seguida de una búsqueda de marcadores.


SQL Server 2005 tiene soporte limitado para índices en columnas de bits. Por ejemplo:

SELECT TOP 1 Username 
FROM Users
WHERE IsActive = 0

provocará una búsqueda de índice a través del índice de cobertura. Pero el caso no cubierto:

SELECT TOP 1 * 
FROM Users
WHERE IsActive = 0

no provocará una búsqueda de índice seguida de una búsqueda de marcador, realizará un escaneo de tabla (o escaneo de índice agrupado), en lugar de realizar la búsqueda de índice seguida de una búsqueda de marcador.

Verificado por experimentación y observación directa.

Ian Boyd
fuente
FYI - SQL Server 2005 Management Studio le permite hacerlo.
jeremcc
Mi copia de SQL Server 2000 me permitió establecer un índice en una columna de bits.
Kibbee
Mi copia de SQL Server 2000 no me permite establecer un índice en una columna de bits.
Ian Boyd
1

respuesta muy tardía ...

Sí, puede ser útil según el equipo de SQL CAT (actualizado, consolidado)

gbn
fuente
1
El vínculo parece estar muerto ahora. Sin embargo, esa publicación parece haberse consolidado junto con varias otras en un libro electrónico . La sección a la que se hace referencia comienza en la página 86. El libro electrónico se puede descargar de SQLCAT.com eBooks en el enlace "Guía de motor relacional de SQLCAT".
mwolfe02
0

¿Es esta una consulta común? Puede valer la pena cuando busque el "puñado" de registros, pero no le ayudará mucho en las otras filas. ¿Existen otras formas de identificar los datos?

jason saldo
fuente
0

La cardinalidad es un factor, el otro es qué tan bien divide el índice sus datos. Si tiene alrededor de la mitad de los 1 y la mitad de los 0, entonces será de ayuda. (Suponiendo que ese índice es una mejor ruta para elegir que algún otro índice). Sin embargo, ¿con qué frecuencia inserta y actualiza? Agregar índices para el rendimiento de SELECT también perjudica el rendimiento de INSERT, UPDATE y DELETE, así que téngalo en cuenta.

Yo diría que si el 1 al 0 (o viceversa) no es mejor que el 75% al ​​25%, no se moleste.

Anthony Potts
fuente
1
Yo no estaría de acuerdo. Si su distribución es 50/50, nunca usaría el índice, ya que sería más rápido hacer un escaneo de tabla. Sin embargo, si solo tiene 5, 1 valores y 1 millón de valores 0, es muy probable que utilice el índice al buscar 1.
Kibbee
0

medir el tiempo de respuesta antes y después y ver si vale la pena; en teoría, debería mejorar el rendimiento de las consultas que utilizan los campos indexados, pero realmente depende de la distribución de valores verdaderos / falsos y los otros campos involucrados en las consultas que le preocupan

Steven A. Lowe
fuente
0

Ian Boyd tiene razón cuando dice que no podría hacerlo a través de Enterprise Manager para SQL 2000 (vea su nota sobre cómo crearlo a través de T-SQL.

Juan B
fuente
0

Debe ser inteligente aquí para consultar, debe conocer el valor de carga en su columna si la carga de verdadero es mayor en su sistema y desea verificar todos los valores verdaderos, escriba su consulta para verificar que no sea falso ... ayudará mucho , es solo un truco.

Chetan Verma
fuente