2011-05-12 14 views
17

Tengo una tabla enorme, que tiene un número mucho más pequeño (por órdenes de magnitud) de valores distintos en alguna columna x.¿Puedo optimizar SELECT DISTINCT x FROM hugeTable query creando un índice en la columna x?

Necesito hacer una consulta como SELECT DISTINCT x FROM hugeTable, y quiero hacerlo relativamente rápido.

Hice algo así como CREATE INDEX hugeTable_by_x ON hugeTable(x), pero por alguna razón, aunque la salida es pequeña, la ejecución de la consulta no es tan rápida. El plan de consulta muestra que el 97% del tiempo se usa en la exploración de índice de hugeTable_by_x, con un número estimado de filas igual al tamaño de toda la tabla. A esto le siguen, entre otras cosas, una operación Hash Match.

Como creé un índice en la columna x, ¿no puedo esperar que esta consulta se ejecute muy rápido?

Tenga en cuenta que estoy utilizando Microsoft SQL Server 2005.

+1

Hola allí, es la columna que está indexando un campo 'int'? La idea de un idex en una tabla es que el sistema trace dónde se encuentra ese índice en el modelo y luego lo haga más fácil de recuperar. Si este campo no tiene relevancia, aparte de ser solo un valor, realmente no hará mucha diferencia, porque todavía necesita escanear la tabla. –

+0

Si, digamos, hay 1000 filas en 'hugeTable' con' x = 1', entonces 'hugeTable_by_x' todavía debe contener 1000 referencias a esas filas en su nivel de hoja para' x = 1'. Y si esas referencias son amplias (¿cuál es la clave de agrupamiento para 'hugeTable'?), El índice va a ser bastante grande. –

+1

Tenga en cuenta que también probé 'SELECT x FROM hugeTable GROUP BY x', y proporciona exactamente el mismo plan de consulta. – polygenelubricants

Respuesta

0

Posiblemente. Aunque no está garantizado, depende completamente de la consulta.

Sugiero leer este artículo de Gail Shaw (part 1 y part 2).

0

Al hacer un SELECT DISTINCT en un campo indexado, un escaneo de índice tiene sentido, ya que la ejecución aún tiene que escanear cada valor en el índice para toda la tabla (asumiendo que no hay una cláusula WHERE, como parece ser el caso) .

Los índices suelen tener un mayor impacto en WHERE condiciones, JOINS y ORDER BY cláusulas.

+0

Realmente "no tiene que escanear cada valor en el índice". Internamente, podría realizar una serie de búsquedas (por ejemplo, búsquedas binarias) para encontrar cambios sucesivos en el valor. – crokusek

0

Según su descripción del plan de ejecución, creo que es la mejor ejecución posible.

Index Scan lee todo el índice como almacenado (no en orden de índice), HASH MATCH hace lo propio.

Puede haber otras formas de solucionar su problema. En SQL Server, las vistas indizadas vienen a mi mente. Sin embargo, eso podría darte un gran golpe de escritura en esa mesa.

20

Es probable que esto no sea un problema de indexación, sino de diseño de datos. Normalización, para ser precisos. El hecho de que necesite consultar valores distintos de un campo, e incluso de querer agregar un índice, es un fuerte indicador de que el campo debe normalizarse en una tabla separada con una clave de combinación (pequeña). Entonces los valores distintos estarán disponibles de inmediato al escanear la tabla extranjera de búsqueda mucho más pequeña.

Actualización
Como solución alternativa, puede crear un indexed view en un agregado por el campo 'distinct'. COUNT_BIG es un agregado que se permite en las vistas indizadas:

create view vwDistinct 
with schemabinding 
as select x, count_big(*) 
from schema.hugetable 
group by x; 

create clustered index cdxDistinct on vwDistinct(x); 

select x from vwDistinct with (noexpand); 
+0

Si bien lo que dices es definitivamente cierto, el caso del que estás hablando puede ser exactamente POR QUÉ se está haciendo un 'SELECCIONAR DISTINCIÓN', como parte del proceso de normalización. Por ejemplo, tenemos un sistema que trae un feed de datos de un conjunto de archivos FTP descargados. Esta información NO está normalizada en absoluto. Esa es la mitad de la función precisa de nuestro proceso: normalizar los datos a medida que los cargamos en nuestro sistema. Entonces, por ejemplo, tenemos una consulta (pseudocódigo) como 'INSERT INTO NORMALIZEDVALUELIST (NAME) SELECT DISTINCT SOMEFIELD FROM UNNORMALIZEDSOURCE WHERE '. – eidylon

+0

En cuanto a la solución provisional, ¿no costaría la CPU a lo largo del tiempo mantener esta vista indexada a través de las operaciones de CRUD que sea más alta que la de un índice simple en la tabla original? Luego, algunas de las otras soluciones publicadas pueden ofrecer mejores opciones. – crokusek

+0

La vista indizada agrega un costo a las operaciones de escritura. Creo que en realidad podría ser más barato que un índice en la columna, si solo hay unos pocos valores distintos y la tabla es grande. Si nada más, el espacio en disco tomado por la vista indexada es mucho menor que el del índice. Entonces la respuesta es "depende". –

1

Si conoce los valores de antelación y hay un índice en la columna x (o si cada valor es probable que aparezca rápidamente en una exploración siguientes del conjunto tabla), es mucho más rápido para consultar cada uno en particular:

select vals.x 
from [values] as vals (x) 
where exists (select 1 from bigtable where bigtable.x = vals.x); 

Procedimiento usando existe() va a hacer el mayor número de búsquedas de índice, ya que hay valores válidos.

La forma en que lo ha escrito (que es correcto si los valores no se conocen de antemano), el motor de consulta tendrá que leer la tabla completa y hash agregar el desorden para extraer los valores. (Lo que hace que el índice sea inútil.)

5

SQL Server no implementa ninguna función para buscar directamente el siguiente valor distinto en un índice omitiendo los duplicados en el camino.

Si tiene muchos duplicados, entonces puede usar un CTE recursivo para simular esto. La técnica proviene de here. ("Súper rápido DISTINCT utilizando un CTE recursivo"). Por ejemplo:

with recursivecte as (
    select min(t.x) as x 
    from hugetable t 
    union all 
    select ranked.x 
    from (
    select t.x, 
      row_number() over (order by t.x) as rnk 
    from hugetable t 
    join recursivecte r 
     on r.x < t.x 
) ranked 
    where ranked.rnk = 1 
) 
select * 
from recursivecte 
option (maxrecursion 0) 
1

No. Pero hay algunas soluciones (con exclusión de normalización):

Una vez que el índice está en su lugar, entonces es posible implementar en SQL lo que el optimizador podría estar haciendo de forma automática:

https://stackoverflow.com/a/29286754/538763 (múltiples soluciones citada)

Otras respuestas dicen que se puede normalizar lo que resolvería el problema, pero ni una sola vez su normalizada de SQL Server todavía le gusta realizar un análisis para encontrar el máximo() dentro del grupo (s). Soluciones:

https://dba.stackexchange.com/questions/48848/efficiently-query-max-over-multiple-ranges?rq=1

Cuestiones relacionadas